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