/*- * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <err.h> #include <errno.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static int dorandom = 0; static int nmcastgroups = IP_MAX_MEMBERSHIPS; static int verbose = 0; /* * The test tool exercises IP-level socket options by interrogating the * getsockopt()/setsockopt() APIs. It does not currently test that the * intended semantics of each option are implemented (i.e., that setting IP * options on the socket results in packets with the desired IP options in * it). */ /* * get_socket() is a wrapper function that returns a socket of the specified * type, and created with or without restored root privilege (if running * with a real uid of root and an effective uid of some other user). This * us to test whether the same rights are granted using a socket with a * privileged cached credential vs. a socket with a regular credential. */ #define PRIV_ASIS 0 #define PRIV_GETROOT 1 static int get_socket_unpriv(int type) { return (socket(PF_INET, type, 0)); } static int get_socket_priv(int type) { uid_t olduid; int sock; if (getuid() != 0) errx(-1, "get_sock_priv: running without real uid 0"); olduid = geteuid(); if (seteuid(0) < 0) err(-1, "get_sock_priv: seteuid(0)"); sock = socket(PF_INET, type, 0); if (seteuid(olduid) < 0) err(-1, "get_sock_priv: seteuid(%d)", olduid); return (sock); } static int get_socket(int type, int priv) { if (priv) return (get_socket_priv(type)); else return (get_socket_unpriv(type)); } /* * Exercise the IP_OPTIONS socket option. Confirm the following properties: * * - That there is no initial set of options (length returned is 0). * - That if we set a specific set of options, we can read it back. * - That if we then reset the options, they go away. * * Use a UDP socket for this. */ static void test_ip_options(int sock, const char *socktypename) { u_int32_t new_options, test_options[2]; socklen_t len; /* * Start off by confirming the default IP options on a socket are to * have no options set. */ len = sizeof(test_options); if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) err(-1, "test_ip_options(%s): initial getsockopt()", socktypename); if (len != 0) errx(-1, "test_ip_options(%s): initial getsockopt() returned " "%d bytes", socktypename, len); #define TEST_MAGIC 0xc34e4212 #define NEW_OPTIONS htonl(IPOPT_EOL | (IPOPT_NOP << 8) | (IPOPT_NOP << 16) \ | (IPOPT_NOP << 24)) /* * Write some new options into the socket. */ new_options = NEW_OPTIONS; if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, &new_options, sizeof(new_options)) < 0) err(-1, "test_ip_options(%s): setsockopt(NOP|NOP|NOP|EOL)", socktypename); /* * Store some random cruft in a local variable and retrieve the * options to make sure they set. Note that we pass in an array * of u_int32_t's so that if whatever ended up in the option was * larger than what we put in, we find out about it here. */ test_options[0] = TEST_MAGIC; test_options[1] = TEST_MAGIC; len = sizeof(test_options); if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) err(-1, "test_ip_options(%s): getsockopt() after set", socktypename); /* * Getting the right amount back is important. */ if (len != sizeof(new_options)) errx(-1, "test_ip_options(%s): getsockopt() after set " "returned %d bytes of data", socktypename, len); /* * One possible failure mode is that the call succeeds but neglects to * copy out the data. */ if (test_options[0] == TEST_MAGIC) errx(-1, "test_ip_options(%s): getsockopt() after set didn't " "return data", socktypename); /* * Make sure we get back what we wrote on. */ if (new_options != test_options[0]) errx(-1, "test_ip_options(%s): getsockopt() after set " "returned wrong options (%08x, %08x)", socktypename, new_options, test_options[0]); /* * Now we reset the value to make sure clearing works. */ if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) err(-1, "test_ip_options(%s): setsockopt() to reset", socktypename); /* * Make sure it was really cleared. */ test_options[0] = TEST_MAGIC; test_options[1] = TEST_MAGIC; len = sizeof(test_options); if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) err(-1, "test_ip_options(%s): getsockopt() after reset", socktypename); if (len != 0) errx(-1, "test_ip_options(%s): getsockopt() after reset " "returned %d bytes", socktypename, len); } /* * This test checks the behavior of the IP_HDRINCL socket option, which * allows users with privilege to specify the full header on an IP raw * socket. We test that the option can only be used with raw IP sockets, not * with UDP or TCP sockets. We also confirm that the raw socket is only * available to a privileged user (subject to the UID when called). We * confirm that it defaults to off * * Unlike other tests, doesn't use caller-provided socket. Probably should * be fixed. */ static void test_ip_hdrincl(void) { int flag[2], sock; socklen_t len; /* * Try to receive or set the IP_HDRINCL flag on a TCP socket. */ sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) err(-1, "test_ip_hdrincl(): socket(SOCK_STREAM)"); flag[0] = -1; len = sizeof(flag[0]); if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) err(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINCL)"); if (errno != ENOPROTOOPT) errx(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINC) " "returned %d (%s) not ENOPROTOOPT", errno, strerror(errno)); flag[0] = 1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) == 0) err(-1,"test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " "succeeded\n"); if (errno != ENOPROTOOPT) errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " "returned %d (%s) not ENOPROTOOPT\n", errno, strerror(errno)); close(sock); /* * Try to receive or set the IP_HDRINCL flag on a UDP socket. */ sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock == -1) err(-1, "test_ip_hdrincl(): socket(SOCK_DGRAM"); flag[0] = -1; len = sizeof(flag[0]); if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " "succeeded\n"); if (errno != ENOPROTOOPT) errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " "returned %d (%s) not ENOPROTOOPT\n", errno, strerror(errno)); if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) == 0) err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " "succeeded\n"); if (errno != ENOPROTOOPT) errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " "returned %d (%s) not ENOPROTOOPT\n", errno, strerror(errno)); close(sock); /* * Now try on a raw socket. Access ontrol should prevent non-root * users from creating the raw socket, so check that here based on * geteuid(). If we're non-root, we just return assuming the socket * create fails since the remainder of the tests apply only on a raw * socket. */ sock = socket(PF_INET, SOCK_RAW, 0); if (geteuid() != 0) { if (sock != -1) errx(-1, "test_ip_hdrincl: created raw socket as " "uid %d", geteuid()); return; } if (sock == -1) err(-1, "test_ip_hdrincl(): socket(PF_INET, SOCK_RAW)"); /* * Make sure the initial value of the flag is 0 (disabled). */ flag[0] = -1; flag[1] = -1; len = sizeof(flag); if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on raw " "socket"); if (len != sizeof(flag[0])) errx(-1, "test_ip_hdrincl(): %d bytes returned on " "initial get\n", len); if (flag[0] != 0) errx(-1, "test_ip_hdrincl(): initial flag value of %d\n", flag[0]); /* * Enable the IP_HDRINCL flag. */ flag[0] = 1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) < 0) err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 1)"); /* * Check that the IP_HDRINCL flag was set. */ flag[0] = -1; flag[1] = -1; len = sizeof(flag); if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " "set"); if (flag[0] == 0) errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " "after set had flag of %d\n", flag[0]); #define HISTORICAL_INP_HDRINCL 8 if (flag[0] != HISTORICAL_INP_HDRINCL) warnx("test_ip_hdrincl(): WARNING: getsockopt(IP_H" "DRINCL) after set had non-historical value of %d\n", flag[0]); /* * Reset the IP_HDRINCL flag to 0. */ flag[0] = 0; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) < 0) err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 0)"); /* * Check that the IP_HDRINCL flag was reset to 0. */ flag[0] = -1; flag[1] = -1; len = sizeof(flag); if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " "reset"); if (flag[0] != 0) errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " "after set had flag of %d\n", flag[0]); close(sock); } /* * As with other non-int or larger sized socket options, the IP_TOS and * IP_TTL fields in kernel is stored as an 8-bit value, reflecting the IP * header fields, but useful I/O to the field occurs using 32-bit integers. * The FreeBSD kernel will permit writes from variables at least an int in * size (and ignore additional bytes), and will permit a read to buffers 1 * byte or larger (but depending on endianness, may truncate out useful * values if the caller provides less room). * * Given the limitations of the API, use a UDP socket to confirm that the * following are true: * * - We can read the IP_TOS/IP_TTL options. * - The initial value of the TOS option is 0, TTL is 64. * - That if we provide more than 32 bits of storage, we get back only 32 * bits of data. * - When we set it to a non-zero value expressible with a u_char, we can * read that value back. * - When we reset it back to zero, we can read it as 0. * - When we set it to a value >255, the value is truncated to something less * than 255. */ static void test_ip_uchar(int sock, const char *socktypename, int option, const char *optionname, int initial) { int val[2]; socklen_t len; /* * Check that the initial value is 0, and that the size is one * u_char; */ val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_uchar(%s, %s): initial getsockopt()", socktypename, optionname); if (len != sizeof(val[0])) errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " "returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() didn't " "return data", socktypename, optionname); if (val[0] != initial) errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " "returned value of %d, not %d", socktypename, optionname, val[0], initial); /* * Set the field to a valid value. */ val[0] = 128; val[1] = -1; if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) err(-1, "test_ip_uchar(%s, %s): setsockopt(128)", socktypename, optionname); /* * Check that when we read back the field, we get the same value. */ val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "128", socktypename, optionname); if (len != sizeof(val[0])) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "128 returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "128 didn't return data", socktypename, optionname); if (val[0] != 128) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "128 returned %d", socktypename, optionname, val[0]); /* * Reset the value to 0, check that it was reset. */ val[0] = 0; val[1] = 0; if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) err(-1, "test_ip_uchar(%s, %s): setsockopt() to reset from " "128", socktypename, optionname); if (len != sizeof(val[0])) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " "from 128 returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " "from 128 didn't return data", socktypename, optionname); if (val[0] != 0) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " "from 128 returned %d", socktypename, optionname, val[0]); /* * Set the value to something out of range and check that it comes * back truncated, or that we get EINVAL back. Traditional u_char * IP socket options truncate, but newer ones (such as multicast * socket options) will return EINVAL. */ val[0] = 32000; val[1] = -1; if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) { /* * EINVAL is a fine outcome, no need to run the truncation * tests. */ if (errno == EINVAL) return; err(-1, "test_ip_uchar(%s, %s): getsockopt(32000)", socktypename, optionname); } val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "32000", socktypename, optionname); if (len != sizeof(val[0])) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "32000 returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "32000 didn't return data", socktypename, optionname); if (val[0] == 32000) errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " "32000 returned 32000: failed to truncate", socktypename, optionname); } /* * Generic test for a boolean socket option. Caller provides the option * number, string name, expected default (initial) value, and whether or not * the option is root-only. For each option, test: * * - That we can read the option. * - That the initial value is as expected. * - That we can modify the value. * - That on modification, the new value can be read back. * - That we can reset the value. * - that on reset, the new value can be read back. */ #define BOOLEAN_ANYONE 1 #define BOOLEAN_ROOTONLY 1 static void test_ip_boolean(int sock, const char *socktypename, int option, char *optionname, int initial, int rootonly) { int newvalue, val[2]; socklen_t len; /* * The default for a boolean might be true or false. If it's false, * we will try setting it to true (but using a non-1 value of true). * If it's true, we'll set it to false. */ if (initial == 0) newvalue = 0xff; else newvalue = 0; val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_boolean: initial getsockopt()"); if (len != sizeof(val[0])) errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " "returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " "didn't return data", socktypename, optionname); if (val[0] != initial) errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " "returned %d (expected %d)", socktypename, optionname, val[0], initial); /* * Set the socket option to a new non-default value. */ if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) < 0) err(-1, "test_ip_boolean(%s, %s): setsockopt() to %d", socktypename, optionname, newvalue); /* * Read the value back and see if it is not the default (note: will * not be what we set it to, as we set it to 0xff above). */ val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_boolean(%s, %s): getsockopt() after set to " "%d", socktypename, optionname, newvalue); if (len != sizeof(val[0])) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " "to %d returned %d bytes", socktypename, optionname, newvalue, len); if (val[0] == -1) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " "to %d didn't return data", socktypename, optionname, newvalue); /* * If we set it to true, check for '1', otherwise '0. */ if (val[0] != (newvalue ? 1 : 0)) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " "to %d returned %d", socktypename, optionname, newvalue, val[0]); /* * Reset to initial value. */ newvalue = initial; if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) < 0) err(-1, "test_ip_boolean(%s, %s): setsockopt() to reset", socktypename, optionname); /* * Check reset version. */ val[0] = -1; val[1] = -1; len = sizeof(val); if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) err(-1, "test_ip_boolean(%s, %s): getsockopt() after reset", socktypename, optionname); if (len != sizeof(val[0])) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " "returned %d bytes", socktypename, optionname, len); if (val[0] == -1) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " "didn't return data", socktypename, optionname); if (val[0] != newvalue) errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " "returned %d", socktypename, optionname, newvalue); } /* * Test the IP_ADD_MEMBERSHIP socket option, and the dynamic allocator * for the imo_membership vector which now hangs off struct ip_moptions. * We then call IP_DROP_MEMBERSHIP for each group so joined. */ static void test_ip_multicast_membership(int sock, const char *socktypename) { char addrbuf[16]; struct ip_mreq mreq; uint32_t basegroup; uint16_t i; int sotype; socklen_t sotypelen; sotypelen = sizeof(sotype); if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &sotype, &sotypelen) < 0) err(-1, "test_ip_multicast_membership(%s): so_type getsockopt()", socktypename); /* * Do not perform the test for SOCK_STREAM sockets, as this makes * no sense. */ if (sotype == SOCK_STREAM) return; /* * The 224/8 range is administratively scoped and has special meaning, * therefore it is not used for this test. * If we were not told to be non-deterministic: * Join multicast groups from 238.1.1.0 up to nmcastgroups. * Otherwise, pick a multicast group ID in subnet 238/5 with 11 random * bits in the middle, and join groups in linear order up to nmcastgroups. */ if (dorandom) { /* be non-deterministic (for interactive operation; a fuller test) */ srandomdev(); basegroup = 0xEE000000; /* 238.0.0.0 */ basegroup |= ((random() % ((1 << 11) - 1)) << 16); /* 11 bits */ } else { /* be deterministic (for automated operation) */ basegroup = 0xEE010100; /* 238.1.1.0 */ } /* * Join the multicast group(s) on the default multicast interface; * this usually maps to the interface to which the default * route is pointing. */ for (i = 1; i < nmcastgroups+1; i++) { mreq.imr_multiaddr.s_addr = htonl((basegroup + i)); mreq.imr_interface.s_addr = INADDR_ANY; inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); if (verbose) fprintf(stderr, "IP_ADD_MEMBERSHIP %s INADDR_ANY\n", addrbuf); if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { err(-1, "test_ip_multicast_membership(%d, %s): failed IP_ADD_MEMBERSHIP (%s, %s)", sock, socktypename, addrbuf, "INADDR_ANY"); } } for (i = 1; i < nmcastgroups+1; i++) { mreq.imr_multiaddr.s_addr = htonl((basegroup + i)); mreq.imr_interface.s_addr = INADDR_ANY; inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); if (verbose) fprintf(stderr, "IP_DROP_MEMBERSHIP %s INADDR_ANY\n", addrbuf); if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { err(-1, "test_ip_multicast_membership(%d, %s): failed IP_DROP_MEMBERSHIP (%s, %s)", sock, socktypename, addrbuf, "INADDR_ANY"); } } } /* * XXX: For now, nothing here. */ static void test_ip_multicast_if(int sock, const char *socktypename) { /* * It's probably worth trying INADDR_ANY and INADDR_LOOPBACK here * to see what happens. */ } /* * XXX: For now, nothing here. */ static void test_ip_multicast_vif(int sock, const char *socktypename) { /* * This requires some knowledge of the number of virtual interfaces, * and what is valid. */ } static void testsuite(int priv) { const char *socktypenameset[] = {"SOCK_DGRAM", "SOCK_STREAM", "SOCK_RAW"}; int socktypeset[] = {SOCK_DGRAM, SOCK_STREAM, SOCK_RAW}; const char *socktypename; int i, sock, socktype; test_ip_hdrincl(); for (i = 0; i < sizeof(socktypeset)/sizeof(int); i++) { socktype = socktypeset[i]; socktypename = socktypenameset[i]; /* * If we can't acquire root privilege, we can't open raw * sockets, so don't actually try. */ if (getuid() != 0 && socktype == SOCK_RAW) continue; if (geteuid() != 0 && !priv && socktype == SOCK_RAW) continue; /* * XXXRW: On 5.3, this seems not to work for SOCK_RAW. */ sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_uchar(IP_TOS)", socktypename, priv); test_ip_uchar(sock, socktypename, IP_TOS, "IP_TOS", 0); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s %d) for test_ip_uchar(IP_TTL)", socktypename, priv); test_ip_uchar(sock, socktypename, IP_TTL, "IP_TTL", 64); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_RECVOPTS)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_RECVOPTS, "IP_RECVOPTS", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_RECVRETOPTS)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_RECVRETOPTS, "IP_RECVRETOPTS", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_RECVDSTADDR)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_RECVDSTADDR, "IP_RECVDSTADDR", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_RECVTTL)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_RECVTTL, "IP_RECVTTL", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_RECVIF)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_RECVIF, "IP_RECVIF", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_FAITH)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_FAITH, "IP_FAITH", 0, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_boolean" "(IP_ONESBCAST)", socktypename, priv); test_ip_boolean(sock, socktypename, IP_ONESBCAST, "IP_ONESBCAST", 0, BOOLEAN_ANYONE); close(sock); /* * Test the multicast TTL exactly as we would the regular * TTL, only expect a different default. */ sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for IP_MULTICAST_TTL", socktypename, priv); test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1); close(sock); /* * The multicast loopback flag can be tested using our * boolean tester, but only because the FreeBSD API is a bit * more flexible than earlir APIs and will accept an int as * well as a u_char. Loopback is enabled by default. */ sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for IP_MULTICAST_LOOP", socktypename, priv); test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_options", socktypename, priv); //test_ip_options(sock, socktypename); close(sock); sock = get_socket(socktype, priv); if (sock == -1) err(-1, "get_socket(%s, %d) for test_ip_options", socktypename, priv); test_ip_multicast_membership(sock, socktypename); close(sock); test_ip_multicast_if(0, NULL); test_ip_multicast_vif(0, NULL); /* * XXX: Still need to test: * IP_PORTRANGE * IP_IPSEC_POLICY? */ } } static void usage() { fprintf(stderr, "usage: ipsockopt [-M ngroups] [-r] [-v]\n"); exit(EXIT_FAILURE); } /* * Very simply exercise that we can get and set each option. If we're running * as root, run it also as nobody. If not as root, complain about that. */ int main(int argc, char *argv[]) { int ch; while ((ch = getopt(argc, argv, "M:rv")) != -1) { switch (ch) { case 'M': nmcastgroups = atoi(optarg); break; case 'r': dorandom = 1; /* introduce non-determinism */ break; case 'v': verbose = 1; break; default: usage(); } } printf("1..1\n"); if (geteuid() != 0) { warnx("Not running as root, can't run tests as root"); fprintf(stderr, "\n"); fprintf(stderr, "Running tests with uid %d sock uid %d\n", geteuid(), geteuid()); testsuite(PRIV_ASIS); } else { fprintf(stderr, "Running tests with ruid %d euid %d sock uid 0\n", getuid(), geteuid()); testsuite(PRIV_ASIS); if (seteuid(65534) != 0) err(-1, "seteuid(65534)"); fprintf(stderr, "Running tests with ruid %d euid %d sock uid 65534\n", getuid(), geteuid()); testsuite(PRIV_ASIS); fprintf(stderr, "Running tests with ruid %d euid %d sock uid 0\n", getuid(), geteuid()); testsuite(PRIV_GETROOT); } printf("ok 1 - ipsockopt\n"); exit(0); }