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