ipsockopt.c revision 137587
1136384Srwatson/*- 2136384Srwatson * Copyright (c) 2004 Robert N. M. Watson 3136384Srwatson * All rights reserved. 4136384Srwatson * 5136384Srwatson * Redistribution and use in source and binary forms, with or without 6136384Srwatson * modification, are permitted provided that the following conditions 7136384Srwatson * are met: 8136384Srwatson * 1. Redistributions of source code must retain the above copyright 9136384Srwatson * notice, this list of conditions and the following disclaimer. 10136384Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11136384Srwatson * notice, this list of conditions and the following disclaimer in the 12136384Srwatson * documentation and/or other materials provided with the distribution. 13136384Srwatson * 14136384Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15136384Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16136384Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17136384Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18136384Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19136384Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20136384Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21136384Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22136384Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23136384Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24136384Srwatson * SUCH DAMAGE. 25136384Srwatson * 26136384Srwatson * $FreeBSD: head/tools/regression/netinet/ipsockopt/ipsockopt.c 137587 2004-11-11 19:47:55Z nik $ 27136384Srwatson */ 28136384Srwatson 29136384Srwatson#include <sys/types.h> 30136384Srwatson#include <sys/socket.h> 31136384Srwatson 32136384Srwatson#include <netinet/in.h> 33136384Srwatson#include <netinet/in_systm.h> 34136384Srwatson#include <netinet/ip.h> 35136384Srwatson 36136384Srwatson#include <err.h> 37136384Srwatson#include <errno.h> 38136384Srwatson#include <stdio.h> 39136384Srwatson#include <stdlib.h> 40136384Srwatson#include <string.h> 41136384Srwatson#include <unistd.h> 42136384Srwatson 43136384Srwatson/* 44136384Srwatson * The test tool exercises IP-level socket options by interrogating the 45136384Srwatson * getsockopt()/setsockopt() APIs. It does not currently test that the 46136384Srwatson * intended semantics of each option are implemented (i.e., that setting IP 47136384Srwatson * options on the socket results in packets with the desired IP options in 48136384Srwatson * it). 49136384Srwatson */ 50136384Srwatson 51136384Srwatson/* 52136390Srwatson * get_socket() is a wrapper function that returns a socket of the specified 53136390Srwatson * type, and created with or without restored root privilege (if running 54136390Srwatson * with a real uid of root and an effective uid of some other user). This 55136390Srwatson * us to test whether the same rights are granted using a socket with a 56136390Srwatson * privileged cached credential vs. a socket with a regular credential. 57136390Srwatson */ 58136390Srwatson#define PRIV_ASIS 0 59136390Srwatson#define PRIV_GETROOT 1 60136390Srwatsonstatic int 61136390Srwatsonget_socket_unpriv(int type) 62136390Srwatson{ 63136390Srwatson 64136390Srwatson return (socket(PF_INET, type, 0)); 65136390Srwatson} 66136390Srwatson 67136390Srwatsonstatic int 68136390Srwatsonget_socket_priv(int type) 69136390Srwatson{ 70136390Srwatson uid_t olduid; 71136390Srwatson int sock; 72136390Srwatson 73136390Srwatson if (getuid() != 0) 74136390Srwatson errx(-1, "get_sock_priv: running without real uid 0"); 75136390Srwatson 76136390Srwatson olduid = geteuid(); 77136390Srwatson if (seteuid(0) < 0) 78136390Srwatson err(-1, "get_sock_priv: seteuid(0)"); 79136390Srwatson 80136390Srwatson sock = socket(PF_INET, type, 0); 81136390Srwatson 82136390Srwatson if (seteuid(olduid) < 0) 83136390Srwatson err(-1, "get_sock_priv: seteuid(%d)", olduid); 84136390Srwatson 85136390Srwatson return (sock); 86136390Srwatson} 87136390Srwatson 88136390Srwatsonstatic int 89136390Srwatsonget_socket(int type, int priv) 90136390Srwatson{ 91136390Srwatson 92136390Srwatson if (priv) 93136390Srwatson return (get_socket_priv(type)); 94136390Srwatson else 95136390Srwatson return (get_socket_unpriv(type)); 96136390Srwatson} 97136390Srwatson 98136390Srwatson/* 99136384Srwatson * Exercise the IP_OPTIONS socket option. Confirm the following properties: 100136384Srwatson * 101136384Srwatson * - That there is no initial set of options (length returned is 0). 102136384Srwatson * - That if we set a specific set of options, we can read it back. 103136384Srwatson * - That if we then reset the options, they go away. 104136384Srwatson * 105136384Srwatson * Use a UDP socket for this. 106136384Srwatson */ 107136384Srwatsonstatic void 108136390Srwatsontest_ip_options(int sock, const char *socktypename) 109136384Srwatson{ 110136384Srwatson u_int32_t new_options, test_options[2]; 111136384Srwatson socklen_t len; 112136384Srwatson 113136384Srwatson /* 114136384Srwatson * Start off by confirming the default IP options on a socket are to 115136384Srwatson * have no options set. 116136384Srwatson */ 117136384Srwatson len = sizeof(test_options); 118136384Srwatson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 119136390Srwatson err(-1, "test_ip_options(%s): initial getsockopt()", 120136390Srwatson socktypename); 121136384Srwatson 122136384Srwatson if (len != 0) 123136390Srwatson errx(-1, "test_ip_options(%s): initial getsockopt() returned " 124136390Srwatson "%d bytes", socktypename, len); 125136384Srwatson 126136384Srwatson#define TEST_MAGIC 0xc34e4212 127136384Srwatson#define NEW_OPTIONS htonl(IPOPT_EOL | (IPOPT_NOP << 8) | (IPOPT_NOP << 16) \ 128136384Srwatson | (IPOPT_NOP << 24)) 129136384Srwatson 130136384Srwatson /* 131136384Srwatson * Write some new options into the socket. 132136384Srwatson */ 133136384Srwatson new_options = NEW_OPTIONS; 134136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, &new_options, 135136384Srwatson sizeof(new_options)) < 0) 136136390Srwatson err(-1, "test_ip_options(%s): setsockopt(NOP|NOP|NOP|EOL)", 137136390Srwatson socktypename); 138136384Srwatson 139136384Srwatson /* 140136384Srwatson * Store some random cruft in a local variable and retrieve the 141136384Srwatson * options to make sure they set. Note that we pass in an array 142136384Srwatson * of u_int32_t's so that if whatever ended up in the option was 143136384Srwatson * larger than what we put in, we find out about it here. 144136384Srwatson */ 145136384Srwatson test_options[0] = TEST_MAGIC; 146136384Srwatson test_options[1] = TEST_MAGIC; 147136384Srwatson len = sizeof(test_options); 148136384Srwatson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 149136390Srwatson err(-1, "test_ip_options(%s): getsockopt() after set", 150136390Srwatson socktypename); 151136384Srwatson 152136384Srwatson /* 153136384Srwatson * Getting the right amount back is important. 154136384Srwatson */ 155136384Srwatson if (len != sizeof(new_options)) 156136390Srwatson errx(-1, "test_ip_options(%s): getsockopt() after set " 157136390Srwatson "returned %d bytes of data", socktypename, len); 158136384Srwatson 159136384Srwatson /* 160136384Srwatson * One posible failure mode is that the call succeeds but neglects to 161136384Srwatson * copy out the data. 162136384Srwatson */ 163136384Srwatson if (test_options[0] == TEST_MAGIC) 164136390Srwatson errx(-1, "test_ip_options(%s): getsockopt() after set didn't " 165136390Srwatson "return data", socktypename); 166136384Srwatson 167136384Srwatson /* 168136384Srwatson * Make sure we get back what we wrote on. 169136384Srwatson */ 170136384Srwatson if (new_options != test_options[0]) 171136390Srwatson errx(-1, "test_ip_options(%s): getsockopt() after set " 172136390Srwatson "returned wrong options (%08x, %08x)", socktypename, 173136390Srwatson new_options, test_options[0]); 174136384Srwatson 175136384Srwatson /* 176136384Srwatson * Now we reset the value to make sure clearing works. 177136384Srwatson */ 178136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) 179136390Srwatson err(-1, "test_ip_options(%s): setsockopt() to reset", 180136390Srwatson socktypename); 181136384Srwatson 182136384Srwatson /* 183136384Srwatson * Make sure it was really cleared. 184136384Srwatson */ 185136384Srwatson test_options[0] = TEST_MAGIC; 186136384Srwatson test_options[1] = TEST_MAGIC; 187136384Srwatson len = sizeof(test_options); 188136384Srwatson if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) 189136390Srwatson err(-1, "test_ip_options(%s): getsockopt() after reset", 190136390Srwatson socktypename); 191136384Srwatson 192136384Srwatson if (len != 0) 193136390Srwatson errx(-1, "test_ip_options(%s): getsockopt() after reset " 194136390Srwatson "returned %d bytes", socktypename, len); 195136384Srwatson} 196136384Srwatson 197136384Srwatson/* 198136384Srwatson * This test checks the behavior of the IP_HDRINCL socket option, which 199136384Srwatson * allows users with privilege to specify the full header on an IP raw 200136384Srwatson * socket. We test that the option can only be used with raw IP sockets, not 201136384Srwatson * with UDP or TCP sockets. We also confirm that the raw socket is only 202136384Srwatson * available to a privileged user (subject to the UID when called). We 203136384Srwatson * confirm that it defaults to off 204136390Srwatson * 205136390Srwatson * Unlike other tests, doesn't use caller-provided socket. Probably should 206136390Srwatson * be fixed. 207136384Srwatson */ 208136384Srwatsonstatic void 209136384Srwatsontest_ip_hdrincl(void) 210136384Srwatson{ 211136384Srwatson int flag[2], sock; 212136384Srwatson socklen_t len; 213136384Srwatson 214136384Srwatson /* 215136384Srwatson * Try to receive or set the IP_HDRINCL flag on a TCP socket. 216136384Srwatson */ 217136384Srwatson sock = socket(PF_INET, SOCK_STREAM, 0); 218136390Srwatson if (sock == -1) 219136390Srwatson err(-1, "test_ip_hdrincl(): socket(SOCK_STREAM)"); 220136384Srwatson 221136384Srwatson flag[0] = -1; 222136384Srwatson len = sizeof(flag[0]); 223136390Srwatson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) 224136390Srwatson err(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINCL)"); 225136384Srwatson 226136390Srwatson if (errno != ENOPROTOOPT) 227136390Srwatson errx(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINC) " 228136390Srwatson "returned %d (%s) not ENOPROTOOPT", errno, 229136390Srwatson strerror(errno)); 230136384Srwatson 231136384Srwatson flag[0] = 1; 232136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 233136390Srwatson == 0) 234136390Srwatson err(-1,"test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " 235136390Srwatson "succeeded\n"); 236136384Srwatson 237136390Srwatson if (errno != ENOPROTOOPT) 238136390Srwatson errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " 239136390Srwatson "returned %d (%s) not ENOPROTOOPT\n", errno, 240136390Srwatson strerror(errno)); 241136384Srwatson 242136384Srwatson close(sock); 243136384Srwatson 244136384Srwatson /* 245136384Srwatson * Try to receive or set the IP_HDRINCL flag on a UDP socket. 246136384Srwatson */ 247136384Srwatson sock = socket(PF_INET, SOCK_DGRAM, 0); 248136390Srwatson if (sock == -1) 249136390Srwatson err(-1, "test_ip_hdrincl(): socket(SOCK_DGRAM"); 250136384Srwatson 251136384Srwatson flag[0] = -1; 252136384Srwatson len = sizeof(flag[0]); 253136390Srwatson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) 254136390Srwatson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " 255136390Srwatson "succeeded\n"); 256136384Srwatson 257136390Srwatson if (errno != ENOPROTOOPT) 258136390Srwatson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " 259136390Srwatson "returned %d (%s) not ENOPROTOOPT\n", errno, 260136390Srwatson strerror(errno)); 261136384Srwatson 262136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 263136390Srwatson == 0) 264136390Srwatson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " 265136390Srwatson "succeeded\n"); 266136384Srwatson 267136390Srwatson if (errno != ENOPROTOOPT) 268136390Srwatson errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " 269136390Srwatson "returned %d (%s) not ENOPROTOOPT\n", errno, 270136390Srwatson strerror(errno)); 271136384Srwatson 272136384Srwatson close(sock); 273136384Srwatson 274136384Srwatson /* 275136384Srwatson * Now try on a raw socket. Access ontrol should prevent non-root 276136384Srwatson * users from creating the raw socket, so check that here based on 277136384Srwatson * geteuid(). If we're non-root, we just return assuming the socket 278136384Srwatson * create fails since the remainder of the tests apply only on a raw 279136384Srwatson * socket. 280136384Srwatson */ 281136384Srwatson sock = socket(PF_INET, SOCK_RAW, 0); 282136384Srwatson if (geteuid() != 0) { 283136384Srwatson if (sock != -1) 284136384Srwatson errx(-1, "test_ip_hdrincl: created raw socket as " 285136384Srwatson "uid %d", geteuid()); 286136384Srwatson return; 287136384Srwatson } 288136390Srwatson if (sock == -1) 289136390Srwatson err(-1, "test_ip_hdrincl(): socket(PF_INET, SOCK_RAW)"); 290136384Srwatson 291136384Srwatson /* 292136384Srwatson * Make sure the initial value of the flag is 0 (disabled). 293136384Srwatson */ 294136384Srwatson flag[0] = -1; 295136384Srwatson flag[1] = -1; 296136384Srwatson len = sizeof(flag); 297136390Srwatson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 298136390Srwatson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on raw " 299136390Srwatson "socket"); 300136384Srwatson 301136390Srwatson if (len != sizeof(flag[0])) 302136390Srwatson errx(-1, "test_ip_hdrincl(): %d bytes returned on " 303136384Srwatson "initial get\n", len); 304136384Srwatson 305136390Srwatson if (flag[0] != 0) 306136390Srwatson errx(-1, "test_ip_hdrincl(): initial flag value of %d\n", 307136384Srwatson flag[0]); 308136384Srwatson 309136384Srwatson /* 310136384Srwatson * Enable the IP_HDRINCL flag. 311136384Srwatson */ 312136384Srwatson flag[0] = 1; 313136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 314136390Srwatson < 0) 315136390Srwatson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 1)"); 316136384Srwatson 317136384Srwatson /* 318136384Srwatson * Check that the IP_HDRINCL flag was set. 319136384Srwatson */ 320136384Srwatson flag[0] = -1; 321136384Srwatson flag[1] = -1; 322136384Srwatson len = sizeof(flag); 323136390Srwatson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 324136390Srwatson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " 325136390Srwatson "set"); 326136384Srwatson 327136390Srwatson if (flag[0] == 0) 328136390Srwatson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " 329136384Srwatson "after set had flag of %d\n", flag[0]); 330136384Srwatson 331136384Srwatson#define HISTORICAL_INP_HDRINCL 8 332136390Srwatson if (flag[0] != HISTORICAL_INP_HDRINCL) 333136390Srwatson warnx("test_ip_hdrincl(): WARNING: getsockopt(IP_H" 334136384Srwatson "DRINCL) after set had non-historical value of %d\n", 335136384Srwatson flag[0]); 336136384Srwatson 337136384Srwatson /* 338136384Srwatson * Reset the IP_HDRINCL flag to 0. 339136384Srwatson */ 340136384Srwatson flag[0] = 0; 341136384Srwatson if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) 342136390Srwatson < 0) 343136390Srwatson err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 0)"); 344136384Srwatson 345136384Srwatson /* 346136384Srwatson * Check that the IP_HDRINCL flag was reset to 0. 347136384Srwatson */ 348136384Srwatson flag[0] = -1; 349136384Srwatson flag[1] = -1; 350136384Srwatson len = sizeof(flag); 351136390Srwatson if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) 352136390Srwatson err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " 353136390Srwatson "reset"); 354136384Srwatson 355136390Srwatson if (flag[0] != 0) 356136390Srwatson errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " 357136384Srwatson "after set had flag of %d\n", flag[0]); 358136384Srwatson 359136384Srwatson close(sock); 360136384Srwatson} 361136384Srwatson 362136384Srwatson/* 363136384Srwatson * As with other non-int or larger sized socket options, the IP_TOS and 364136384Srwatson * IP_TTL fields in kernel is stored as an 8-bit value, reflecting the IP 365136384Srwatson * header fields, but useful I/O to the field occurs using 32-bit integers. 366136384Srwatson * The FreeBSD kernel will permit writes from variables at least an int in 367136384Srwatson * size (and ignore additional bytes), and will permit a read to buffers 1 368136384Srwatson * byte or larger (but depending on endianness, may truncate out useful 369136384Srwatson * values if the caller provides less room). 370136384Srwatson * 371136384Srwatson * Given the limitations of the API, use a UDP socket to confirm that the 372136384Srwatson * following are true: 373136384Srwatson * 374136384Srwatson * - We can read the IP_TOS/IP_TTL options. 375136384Srwatson * - The initial value of the TOS option is 0, TTL is 64. 376136384Srwatson * - That if we provide more than 32 bits of storage, we get back only 32 377136384Srwatson * bits of data. 378136384Srwatson * - When we set it to a non-zero value expressible with a u_char, we can 379136384Srwatson * read that value back. 380136384Srwatson * - When we reset it back to zero, we can read it as 0. 381136384Srwatson * - When we set it to a value >255, the value is truncated to something less 382136384Srwatson * than 255. 383136384Srwatson */ 384136384Srwatsonstatic void 385136390Srwatsontest_ip_uchar(int sock, const char *socktypename, int option, 386136390Srwatson const char *optionname, int initial) 387136384Srwatson{ 388136390Srwatson int val[2]; 389136384Srwatson socklen_t len; 390136384Srwatson 391136384Srwatson /* 392136384Srwatson * Check that the initial value is 0, and that the size is one 393136384Srwatson * u_char; 394136384Srwatson */ 395136384Srwatson val[0] = -1; 396136384Srwatson val[1] = -1; 397136384Srwatson len = sizeof(val); 398136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 399136390Srwatson err(-1, "test_ip_uchar(%s, %s): initial getsockopt()", 400136390Srwatson socktypename, optionname); 401136384Srwatson 402136384Srwatson if (len != sizeof(val[0])) 403136390Srwatson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " 404136390Srwatson "returned %d bytes", socktypename, optionname, len); 405136384Srwatson 406136384Srwatson if (val[0] == -1) 407136390Srwatson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() didn't " 408136390Srwatson "return data", socktypename, optionname); 409136384Srwatson 410136384Srwatson if (val[0] != initial) 411136390Srwatson errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " 412136390Srwatson "returned value of %d, not %d", socktypename, optionname, 413136390Srwatson val[0], initial); 414136384Srwatson 415136384Srwatson /* 416136384Srwatson * Set the field to a valid value. 417136384Srwatson */ 418136384Srwatson val[0] = 128; 419136384Srwatson val[1] = -1; 420136384Srwatson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) 421136390Srwatson err(-1, "test_ip_uchar(%s, %s): setsockopt(128)", 422136390Srwatson socktypename, optionname); 423136384Srwatson 424136384Srwatson /* 425136384Srwatson * Check that when we read back the field, we get the same value. 426136384Srwatson */ 427136384Srwatson val[0] = -1; 428136384Srwatson val[1] = -1; 429136384Srwatson len = sizeof(val); 430136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 431136390Srwatson err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 432136390Srwatson "128", socktypename, optionname); 433136384Srwatson 434136384Srwatson if (len != sizeof(val[0])) 435136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 436136390Srwatson "128 returned %d bytes", socktypename, optionname, len); 437136384Srwatson 438136384Srwatson if (val[0] == -1) 439136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 440136390Srwatson "128 didn't return data", socktypename, optionname); 441136384Srwatson 442136384Srwatson if (val[0] != 128) 443136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 444136390Srwatson "128 returned %d", socktypename, optionname, val[0]); 445136384Srwatson 446136384Srwatson /* 447136384Srwatson * Reset the value to 0, check that it was reset. 448136384Srwatson */ 449136384Srwatson val[0] = 0; 450136384Srwatson val[1] = 0; 451136384Srwatson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) 452136390Srwatson err(-1, "test_ip_uchar(%s, %s): setsockopt() to reset from " 453136390Srwatson "128", socktypename, optionname); 454136384Srwatson 455136384Srwatson if (len != sizeof(val[0])) 456136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 457136390Srwatson "from 128 returned %d bytes", socktypename, optionname, 458136390Srwatson len); 459136384Srwatson 460136384Srwatson if (val[0] == -1) 461136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 462136390Srwatson "from 128 didn't return data", socktypename, optionname); 463136384Srwatson 464136384Srwatson if (val[0] != 0) 465136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " 466136390Srwatson "from 128 returned %d", socktypename, optionname, 467136390Srwatson val[0]); 468136384Srwatson 469136384Srwatson /* 470136384Srwatson * Set the value to something out of range and check that it comes 471136384Srwatson * back truncated, or that we get EINVAL back. Traditional u_char 472136384Srwatson * IP socket options truncate, but newer ones (such as multicast 473136384Srwatson * socket options) will return EINVAL. 474136384Srwatson */ 475136384Srwatson val[0] = 32000; 476136384Srwatson val[1] = -1; 477136384Srwatson if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) { 478136384Srwatson /* 479136384Srwatson * EINVAL is a fine outcome, no need to run the truncation 480136384Srwatson * tests. 481136384Srwatson */ 482136390Srwatson if (errno == EINVAL) 483136384Srwatson return; 484136390Srwatson err(-1, "test_ip_uchar(%s, %s): getsockopt(32000)", 485136390Srwatson socktypename, optionname); 486136384Srwatson } 487136384Srwatson 488136384Srwatson val[0] = -1; 489136384Srwatson val[1] = -1; 490136384Srwatson len = sizeof(val); 491136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 492136390Srwatson err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 493136390Srwatson "32000", socktypename, optionname); 494136384Srwatson 495136384Srwatson if (len != sizeof(val[0])) 496136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 497136390Srwatson "32000 returned %d bytes", socktypename, optionname, 498136390Srwatson len); 499136384Srwatson 500136384Srwatson if (val[0] == -1) 501136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 502136390Srwatson "32000 didn't return data", socktypename, optionname); 503136384Srwatson 504136384Srwatson if (val[0] == 32000) 505136390Srwatson errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " 506136390Srwatson "32000 returned 32000: failed to truncate", socktypename, 507136390Srwatson optionname); 508136384Srwatson} 509136384Srwatson 510136384Srwatson/* 511136384Srwatson * Generic test for a boolean socket option. Caller provides the option 512136384Srwatson * number, string name, expected default (initial) value, and whether or not 513136384Srwatson * the option is root-only. For each option, test: 514136384Srwatson * 515136384Srwatson * - That we can read the option. 516136384Srwatson * - That the initial value is as expected. 517136384Srwatson * - That we can modify the value. 518136384Srwatson * - That on modification, the new value can be read back. 519136384Srwatson * - That we can reset the value. 520136384Srwatson * - that on reset, the new value can be read back. 521136384Srwatson */ 522136384Srwatson#define BOOLEAN_ANYONE 1 523136384Srwatson#define BOOLEAN_ROOTONLY 1 524136384Srwatsonstatic void 525136390Srwatsontest_ip_boolean(int sock, const char *socktypename, int option, 526136390Srwatson char *optionname, int initial, int rootonly) 527136384Srwatson{ 528136390Srwatson int newvalue, val[2]; 529136384Srwatson socklen_t len; 530136384Srwatson 531136384Srwatson /* 532136384Srwatson * The default for a boolean might be true or false. If it's false, 533136384Srwatson * we will try setting it to true (but using a non-1 value of true). 534136384Srwatson * If it's true, we'll set it to false. 535136384Srwatson */ 536136384Srwatson if (initial == 0) 537136384Srwatson newvalue = 0xff; 538136384Srwatson else 539136384Srwatson newvalue = 0; 540136384Srwatson 541136384Srwatson val[0] = -1; 542136384Srwatson val[1] = -1; 543136384Srwatson len = sizeof(val); 544136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 545136384Srwatson err(-1, "test_ip_boolean: initial getsockopt()"); 546136384Srwatson 547136384Srwatson if (len != sizeof(val[0])) 548136390Srwatson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 549136390Srwatson "returned %d bytes", socktypename, optionname, len); 550136384Srwatson 551136384Srwatson if (val[0] == -1) 552136390Srwatson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 553136390Srwatson "didn't return data", socktypename, optionname); 554136384Srwatson 555136384Srwatson if (val[0] != initial) 556136390Srwatson errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " 557136390Srwatson "returned %d (expected %d)", socktypename, optionname, 558136390Srwatson val[0], initial); 559136384Srwatson 560136384Srwatson /* 561136384Srwatson * Set the socket option to a new non-default value. 562136384Srwatson */ 563136384Srwatson if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) 564136384Srwatson < 0) 565136390Srwatson err(-1, "test_ip_boolean(%s, %s): setsockopt() to %d", 566136390Srwatson socktypename, optionname, newvalue); 567136384Srwatson 568136384Srwatson /* 569136384Srwatson * Read the value back and see if it is not the default (note: will 570136384Srwatson * not be what we set it to, as we set it to 0xff above). 571136384Srwatson */ 572136384Srwatson val[0] = -1; 573136384Srwatson val[1] = -1; 574136384Srwatson len = sizeof(val); 575136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 576136390Srwatson err(-1, "test_ip_boolean(%s, %s): getsockopt() after set to " 577136390Srwatson "%d", socktypename, optionname, newvalue); 578136384Srwatson 579136384Srwatson if (len != sizeof(val[0])) 580136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 581136390Srwatson "to %d returned %d bytes", socktypename, optionname, 582136390Srwatson newvalue, len); 583136384Srwatson 584136384Srwatson if (val[0] == -1) 585136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 586136390Srwatson "to %d didn't return data", socktypename, optionname, 587136390Srwatson newvalue); 588136384Srwatson 589136384Srwatson /* 590136384Srwatson * If we set it to true, check for '1', otherwise '0. 591136384Srwatson */ 592136384Srwatson if (val[0] != (newvalue ? 1 : 0)) 593136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " 594136390Srwatson "to %d returned %d", socktypename, optionname, newvalue, 595136390Srwatson val[0]); 596136384Srwatson 597136384Srwatson /* 598136384Srwatson * Reset to initial value. 599136384Srwatson */ 600136384Srwatson newvalue = initial; 601136384Srwatson if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) 602136384Srwatson < 0) 603136390Srwatson err(-1, "test_ip_boolean(%s, %s): setsockopt() to reset", 604136390Srwatson socktypename, optionname); 605136384Srwatson 606136384Srwatson /* 607136384Srwatson * Check reset version. 608136384Srwatson */ 609136384Srwatson val[0] = -1; 610136384Srwatson val[1] = -1; 611136384Srwatson len = sizeof(val); 612136384Srwatson if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) 613136390Srwatson err(-1, "test_ip_boolean(%s, %s): getsockopt() after reset", 614136390Srwatson socktypename, optionname); 615136384Srwatson 616136384Srwatson if (len != sizeof(val[0])) 617136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 618136390Srwatson "returned %d bytes", socktypename, optionname, len); 619136384Srwatson 620136384Srwatson if (val[0] == -1) 621136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 622136390Srwatson "didn't return data", socktypename, optionname); 623136384Srwatson 624136384Srwatson if (val[0] != newvalue) 625136390Srwatson errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " 626136390Srwatson "returned %d", socktypename, optionname, newvalue); 627136384Srwatson} 628136384Srwatson 629136384Srwatson/* 630136384Srwatson * XXX: For now, nothing here. 631136384Srwatson */ 632136384Srwatsonstatic void 633136390Srwatsontest_ip_multicast_if(int sock, const char *socktypename) 634136384Srwatson{ 635136384Srwatson 636136384Srwatson /* 637136384Srwatson * It's probably worth trying INADDR_ANY and INADDR_LOOPBACK here 638136384Srwatson * to see what happens. 639136384Srwatson */ 640136384Srwatson} 641136384Srwatson 642136384Srwatson/* 643136384Srwatson * XXX: For now, nothing here. 644136384Srwatson */ 645136384Srwatsonstatic void 646136390Srwatsontest_ip_multicast_vif(int sock, const char *socktypename) 647136384Srwatson{ 648136384Srwatson 649136384Srwatson /* 650136384Srwatson * This requires some knowledge of the number of virtual interfaces, 651136384Srwatson * and what is valid. 652136384Srwatson */ 653136384Srwatson} 654136384Srwatson 655136384Srwatson/* 656136384Srwatson * XXX: For now, nothing here. 657136384Srwatson */ 658136384Srwatsonstatic void 659136390Srwatsontest_ip_multicast_membership(int sock, const char *socktypename) 660136384Srwatson{ 661136384Srwatson 662136384Srwatson} 663136384Srwatson 664136384Srwatsonstatic void 665136390Srwatsontestsuite(int priv) 666136384Srwatson{ 667136390Srwatson const char *socktypenameset[] = {"SOCK_DGRAM", "SOCK_STREAM", 668136390Srwatson "SOCK_RAW"}; 669136390Srwatson int socktypeset[] = {SOCK_DGRAM, SOCK_STREAM, SOCK_RAW}; 670136390Srwatson const char *socktypename; 671136390Srwatson int i, sock, socktype; 672136384Srwatson 673136390Srwatson test_ip_hdrincl(); 674136384Srwatson 675136390Srwatson for (i = 0; i < sizeof(socktypeset)/sizeof(int); i++) { 676136390Srwatson socktype = socktypeset[i]; 677136390Srwatson socktypename = socktypenameset[i]; 678136384Srwatson 679136390Srwatson /* 680136390Srwatson * If we can't acquire root privilege, we can't open raw 681136390Srwatson * sockets, so don't actually try. 682136390Srwatson */ 683136390Srwatson if (getuid() != 0 && socktype == SOCK_RAW) 684136390Srwatson continue; 685136391Srwatson if (geteuid() != 0 && !priv && socktype == SOCK_RAW) 686136391Srwatson continue; 687136384Srwatson 688136390Srwatson /* 689136390Srwatson * XXXRW: On 5.3, this seems not to work for SOCK_RAW. 690136390Srwatson */ 691136390Srwatson sock = get_socket(socktype, priv); 692136390Srwatson if (sock == -1) 693136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_uchar(IP_TOS)", 694136390Srwatson socktypename, priv); 695136390Srwatson test_ip_uchar(sock, socktypename, IP_TOS, "IP_TOS", 0); 696136390Srwatson close(sock); 697136384Srwatson 698136390Srwatson sock = get_socket(socktype, priv); 699136390Srwatson if (sock == -1) 700136390Srwatson err(-1, "get_socket(%s %d) for test_ip_uchar(IP_TTL)", 701136390Srwatson socktypename, priv); 702136390Srwatson test_ip_uchar(sock, socktypename, IP_TTL, "IP_TTL", 64); 703136390Srwatson close(sock); 704136384Srwatson 705136390Srwatson sock = get_socket(socktype, priv); 706136390Srwatson if (sock == -1) 707136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 708136390Srwatson "(IP_RECVOPTS)", socktypename, priv); 709136390Srwatson test_ip_boolean(sock, socktypename, IP_RECVOPTS, 710136390Srwatson "IP_RECVOPTS", 0, BOOLEAN_ANYONE); 711136390Srwatson close(sock); 712136384Srwatson 713136390Srwatson sock = get_socket(socktype, priv); 714136390Srwatson if (sock == -1) 715136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 716136390Srwatson "(IP_RECVRETOPTS)", socktypename, priv); 717136390Srwatson test_ip_boolean(sock, socktypename, IP_RECVRETOPTS, 718136390Srwatson "IP_RECVRETOPTS", 0, BOOLEAN_ANYONE); 719136390Srwatson close(sock); 720136384Srwatson 721136390Srwatson sock = get_socket(socktype, priv); 722136390Srwatson if (sock == -1) 723136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 724136390Srwatson "(IP_RECVDSTADDR)", socktypename, priv); 725136390Srwatson test_ip_boolean(sock, socktypename, IP_RECVDSTADDR, 726136390Srwatson "IP_RECVDSTADDR", 0, BOOLEAN_ANYONE); 727136390Srwatson close(sock); 728136384Srwatson 729136390Srwatson sock = get_socket(socktype, priv); 730136390Srwatson if (sock == -1) 731136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 732136390Srwatson "(IP_RECVTTL)", socktypename, priv); 733136390Srwatson test_ip_boolean(sock, socktypename, IP_RECVTTL, "IP_RECVTTL", 734136390Srwatson 0, BOOLEAN_ANYONE); 735136390Srwatson close(sock); 736136384Srwatson 737136390Srwatson sock = get_socket(socktype, priv); 738136390Srwatson if (sock == -1) 739136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 740136390Srwatson "(IP_RECVIF)", socktypename, priv); 741136390Srwatson test_ip_boolean(sock, socktypename, IP_RECVIF, "IP_RECVIF", 742136390Srwatson 0, BOOLEAN_ANYONE); 743136390Srwatson close(sock); 744136384Srwatson 745136390Srwatson sock = get_socket(socktype, priv); 746136390Srwatson if (sock == -1) 747136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 748136390Srwatson "(IP_FAITH)", socktypename, priv); 749136390Srwatson test_ip_boolean(sock, socktypename, IP_FAITH, "IP_FAITH", 0, 750136390Srwatson BOOLEAN_ANYONE); 751136390Srwatson close(sock); 752136390Srwatson 753136390Srwatson sock = get_socket(socktype, priv); 754136390Srwatson if (sock == -1) 755136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_boolean" 756136390Srwatson "(IP_ONESBCAST)", socktypename, priv); 757136390Srwatson test_ip_boolean(sock, socktypename, IP_ONESBCAST, 758136390Srwatson "IP_ONESBCAST", 0, BOOLEAN_ANYONE); 759136390Srwatson close(sock); 760136390Srwatson 761136390Srwatson /* 762136390Srwatson * Test the multicast TTL exactly as we would the regular 763136390Srwatson * TTL, only expect a different default. 764136390Srwatson */ 765136390Srwatson sock = get_socket(socktype, priv); 766136390Srwatson if (sock == -1) 767136390Srwatson err(-1, "get_socket(%s, %d) for IP_MULTICAST_TTL", 768136390Srwatson socktypename, priv); 769136390Srwatson test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, 770136390Srwatson "IP_MULTICAST_TTL", 1); 771136390Srwatson close(sock); 772136390Srwatson 773136390Srwatson /* 774136390Srwatson * The multicast loopback flag can be tested using our 775136390Srwatson * boolean tester, but only because the FreeBSD API is a bit 776136390Srwatson * more flexible than earlir APIs and will accept an int as 777136390Srwatson * well as a u_char. Loopback is enabled by default. 778136390Srwatson */ 779136390Srwatson sock = get_socket(socktype, priv); 780136390Srwatson if (sock == -1) 781136390Srwatson err(-1, "get_socket(%s, %d) for IP_MULTICAST_LOOP", 782136390Srwatson socktypename, priv); 783136390Srwatson test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, 784136390Srwatson "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); 785136390Srwatson close(sock); 786136390Srwatson 787136390Srwatson sock = get_socket(socktype, priv); 788136390Srwatson if (sock == -1) 789136390Srwatson err(-1, "get_socket(%s, %d) for test_ip_options", 790136390Srwatson socktypename, priv); 791136390Srwatson //test_ip_options(sock, socktypename); 792136390Srwatson close(sock); 793136390Srwatson 794136390Srwatson test_ip_multicast_if(0, NULL); 795136390Srwatson test_ip_multicast_vif(0, NULL); 796136390Srwatson test_ip_multicast_membership(0, NULL); 797136390Srwatson /* 798136390Srwatson * XXX: Still need to test: 799136390Srwatson * IP_PORTRANGE 800136390Srwatson * IP_IPSEC_POLICY? 801136390Srwatson */ 802136390Srwatson } 803136384Srwatson} 804136384Srwatson 805136384Srwatson/* 806136384Srwatson * Very simply exercise that we can get and set each option. If we're running 807136384Srwatson * as root, run it also as nobody. If not as root, complain about that. 808136384Srwatson */ 809136384Srwatsonint 810136384Srwatsonmain(int argc, char *argv[]) 811136384Srwatson{ 812136384Srwatson 813137587Snik printf("1..1\n"); 814136384Srwatson if (geteuid() != 0) { 815136390Srwatson warnx("Not running as root, can't run tests as root"); 816136384Srwatson fprintf(stderr, "\n"); 817136390Srwatson fprintf(stderr, 818136390Srwatson "Running tests with uid %d sock uid %d\n", geteuid(), 819136390Srwatson geteuid()); 820136390Srwatson testsuite(PRIV_ASIS); 821136384Srwatson } else { 822136390Srwatson fprintf(stderr, 823136390Srwatson "Running tests with ruid %d euid %d sock uid 0\n", 824136390Srwatson getuid(), geteuid()); 825136390Srwatson testsuite(PRIV_ASIS); 826136390Srwatson if (seteuid(65534) != 0) 827136390Srwatson err(-1, "seteuid(65534)"); 828136390Srwatson fprintf(stderr, 829136391Srwatson "Running tests with ruid %d euid %d sock uid 65534\n", 830136391Srwatson getuid(), geteuid()); 831136391Srwatson testsuite(PRIV_ASIS); 832136391Srwatson fprintf(stderr, 833136390Srwatson "Running tests with ruid %d euid %d sock uid 0\n", 834136390Srwatson getuid(), geteuid()); 835136390Srwatson testsuite(PRIV_GETROOT); 836136384Srwatson } 837137587Snik printf("ok 1 - ipsockopt\n"); 838136384Srwatson exit(0); 839136384Srwatson} 840