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 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 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 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 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 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 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