xref: /freebsd/tools/regression/netinet/ipsockopt/ipsockopt.c (revision 3250f640aa20d4b5b1788bb0bc1a756a6027389b)
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