1f1f6501dSRobert Watson /*- 2f1f6501dSRobert Watson * Copyright (c) 2004 Robert N. M. Watson 3f1f6501dSRobert Watson * All rights reserved. 4f1f6501dSRobert Watson * 5f1f6501dSRobert Watson * Redistribution and use in source and binary forms, with or without 6f1f6501dSRobert Watson * modification, are permitted provided that the following conditions 7f1f6501dSRobert Watson * are met: 8f1f6501dSRobert Watson * 1. Redistributions of source code must retain the above copyright 9f1f6501dSRobert Watson * notice, this list of conditions and the following disclaimer. 10f1f6501dSRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 11f1f6501dSRobert Watson * notice, this list of conditions and the following disclaimer in the 12f1f6501dSRobert Watson * documentation and/or other materials provided with the distribution. 13f1f6501dSRobert Watson * 14f1f6501dSRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15f1f6501dSRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16f1f6501dSRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17f1f6501dSRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18f1f6501dSRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19f1f6501dSRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20f1f6501dSRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21f1f6501dSRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22f1f6501dSRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23f1f6501dSRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24f1f6501dSRobert Watson * SUCH DAMAGE. 25f1f6501dSRobert Watson * 26f1f6501dSRobert Watson * $FreeBSD$ 27f1f6501dSRobert Watson */ 28f1f6501dSRobert Watson 29f1f6501dSRobert Watson #include <sys/types.h> 30f1f6501dSRobert Watson #include <sys/socket.h> 31f1f6501dSRobert Watson 32f1f6501dSRobert Watson #include <netinet/in.h> 33f1f6501dSRobert Watson #include <netinet/in_systm.h> 34f1f6501dSRobert Watson #include <netinet/ip.h> 353250f640SBruce M Simpson #include <arpa/inet.h> 36f1f6501dSRobert Watson 37f1f6501dSRobert Watson #include <err.h> 38f1f6501dSRobert Watson #include <errno.h> 393250f640SBruce M Simpson #include <getopt.h> 40f1f6501dSRobert Watson #include <stdio.h> 41f1f6501dSRobert Watson #include <stdlib.h> 42f1f6501dSRobert Watson #include <string.h> 43f1f6501dSRobert Watson #include <unistd.h> 44f1f6501dSRobert Watson 453250f640SBruce M Simpson static int dorandom = 0; 463250f640SBruce M Simpson static int nmcastgroups = IP_MAX_MEMBERSHIPS; 473250f640SBruce M Simpson static int verbose = 0; 483250f640SBruce M Simpson 49f1f6501dSRobert Watson /* 50f1f6501dSRobert Watson * The test tool exercises IP-level socket options by interrogating the 51f1f6501dSRobert Watson * getsockopt()/setsockopt() APIs. It does not currently test that the 52f1f6501dSRobert Watson * intended semantics of each option are implemented (i.e., that setting IP 53f1f6501dSRobert Watson * options on the socket results in packets with the desired IP options in 54f1f6501dSRobert Watson * it). 55f1f6501dSRobert Watson */ 56f1f6501dSRobert Watson 57f1f6501dSRobert Watson /* 58cdeeed7aSRobert Watson * get_socket() is a wrapper function that returns a socket of the specified 59cdeeed7aSRobert Watson * type, and created with or without restored root privilege (if running 60cdeeed7aSRobert Watson * with a real uid of root and an effective uid of some other user). This 61cdeeed7aSRobert Watson * us to test whether the same rights are granted using a socket with a 62cdeeed7aSRobert Watson * privileged cached credential vs. a socket with a regular credential. 63cdeeed7aSRobert Watson */ 64cdeeed7aSRobert Watson #define PRIV_ASIS 0 65cdeeed7aSRobert Watson #define PRIV_GETROOT 1 66cdeeed7aSRobert Watson static int 67cdeeed7aSRobert Watson get_socket_unpriv(int type) 68cdeeed7aSRobert Watson { 69cdeeed7aSRobert Watson 70cdeeed7aSRobert Watson return (socket(PF_INET, type, 0)); 71cdeeed7aSRobert Watson } 72cdeeed7aSRobert Watson 73cdeeed7aSRobert Watson static int 74cdeeed7aSRobert Watson get_socket_priv(int type) 75cdeeed7aSRobert Watson { 76cdeeed7aSRobert Watson uid_t olduid; 77cdeeed7aSRobert Watson int sock; 78cdeeed7aSRobert Watson 79cdeeed7aSRobert Watson if (getuid() != 0) 80cdeeed7aSRobert Watson errx(-1, "get_sock_priv: running without real uid 0"); 81cdeeed7aSRobert Watson 82cdeeed7aSRobert Watson olduid = geteuid(); 83cdeeed7aSRobert Watson if (seteuid(0) < 0) 84cdeeed7aSRobert Watson err(-1, "get_sock_priv: seteuid(0)"); 85cdeeed7aSRobert Watson 86cdeeed7aSRobert Watson sock = socket(PF_INET, type, 0); 87cdeeed7aSRobert Watson 88cdeeed7aSRobert Watson if (seteuid(olduid) < 0) 89cdeeed7aSRobert Watson err(-1, "get_sock_priv: seteuid(%d)", olduid); 90cdeeed7aSRobert Watson 91cdeeed7aSRobert Watson return (sock); 92cdeeed7aSRobert Watson } 93cdeeed7aSRobert Watson 94cdeeed7aSRobert Watson static int 95cdeeed7aSRobert Watson get_socket(int type, int priv) 96cdeeed7aSRobert Watson { 97cdeeed7aSRobert Watson 98cdeeed7aSRobert Watson if (priv) 99cdeeed7aSRobert Watson return (get_socket_priv(type)); 100cdeeed7aSRobert Watson else 101cdeeed7aSRobert Watson return (get_socket_unpriv(type)); 102cdeeed7aSRobert Watson } 103cdeeed7aSRobert Watson 104cdeeed7aSRobert Watson /* 105f1f6501dSRobert Watson * Exercise the IP_OPTIONS socket option. Confirm the following properties: 106f1f6501dSRobert Watson * 107f1f6501dSRobert Watson * - That there is no initial set of options (length returned is 0). 108f1f6501dSRobert Watson * - That if we set a specific set of options, we can read it back. 109f1f6501dSRobert Watson * - That if we then reset the options, they go away. 110f1f6501dSRobert Watson * 111f1f6501dSRobert Watson * Use a UDP socket for this. 112f1f6501dSRobert Watson */ 113f1f6501dSRobert Watson static void 114cdeeed7aSRobert Watson test_ip_options(int sock, const char *socktypename) 115f1f6501dSRobert Watson { 116f1f6501dSRobert Watson u_int32_t new_options, test_options[2]; 117f1f6501dSRobert Watson socklen_t len; 118f1f6501dSRobert Watson 119f1f6501dSRobert Watson /* 120f1f6501dSRobert Watson * Start off by confirming the default IP options on a socket are to 121f1f6501dSRobert Watson * have no options set. 122f1f6501dSRobert Watson */ 123f1f6501dSRobert Watson len = sizeof(test_options); 124f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 125cdeeed7aSRobert Watson err(-1, "test_ip_options(%s): initial getsockopt()", 126cdeeed7aSRobert Watson socktypename); 127f1f6501dSRobert Watson 128f1f6501dSRobert Watson if (len != 0) 129cdeeed7aSRobert Watson errx(-1, "test_ip_options(%s): initial getsockopt() returned " 130cdeeed7aSRobert Watson "%d bytes", socktypename, len); 131f1f6501dSRobert Watson 132f1f6501dSRobert Watson #define TEST_MAGIC 0xc34e4212 133f1f6501dSRobert Watson #define NEW_OPTIONS htonl(IPOPT_EOL | (IPOPT_NOP << 8) | (IPOPT_NOP << 16) \ 134f1f6501dSRobert Watson | (IPOPT_NOP << 24)) 135f1f6501dSRobert Watson 136f1f6501dSRobert Watson /* 137f1f6501dSRobert Watson * Write some new options into the socket. 138f1f6501dSRobert Watson */ 139f1f6501dSRobert Watson new_options = NEW_OPTIONS; 140f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, &new_options, 141f1f6501dSRobert Watson sizeof(new_options)) < 0) 142cdeeed7aSRobert Watson err(-1, "test_ip_options(%s): setsockopt(NOP|NOP|NOP|EOL)", 143cdeeed7aSRobert Watson socktypename); 144f1f6501dSRobert Watson 145f1f6501dSRobert Watson /* 146f1f6501dSRobert Watson * Store some random cruft in a local variable and retrieve the 147f1f6501dSRobert Watson * options to make sure they set. Note that we pass in an array 148f1f6501dSRobert Watson * of u_int32_t's so that if whatever ended up in the option was 149f1f6501dSRobert Watson * larger than what we put in, we find out about it here. 150f1f6501dSRobert Watson */ 151f1f6501dSRobert Watson test_options[0] = TEST_MAGIC; 152f1f6501dSRobert Watson test_options[1] = TEST_MAGIC; 153f1f6501dSRobert Watson len = sizeof(test_options); 154f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 155cdeeed7aSRobert Watson err(-1, "test_ip_options(%s): getsockopt() after set", 156cdeeed7aSRobert Watson socktypename); 157f1f6501dSRobert Watson 158f1f6501dSRobert Watson /* 159f1f6501dSRobert Watson * Getting the right amount back is important. 160f1f6501dSRobert Watson */ 161f1f6501dSRobert Watson if (len != sizeof(new_options)) 162cdeeed7aSRobert Watson errx(-1, "test_ip_options(%s): getsockopt() after set " 163cdeeed7aSRobert Watson "returned %d bytes of data", socktypename, len); 164f1f6501dSRobert Watson 165f1f6501dSRobert Watson /* 166f1f6501dSRobert Watson * One posible failure mode is that the call succeeds but neglects to 167f1f6501dSRobert Watson * copy out the data. 168f1f6501dSRobert Watson */ 169f1f6501dSRobert Watson if (test_options[0] == TEST_MAGIC) 170cdeeed7aSRobert Watson errx(-1, "test_ip_options(%s): getsockopt() after set didn't " 171cdeeed7aSRobert Watson "return data", socktypename); 172f1f6501dSRobert Watson 173f1f6501dSRobert Watson /* 174f1f6501dSRobert Watson * Make sure we get back what we wrote on. 175f1f6501dSRobert Watson */ 176f1f6501dSRobert Watson if (new_options != test_options[0]) 177cdeeed7aSRobert Watson errx(-1, "test_ip_options(%s): getsockopt() after set " 178cdeeed7aSRobert Watson "returned wrong options (%08x, %08x)", socktypename, 179cdeeed7aSRobert Watson new_options, test_options[0]); 180f1f6501dSRobert Watson 181f1f6501dSRobert Watson /* 182f1f6501dSRobert Watson * Now we reset the value to make sure clearing works. 183f1f6501dSRobert Watson */ 184f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) 185cdeeed7aSRobert Watson err(-1, "test_ip_options(%s): setsockopt() to reset", 186cdeeed7aSRobert Watson socktypename); 187f1f6501dSRobert Watson 188f1f6501dSRobert Watson /* 189f1f6501dSRobert Watson * Make sure it was really cleared. 190f1f6501dSRobert Watson */ 191f1f6501dSRobert Watson test_options[0] = TEST_MAGIC; 192f1f6501dSRobert Watson test_options[1] = TEST_MAGIC; 193f1f6501dSRobert Watson len = sizeof(test_options); 194f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 195cdeeed7aSRobert Watson err(-1, "test_ip_options(%s): getsockopt() after reset", 196cdeeed7aSRobert Watson socktypename); 197f1f6501dSRobert Watson 198f1f6501dSRobert Watson if (len != 0) 199cdeeed7aSRobert Watson errx(-1, "test_ip_options(%s): getsockopt() after reset " 200cdeeed7aSRobert Watson "returned %d bytes", socktypename, len); 201f1f6501dSRobert Watson } 202f1f6501dSRobert Watson 203f1f6501dSRobert Watson /* 204f1f6501dSRobert Watson * This test checks the behavior of the IP_HDRINCL socket option, which 205f1f6501dSRobert Watson * allows users with privilege to specify the full header on an IP raw 206f1f6501dSRobert Watson * socket. We test that the option can only be used with raw IP sockets, not 207f1f6501dSRobert Watson * with UDP or TCP sockets. We also confirm that the raw socket is only 208f1f6501dSRobert Watson * available to a privileged user (subject to the UID when called). We 209f1f6501dSRobert Watson * confirm that it defaults to off 210cdeeed7aSRobert Watson * 211cdeeed7aSRobert Watson * Unlike other tests, doesn't use caller-provided socket. Probably should 212cdeeed7aSRobert Watson * be fixed. 213f1f6501dSRobert Watson */ 214f1f6501dSRobert Watson static void 215f1f6501dSRobert Watson test_ip_hdrincl(void) 216f1f6501dSRobert Watson { 217f1f6501dSRobert Watson int flag[2], sock; 218f1f6501dSRobert Watson socklen_t len; 219f1f6501dSRobert Watson 220f1f6501dSRobert Watson /* 221f1f6501dSRobert Watson * Try to receive or set the IP_HDRINCL flag on a TCP socket. 222f1f6501dSRobert Watson */ 223f1f6501dSRobert Watson sock = socket(PF_INET, SOCK_STREAM, 0); 224cdeeed7aSRobert Watson if (sock == -1) 225cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): socket(SOCK_STREAM)"); 226f1f6501dSRobert Watson 227f1f6501dSRobert Watson flag[0] = -1; 228f1f6501dSRobert Watson len = sizeof(flag[0]); 229cdeeed7aSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) 230cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINCL)"); 231f1f6501dSRobert Watson 232cdeeed7aSRobert Watson if (errno != ENOPROTOOPT) 233cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINC) " 234cdeeed7aSRobert Watson "returned %d (%s) not ENOPROTOOPT", errno, 235cdeeed7aSRobert Watson strerror(errno)); 236f1f6501dSRobert Watson 237f1f6501dSRobert Watson flag[0] = 1; 238f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 239cdeeed7aSRobert Watson == 0) 240cdeeed7aSRobert Watson err(-1,"test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " 241cdeeed7aSRobert Watson "succeeded\n"); 242f1f6501dSRobert Watson 243cdeeed7aSRobert Watson if (errno != ENOPROTOOPT) 244cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " 245cdeeed7aSRobert Watson "returned %d (%s) not ENOPROTOOPT\n", errno, 246cdeeed7aSRobert Watson strerror(errno)); 247f1f6501dSRobert Watson 248f1f6501dSRobert Watson close(sock); 249f1f6501dSRobert Watson 250f1f6501dSRobert Watson /* 251f1f6501dSRobert Watson * Try to receive or set the IP_HDRINCL flag on a UDP socket. 252f1f6501dSRobert Watson */ 253f1f6501dSRobert Watson sock = socket(PF_INET, SOCK_DGRAM, 0); 254cdeeed7aSRobert Watson if (sock == -1) 255cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): socket(SOCK_DGRAM"); 256f1f6501dSRobert Watson 257f1f6501dSRobert Watson flag[0] = -1; 258f1f6501dSRobert Watson len = sizeof(flag[0]); 259cdeeed7aSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) 260cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " 261cdeeed7aSRobert Watson "succeeded\n"); 262f1f6501dSRobert Watson 263cdeeed7aSRobert Watson if (errno != ENOPROTOOPT) 264cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " 265cdeeed7aSRobert Watson "returned %d (%s) not ENOPROTOOPT\n", errno, 266cdeeed7aSRobert Watson strerror(errno)); 267f1f6501dSRobert Watson 268f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 269cdeeed7aSRobert Watson == 0) 270cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " 271cdeeed7aSRobert Watson "succeeded\n"); 272f1f6501dSRobert Watson 273cdeeed7aSRobert Watson if (errno != ENOPROTOOPT) 274cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " 275cdeeed7aSRobert Watson "returned %d (%s) not ENOPROTOOPT\n", errno, 276cdeeed7aSRobert Watson strerror(errno)); 277f1f6501dSRobert Watson 278f1f6501dSRobert Watson close(sock); 279f1f6501dSRobert Watson 280f1f6501dSRobert Watson /* 281f1f6501dSRobert Watson * Now try on a raw socket. Access ontrol should prevent non-root 282f1f6501dSRobert Watson * users from creating the raw socket, so check that here based on 283f1f6501dSRobert Watson * geteuid(). If we're non-root, we just return assuming the socket 284f1f6501dSRobert Watson * create fails since the remainder of the tests apply only on a raw 285f1f6501dSRobert Watson * socket. 286f1f6501dSRobert Watson */ 287f1f6501dSRobert Watson sock = socket(PF_INET, SOCK_RAW, 0); 288f1f6501dSRobert Watson if (geteuid() != 0) { 289f1f6501dSRobert Watson if (sock != -1) 290f1f6501dSRobert Watson errx(-1, "test_ip_hdrincl: created raw socket as " 291f1f6501dSRobert Watson "uid %d", geteuid()); 292f1f6501dSRobert Watson return; 293f1f6501dSRobert Watson } 294cdeeed7aSRobert Watson if (sock == -1) 295cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): socket(PF_INET, SOCK_RAW)"); 296f1f6501dSRobert Watson 297f1f6501dSRobert Watson /* 298f1f6501dSRobert Watson * Make sure the initial value of the flag is 0 (disabled). 299f1f6501dSRobert Watson */ 300f1f6501dSRobert Watson flag[0] = -1; 301f1f6501dSRobert Watson flag[1] = -1; 302f1f6501dSRobert Watson len = sizeof(flag); 303cdeeed7aSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 304cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on raw " 305cdeeed7aSRobert Watson "socket"); 306f1f6501dSRobert Watson 307cdeeed7aSRobert Watson if (len != sizeof(flag[0])) 308cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): %d bytes returned on " 309f1f6501dSRobert Watson "initial get\n", len); 310f1f6501dSRobert Watson 311cdeeed7aSRobert Watson if (flag[0] != 0) 312cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): initial flag value of %d\n", 313f1f6501dSRobert Watson flag[0]); 314f1f6501dSRobert Watson 315f1f6501dSRobert Watson /* 316f1f6501dSRobert Watson * Enable the IP_HDRINCL flag. 317f1f6501dSRobert Watson */ 318f1f6501dSRobert Watson flag[0] = 1; 319f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 320cdeeed7aSRobert Watson < 0) 321cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 1)"); 322f1f6501dSRobert Watson 323f1f6501dSRobert Watson /* 324f1f6501dSRobert Watson * Check that the IP_HDRINCL flag was set. 325f1f6501dSRobert Watson */ 326f1f6501dSRobert Watson flag[0] = -1; 327f1f6501dSRobert Watson flag[1] = -1; 328f1f6501dSRobert Watson len = sizeof(flag); 329cdeeed7aSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 330cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " 331cdeeed7aSRobert Watson "set"); 332f1f6501dSRobert Watson 333cdeeed7aSRobert Watson if (flag[0] == 0) 334cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " 335f1f6501dSRobert Watson "after set had flag of %d\n", flag[0]); 336f1f6501dSRobert Watson 337f1f6501dSRobert Watson #define HISTORICAL_INP_HDRINCL 8 338cdeeed7aSRobert Watson if (flag[0] != HISTORICAL_INP_HDRINCL) 339cdeeed7aSRobert Watson warnx("test_ip_hdrincl(): WARNING: getsockopt(IP_H" 340f1f6501dSRobert Watson "DRINCL) after set had non-historical value of %d\n", 341f1f6501dSRobert Watson flag[0]); 342f1f6501dSRobert Watson 343f1f6501dSRobert Watson /* 344f1f6501dSRobert Watson * Reset the IP_HDRINCL flag to 0. 345f1f6501dSRobert Watson */ 346f1f6501dSRobert Watson flag[0] = 0; 347f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 348cdeeed7aSRobert Watson < 0) 349cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 0)"); 350f1f6501dSRobert Watson 351f1f6501dSRobert Watson /* 352f1f6501dSRobert Watson * Check that the IP_HDRINCL flag was reset to 0. 353f1f6501dSRobert Watson */ 354f1f6501dSRobert Watson flag[0] = -1; 355f1f6501dSRobert Watson flag[1] = -1; 356f1f6501dSRobert Watson len = sizeof(flag); 357cdeeed7aSRobert Watson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 358cdeeed7aSRobert Watson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " 359cdeeed7aSRobert Watson "reset"); 360f1f6501dSRobert Watson 361cdeeed7aSRobert Watson if (flag[0] != 0) 362cdeeed7aSRobert Watson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " 363f1f6501dSRobert Watson "after set had flag of %d\n", flag[0]); 364f1f6501dSRobert Watson 365f1f6501dSRobert Watson close(sock); 366f1f6501dSRobert Watson } 367f1f6501dSRobert Watson 368f1f6501dSRobert Watson /* 369f1f6501dSRobert Watson * As with other non-int or larger sized socket options, the IP_TOS and 370f1f6501dSRobert Watson * IP_TTL fields in kernel is stored as an 8-bit value, reflecting the IP 371f1f6501dSRobert Watson * header fields, but useful I/O to the field occurs using 32-bit integers. 372f1f6501dSRobert Watson * The FreeBSD kernel will permit writes from variables at least an int in 373f1f6501dSRobert Watson * size (and ignore additional bytes), and will permit a read to buffers 1 374f1f6501dSRobert Watson * byte or larger (but depending on endianness, may truncate out useful 375f1f6501dSRobert Watson * values if the caller provides less room). 376f1f6501dSRobert Watson * 377f1f6501dSRobert Watson * Given the limitations of the API, use a UDP socket to confirm that the 378f1f6501dSRobert Watson * following are true: 379f1f6501dSRobert Watson * 380f1f6501dSRobert Watson * - We can read the IP_TOS/IP_TTL options. 381f1f6501dSRobert Watson * - The initial value of the TOS option is 0, TTL is 64. 382f1f6501dSRobert Watson * - That if we provide more than 32 bits of storage, we get back only 32 383f1f6501dSRobert Watson * bits of data. 384f1f6501dSRobert Watson * - When we set it to a non-zero value expressible with a u_char, we can 385f1f6501dSRobert Watson * read that value back. 386f1f6501dSRobert Watson * - When we reset it back to zero, we can read it as 0. 387f1f6501dSRobert Watson * - When we set it to a value >255, the value is truncated to something less 388f1f6501dSRobert Watson * than 255. 389f1f6501dSRobert Watson */ 390f1f6501dSRobert Watson static void 391cdeeed7aSRobert Watson test_ip_uchar(int sock, const char *socktypename, int option, 392cdeeed7aSRobert Watson const char *optionname, int initial) 393f1f6501dSRobert Watson { 394cdeeed7aSRobert Watson int val[2]; 395f1f6501dSRobert Watson socklen_t len; 396f1f6501dSRobert Watson 397f1f6501dSRobert Watson /* 398f1f6501dSRobert Watson * Check that the initial value is 0, and that the size is one 399f1f6501dSRobert Watson * u_char; 400f1f6501dSRobert Watson */ 401f1f6501dSRobert Watson val[0] = -1; 402f1f6501dSRobert Watson val[1] = -1; 403f1f6501dSRobert Watson len = sizeof(val); 404f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 405cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): initial getsockopt()", 406cdeeed7aSRobert Watson socktypename, optionname); 407f1f6501dSRobert Watson 408f1f6501dSRobert Watson if (len != sizeof(val[0])) 409cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " 410cdeeed7aSRobert Watson "returned %d bytes", socktypename, optionname, len); 411f1f6501dSRobert Watson 412f1f6501dSRobert Watson if (val[0] == -1) 413cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() didn't " 414cdeeed7aSRobert Watson "return data", socktypename, optionname); 415f1f6501dSRobert Watson 416f1f6501dSRobert Watson if (val[0] != initial) 417cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " 418cdeeed7aSRobert Watson "returned value of %d, not %d", socktypename, optionname, 419cdeeed7aSRobert Watson val[0], initial); 420f1f6501dSRobert Watson 421f1f6501dSRobert Watson /* 422f1f6501dSRobert Watson * Set the field to a valid value. 423f1f6501dSRobert Watson */ 424f1f6501dSRobert Watson val[0] = 128; 425f1f6501dSRobert Watson val[1] = -1; 426f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) 427cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): setsockopt(128)", 428cdeeed7aSRobert Watson socktypename, optionname); 429f1f6501dSRobert Watson 430f1f6501dSRobert Watson /* 431f1f6501dSRobert Watson * Check that when we read back the field, we get the same value. 432f1f6501dSRobert Watson */ 433f1f6501dSRobert Watson val[0] = -1; 434f1f6501dSRobert Watson val[1] = -1; 435f1f6501dSRobert Watson len = sizeof(val); 436f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 437cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 438cdeeed7aSRobert Watson "128", socktypename, optionname); 439f1f6501dSRobert Watson 440f1f6501dSRobert Watson if (len != sizeof(val[0])) 441cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 442cdeeed7aSRobert Watson "128 returned %d bytes", socktypename, optionname, len); 443f1f6501dSRobert Watson 444f1f6501dSRobert Watson if (val[0] == -1) 445cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 446cdeeed7aSRobert Watson "128 didn't return data", socktypename, optionname); 447f1f6501dSRobert Watson 448f1f6501dSRobert Watson if (val[0] != 128) 449cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 450cdeeed7aSRobert Watson "128 returned %d", socktypename, optionname, val[0]); 451f1f6501dSRobert Watson 452f1f6501dSRobert Watson /* 453f1f6501dSRobert Watson * Reset the value to 0, check that it was reset. 454f1f6501dSRobert Watson */ 455f1f6501dSRobert Watson val[0] = 0; 456f1f6501dSRobert Watson val[1] = 0; 457f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) 458cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): setsockopt() to reset from " 459cdeeed7aSRobert Watson "128", socktypename, optionname); 460f1f6501dSRobert Watson 461f1f6501dSRobert Watson if (len != sizeof(val[0])) 462cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 463cdeeed7aSRobert Watson "from 128 returned %d bytes", socktypename, optionname, 464cdeeed7aSRobert Watson len); 465f1f6501dSRobert Watson 466f1f6501dSRobert Watson if (val[0] == -1) 467cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 468cdeeed7aSRobert Watson "from 128 didn't return data", socktypename, optionname); 469f1f6501dSRobert Watson 470f1f6501dSRobert Watson if (val[0] != 0) 471cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 472cdeeed7aSRobert Watson "from 128 returned %d", socktypename, optionname, 473cdeeed7aSRobert Watson val[0]); 474f1f6501dSRobert Watson 475f1f6501dSRobert Watson /* 476f1f6501dSRobert Watson * Set the value to something out of range and check that it comes 477f1f6501dSRobert Watson * back truncated, or that we get EINVAL back. Traditional u_char 478f1f6501dSRobert Watson * IP socket options truncate, but newer ones (such as multicast 479f1f6501dSRobert Watson * socket options) will return EINVAL. 480f1f6501dSRobert Watson */ 481f1f6501dSRobert Watson val[0] = 32000; 482f1f6501dSRobert Watson val[1] = -1; 483f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) { 484f1f6501dSRobert Watson /* 485f1f6501dSRobert Watson * EINVAL is a fine outcome, no need to run the truncation 486f1f6501dSRobert Watson * tests. 487f1f6501dSRobert Watson */ 488cdeeed7aSRobert Watson if (errno == EINVAL) 489f1f6501dSRobert Watson return; 490cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): getsockopt(32000)", 491cdeeed7aSRobert Watson socktypename, optionname); 492f1f6501dSRobert Watson } 493f1f6501dSRobert Watson 494f1f6501dSRobert Watson val[0] = -1; 495f1f6501dSRobert Watson val[1] = -1; 496f1f6501dSRobert Watson len = sizeof(val); 497f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 498cdeeed7aSRobert Watson err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 499cdeeed7aSRobert Watson "32000", socktypename, optionname); 500f1f6501dSRobert Watson 501f1f6501dSRobert Watson if (len != sizeof(val[0])) 502cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 503cdeeed7aSRobert Watson "32000 returned %d bytes", socktypename, optionname, 504cdeeed7aSRobert Watson len); 505f1f6501dSRobert Watson 506f1f6501dSRobert Watson if (val[0] == -1) 507cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 508cdeeed7aSRobert Watson "32000 didn't return data", socktypename, optionname); 509f1f6501dSRobert Watson 510f1f6501dSRobert Watson if (val[0] == 32000) 511cdeeed7aSRobert Watson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 512cdeeed7aSRobert Watson "32000 returned 32000: failed to truncate", socktypename, 513cdeeed7aSRobert Watson optionname); 514f1f6501dSRobert Watson } 515f1f6501dSRobert Watson 516f1f6501dSRobert Watson /* 517f1f6501dSRobert Watson * Generic test for a boolean socket option. Caller provides the option 518f1f6501dSRobert Watson * number, string name, expected default (initial) value, and whether or not 519f1f6501dSRobert Watson * the option is root-only. For each option, test: 520f1f6501dSRobert Watson * 521f1f6501dSRobert Watson * - That we can read the option. 522f1f6501dSRobert Watson * - That the initial value is as expected. 523f1f6501dSRobert Watson * - That we can modify the value. 524f1f6501dSRobert Watson * - That on modification, the new value can be read back. 525f1f6501dSRobert Watson * - That we can reset the value. 526f1f6501dSRobert Watson * - that on reset, the new value can be read back. 527f1f6501dSRobert Watson */ 528f1f6501dSRobert Watson #define BOOLEAN_ANYONE 1 529f1f6501dSRobert Watson #define BOOLEAN_ROOTONLY 1 530f1f6501dSRobert Watson static void 531cdeeed7aSRobert Watson test_ip_boolean(int sock, const char *socktypename, int option, 532cdeeed7aSRobert Watson char *optionname, int initial, int rootonly) 533f1f6501dSRobert Watson { 534cdeeed7aSRobert Watson int newvalue, val[2]; 535f1f6501dSRobert Watson socklen_t len; 536f1f6501dSRobert Watson 537f1f6501dSRobert Watson /* 538f1f6501dSRobert Watson * The default for a boolean might be true or false. If it's false, 539f1f6501dSRobert Watson * we will try setting it to true (but using a non-1 value of true). 540f1f6501dSRobert Watson * If it's true, we'll set it to false. 541f1f6501dSRobert Watson */ 542f1f6501dSRobert Watson if (initial == 0) 543f1f6501dSRobert Watson newvalue = 0xff; 544f1f6501dSRobert Watson else 545f1f6501dSRobert Watson newvalue = 0; 546f1f6501dSRobert Watson 547f1f6501dSRobert Watson val[0] = -1; 548f1f6501dSRobert Watson val[1] = -1; 549f1f6501dSRobert Watson len = sizeof(val); 550f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 551f1f6501dSRobert Watson err(-1, "test_ip_boolean: initial getsockopt()"); 552f1f6501dSRobert Watson 553f1f6501dSRobert Watson if (len != sizeof(val[0])) 554cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 555cdeeed7aSRobert Watson "returned %d bytes", socktypename, optionname, len); 556f1f6501dSRobert Watson 557f1f6501dSRobert Watson if (val[0] == -1) 558cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 559cdeeed7aSRobert Watson "didn't return data", socktypename, optionname); 560f1f6501dSRobert Watson 561f1f6501dSRobert Watson if (val[0] != initial) 562cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 563cdeeed7aSRobert Watson "returned %d (expected %d)", socktypename, optionname, 564cdeeed7aSRobert Watson val[0], initial); 565f1f6501dSRobert Watson 566f1f6501dSRobert Watson /* 567f1f6501dSRobert Watson * Set the socket option to a new non-default value. 568f1f6501dSRobert Watson */ 569f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) 570f1f6501dSRobert Watson < 0) 571cdeeed7aSRobert Watson err(-1, "test_ip_boolean(%s, %s): setsockopt() to %d", 572cdeeed7aSRobert Watson socktypename, optionname, newvalue); 573f1f6501dSRobert Watson 574f1f6501dSRobert Watson /* 575f1f6501dSRobert Watson * Read the value back and see if it is not the default (note: will 576f1f6501dSRobert Watson * not be what we set it to, as we set it to 0xff above). 577f1f6501dSRobert Watson */ 578f1f6501dSRobert Watson val[0] = -1; 579f1f6501dSRobert Watson val[1] = -1; 580f1f6501dSRobert Watson len = sizeof(val); 581f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 582cdeeed7aSRobert Watson err(-1, "test_ip_boolean(%s, %s): getsockopt() after set to " 583cdeeed7aSRobert Watson "%d", socktypename, optionname, newvalue); 584f1f6501dSRobert Watson 585f1f6501dSRobert Watson if (len != sizeof(val[0])) 586cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 587cdeeed7aSRobert Watson "to %d returned %d bytes", socktypename, optionname, 588cdeeed7aSRobert Watson newvalue, len); 589f1f6501dSRobert Watson 590f1f6501dSRobert Watson if (val[0] == -1) 591cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 592cdeeed7aSRobert Watson "to %d didn't return data", socktypename, optionname, 593cdeeed7aSRobert Watson newvalue); 594f1f6501dSRobert Watson 595f1f6501dSRobert Watson /* 596f1f6501dSRobert Watson * If we set it to true, check for '1', otherwise '0. 597f1f6501dSRobert Watson */ 598f1f6501dSRobert Watson if (val[0] != (newvalue ? 1 : 0)) 599cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 600cdeeed7aSRobert Watson "to %d returned %d", socktypename, optionname, newvalue, 601cdeeed7aSRobert Watson val[0]); 602f1f6501dSRobert Watson 603f1f6501dSRobert Watson /* 604f1f6501dSRobert Watson * Reset to initial value. 605f1f6501dSRobert Watson */ 606f1f6501dSRobert Watson newvalue = initial; 607f1f6501dSRobert Watson if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) 608f1f6501dSRobert Watson < 0) 609cdeeed7aSRobert Watson err(-1, "test_ip_boolean(%s, %s): setsockopt() to reset", 610cdeeed7aSRobert Watson socktypename, optionname); 611f1f6501dSRobert Watson 612f1f6501dSRobert Watson /* 613f1f6501dSRobert Watson * Check reset version. 614f1f6501dSRobert Watson */ 615f1f6501dSRobert Watson val[0] = -1; 616f1f6501dSRobert Watson val[1] = -1; 617f1f6501dSRobert Watson len = sizeof(val); 618f1f6501dSRobert Watson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 619cdeeed7aSRobert Watson err(-1, "test_ip_boolean(%s, %s): getsockopt() after reset", 620cdeeed7aSRobert Watson socktypename, optionname); 621f1f6501dSRobert Watson 622f1f6501dSRobert Watson if (len != sizeof(val[0])) 623cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 624cdeeed7aSRobert Watson "returned %d bytes", socktypename, optionname, len); 625f1f6501dSRobert Watson 626f1f6501dSRobert Watson if (val[0] == -1) 627cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 628cdeeed7aSRobert Watson "didn't return data", socktypename, optionname); 629f1f6501dSRobert Watson 630f1f6501dSRobert Watson if (val[0] != newvalue) 631cdeeed7aSRobert Watson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 632cdeeed7aSRobert Watson "returned %d", socktypename, optionname, newvalue); 633f1f6501dSRobert Watson } 634f1f6501dSRobert Watson 635f1f6501dSRobert Watson /* 6369b5b26f6SBruce M Simpson * Test the IP_ADD_MEMBERSHIP socket option, and the dynamic allocator 6379b5b26f6SBruce M Simpson * for the imo_membership vector which now hangs off struct ip_moptions. 6389b5b26f6SBruce M Simpson * We then call IP_DROP_MEMBERSHIP for each group so joined. 6399b5b26f6SBruce M Simpson */ 6409b5b26f6SBruce M Simpson static void 6419b5b26f6SBruce M Simpson test_ip_multicast_membership(int sock, const char *socktypename) 6429b5b26f6SBruce M Simpson { 6433250f640SBruce M Simpson char addrbuf[16]; 6449b5b26f6SBruce M Simpson struct ip_mreq mreq; 6459b5b26f6SBruce M Simpson uint32_t basegroup; 6469b5b26f6SBruce M Simpson uint16_t i; 6479b5b26f6SBruce M Simpson int sotype; 6489b5b26f6SBruce M Simpson socklen_t sotypelen; 6499b5b26f6SBruce M Simpson 6509b5b26f6SBruce M Simpson sotypelen = sizeof(sotype); 6519b5b26f6SBruce M Simpson if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &sotype, &sotypelen) < 0) 6529b5b26f6SBruce M Simpson err(-1, "test_ip_multicast_membership(%s): so_type getsockopt()", 6539b5b26f6SBruce M Simpson socktypename); 6549b5b26f6SBruce M Simpson /* 6559b5b26f6SBruce M Simpson * Do not perform the test for SOCK_STREAM sockets, as this makes 6569b5b26f6SBruce M Simpson * no sense. 6579b5b26f6SBruce M Simpson */ 6589b5b26f6SBruce M Simpson if (sotype == SOCK_STREAM) 6599b5b26f6SBruce M Simpson return; 6609b5b26f6SBruce M Simpson /* 6613250f640SBruce M Simpson * The 224/8 range is administratively scoped and has special meaning, 6623250f640SBruce M Simpson * therefore it is not used for this test. 6633250f640SBruce M Simpson * If we were not told to be non-deterministic: 6643250f640SBruce M Simpson * Join multicast groups from 238.1.1.0 up to nmcastgroups. 6653250f640SBruce M Simpson * Otherwise, pick a multicast group ID in subnet 238/5 with 11 random 6663250f640SBruce M Simpson * bits in the middle, and join groups in linear order up to nmcastgroups. 6679b5b26f6SBruce M Simpson */ 6683250f640SBruce M Simpson if (dorandom) { 6693250f640SBruce M Simpson /* be non-deterministic (for interactive operation; a fuller test) */ 6703250f640SBruce M Simpson srandomdev(); 6713250f640SBruce M Simpson basegroup = 0xEE000000; /* 238.0.0.0 */ 6723250f640SBruce M Simpson basegroup |= ((random() % ((1 << 11) - 1)) << 16); /* 11 bits */ 6733250f640SBruce M Simpson } else { 6743250f640SBruce M Simpson /* be deterministic (for automated operation) */ 6753250f640SBruce M Simpson basegroup = 0xEE010100; /* 238.1.1.0 */ 6763250f640SBruce M Simpson } 6779b5b26f6SBruce M Simpson /* 6789b5b26f6SBruce M Simpson * Join the multicast group(s) on the default multicast interface; 6799b5b26f6SBruce M Simpson * this usually maps to the interface to which the default 6809b5b26f6SBruce M Simpson * route is pointing. 6819b5b26f6SBruce M Simpson */ 6823250f640SBruce M Simpson for (i = 0; i < nmcastgroups; i++) { 6839b5b26f6SBruce M Simpson mreq.imr_multiaddr.s_addr = htonl((basegroup | i)); 6849b5b26f6SBruce M Simpson mreq.imr_interface.s_addr = INADDR_ANY; 6853250f640SBruce M Simpson inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); 6863250f640SBruce M Simpson if (verbose) 6873250f640SBruce M Simpson fprintf(stderr, "IP_ADD_MEMBERSHIP %s INADDR_ANY\n", addrbuf); 6889b5b26f6SBruce M Simpson if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, 6899b5b26f6SBruce M Simpson sizeof(mreq)) < 0) { 6909b5b26f6SBruce M Simpson err(-1, 6919b5b26f6SBruce M Simpson "test_ip_multicast_membership(%d, %s): failed IP_ADD_MEMBERSHIP (%s, %s)", 6923250f640SBruce M Simpson sock, socktypename, addrbuf, "INADDR_ANY"); 6939b5b26f6SBruce M Simpson } 6949b5b26f6SBruce M Simpson } 6953250f640SBruce M Simpson for (i = 0; i < nmcastgroups; i++) { 6969b5b26f6SBruce M Simpson mreq.imr_multiaddr.s_addr = htonl((basegroup | i)); 6979b5b26f6SBruce M Simpson mreq.imr_interface.s_addr = INADDR_ANY; 6983250f640SBruce M Simpson inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); 6993250f640SBruce M Simpson if (verbose) 7003250f640SBruce M Simpson fprintf(stderr, "IP_DROP_MEMBERSHIP %s INADDR_ANY\n", addrbuf); 7019b5b26f6SBruce M Simpson if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, 7029b5b26f6SBruce M Simpson sizeof(mreq)) < 0) { 7039b5b26f6SBruce M Simpson err(-1, 7049b5b26f6SBruce M Simpson "test_ip_multicast_membership(%d, %s): failed IP_DROP_MEMBERSHIP (%s, %s)", 7053250f640SBruce M Simpson sock, socktypename, addrbuf, "INADDR_ANY"); 7069b5b26f6SBruce M Simpson } 7079b5b26f6SBruce M Simpson } 7089b5b26f6SBruce M Simpson } 7099b5b26f6SBruce M Simpson 7109b5b26f6SBruce M Simpson /* 711f1f6501dSRobert Watson * XXX: For now, nothing here. 712f1f6501dSRobert Watson */ 713f1f6501dSRobert Watson static void 714cdeeed7aSRobert Watson test_ip_multicast_if(int sock, const char *socktypename) 715f1f6501dSRobert Watson { 716f1f6501dSRobert Watson 717f1f6501dSRobert Watson /* 718f1f6501dSRobert Watson * It's probably worth trying INADDR_ANY and INADDR_LOOPBACK here 719f1f6501dSRobert Watson * to see what happens. 720f1f6501dSRobert Watson */ 721f1f6501dSRobert Watson } 722f1f6501dSRobert Watson 723f1f6501dSRobert Watson /* 724f1f6501dSRobert Watson * XXX: For now, nothing here. 725f1f6501dSRobert Watson */ 726f1f6501dSRobert Watson static void 727cdeeed7aSRobert Watson test_ip_multicast_vif(int sock, const char *socktypename) 728f1f6501dSRobert Watson { 729f1f6501dSRobert Watson 730f1f6501dSRobert Watson /* 731f1f6501dSRobert Watson * This requires some knowledge of the number of virtual interfaces, 732f1f6501dSRobert Watson * and what is valid. 733f1f6501dSRobert Watson */ 734f1f6501dSRobert Watson } 735f1f6501dSRobert Watson 736f1f6501dSRobert Watson static void 737cdeeed7aSRobert Watson testsuite(int priv) 738f1f6501dSRobert Watson { 739cdeeed7aSRobert Watson const char *socktypenameset[] = {"SOCK_DGRAM", "SOCK_STREAM", 740cdeeed7aSRobert Watson "SOCK_RAW"}; 741cdeeed7aSRobert Watson int socktypeset[] = {SOCK_DGRAM, SOCK_STREAM, SOCK_RAW}; 742cdeeed7aSRobert Watson const char *socktypename; 743cdeeed7aSRobert Watson int i, sock, socktype; 744f1f6501dSRobert Watson 745f1f6501dSRobert Watson test_ip_hdrincl(); 746f1f6501dSRobert Watson 747cdeeed7aSRobert Watson for (i = 0; i < sizeof(socktypeset)/sizeof(int); i++) { 748cdeeed7aSRobert Watson socktype = socktypeset[i]; 749cdeeed7aSRobert Watson socktypename = socktypenameset[i]; 750f1f6501dSRobert Watson 751cdeeed7aSRobert Watson /* 752cdeeed7aSRobert Watson * If we can't acquire root privilege, we can't open raw 753cdeeed7aSRobert Watson * sockets, so don't actually try. 754cdeeed7aSRobert Watson */ 755cdeeed7aSRobert Watson if (getuid() != 0 && socktype == SOCK_RAW) 756cdeeed7aSRobert Watson continue; 757a1626faaSRobert Watson if (geteuid() != 0 && !priv && socktype == SOCK_RAW) 758a1626faaSRobert Watson continue; 759f1f6501dSRobert Watson 760f1f6501dSRobert Watson /* 761cdeeed7aSRobert Watson * XXXRW: On 5.3, this seems not to work for SOCK_RAW. 762cdeeed7aSRobert Watson */ 763cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 764cdeeed7aSRobert Watson if (sock == -1) 765cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_uchar(IP_TOS)", 766cdeeed7aSRobert Watson socktypename, priv); 767cdeeed7aSRobert Watson test_ip_uchar(sock, socktypename, IP_TOS, "IP_TOS", 0); 768cdeeed7aSRobert Watson close(sock); 769cdeeed7aSRobert Watson 770cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 771cdeeed7aSRobert Watson if (sock == -1) 772cdeeed7aSRobert Watson err(-1, "get_socket(%s %d) for test_ip_uchar(IP_TTL)", 773cdeeed7aSRobert Watson socktypename, priv); 774cdeeed7aSRobert Watson test_ip_uchar(sock, socktypename, IP_TTL, "IP_TTL", 64); 775cdeeed7aSRobert Watson close(sock); 776cdeeed7aSRobert Watson 777cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 778cdeeed7aSRobert Watson if (sock == -1) 779cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 780cdeeed7aSRobert Watson "(IP_RECVOPTS)", socktypename, priv); 781cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_RECVOPTS, 782cdeeed7aSRobert Watson "IP_RECVOPTS", 0, BOOLEAN_ANYONE); 783cdeeed7aSRobert Watson close(sock); 784cdeeed7aSRobert Watson 785cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 786cdeeed7aSRobert Watson if (sock == -1) 787cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 788cdeeed7aSRobert Watson "(IP_RECVRETOPTS)", socktypename, priv); 789cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_RECVRETOPTS, 790cdeeed7aSRobert Watson "IP_RECVRETOPTS", 0, BOOLEAN_ANYONE); 791cdeeed7aSRobert Watson close(sock); 792cdeeed7aSRobert Watson 793cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 794cdeeed7aSRobert Watson if (sock == -1) 795cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 796cdeeed7aSRobert Watson "(IP_RECVDSTADDR)", socktypename, priv); 797cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_RECVDSTADDR, 798cdeeed7aSRobert Watson "IP_RECVDSTADDR", 0, BOOLEAN_ANYONE); 799cdeeed7aSRobert Watson close(sock); 800cdeeed7aSRobert Watson 801cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 802cdeeed7aSRobert Watson if (sock == -1) 803cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 804cdeeed7aSRobert Watson "(IP_RECVTTL)", socktypename, priv); 805cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_RECVTTL, "IP_RECVTTL", 806cdeeed7aSRobert Watson 0, BOOLEAN_ANYONE); 807cdeeed7aSRobert Watson close(sock); 808cdeeed7aSRobert Watson 809cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 810cdeeed7aSRobert Watson if (sock == -1) 811cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 812cdeeed7aSRobert Watson "(IP_RECVIF)", socktypename, priv); 813cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_RECVIF, "IP_RECVIF", 814cdeeed7aSRobert Watson 0, BOOLEAN_ANYONE); 815cdeeed7aSRobert Watson close(sock); 816cdeeed7aSRobert Watson 817cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 818cdeeed7aSRobert Watson if (sock == -1) 819cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 820cdeeed7aSRobert Watson "(IP_FAITH)", socktypename, priv); 821cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_FAITH, "IP_FAITH", 0, 822cdeeed7aSRobert Watson BOOLEAN_ANYONE); 823cdeeed7aSRobert Watson close(sock); 824cdeeed7aSRobert Watson 825cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 826cdeeed7aSRobert Watson if (sock == -1) 827cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_boolean" 828cdeeed7aSRobert Watson "(IP_ONESBCAST)", socktypename, priv); 829cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_ONESBCAST, 830cdeeed7aSRobert Watson "IP_ONESBCAST", 0, BOOLEAN_ANYONE); 831cdeeed7aSRobert Watson close(sock); 832cdeeed7aSRobert Watson 833cdeeed7aSRobert Watson /* 834cdeeed7aSRobert Watson * Test the multicast TTL exactly as we would the regular 835cdeeed7aSRobert Watson * TTL, only expect a different default. 836cdeeed7aSRobert Watson */ 837cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 838cdeeed7aSRobert Watson if (sock == -1) 839cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for IP_MULTICAST_TTL", 840cdeeed7aSRobert Watson socktypename, priv); 841cdeeed7aSRobert Watson test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, 842cdeeed7aSRobert Watson "IP_MULTICAST_TTL", 1); 843cdeeed7aSRobert Watson close(sock); 844cdeeed7aSRobert Watson 845cdeeed7aSRobert Watson /* 846cdeeed7aSRobert Watson * The multicast loopback flag can be tested using our 847cdeeed7aSRobert Watson * boolean tester, but only because the FreeBSD API is a bit 848cdeeed7aSRobert Watson * more flexible than earlir APIs and will accept an int as 849cdeeed7aSRobert Watson * well as a u_char. Loopback is enabled by default. 850cdeeed7aSRobert Watson */ 851cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 852cdeeed7aSRobert Watson if (sock == -1) 853cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for IP_MULTICAST_LOOP", 854cdeeed7aSRobert Watson socktypename, priv); 855cdeeed7aSRobert Watson test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, 856cdeeed7aSRobert Watson "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); 857cdeeed7aSRobert Watson close(sock); 858cdeeed7aSRobert Watson 859cdeeed7aSRobert Watson sock = get_socket(socktype, priv); 860cdeeed7aSRobert Watson if (sock == -1) 861cdeeed7aSRobert Watson err(-1, "get_socket(%s, %d) for test_ip_options", 862cdeeed7aSRobert Watson socktypename, priv); 863cdeeed7aSRobert Watson //test_ip_options(sock, socktypename); 864cdeeed7aSRobert Watson close(sock); 865cdeeed7aSRobert Watson 8669b5b26f6SBruce M Simpson sock = get_socket(socktype, priv); 8679b5b26f6SBruce M Simpson if (sock == -1) 8689b5b26f6SBruce M Simpson err(-1, "get_socket(%s, %d) for test_ip_options", 8699b5b26f6SBruce M Simpson socktypename, priv); 8709b5b26f6SBruce M Simpson test_ip_multicast_membership(sock, socktypename); 8719b5b26f6SBruce M Simpson close(sock); 8729b5b26f6SBruce M Simpson 873cdeeed7aSRobert Watson test_ip_multicast_if(0, NULL); 874cdeeed7aSRobert Watson test_ip_multicast_vif(0, NULL); 875cdeeed7aSRobert Watson /* 876f1f6501dSRobert Watson * XXX: Still need to test: 877f1f6501dSRobert Watson * IP_PORTRANGE 878f1f6501dSRobert Watson * IP_IPSEC_POLICY? 879f1f6501dSRobert Watson */ 880cdeeed7aSRobert Watson } 881f1f6501dSRobert Watson } 882f1f6501dSRobert Watson 8833250f640SBruce M Simpson static void 8843250f640SBruce M Simpson usage() 8853250f640SBruce M Simpson { 8863250f640SBruce M Simpson 8873250f640SBruce M Simpson fprintf(stderr, "usage: ipsockopt [-M ngroups] [-r] [-v]\n"); 8883250f640SBruce M Simpson exit(EXIT_FAILURE); 8893250f640SBruce M Simpson } 8903250f640SBruce M Simpson 891f1f6501dSRobert Watson /* 892f1f6501dSRobert Watson * Very simply exercise that we can get and set each option. If we're running 893f1f6501dSRobert Watson * as root, run it also as nobody. If not as root, complain about that. 894f1f6501dSRobert Watson */ 895f1f6501dSRobert Watson int 896f1f6501dSRobert Watson main(int argc, char *argv[]) 897f1f6501dSRobert Watson { 8983250f640SBruce M Simpson int ch; 8993250f640SBruce M Simpson 9003250f640SBruce M Simpson while ((ch = getopt(argc, argv, "M:rv")) != -1) { 9013250f640SBruce M Simpson switch (ch) { 9023250f640SBruce M Simpson case 'M': 9033250f640SBruce M Simpson nmcastgroups = atoi(optarg); 9043250f640SBruce M Simpson break; 9053250f640SBruce M Simpson case 'r': 9063250f640SBruce M Simpson dorandom = 1; /* introduce non-determinism */ 9073250f640SBruce M Simpson break; 9083250f640SBruce M Simpson case 'v': 9093250f640SBruce M Simpson verbose = 1; 9103250f640SBruce M Simpson break; 9113250f640SBruce M Simpson default: 9123250f640SBruce M Simpson usage(); 9133250f640SBruce M Simpson } 9143250f640SBruce M Simpson } 915f1f6501dSRobert Watson 91600e13b1dSNik Clayton printf("1..1\n"); 9173250f640SBruce M Simpson 918f1f6501dSRobert Watson if (geteuid() != 0) { 919cdeeed7aSRobert Watson warnx("Not running as root, can't run tests as root"); 920f1f6501dSRobert Watson fprintf(stderr, "\n"); 921cdeeed7aSRobert Watson fprintf(stderr, 922cdeeed7aSRobert Watson "Running tests with uid %d sock uid %d\n", geteuid(), 923cdeeed7aSRobert Watson geteuid()); 924cdeeed7aSRobert Watson testsuite(PRIV_ASIS); 925f1f6501dSRobert Watson } else { 926cdeeed7aSRobert Watson fprintf(stderr, 927cdeeed7aSRobert Watson "Running tests with ruid %d euid %d sock uid 0\n", 928cdeeed7aSRobert Watson getuid(), geteuid()); 929cdeeed7aSRobert Watson testsuite(PRIV_ASIS); 930cdeeed7aSRobert Watson if (seteuid(65534) != 0) 931cdeeed7aSRobert Watson err(-1, "seteuid(65534)"); 932cdeeed7aSRobert Watson fprintf(stderr, 933cdeeed7aSRobert Watson "Running tests with ruid %d euid %d sock uid 65534\n", 934cdeeed7aSRobert Watson getuid(), geteuid()); 935cdeeed7aSRobert Watson testsuite(PRIV_ASIS); 936a1626faaSRobert Watson fprintf(stderr, 937a1626faaSRobert Watson "Running tests with ruid %d euid %d sock uid 0\n", 938a1626faaSRobert Watson getuid(), geteuid()); 939a1626faaSRobert Watson testsuite(PRIV_GETROOT); 940f1f6501dSRobert Watson } 94100e13b1dSNik Clayton printf("ok 1 - ipsockopt\n"); 942f1f6501dSRobert Watson exit(0); 943f1f6501dSRobert Watson } 944