1 /* $NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 #ifdef __RCSID 33 __RCSID("$NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $"); 34 #else 35 extern const char *__progname; 36 #define getprogname() __progname 37 #endif 38 39 #include <sys/types.h> 40 #include <sys/socket.h> 41 #include <sys/wait.h> 42 #include <sys/time.h> 43 #include <netinet/in.h> 44 45 #include <assert.h> 46 #include <netdb.h> 47 #include <time.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <poll.h> 56 #include <stdbool.h> 57 58 #ifdef ATF 59 #include <atf-c.h> 60 61 #define ERRX(ev, msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) 62 #define ERRX0(ev, msg) ATF_REQUIRE_MSG(0, msg) 63 64 #define SKIPX(ev, msg, ...) do { \ 65 atf_tc_skip(msg, __VA_ARGS__); \ 66 return; \ 67 } while(/*CONSTCOND*/0) 68 69 #else 70 #define ERRX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) 71 #define ERRX0(ev, msg) errx(ev, msg) 72 #define SKIPX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) 73 #endif 74 75 static int debug; 76 static int nsleep; 77 78 #define TOTAL 10 79 #define PORT_V4MAPPED "6666" 80 #define HOST_V4MAPPED "::FFFF:239.1.1.1" 81 #define PORT_V4 "6666" 82 #define HOST_V4 "239.1.1.1" 83 #define PORT_V6 "6666" 84 #define HOST_V6 "FF05:1:0:0:0:0:0:1" 85 86 struct message { 87 size_t seq; 88 struct timespec ts; 89 }; 90 91 static int 92 addmc(int s, struct addrinfo *ai, bool bug) 93 { 94 struct ip_mreq m4; 95 struct ipv6_mreq m6; 96 struct sockaddr_in *s4; 97 struct sockaddr_in6 *s6; 98 unsigned int ifc; 99 100 switch (ai->ai_family) { 101 case AF_INET: 102 s4 = (void *)ai->ai_addr; 103 assert(sizeof(*s4) == ai->ai_addrlen); 104 m4.imr_multiaddr = s4->sin_addr; 105 m4.imr_interface.s_addr = htonl(INADDR_ANY); 106 return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 107 &m4, sizeof(m4)); 108 case AF_INET6: 109 s6 = (void *)ai->ai_addr; 110 /* 111 * Linux: Does not support the v6 ioctls on v4 mapped 112 * sockets but it does support the v4 ones and 113 * it works. 114 * MacOS/X: Supports the v6 ioctls on v4 mapped sockets, 115 * but does not work and also does not support 116 * the v4 ioctls. So no way to make multicasting 117 * work with mapped addresses. 118 * NetBSD: Supports both and works for both. 119 */ 120 if (bug && IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) { 121 memcpy(&m4.imr_multiaddr, &s6->sin6_addr.s6_addr[12], 122 sizeof(m4.imr_multiaddr)); 123 m4.imr_interface.s_addr = htonl(INADDR_ANY); 124 return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 125 &m4, sizeof(m4)); 126 } 127 assert(sizeof(*s6) == ai->ai_addrlen); 128 memset(&m6, 0, sizeof(m6)); 129 #if 0 130 ifc = 1; 131 if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 132 &ifc, sizeof(ifc)) == -1) 133 return -1; 134 ifc = 224; 135 if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 136 &ifc, sizeof(ifc)) == -1) 137 return -1; 138 ifc = 1; /* XXX should pick a proper interface */ 139 if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifc, 140 sizeof(ifc)) == -1) 141 return -1; 142 #else 143 ifc = 0; /* Let pick an appropriate interface */ 144 #endif 145 m6.ipv6mr_interface = ifc; 146 m6.ipv6mr_multiaddr = s6->sin6_addr; 147 return setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, 148 &m6, sizeof(m6)); 149 default: 150 errno = EOPNOTSUPP; 151 return -1; 152 } 153 } 154 155 static int 156 allowv4mapped(int s, struct addrinfo *ai) 157 { 158 struct sockaddr_in6 *s6; 159 int zero = 0; 160 161 if (ai->ai_family != AF_INET6) 162 return 0; 163 164 s6 = (void *)ai->ai_addr; 165 166 if (!IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) 167 return 0; 168 return setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)); 169 } 170 171 static struct sockaddr_storage ss; 172 static int 173 connector(int fd, const struct sockaddr *sa, socklen_t slen) 174 { 175 assert(sizeof(ss) > slen); 176 memcpy(&ss, sa, slen); 177 return 0; 178 } 179 180 static void 181 show(const char *prefix, const struct message *msg) 182 { 183 printf("%10.10s: %zu [%jd.%ld]\n", prefix, msg->seq, (intmax_t) 184 msg->ts.tv_sec, msg->ts.tv_nsec); 185 } 186 187 static int 188 getsocket(const char *host, const char *port, 189 int (*f)(int, const struct sockaddr *, socklen_t), socklen_t *slen, 190 bool bug) 191 { 192 int e, s, lasterrno = 0; 193 struct addrinfo hints, *ai0, *ai; 194 const char *cause = "?"; 195 196 memset(&hints, 0, sizeof(hints)); 197 hints.ai_family = AF_UNSPEC; 198 hints.ai_socktype = SOCK_DGRAM; 199 e = getaddrinfo(host, port, &hints, &ai0); 200 if (e) 201 ERRX(EXIT_FAILURE, "Can't resolve %s:%s (%s)", host, port, 202 gai_strerror(e)); 203 204 s = -1; 205 for (ai = ai0; ai; ai = ai->ai_next) { 206 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 207 if (s == -1) { 208 lasterrno = errno; 209 cause = "socket"; 210 continue; 211 } 212 if (allowv4mapped(s, ai) == -1) { 213 cause = "allow v4 mapped"; 214 goto out; 215 } 216 if ((*f)(s, ai->ai_addr, ai->ai_addrlen) == -1) { 217 cause = f == bind ? "bind" : "connect"; 218 goto out; 219 } 220 if ((f == bind || f == connector) && addmc(s, ai, bug) == -1) { 221 cause = "join group"; 222 goto out; 223 } 224 *slen = ai->ai_addrlen; 225 break; 226 out: 227 lasterrno = errno; 228 close(s); 229 s = -1; 230 continue; 231 } 232 freeaddrinfo(ai0); 233 if (s == -1) 234 ERRX(EXIT_FAILURE, "%s (%s)", cause, strerror(lasterrno)); 235 return s; 236 } 237 238 static int 239 synchronize(const int fd, bool waiter) 240 { 241 int syncmsg = 0; 242 int r; 243 struct pollfd pfd; 244 245 if (waiter) { 246 pfd.fd = fd; 247 pfd.events = POLLIN; 248 249 /* We use poll to avoid lock up when the peer died unexpectedly */ 250 r = poll(&pfd, 1, 10000); 251 if (r == -1) 252 ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno)); 253 if (r == 0) 254 /* Timed out */ 255 return -1; 256 257 if (read(fd, &syncmsg, sizeof(syncmsg)) == -1) 258 ERRX(EXIT_FAILURE, "read (%s)", strerror(errno)); 259 } else { 260 if (write(fd, &syncmsg, sizeof(syncmsg)) == -1) 261 ERRX(EXIT_FAILURE, "write (%s)", strerror(errno)); 262 } 263 264 return 0; 265 } 266 267 static int 268 sender(const int fd, const char *host, const char *port, size_t n, bool conn, 269 bool bug) 270 { 271 int s; 272 ssize_t l; 273 struct message msg; 274 275 socklen_t slen; 276 277 s = getsocket(host, port, conn ? connect : connector, &slen, bug); 278 279 /* Wait until receiver gets ready. */ 280 if (synchronize(fd, true) == -1) 281 return -1; 282 283 for (msg.seq = 0; msg.seq < n; msg.seq++) { 284 #ifdef CLOCK_MONOTONIC 285 if (clock_gettime(CLOCK_MONOTONIC, &msg.ts) == -1) 286 ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); 287 #else 288 struct timeval tv; 289 if (gettimeofday(&tv, NULL) == -1) 290 ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); 291 msg.ts.tv_sec = tv.tv_sec; 292 msg.ts.tv_nsec = tv.tv_usec * 1000; 293 #endif 294 if (debug) 295 show("sending", &msg); 296 l = conn ? send(s, &msg, sizeof(msg), 0) : 297 sendto(s, &msg, sizeof(msg), 0, (void *)&ss, slen); 298 if (l == -1) 299 ERRX(EXIT_FAILURE, "send (%s)", strerror(errno)); 300 usleep(100); 301 } 302 303 /* Wait until receiver finishes its work. */ 304 if (synchronize(fd, true) == -1) 305 return -1; 306 307 return 0; 308 } 309 310 static void 311 receiver(const int fd, const char *host, const char *port, size_t n, bool conn, 312 bool bug) 313 { 314 int s; 315 ssize_t l; 316 size_t seq; 317 struct message msg; 318 struct pollfd pfd; 319 socklen_t slen; 320 321 s = getsocket(host, port, bind, &slen, bug); 322 pfd.fd = s; 323 pfd.events = POLLIN; 324 325 /* Tell I'm ready */ 326 synchronize(fd, false); 327 328 for (seq = 0; seq < n; seq++) { 329 if (poll(&pfd, 1, 10000) == -1) 330 ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno)); 331 l = conn ? recv(s, &msg, sizeof(msg), 0) : 332 recvfrom(s, &msg, sizeof(msg), 0, (void *)&ss, &slen); 333 if (l == -1) 334 ERRX(EXIT_FAILURE, "recv (%s)", strerror(errno)); 335 if (debug) 336 show("got", &msg); 337 if (seq != msg.seq) 338 ERRX(EXIT_FAILURE, "seq: expect=%zu actual=%zu", 339 seq, msg.seq); 340 } 341 342 if (nsleep) 343 sleep(nsleep); 344 /* Tell I'm finished */ 345 synchronize(fd, false); 346 } 347 348 static void 349 run(const char *host, const char *port, size_t n, bool conn, bool bug) 350 { 351 pid_t pid; 352 int status; 353 int syncfds[2]; 354 int error; 355 356 if (socketpair(AF_UNIX, SOCK_STREAM, 0, syncfds) == -1) 357 ERRX(EXIT_FAILURE, "socketpair (%s)", strerror(errno)); 358 359 switch ((pid = fork())) { 360 case 0: 361 receiver(syncfds[0], host, port, n, conn, bug); 362 return; 363 case -1: 364 ERRX(EXIT_FAILURE, "fork (%s)", strerror(errno)); 365 default: 366 error = sender(syncfds[1], host, port, n, conn, bug); 367 again: 368 switch (waitpid(pid, &status, WNOHANG)) { 369 case -1: 370 ERRX(EXIT_FAILURE, "wait (%s)", strerror(errno)); 371 case 0: 372 if (error == 0) 373 /* 374 * Receiver is still alive, but we know 375 * it will exit soon. 376 */ 377 goto again; 378 379 if (kill(pid, SIGTERM) == -1) 380 ERRX(EXIT_FAILURE, "kill (%s)", 381 strerror(errno)); 382 goto again; 383 default: 384 if (WIFSIGNALED(status)) { 385 if (WTERMSIG(status) == SIGTERM) 386 ERRX0(EXIT_FAILURE, 387 "receiver failed and was killed" \ 388 "by sender"); 389 else 390 ERRX(EXIT_FAILURE, 391 "receiver got signaled (%s)", 392 strsignal(WTERMSIG(status))); 393 } else if (WIFEXITED(status)) { 394 if (WEXITSTATUS(status) != 0) 395 ERRX(EXIT_FAILURE, 396 "receiver exited with status %d", 397 WEXITSTATUS(status)); 398 } else { 399 ERRX(EXIT_FAILURE, 400 "receiver exited with unexpected status %d", 401 status); 402 } 403 break; 404 } 405 return; 406 } 407 } 408 409 #ifndef ATF 410 int 411 main(int argc, char *argv[]) 412 { 413 const char *host, *port; 414 int c; 415 size_t n; 416 bool conn, bug; 417 418 host = HOST_V4; 419 port = PORT_V4; 420 n = TOTAL; 421 bug = conn = false; 422 423 while ((c = getopt(argc, argv, "46bcdmn:s:")) != -1) 424 switch (c) { 425 case '4': 426 host = HOST_V4; 427 port = PORT_V4; 428 break; 429 case '6': 430 host = HOST_V6; 431 port = PORT_V6; 432 break; 433 case 'b': 434 bug = true; 435 break; 436 case 'c': 437 conn = true; 438 break; 439 case 'd': 440 debug++; 441 break; 442 case 'm': 443 host = HOST_V4MAPPED; 444 port = PORT_V4MAPPED; 445 break; 446 case 'n': 447 n = atoi(optarg); 448 break; 449 case 's': 450 nsleep = atoi(optarg); 451 break; 452 default: 453 fprintf(stderr, "Usage: %s [-cdm46] [-n <tot>]" 454 " [-s <sleep>]", 455 getprogname()); 456 return 1; 457 } 458 459 run(host, port, n, conn, bug); 460 return 0; 461 } 462 #else 463 464 ATF_TC(conninet4); 465 ATF_TC_HEAD(conninet4, tc) 466 { 467 atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv4"); 468 } 469 470 ATF_TC_BODY(conninet4, tc) 471 { 472 run(HOST_V4, PORT_V4, TOTAL, true, false); 473 } 474 475 ATF_TC(connmappedinet4); 476 ATF_TC_HEAD(connmappedinet4, tc) 477 { 478 atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4"); 479 } 480 481 ATF_TC_BODY(connmappedinet4, tc) 482 { 483 run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, false); 484 } 485 486 ATF_TC(connmappedbuginet4); 487 ATF_TC_HEAD(connmappedbuginet4, tc) 488 { 489 atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4 using the v4 ioctls"); 490 } 491 492 ATF_TC_BODY(connmappedbuginet4, tc) 493 { 494 run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, true); 495 } 496 497 ATF_TC(conninet6); 498 ATF_TC_HEAD(conninet6, tc) 499 { 500 atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv6"); 501 } 502 503 ATF_TC_BODY(conninet6, tc) 504 { 505 run(HOST_V6, PORT_V6, TOTAL, true, false); 506 } 507 508 ATF_TC(unconninet4); 509 ATF_TC_HEAD(unconninet4, tc) 510 { 511 atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv4"); 512 } 513 514 ATF_TC_BODY(unconninet4, tc) 515 { 516 run(HOST_V4, PORT_V4, TOTAL, false, false); 517 } 518 519 ATF_TC(unconnmappedinet4); 520 ATF_TC_HEAD(unconnmappedinet4, tc) 521 { 522 atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4"); 523 } 524 525 ATF_TC_BODY(unconnmappedinet4, tc) 526 { 527 run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, false); 528 } 529 530 ATF_TC(unconnmappedbuginet4); 531 ATF_TC_HEAD(unconnmappedbuginet4, tc) 532 { 533 atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4 using the v4 ioctls"); 534 } 535 536 ATF_TC_BODY(unconnmappedbuginet4, tc) 537 { 538 run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, true); 539 } 540 541 ATF_TC(unconninet6); 542 ATF_TC_HEAD(unconninet6, tc) 543 { 544 atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv6"); 545 } 546 547 ATF_TC_BODY(unconninet6, tc) 548 { 549 run(HOST_V6, PORT_V6, TOTAL, false, false); 550 } 551 552 ATF_TP_ADD_TCS(tp) 553 { 554 debug++; 555 ATF_TP_ADD_TC(tp, conninet4); 556 ATF_TP_ADD_TC(tp, connmappedinet4); 557 ATF_TP_ADD_TC(tp, connmappedbuginet4); 558 ATF_TP_ADD_TC(tp, conninet6); 559 ATF_TP_ADD_TC(tp, unconninet4); 560 ATF_TP_ADD_TC(tp, unconnmappedinet4); 561 ATF_TP_ADD_TC(tp, unconnmappedbuginet4); 562 ATF_TP_ADD_TC(tp, unconninet6); 563 564 return atf_no_error(); 565 } 566 #endif 567