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