1*b7b85f24SRobert Watson /*-
2*b7b85f24SRobert Watson * Copyright (c) 2005 Robert N. M. Watson
3*b7b85f24SRobert Watson * All rights reserved.
4*b7b85f24SRobert Watson *
5*b7b85f24SRobert Watson * Redistribution and use in source and binary forms, with or without
6*b7b85f24SRobert Watson * modification, are permitted provided that the following conditions
7*b7b85f24SRobert Watson * are met:
8*b7b85f24SRobert Watson * 1. Redistributions of source code must retain the above copyright
9*b7b85f24SRobert Watson * notice, this list of conditions and the following disclaimer.
10*b7b85f24SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
11*b7b85f24SRobert Watson * notice, this list of conditions and the following disclaimer in the
12*b7b85f24SRobert Watson * documentation and/or other materials provided with the distribution.
13*b7b85f24SRobert Watson *
14*b7b85f24SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*b7b85f24SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*b7b85f24SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*b7b85f24SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*b7b85f24SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*b7b85f24SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*b7b85f24SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*b7b85f24SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*b7b85f24SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*b7b85f24SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*b7b85f24SRobert Watson * SUCH DAMAGE.
25*b7b85f24SRobert Watson */
26*b7b85f24SRobert Watson
27*b7b85f24SRobert Watson #include <sys/types.h>
28*b7b85f24SRobert Watson #include <sys/socket.h>
29*b7b85f24SRobert Watson
30*b7b85f24SRobert Watson #include <netinet/in.h>
31*b7b85f24SRobert Watson
32*b7b85f24SRobert Watson #include <arpa/inet.h>
33*b7b85f24SRobert Watson
34*b7b85f24SRobert Watson #include <err.h>
35*b7b85f24SRobert Watson #include <errno.h>
36*b7b85f24SRobert Watson #include <fcntl.h>
37*b7b85f24SRobert Watson #include <stdio.h>
38*b7b85f24SRobert Watson #include <string.h>
39*b7b85f24SRobert Watson #include <unistd.h>
40*b7b85f24SRobert Watson
41*b7b85f24SRobert Watson /*
42*b7b85f24SRobert Watson * Regression test for multicast sockets and options:
43*b7b85f24SRobert Watson *
44*b7b85f24SRobert Watson * - Check the defaults for ttl, if, and loopback. Make sure they can be set
45*b7b85f24SRobert Watson * and then read.
46*b7b85f24SRobert Watson *
47*b7b85f24SRobert Watson * - Check that adding and removing multicast addresses seems to work.
48*b7b85f24SRobert Watson *
49*b7b85f24SRobert Watson * - Send a test message over loop back multicast and make sure it arrives.
50*b7b85f24SRobert Watson *
51*b7b85f24SRobert Watson * NB:
52*b7b85f24SRobert Watson *
53*b7b85f24SRobert Watson * Would be nice to use BPF or if_tap to actually check packet contents and
54*b7b85f24SRobert Watson * layout, make sure that the ttl is set right, etc.
55*b7b85f24SRobert Watson *
56*b7b85f24SRobert Watson * Would be nice if attempts to use multicast options on TCP sockets returned
57*b7b85f24SRobert Watson * an error, as the docs suggest it might.
58*b7b85f24SRobert Watson */
59*b7b85f24SRobert Watson
60*b7b85f24SRobert Watson #ifdef WARN_TCP
61*b7b85f24SRobert Watson #define WARN_SUCCESS 0x00000001 /* Set for TCP to warn on success. */
62*b7b85f24SRobert Watson #else
63*b7b85f24SRobert Watson #define WARN_SUCCESS 0x00000000
64*b7b85f24SRobert Watson #endif
65*b7b85f24SRobert Watson
66*b7b85f24SRobert Watson /*
67*b7b85f24SRobert Watson * Multicast test address, picked arbitrarily. Will be used with the
68*b7b85f24SRobert Watson * loopback interface.
69*b7b85f24SRobert Watson */
70*b7b85f24SRobert Watson #define TEST_MADDR "224.100.100.100"
71*b7b85f24SRobert Watson
72*b7b85f24SRobert Watson /*
73*b7b85f24SRobert Watson * Test that a given IP socket option (optname) has a default value of
74*b7b85f24SRobert Watson * 'defaultv', that we can set it to 'modifiedv', and use 'fakev' as a dummy
75*b7b85f24SRobert Watson * value that shouldn't be returned at any point during the tests. Perform
76*b7b85f24SRobert Watson * the tests on the raw socket, tcp socket, and upd socket passed.
77*b7b85f24SRobert Watson * 'optstring' is used in printing warnings and errors as needed.
78*b7b85f24SRobert Watson */
79*b7b85f24SRobert Watson static void
test_u_char(int optname,const char * optstring,u_char defaultv,u_char modifiedv,u_char fakev,const char * socktype,int sock,int flags)80*b7b85f24SRobert Watson test_u_char(int optname, const char *optstring, u_char defaultv,
81*b7b85f24SRobert Watson u_char modifiedv, u_char fakev, const char *socktype, int sock,
82*b7b85f24SRobert Watson int flags)
83*b7b85f24SRobert Watson {
84*b7b85f24SRobert Watson socklen_t socklen;
85*b7b85f24SRobert Watson u_char uc;
86*b7b85f24SRobert Watson int ret;
87*b7b85f24SRobert Watson
88*b7b85f24SRobert Watson /*
89*b7b85f24SRobert Watson * Check that we read back the expected default.
90*b7b85f24SRobert Watson */
91*b7b85f24SRobert Watson uc = fakev;
92*b7b85f24SRobert Watson socklen = sizeof(uc);
93*b7b85f24SRobert Watson
94*b7b85f24SRobert Watson ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
95*b7b85f24SRobert Watson if (ret < 0)
96*b7b85f24SRobert Watson err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
97*b7b85f24SRobert Watson socktype, optstring);
98*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
99*b7b85f24SRobert Watson warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
100*b7b85f24SRobert Watson socktype, optstring);
101*b7b85f24SRobert Watson if (uc != defaultv)
102*b7b85f24SRobert Watson errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
103*b7b85f24SRobert Watson "%d not %d", socktype, optstring, uc, defaultv);
104*b7b85f24SRobert Watson
105*b7b85f24SRobert Watson /*
106*b7b85f24SRobert Watson * Set to a modifiedv value, read it back and make sure it got there.
107*b7b85f24SRobert Watson */
108*b7b85f24SRobert Watson uc = modifiedv;
109*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, optname, &uc, sizeof(uc));
110*b7b85f24SRobert Watson if (ret == -1)
111*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
112*b7b85f24SRobert Watson socktype, optstring);
113*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
114*b7b85f24SRobert Watson warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
115*b7b85f24SRobert Watson socktype, optstring);
116*b7b85f24SRobert Watson
117*b7b85f24SRobert Watson uc = fakev;
118*b7b85f24SRobert Watson socklen = sizeof(uc);
119*b7b85f24SRobert Watson ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
120*b7b85f24SRobert Watson if (ret < 0)
121*b7b85f24SRobert Watson err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
122*b7b85f24SRobert Watson socktype, optstring);
123*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
124*b7b85f24SRobert Watson warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
125*b7b85f24SRobert Watson socktype, optstring);
126*b7b85f24SRobert Watson if (uc != modifiedv)
127*b7b85f24SRobert Watson errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
128*b7b85f24SRobert Watson "%d not %d", socktype, optstring, uc, modifiedv);
129*b7b85f24SRobert Watson }
130*b7b85f24SRobert Watson
131*b7b85f24SRobert Watson /*
132*b7b85f24SRobert Watson * test_in_addr() is like test_u_char(), only it runs on a struct in_addr
133*b7b85f24SRobert Watson * (surprise).
134*b7b85f24SRobert Watson */
135*b7b85f24SRobert Watson static void
test_in_addr(int optname,const char * optstring,struct in_addr defaultv,struct in_addr modifiedv,struct in_addr fakev,const char * socktype,int sock,int flags)136*b7b85f24SRobert Watson test_in_addr(int optname, const char *optstring, struct in_addr defaultv,
137*b7b85f24SRobert Watson struct in_addr modifiedv, struct in_addr fakev, const char *socktype,
138*b7b85f24SRobert Watson int sock, int flags)
139*b7b85f24SRobert Watson {
140*b7b85f24SRobert Watson socklen_t socklen;
141*b7b85f24SRobert Watson struct in_addr ia;
142*b7b85f24SRobert Watson int ret;
143*b7b85f24SRobert Watson
144*b7b85f24SRobert Watson /*
145*b7b85f24SRobert Watson * Check that we read back the expected default.
146*b7b85f24SRobert Watson */
147*b7b85f24SRobert Watson ia = fakev;
148*b7b85f24SRobert Watson socklen = sizeof(ia);
149*b7b85f24SRobert Watson
150*b7b85f24SRobert Watson ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
151*b7b85f24SRobert Watson if (ret < 0)
152*b7b85f24SRobert Watson err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
153*b7b85f24SRobert Watson socktype, optstring);
154*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
155*b7b85f24SRobert Watson warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
156*b7b85f24SRobert Watson socktype, optstring);
157*b7b85f24SRobert Watson if (memcmp(&ia, &defaultv, sizeof(struct in_addr)))
158*b7b85f24SRobert Watson errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
159*b7b85f24SRobert Watson "%s not %s", socktype, optstring, inet_ntoa(ia),
160*b7b85f24SRobert Watson inet_ntoa(defaultv));
161*b7b85f24SRobert Watson
162*b7b85f24SRobert Watson /*
163*b7b85f24SRobert Watson * Set to a modifiedv value, read it back and make sure it got there.
164*b7b85f24SRobert Watson */
165*b7b85f24SRobert Watson ia = modifiedv;
166*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, optname, &ia, sizeof(ia));
167*b7b85f24SRobert Watson if (ret == -1)
168*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
169*b7b85f24SRobert Watson socktype, optstring);
170*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
171*b7b85f24SRobert Watson warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
172*b7b85f24SRobert Watson socktype, optstring);
173*b7b85f24SRobert Watson
174*b7b85f24SRobert Watson ia = fakev;
175*b7b85f24SRobert Watson socklen = sizeof(ia);
176*b7b85f24SRobert Watson ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
177*b7b85f24SRobert Watson if (ret < 0)
178*b7b85f24SRobert Watson err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
179*b7b85f24SRobert Watson socktype, optstring);
180*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS))
181*b7b85f24SRobert Watson warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
182*b7b85f24SRobert Watson socktype, optstring);
183*b7b85f24SRobert Watson if (memcmp(&ia, &modifiedv, sizeof(struct in_addr)))
184*b7b85f24SRobert Watson errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
185*b7b85f24SRobert Watson "%s not %s", socktype, optstring, inet_ntoa(ia),
186*b7b85f24SRobert Watson inet_ntoa(modifiedv));
187*b7b85f24SRobert Watson }
188*b7b85f24SRobert Watson
189*b7b85f24SRobert Watson static void
test_ttl(int raw_sock,int tcp_sock,int udp_sock)190*b7b85f24SRobert Watson test_ttl(int raw_sock, int tcp_sock, int udp_sock)
191*b7b85f24SRobert Watson {
192*b7b85f24SRobert Watson
193*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
194*b7b85f24SRobert Watson "raw_sock", raw_sock, 0);
195*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
196*b7b85f24SRobert Watson "tcp_sock", tcp_sock, WARN_SUCCESS);
197*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
198*b7b85f24SRobert Watson "udp_sock", udp_sock, 0);
199*b7b85f24SRobert Watson }
200*b7b85f24SRobert Watson
201*b7b85f24SRobert Watson static void
test_loop(int raw_sock,int tcp_sock,int udp_sock)202*b7b85f24SRobert Watson test_loop(int raw_sock, int tcp_sock, int udp_sock)
203*b7b85f24SRobert Watson {
204*b7b85f24SRobert Watson
205*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
206*b7b85f24SRobert Watson "raw_sock", raw_sock, 0);
207*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
208*b7b85f24SRobert Watson "tcp_sock", tcp_sock, WARN_SUCCESS);
209*b7b85f24SRobert Watson test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
210*b7b85f24SRobert Watson "udp_sock", udp_sock, 0);
211*b7b85f24SRobert Watson }
212*b7b85f24SRobert Watson
213*b7b85f24SRobert Watson static void
test_if(int raw_sock,int tcp_sock,int udp_sock)214*b7b85f24SRobert Watson test_if(int raw_sock, int tcp_sock, int udp_sock)
215*b7b85f24SRobert Watson {
216*b7b85f24SRobert Watson struct in_addr defaultv, modifiedv, fakev;
217*b7b85f24SRobert Watson
218*b7b85f24SRobert Watson defaultv.s_addr = inet_addr("0.0.0.0");
219*b7b85f24SRobert Watson
220*b7b85f24SRobert Watson /* Should be valid on all hosts. */
221*b7b85f24SRobert Watson modifiedv.s_addr = inet_addr("127.0.0.1");
222*b7b85f24SRobert Watson
223*b7b85f24SRobert Watson /* Should not happen. */
224*b7b85f24SRobert Watson fakev.s_addr = inet_addr("255.255.255.255");
225*b7b85f24SRobert Watson
226*b7b85f24SRobert Watson test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
227*b7b85f24SRobert Watson fakev, "raw_sock", raw_sock, 0);
228*b7b85f24SRobert Watson test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
229*b7b85f24SRobert Watson fakev, "tcp_sock", tcp_sock, WARN_SUCCESS);
230*b7b85f24SRobert Watson test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
231*b7b85f24SRobert Watson fakev, "udp_sock", udp_sock, 0);
232*b7b85f24SRobert Watson }
233*b7b85f24SRobert Watson
234*b7b85f24SRobert Watson /*
235*b7b85f24SRobert Watson * Add a multicast address to an interface. Warn if appropriate. No query
236*b7b85f24SRobert Watson * interface so can't check if it's there directly; instead we have to try
237*b7b85f24SRobert Watson * to add it a second time and make sure we get back EADDRINUSE.
238*b7b85f24SRobert Watson */
239*b7b85f24SRobert Watson static void
test_add_multi(int sock,const char * socktype,struct ip_mreq imr,int flags)240*b7b85f24SRobert Watson test_add_multi(int sock, const char *socktype, struct ip_mreq imr,
241*b7b85f24SRobert Watson int flags)
242*b7b85f24SRobert Watson {
243*b7b85f24SRobert Watson char buf[128];
244*b7b85f24SRobert Watson int ret;
245*b7b85f24SRobert Watson
246*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
247*b7b85f24SRobert Watson sizeof(imr));
248*b7b85f24SRobert Watson if (ret < 0) {
249*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
250*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
251*b7b85f24SRobert Watson "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
252*b7b85f24SRobert Watson }
253*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS)) {
254*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
255*b7b85f24SRobert Watson warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
256*b7b85f24SRobert Watson "%s, %s) returned 0", socktype, buf,
257*b7b85f24SRobert Watson inet_ntoa(imr.imr_interface));
258*b7b85f24SRobert Watson }
259*b7b85f24SRobert Watson
260*b7b85f24SRobert Watson /* Try to add a second time to make sure it got there. */
261*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
262*b7b85f24SRobert Watson sizeof(imr));
263*b7b85f24SRobert Watson if (ret == 0) {
264*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
265*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
266*b7b85f24SRobert Watson "%s, %s) dup returned 0", socktype, buf,
267*b7b85f24SRobert Watson inet_ntoa(imr.imr_interface));
268*b7b85f24SRobert Watson }
269*b7b85f24SRobert Watson if (ret < 0 && errno != EADDRINUSE) {
270*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
271*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
272*b7b85f24SRobert Watson "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
273*b7b85f24SRobert Watson }
274*b7b85f24SRobert Watson }
275*b7b85f24SRobert Watson
276*b7b85f24SRobert Watson /*
277*b7b85f24SRobert Watson * Drop a multicast address from an interface. Warn if appropriate. No
278*b7b85f24SRobert Watson * query interface so can't check if it's gone directly; instead we have to
279*b7b85f24SRobert Watson * try to drop it a second time and make sure we get back EADDRNOTAVAIL.
280*b7b85f24SRobert Watson */
281*b7b85f24SRobert Watson static void
test_drop_multi(int sock,const char * socktype,struct ip_mreq imr,int flags)282*b7b85f24SRobert Watson test_drop_multi(int sock, const char *socktype, struct ip_mreq imr,
283*b7b85f24SRobert Watson int flags)
284*b7b85f24SRobert Watson {
285*b7b85f24SRobert Watson char buf[128];
286*b7b85f24SRobert Watson int ret;
287*b7b85f24SRobert Watson
288*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
289*b7b85f24SRobert Watson sizeof(imr));
290*b7b85f24SRobert Watson if (ret < 0) {
291*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
292*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
293*b7b85f24SRobert Watson "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
294*b7b85f24SRobert Watson }
295*b7b85f24SRobert Watson if (ret == 0 && (flags & WARN_SUCCESS)) {
296*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
297*b7b85f24SRobert Watson warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
298*b7b85f24SRobert Watson "%s, %s) returned 0", socktype, buf,
299*b7b85f24SRobert Watson inet_ntoa(imr.imr_interface));
300*b7b85f24SRobert Watson }
301*b7b85f24SRobert Watson
302*b7b85f24SRobert Watson /* Try a second time to make sure it's gone. */
303*b7b85f24SRobert Watson ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
304*b7b85f24SRobert Watson sizeof(imr));
305*b7b85f24SRobert Watson if (ret == 0) {
306*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
307*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
308*b7b85f24SRobert Watson "%s, %s) returned 0", socktype, buf,
309*b7b85f24SRobert Watson inet_ntoa(imr.imr_interface));
310*b7b85f24SRobert Watson }
311*b7b85f24SRobert Watson if (ret < 0 && errno != EADDRNOTAVAIL) {
312*b7b85f24SRobert Watson strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
313*b7b85f24SRobert Watson err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
314*b7b85f24SRobert Watson "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
315*b7b85f24SRobert Watson }
316*b7b85f24SRobert Watson }
317*b7b85f24SRobert Watson
318*b7b85f24SRobert Watson /*
319*b7b85f24SRobert Watson * Should really also test trying to add an invalid address, delete one
320*b7b85f24SRobert Watson * that's not there, etc.
321*b7b85f24SRobert Watson */
322*b7b85f24SRobert Watson static void
test_addr(int raw_sock,int tcp_sock,int udp_sock)323*b7b85f24SRobert Watson test_addr(int raw_sock, int tcp_sock, int udp_sock)
324*b7b85f24SRobert Watson {
325*b7b85f24SRobert Watson struct ip_mreq imr;
326*b7b85f24SRobert Watson
327*b7b85f24SRobert Watson /* Arbitrary. */
328*b7b85f24SRobert Watson imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
329*b7b85f24SRobert Watson
330*b7b85f24SRobert Watson /* Localhost should be OK. */
331*b7b85f24SRobert Watson imr.imr_interface.s_addr = inet_addr("127.0.0.1");
332*b7b85f24SRobert Watson
333*b7b85f24SRobert Watson test_add_multi(raw_sock, "raw_sock", imr, 0);
334*b7b85f24SRobert Watson test_drop_multi(raw_sock, "raw_sock", imr, 0);
335*b7b85f24SRobert Watson
336*b7b85f24SRobert Watson test_add_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
337*b7b85f24SRobert Watson test_drop_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
338*b7b85f24SRobert Watson
339*b7b85f24SRobert Watson test_add_multi(udp_sock, "raw_sock", imr, 0);
340*b7b85f24SRobert Watson test_drop_multi(udp_sock, "raw_sock", imr, 0);
341*b7b85f24SRobert Watson }
342*b7b85f24SRobert Watson
343*b7b85f24SRobert Watson /*
344*b7b85f24SRobert Watson * Test an actual simple UDP message - send a single byte to an address we're
345*b7b85f24SRobert Watson * subscribed to, and hope to get it back. We create a new UDP socket for
346*b7b85f24SRobert Watson * this purpose because we will need to bind it.
347*b7b85f24SRobert Watson */
348*b7b85f24SRobert Watson #define UDP_PORT 5012
349*b7b85f24SRobert Watson static void
test_udp(void)350*b7b85f24SRobert Watson test_udp(void)
351*b7b85f24SRobert Watson {
352*b7b85f24SRobert Watson struct sockaddr_in sin;
353*b7b85f24SRobert Watson struct ip_mreq imr;
354*b7b85f24SRobert Watson struct in_addr if_addr;
355*b7b85f24SRobert Watson char message;
356*b7b85f24SRobert Watson ssize_t len;
357*b7b85f24SRobert Watson int sock;
358*b7b85f24SRobert Watson
359*b7b85f24SRobert Watson sock = socket(PF_INET, SOCK_DGRAM, 0);
360*b7b85f24SRobert Watson if (sock < 0)
361*b7b85f24SRobert Watson err(-1, "FAIL: test_udp: socket(PF_INET, SOCK_DGRAM)");
362*b7b85f24SRobert Watson
363*b7b85f24SRobert Watson if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
364*b7b85f24SRobert Watson err(-1, "FAIL: test_udp: fcntl(F_SETFL, O_NONBLOCK)");
365*b7b85f24SRobert Watson
366*b7b85f24SRobert Watson bzero(&sin, sizeof(sin));
367*b7b85f24SRobert Watson sin.sin_len = sizeof(sin);
368*b7b85f24SRobert Watson sin.sin_family = AF_INET;
369*b7b85f24SRobert Watson sin.sin_port = htons(UDP_PORT);
370*b7b85f24SRobert Watson sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
371*b7b85f24SRobert Watson
372*b7b85f24SRobert Watson if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
373*b7b85f24SRobert Watson err(-1, "FAIL: test_udp: bind(udp_sock, 127.0.0.1:%d",
374*b7b85f24SRobert Watson UDP_PORT);
375*b7b85f24SRobert Watson
376*b7b85f24SRobert Watson /* Arbitrary. */
377*b7b85f24SRobert Watson imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
378*b7b85f24SRobert Watson
379*b7b85f24SRobert Watson /* Localhost should be OK. */
380*b7b85f24SRobert Watson imr.imr_interface.s_addr = inet_addr("127.0.0.1");
381*b7b85f24SRobert Watson
382*b7b85f24SRobert Watson /*
383*b7b85f24SRobert Watson * Tell socket what interface to send on -- use localhost.
384*b7b85f24SRobert Watson */
385*b7b85f24SRobert Watson if_addr.s_addr = inet_addr("127.0.0.1");
386*b7b85f24SRobert Watson if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &if_addr,
387*b7b85f24SRobert Watson sizeof(if_addr)) < 0)
388*b7b85f24SRobert Watson err(-1, "test_udp: setsockopt(IPPROTO_IP, IP_MULTICAST_IF)");
389*b7b85f24SRobert Watson
390*b7b85f24SRobert Watson test_add_multi(sock, "udp_sock", imr, 0);
391*b7b85f24SRobert Watson
392*b7b85f24SRobert Watson bzero(&sin, sizeof(sin));
393*b7b85f24SRobert Watson sin.sin_len = sizeof(sin);
394*b7b85f24SRobert Watson sin.sin_family = AF_INET;
395*b7b85f24SRobert Watson sin.sin_port = htons(UDP_PORT);
396*b7b85f24SRobert Watson sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
397*b7b85f24SRobert Watson
398*b7b85f24SRobert Watson message = 'A';
399*b7b85f24SRobert Watson len = sizeof(message);
400*b7b85f24SRobert Watson len = sendto(sock, &message, len, 0, (struct sockaddr *)&sin,
401*b7b85f24SRobert Watson sizeof(sin));
402*b7b85f24SRobert Watson if (len < 0)
403*b7b85f24SRobert Watson err(-1, "test_udp: sendto");
404*b7b85f24SRobert Watson
405*b7b85f24SRobert Watson if (len != sizeof(message))
406*b7b85f24SRobert Watson errx(-1, "test_udp: sendto: expected to send %d, instead %d",
407*b7b85f24SRobert Watson sizeof(message), len);
408*b7b85f24SRobert Watson
409*b7b85f24SRobert Watson message = 'B';
410*b7b85f24SRobert Watson len = sizeof(sin);
411*b7b85f24SRobert Watson len = recvfrom(sock, &message, sizeof(message), 0,
412*b7b85f24SRobert Watson (struct sockaddr *)&sin, &len);
413*b7b85f24SRobert Watson if (len < 0)
414*b7b85f24SRobert Watson err(-1, "test_udp: recvfrom");
415*b7b85f24SRobert Watson
416*b7b85f24SRobert Watson if (len != sizeof(message))
417*b7b85f24SRobert Watson errx(-1, "test_udp: recvfrom: len %d != message len %d",
418*b7b85f24SRobert Watson len, sizeof(message));
419*b7b85f24SRobert Watson
420*b7b85f24SRobert Watson if (message != 'A')
421*b7b85f24SRobert Watson errx(-1, "test_udp: recvfrom: expected 'A', got '%c'",
422*b7b85f24SRobert Watson message);
423*b7b85f24SRobert Watson
424*b7b85f24SRobert Watson test_drop_multi(sock, "udp_sock", imr, 0);
425*b7b85f24SRobert Watson
426*b7b85f24SRobert Watson close(sock);
427*b7b85f24SRobert Watson }
428*b7b85f24SRobert Watson #undef UDP_PORT
429*b7b85f24SRobert Watson
430*b7b85f24SRobert Watson int
main(int argc,char * argv[])431*b7b85f24SRobert Watson main(int argc, char *argv[])
432*b7b85f24SRobert Watson {
433*b7b85f24SRobert Watson int raw_sock, tcp_sock, udp_sock;
434*b7b85f24SRobert Watson
435*b7b85f24SRobert Watson if (geteuid() != 0)
436*b7b85f24SRobert Watson errx(-1, "FAIL: root privilege required");
437*b7b85f24SRobert Watson
438*b7b85f24SRobert Watson raw_sock = socket(PF_INET, SOCK_RAW, 0);
439*b7b85f24SRobert Watson if (raw_sock == -1)
440*b7b85f24SRobert Watson err(-1, "FAIL: socket(PF_INET, SOCK_RAW)");
441*b7b85f24SRobert Watson
442*b7b85f24SRobert Watson tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
443*b7b85f24SRobert Watson if (raw_sock == -1)
444*b7b85f24SRobert Watson err(-1, "FAIL: socket(PF_INET, SOCK_STREAM)");
445*b7b85f24SRobert Watson
446*b7b85f24SRobert Watson udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
447*b7b85f24SRobert Watson if (raw_sock == -1)
448*b7b85f24SRobert Watson err(-1, "FAIL: socket(PF_INET, SOCK_DGRAM)");
449*b7b85f24SRobert Watson
450*b7b85f24SRobert Watson test_ttl(raw_sock, tcp_sock, udp_sock);
451*b7b85f24SRobert Watson test_loop(raw_sock, tcp_sock, udp_sock);
452*b7b85f24SRobert Watson test_if(raw_sock, tcp_sock, udp_sock);
453*b7b85f24SRobert Watson test_addr(raw_sock, tcp_sock, udp_sock);
454*b7b85f24SRobert Watson
455*b7b85f24SRobert Watson close(udp_sock);
456*b7b85f24SRobert Watson close(tcp_sock);
457*b7b85f24SRobert Watson close(raw_sock);
458*b7b85f24SRobert Watson
459*b7b85f24SRobert Watson test_udp();
460*b7b85f24SRobert Watson
461*b7b85f24SRobert Watson return (0);
462*b7b85f24SRobert Watson }
463