1 /* $OpenBSD: runtest.c,v 1.7 2022/04/10 14:08:35 claudio Exp $ */
2 /*
3  * Copyright (c) 2015 Vincent Gross <vincent.gross@kilob.yt>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <err.h>
24 #include <netdb.h>
25 
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <net/if.h>
30 #include <ifaddrs.h>
31 
32 int
runtest(int * sockp,struct addrinfo * ai,int reuseaddr,int reuseport,void * mreq,int expected)33 runtest(int *sockp, struct addrinfo *ai, int reuseaddr, int reuseport,
34     void *mreq, int expected)
35 {
36 	int error, optval;
37 	struct ip_mreq imr;
38 
39 	*sockp = socket(ai->ai_family, ai->ai_socktype, 0);
40 	if (*sockp == -1) {
41 		warn("%s : socket()", ai->ai_canonname);
42 		return (3);
43 	}
44 
45 	if (reuseaddr) {
46 		optval = 1;
47 		error = setsockopt(*sockp, SOL_SOCKET, SO_REUSEADDR,
48 		    &optval, sizeof(int));
49 		if (error) {
50 			warn("%s : setsockopt(SO_REUSEADDR)", ai->ai_canonname);
51 			return (2);
52 		}
53 	}
54 
55 	if (reuseport) {
56 		optval = 1;
57 		error = setsockopt(*sockp, SOL_SOCKET, SO_REUSEPORT,
58 		    &optval, sizeof(int));
59 		if (error) {
60 			warn("%s : setsockopt(SO_REUSEPORT)", ai->ai_canonname);
61 			return (2);
62 		}
63 	}
64 
65 	if (mreq) {
66 		switch (ai->ai_family) {
67 		case AF_INET6:
68 			error = setsockopt(*sockp, IPPROTO_IPV6, IPV6_JOIN_GROUP,
69 			    mreq, sizeof(struct ipv6_mreq));
70 			if (error) {
71 				warn("%s : setsockopt(IPV6_JOIN_GROUP)",
72 				    ai->ai_canonname);
73 				return (2);
74 			}
75 			break;
76 		case AF_INET:
77 			error = setsockopt(*sockp, IPPROTO_IP, IP_ADD_MEMBERSHIP,
78 			    mreq, sizeof(struct ip_mreq));
79 			if (error) {
80 				warn("%s : setsockopt(IP_ADD_MEMBERSHIP)",
81 				    ai->ai_canonname);
82 				return (2);
83 			}
84 			break;
85 		default:
86 			warnx("%s : trying to join multicast group in unknown AF",
87 			    ai->ai_canonname);
88 			return (2);
89 		}
90 	}
91 
92 
93 	error = bind(*sockp, ai->ai_addr, ai->ai_addrlen);
94 	if (error && (expected == 0 || expected != errno)) {
95 		warn("bind(%s,%s,%s)", ai->ai_canonname,
96 		    reuseaddr ? "REUSEADDR" : "", reuseport ? "REUSEPORT" : "");
97 		return (1);
98 	}
99 	if (error == 0 && expected != 0) {
100 		warnx("bind(%s,%s,%s) succeeded, expected : %s", ai->ai_canonname,
101 		    reuseaddr ? "REUSEADDR" : "", reuseport ? "REUSEPORT" : "",
102 		    strerror(expected));
103 		return (1);
104 	}
105 
106 	return (0);
107 }
108 
109 void
cleanup(int * fds,int num_fds)110 cleanup(int *fds, int num_fds)
111 {
112 	while (num_fds-- > 0)
113 		if (close(*fds++) && errno != EBADF)
114 			err(2, "unable to clean up sockets, aborting");
115 }
116 
117 int
unicast_testsuite(struct addrinfo * local,struct addrinfo * any)118 unicast_testsuite(struct addrinfo *local, struct addrinfo *any)
119 {
120 	int test_rc, rc, *s;
121 	int sockets[4];
122 
123 	test_rc = 0;
124 	rc = 0; s = sockets;
125 	rc |= runtest(s++, local, 0, 0, NULL, 0);
126 	rc |= runtest(s++, any,   0, 0, NULL, EADDRINUSE);
127 	rc |= runtest(s++, any,   1, 0, NULL, 0);
128 	cleanup(sockets, 3);
129 	test_rc |= rc;
130 	if (rc)
131 		warnx("%s : test #%d failed", __func__, 1);
132 
133 	rc = 0; s = sockets;
134 	rc |= runtest(s++, any,   0, 0, NULL, 0);
135 	rc |= runtest(s++, local, 0, 0, NULL, EADDRINUSE);
136 	rc |= runtest(s++, local, 1, 0, NULL, 0);
137 	cleanup(sockets, 3);
138 	test_rc |= rc;
139 	if (rc)
140 		warnx("%s : test #%d failed", __func__, 2);
141 
142 	rc = 0; s = sockets;
143 	rc |= runtest(s++, local, 0, 1, NULL, 0);
144 	rc |= runtest(s++, local, 0, 1, NULL, 0);
145 	rc |= runtest(s++, local, 1, 0, NULL, EADDRINUSE);
146 	rc |= runtest(s++, local, 0, 0, NULL, EADDRINUSE);
147 	cleanup(sockets, 4);
148 	test_rc |= rc;
149 	if (rc)
150 		warnx("%s : test #%d failed", __func__, 3);
151 
152 	rc = 0; s = sockets;
153 	rc |= runtest(s++, any, 0, 1, NULL, 0);
154 	rc |= runtest(s++, any, 0, 1, NULL, 0);
155 	rc |= runtest(s++, any, 1, 0, NULL, EADDRINUSE);
156 	rc |= runtest(s++, any, 0, 0, NULL, EADDRINUSE);
157 	cleanup(sockets, 4);
158 	test_rc |= rc;
159 	if (rc)
160 		warnx("%s : test #%d failed", __func__, 4);
161 
162 	rc = 0; s = sockets;
163 	rc |= runtest(s++, local, 1, 0, NULL, 0);
164 	rc |= runtest(s++, local, 1, 0, NULL, EADDRINUSE);
165 	rc |= runtest(s++, local, 0, 1, NULL, EADDRINUSE);
166 	cleanup(sockets, 3);
167 	test_rc |= rc;
168 	if (rc)
169 		warnx("%s : test #%d failed", __func__, 5);
170 
171 	rc = 0; s = sockets;
172 	rc |= runtest(s++, any, 1, 0, NULL, 0);
173 	rc |= runtest(s++, any, 1, 0, NULL, EADDRINUSE);
174 	rc |= runtest(s++, any, 0, 1, NULL, EADDRINUSE);
175 	cleanup(sockets, 3);
176 	test_rc |= rc;
177 	if (rc)
178 		warnx("%s : test #%d failed", __func__, 6);
179 
180 	return (test_rc);
181 }
182 
183 int
mcast_reuse_testsuite(struct addrinfo * local,void * mr)184 mcast_reuse_testsuite(struct addrinfo *local, void *mr)
185 {
186 	int test_rc, rc, *s;
187 	int sockets[6];
188 	int testnum = 1;
189 
190 	test_rc = 0;
191 	rc = 0; s = sockets;
192 	rc |= runtest(s++, local, 0, 0, mr, 0);
193 	rc |= runtest(s++, local, 1, 0, mr, EADDRINUSE);
194 	rc |= runtest(s++, local, 0, 1, mr, EADDRINUSE);
195 	rc |= runtest(s++, local, 1, 1, mr, EADDRINUSE);
196 	cleanup(sockets, 4);
197 	test_rc |= rc;
198 	if (rc)
199 		warnx("%s : test #%d failed", __func__, 1);
200 
201 	rc = 0; s = sockets;
202 	rc |= runtest(s++, local, 0, 1, mr, 0);
203 	rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
204 	rc |= runtest(s++, local, 0, 1, mr, 0);
205 	rc |= runtest(s++, local, 1, 0, mr, 0);
206 	rc |= runtest(s++, local, 1, 1, mr, 0);
207 	cleanup(sockets, 5);
208 	test_rc |= rc;
209 	if (rc)
210 		warnx("%s : test #%d failed", __func__, 2);
211 
212 	rc = 0; s = sockets;
213 	rc |= runtest(s++, local, 1, 0, mr, 0);
214 	rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
215 	rc |= runtest(s++, local, 1, 0, mr, 0);
216 	rc |= runtest(s++, local, 0, 1, mr, 0);
217 	rc |= runtest(s++, local, 1, 1, mr, 0);
218 	cleanup(sockets, 5);
219 	test_rc |= rc;
220 	if (rc)
221 		warnx("%s : test #%d failed", __func__, 3);
222 
223 	rc = 0; s = sockets;
224 	rc |= runtest(s++, local, 1, 1, mr, 0);
225 	rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
226 	rc |= runtest(s++, local, 0, 1, mr, 0);
227 	rc |= runtest(s++, local, 1, 0, mr, 0);
228 	rc |= runtest(s++, local, 1, 1, mr, 0);
229 	cleanup(sockets, 5);
230 	test_rc |= rc;
231 	if (rc)
232 		warnx("%s : test #%d failed", __func__, 4);
233 
234 #if 0
235 	rc = 0; s = sockets;
236 	rc |= runtest(s++, local, 1, 1, mr, 0);
237 	rc |= runtest(s++, local, 1, 0, mr, 0);
238 	rc |= runtest(s++, local, 0, 1, mr, 0);
239 	cleanup(sockets, 3);
240 	test_rc |= rc;
241 	if (rc)
242 		warnx("%s : test #%d failed", __func__, 5);
243 
244 	rc = 0; s = sockets;
245 	rc |= runtest(s++, local, 1, 1, mr, 0);
246 	rc |= runtest(s++, local, 1, 0, mr, 0);
247 	rc |= runtest(s++, local, 1, 0, mr, 0);
248 	rc |= runtest(s++, local, 1, 1, mr, 0);
249 	rc |= runtest(s++, local, 0, 1, mr, 0);
250 	cleanup(sockets, 5);
251 	test_rc |= rc;
252 	if (rc)
253 		warnx("%s : test #%d failed", __func__, 6);
254 
255 	rc = 0; s = sockets;
256 	rc |= runtest(s++, local, 1, 1, mr, 0);
257 	rc |= runtest(s++, local, 1, 0, mr, 0);
258 	rc |= runtest(s++, local, 1, 1, mr, 0);
259 	rc |= runtest(s++, local, 1, 0, mr, 0);
260 	rc |= runtest(s++, local, 0, 1, mr, 0);
261 	cleanup(sockets, 5);
262 	test_rc |= rc;
263 	if (rc)
264 		warnx("%s : test #%d failed", __func__, 7);
265 #endif
266 	return (test_rc);
267 }
268 
269 int
mcast6_testsuite(struct addrinfo * local,struct ipv6_mreq * local_mreq,struct addrinfo * any,struct ipv6_mreq * any_mreq)270 mcast6_testsuite(struct addrinfo *local, struct ipv6_mreq *local_mreq,
271     struct addrinfo *any, struct ipv6_mreq *any_mreq)
272 {
273 	int test_rc, rc, *s;
274 	int sockets[4];
275 	int testnum = 1;
276 
277 	test_rc = 0;
278 	rc = 0; s = sockets;
279 	rc |= runtest(s++, local, 0, 0, local_mreq, 0);
280 	rc |= runtest(s++, any,   0, 0, any_mreq,   EADDRINUSE);
281 	rc |= runtest(s++, any,   1, 0, any_mreq,   0);
282 	cleanup(sockets, 3);
283 	test_rc |= rc;
284 	if (rc)
285 		warnx("%s : test #%d failed", __func__, 1);
286 
287 	rc = 0; s = sockets;
288 	rc |= runtest(s++, any,   0, 0, any_mreq,   0);
289 	rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
290 	rc |= runtest(s++, local, 1, 0, local_mreq, 0);
291 	cleanup(sockets, 3);
292 	test_rc |= rc;
293 	if (rc)
294 		warnx("%s : test #%d failed", __func__, 2);
295 
296 	rc = 0; s = sockets;
297 	rc |= runtest(s++, local, 0, 1, local_mreq, 0);
298 	rc |= runtest(s++, local, 0, 1, local_mreq, 0);
299 	rc |= runtest(s++, local, 1, 0, local_mreq, 0);
300 	rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
301 	cleanup(sockets, 4);
302 	test_rc |= rc;
303 	if (rc)
304 		warnx("%s : test #%d failed", __func__, 3);
305 
306 	/*
307 	 * :: is not a multicast address, SO_REUSEADDR and SO_REUSEPORT
308 	 * keep their unicast semantics although we are binding on multicast
309 	 */
310 
311 	rc = 0; s = sockets;
312 	rc |= runtest(s++, any, 0, 1, any_mreq, 0);
313 	rc |= runtest(s++, any, 0, 1, any_mreq, 0);
314 	rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
315 	rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
316 	cleanup(sockets, 4);
317 	test_rc |= rc;
318 	if (rc)
319 		warnx("%s : test #%d failed", __func__, 4);
320 
321 	rc = 0; s = sockets;
322 	rc |= runtest(s++, local, 1, 0, local_mreq, 0);
323 	rc |= runtest(s++, local, 1, 0, local_mreq, 0);
324 	rc |= runtest(s++, local, 0, 1, local_mreq, 0);
325 	rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
326 	cleanup(sockets, 4);
327 	test_rc |= rc;
328 	if (rc)
329 		warnx("%s : test #%d failed", __func__, 5);
330 
331 	/* See above */
332 
333 	rc = 0; s = sockets;
334 	rc |= runtest(s++, any, 1, 0, any_mreq, 0);
335 	rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
336 	rc |= runtest(s++, any, 0, 1, any_mreq, EADDRINUSE);
337 	rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
338 	cleanup(sockets, 4);
339 	test_rc |= rc;
340 	if (rc)
341 		warnx("%s : test #%d failed", __func__, 6);
342 
343 	return (test_rc);
344 }
345 
346 int
main(int argc,char * argv[])347 main(int argc, char *argv[])
348 {
349 	int error, rc;
350 	char *baddr_s, *bport_s, *bmifa_s;
351 	struct addrinfo hints, *baddr, *any, *mifa;
352 	struct ifaddrs *ifap, *curifa;
353 	struct ip_mreq local_imr, any_imr;
354 	struct ipv6_mreq local_i6mr, any_i6mr;
355 	struct sockaddr_in *sin;
356 	struct sockaddr_in6 *sin6;
357 	int *s;
358 
359 	memset(&hints, 0, sizeof(hints));
360 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV | \
361 	    AI_PASSIVE;
362 	hints.ai_socktype = SOCK_DGRAM;
363 
364 	baddr_s = argv[1];
365 	bport_s = argv[2];
366 
367 	if ((error = getaddrinfo(baddr_s, bport_s, &hints, &baddr)))
368 		errx(2, "getaddrinfo(%s,%s): %s", baddr_s, bport_s,
369 		    gai_strerror(error));
370 	baddr->ai_canonname = baddr_s;
371 
372 	hints.ai_family = baddr->ai_family;
373 	if ((error = getaddrinfo(NULL, bport_s, &hints, &any)))
374 		errx(2, "getaddrinfo(NULL,%s): %s", bport_s,
375 		    gai_strerror(error));
376 	any->ai_canonname = "*";
377 
378 	switch (baddr->ai_family) {
379 	case AF_INET:
380 		sin = (struct sockaddr_in *)baddr->ai_addr;
381 		if (!IN_MULTICAST( ntohl(sin->sin_addr.s_addr) )) {
382 			puts("executing unicast testsuite");
383 			return unicast_testsuite(baddr, any);
384 		}
385 		bmifa_s = argv[3];
386 		hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
387 
388 		if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
389 			errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
390 			    gai_strerror(error));
391 
392 		local_imr.imr_interface =
393 		    ((struct sockaddr_in *)mifa->ai_addr)->sin_addr;
394 		local_imr.imr_multiaddr =
395 		    ((struct sockaddr_in *)baddr->ai_addr)->sin_addr;
396 
397 		puts("executing ipv4 multicast testsuite");
398 
399 		/* no 'any' mcast group in ipv4 */
400 		return mcast_reuse_testsuite(baddr, &local_imr);
401 	case AF_INET6:
402 		sin6 = (struct sockaddr_in6 *)baddr->ai_addr;
403 		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
404 			puts("executing unicast testsuite");
405 			return unicast_testsuite(baddr, any);
406 		}
407 		bmifa_s = argv[3];
408 		hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
409 
410 		if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
411 			errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
412 			    gai_strerror(error));
413 
414 		if (getifaddrs(&ifap))
415 			err(2, "getifaddrs()");
416 		curifa = ifap;
417 		while (curifa) {
418 			if (curifa->ifa_addr != NULL &&
419 			    memcmp(curifa->ifa_addr,
420 			    mifa->ai_addr,
421 			    mifa->ai_addrlen) == 0)
422 				break;
423 			curifa = curifa->ifa_next;
424 		}
425 		if (curifa == NULL)
426 			errx(2, "no interface configured with %s", argv[4]);
427 		local_i6mr.ipv6mr_interface =
428 		    if_nametoindex(curifa->ifa_name);
429 		if (local_i6mr.ipv6mr_interface == 0)
430 			errx(2, "unable to get \"%s\" index",
431 			    curifa->ifa_name);
432 		freeifaddrs(ifap);
433 
434 		local_i6mr.ipv6mr_multiaddr =
435 		    ((struct sockaddr_in6 *)baddr->ai_addr)->sin6_addr;
436 
437 		any_i6mr.ipv6mr_interface = local_i6mr.ipv6mr_interface;
438 		any_i6mr.ipv6mr_multiaddr =
439 		    ((struct sockaddr_in6 *)any->ai_addr)->sin6_addr;
440 
441 		puts("executing ipv6 multicast testsuite");
442 
443 		rc = 0;
444 		rc |= mcast_reuse_testsuite(baddr, &local_i6mr);
445 		if (geteuid() == 0)
446 			rc |= mcast6_testsuite(baddr, &local_i6mr, any, &any_i6mr);
447 		else
448 			warnx("skipping mcast6_testsuite() due to insufficient privs, please run again as root");
449 		return (rc);
450 	default:
451 		errx(2,"unknown AF");
452 	}
453 
454 	return (2);
455 }
456