1 /* $NetBSD: inpcb_bind.c,v 1.2 2022/11/17 08:38:58 ozaki-r Exp $ */
2 /* $OpenBSD: runtest.c,v 1.7 2022/04/10 14:08:35 claudio Exp $ */
3 /*
4 * Copyright (c) 2015 Vincent Gross <vincent.gross@kilob.yt>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <err.h>
25 #include <netdb.h>
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <net/if.h>
31 #include <ifaddrs.h>
32
33 static int
runtest(int * sockp,struct addrinfo * ai,int reuseaddr,int reuseport,void * mreq,int expected)34 runtest(int *sockp, struct addrinfo *ai, int reuseaddr, int reuseport,
35 void *mreq, int expected)
36 {
37 int error, optval;
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 static 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 static 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 static 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
189 test_rc = 0;
190 rc = 0; s = sockets;
191 rc |= runtest(s++, local, 0, 0, mr, 0);
192 rc |= runtest(s++, local, 1, 0, mr, EADDRINUSE);
193 rc |= runtest(s++, local, 0, 1, mr, EADDRINUSE);
194 rc |= runtest(s++, local, 1, 1, mr, EADDRINUSE);
195 cleanup(sockets, 4);
196 test_rc |= rc;
197 if (rc)
198 warnx("%s : test #%d failed", __func__, 1);
199
200 rc = 0; s = sockets;
201 rc |= runtest(s++, local, 0, 1, mr, 0);
202 rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
203 rc |= runtest(s++, local, 0, 1, mr, 0);
204 rc |= runtest(s++, local, 1, 0, mr, 0);
205 rc |= runtest(s++, local, 1, 1, mr, 0);
206 cleanup(sockets, 5);
207 test_rc |= rc;
208 if (rc)
209 warnx("%s : test #%d failed", __func__, 2);
210
211 rc = 0; s = sockets;
212 rc |= runtest(s++, local, 1, 0, mr, 0);
213 rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
214 rc |= runtest(s++, local, 1, 0, mr, 0);
215 rc |= runtest(s++, local, 0, 1, mr, 0);
216 rc |= runtest(s++, local, 1, 1, mr, 0);
217 cleanup(sockets, 5);
218 test_rc |= rc;
219 if (rc)
220 warnx("%s : test #%d failed", __func__, 3);
221
222 rc = 0; s = sockets;
223 rc |= runtest(s++, local, 1, 1, mr, 0);
224 rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
225 rc |= runtest(s++, local, 0, 1, mr, 0);
226 rc |= runtest(s++, local, 1, 0, mr, 0);
227 rc |= runtest(s++, local, 1, 1, mr, 0);
228 cleanup(sockets, 5);
229 test_rc |= rc;
230 if (rc)
231 warnx("%s : test #%d failed", __func__, 4);
232
233 #if 0
234 rc = 0; s = sockets;
235 rc |= runtest(s++, local, 1, 1, mr, 0);
236 rc |= runtest(s++, local, 1, 0, mr, 0);
237 rc |= runtest(s++, local, 0, 1, mr, 0);
238 cleanup(sockets, 3);
239 test_rc |= rc;
240 if (rc)
241 warnx("%s : test #%d failed", __func__, 5);
242
243 rc = 0; s = sockets;
244 rc |= runtest(s++, local, 1, 1, mr, 0);
245 rc |= runtest(s++, local, 1, 0, mr, 0);
246 rc |= runtest(s++, local, 1, 0, mr, 0);
247 rc |= runtest(s++, local, 1, 1, mr, 0);
248 rc |= runtest(s++, local, 0, 1, mr, 0);
249 cleanup(sockets, 5);
250 test_rc |= rc;
251 if (rc)
252 warnx("%s : test #%d failed", __func__, 6);
253
254 rc = 0; s = sockets;
255 rc |= runtest(s++, local, 1, 1, mr, 0);
256 rc |= runtest(s++, local, 1, 0, mr, 0);
257 rc |= runtest(s++, local, 1, 1, mr, 0);
258 rc |= runtest(s++, local, 1, 0, mr, 0);
259 rc |= runtest(s++, local, 0, 1, mr, 0);
260 cleanup(sockets, 5);
261 test_rc |= rc;
262 if (rc)
263 warnx("%s : test #%d failed", __func__, 7);
264 #endif
265 return (test_rc);
266 }
267
268 static int
mcast6_testsuite(struct addrinfo * local,struct ipv6_mreq * local_mreq,struct addrinfo * any,struct ipv6_mreq * any_mreq)269 mcast6_testsuite(struct addrinfo *local, struct ipv6_mreq *local_mreq,
270 struct addrinfo *any, struct ipv6_mreq *any_mreq)
271 {
272 int test_rc, rc, *s;
273 int sockets[4];
274
275 test_rc = 0;
276 rc = 0; s = sockets;
277 rc |= runtest(s++, local, 0, 0, local_mreq, 0);
278 rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
279 rc |= runtest(s++, any, 1, 0, any_mreq, 0);
280 cleanup(sockets, 3);
281 test_rc |= rc;
282 if (rc)
283 warnx("%s : test #%d failed", __func__, 1);
284
285 rc = 0; s = sockets;
286 rc |= runtest(s++, any, 0, 0, any_mreq, 0);
287 rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
288 rc |= runtest(s++, local, 1, 0, local_mreq, 0);
289 cleanup(sockets, 3);
290 test_rc |= rc;
291 if (rc)
292 warnx("%s : test #%d failed", __func__, 2);
293
294 rc = 0; s = sockets;
295 rc |= runtest(s++, local, 0, 1, local_mreq, 0);
296 rc |= runtest(s++, local, 0, 1, local_mreq, 0);
297 rc |= runtest(s++, local, 1, 0, local_mreq, 0);
298 rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
299 cleanup(sockets, 4);
300 test_rc |= rc;
301 if (rc)
302 warnx("%s : test #%d failed", __func__, 3);
303
304 /*
305 * :: is not a multicast address, SO_REUSEADDR and SO_REUSEPORT
306 * keep their unicast semantics although we are binding on multicast
307 */
308
309 rc = 0; s = sockets;
310 rc |= runtest(s++, any, 0, 1, any_mreq, 0);
311 rc |= runtest(s++, any, 0, 1, any_mreq, 0);
312 rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
313 rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
314 cleanup(sockets, 4);
315 test_rc |= rc;
316 if (rc)
317 warnx("%s : test #%d failed", __func__, 4);
318
319 rc = 0; s = sockets;
320 rc |= runtest(s++, local, 1, 0, local_mreq, 0);
321 rc |= runtest(s++, local, 1, 0, local_mreq, 0);
322 rc |= runtest(s++, local, 0, 1, local_mreq, 0);
323 rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
324 cleanup(sockets, 4);
325 test_rc |= rc;
326 if (rc)
327 warnx("%s : test #%d failed", __func__, 5);
328
329 /* See above */
330
331 rc = 0; s = sockets;
332 rc |= runtest(s++, any, 1, 0, any_mreq, 0);
333 rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
334 rc |= runtest(s++, any, 0, 1, any_mreq, EADDRINUSE);
335 rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
336 cleanup(sockets, 4);
337 test_rc |= rc;
338 if (rc)
339 warnx("%s : test #%d failed", __func__, 6);
340
341 return (test_rc);
342 }
343
344 int
main(int argc,char * argv[])345 main(int argc, char *argv[])
346 {
347 int error, rc;
348 char *baddr_s, *bport_s, *bmifa_s;
349 struct addrinfo hints, *baddr, *any, *mifa;
350 struct ifaddrs *ifap, *curifa;
351 struct ip_mreq local_imr;
352 struct ipv6_mreq local_i6mr, any_i6mr;
353 struct sockaddr_in *sin;
354 struct sockaddr_in6 *sin6;
355
356 memset(&hints, 0, sizeof(hints));
357 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV | \
358 AI_PASSIVE;
359 hints.ai_socktype = SOCK_DGRAM;
360
361 baddr_s = argv[1];
362 bport_s = argv[2];
363
364 if ((error = getaddrinfo(baddr_s, bport_s, &hints, &baddr)))
365 errx(2, "getaddrinfo(%s,%s): %s", baddr_s, bport_s,
366 gai_strerror(error));
367 baddr->ai_canonname = baddr_s;
368
369 hints.ai_family = baddr->ai_family;
370 if ((error = getaddrinfo(NULL, bport_s, &hints, &any)))
371 errx(2, "getaddrinfo(NULL,%s): %s", bport_s,
372 gai_strerror(error));
373 any->ai_canonname = strdup("*");
374
375 switch (baddr->ai_family) {
376 case AF_INET:
377 sin = (struct sockaddr_in *)baddr->ai_addr;
378 if (!IN_MULTICAST( ntohl(sin->sin_addr.s_addr) )) {
379 puts("executing unicast testsuite");
380 return unicast_testsuite(baddr, any);
381 }
382 bmifa_s = argv[3];
383 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
384
385 if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
386 errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
387 gai_strerror(error));
388
389 local_imr.imr_interface =
390 ((struct sockaddr_in *)mifa->ai_addr)->sin_addr;
391 local_imr.imr_multiaddr =
392 ((struct sockaddr_in *)baddr->ai_addr)->sin_addr;
393
394 puts("executing ipv4 multicast testsuite");
395
396 /* no 'any' mcast group in ipv4 */
397 return mcast_reuse_testsuite(baddr, &local_imr);
398 case AF_INET6:
399 sin6 = (struct sockaddr_in6 *)baddr->ai_addr;
400 if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
401 puts("executing unicast testsuite");
402 return unicast_testsuite(baddr, any);
403 }
404 bmifa_s = argv[3];
405 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
406
407 if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
408 errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
409 gai_strerror(error));
410
411 if (getifaddrs(&ifap))
412 err(2, "getifaddrs()");
413 curifa = ifap;
414 while (curifa) {
415 if (curifa->ifa_addr != NULL &&
416 memcmp(curifa->ifa_addr,
417 mifa->ai_addr,
418 mifa->ai_addrlen) == 0)
419 break;
420 curifa = curifa->ifa_next;
421 }
422 if (curifa == NULL)
423 errx(2, "no interface configured with %s", argv[4]);
424 local_i6mr.ipv6mr_interface =
425 if_nametoindex(curifa->ifa_name);
426 if (local_i6mr.ipv6mr_interface == 0)
427 errx(2, "unable to get \"%s\" index",
428 curifa->ifa_name);
429 freeifaddrs(ifap);
430
431 local_i6mr.ipv6mr_multiaddr =
432 ((struct sockaddr_in6 *)baddr->ai_addr)->sin6_addr;
433
434 any_i6mr.ipv6mr_interface = local_i6mr.ipv6mr_interface;
435 any_i6mr.ipv6mr_multiaddr =
436 ((struct sockaddr_in6 *)any->ai_addr)->sin6_addr;
437
438 puts("executing ipv6 multicast testsuite");
439
440 rc = 0;
441 rc |= mcast_reuse_testsuite(baddr, &local_i6mr);
442 if (geteuid() == 0)
443 rc |= mcast6_testsuite(baddr, &local_i6mr, any, &any_i6mr);
444 else
445 warnx("skipping mcast6_testsuite() due to insufficient privs, please run again as root");
446 return (rc);
447 default:
448 errx(2,"unknown AF");
449 }
450
451 return (2);
452 }
453