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
32int
33runtest(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
109void
110cleanup(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
117int
118unicast_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
183int
184mcast_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
269int
270mcast6_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
346int
347main(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