1 /*- 2 * Copyright (c) 2007 Bruce M. Simpson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * Regression test utility for RFC 3678 Advanced Multicast API in FreeBSD. 29 * 30 * TODO: Test the SSM paths. 31 * TODO: Support INET6. The code has been written to facilitate this later. 32 * TODO: Merge multicast socket option tests from ipsockopt. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/ioctl.h> 38 #include <sys/socket.h> 39 40 #include <net/if.h> 41 #include <net/if_dl.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <netdb.h> 45 46 #include <assert.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <getopt.h> 50 #include <libgen.h> 51 #include <pwd.h> 52 #include <setjmp.h> 53 #include <signal.h> 54 #include <stddef.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <sysexits.h> 59 #include <time.h> 60 #include <unistd.h> 61 62 #ifndef __SOCKUNION_DECLARED 63 union sockunion { 64 struct sockaddr_storage ss; 65 struct sockaddr sa; 66 struct sockaddr_dl sdl; 67 struct sockaddr_in sin; 68 #ifdef INET6 69 struct sockaddr_in6 sin6; 70 #endif 71 }; 72 typedef union sockunion sockunion_t; 73 #define __SOCKUNION_DECLARED 74 #endif /* __SOCKUNION_DECLARED */ 75 76 #define ADDRBUF_LEN 16 77 #define DEFAULT_GROUP_STR "238.1.1.0" 78 #define DEFAULT_IFNAME "lo0" 79 #define DEFAULT_IFADDR_STR "127.0.0.1" 80 #define DEFAULT_PORT 6698 81 #define DEFAULT_TIMEOUT 0 /* don't wait for traffic */ 82 #define RXBUFSIZE 2048 83 84 static sockunion_t basegroup; 85 static const char *basegroup_str = NULL; 86 static int dobindaddr = 0; 87 static int dodebug = 1; 88 static int doipv4 = 0; 89 static int domiscopts = 0; 90 static int dorandom = 0; 91 static int doreuseport = 0; 92 static int dossm = 0; 93 static int dossf = 0; 94 static int doverbose = 0; 95 static sockunion_t ifaddr; 96 static const char *ifaddr_str = NULL; 97 static uint32_t ifindex = 0; 98 static const char *ifname = NULL; 99 struct in_addr *ipv4_sources = NULL; 100 static jmp_buf jmpbuf; 101 static size_t nmcastgroups = IP_MAX_MEMBERSHIPS; 102 static size_t nmcastsources = 0; 103 static uint16_t portno = DEFAULT_PORT; 104 static char *progname = NULL; 105 struct sockaddr_storage *ss_sources = NULL; 106 static uint32_t timeout = 0; 107 108 static int do_asm_ipv4(void); 109 static int do_asm_pim(void); 110 #ifdef notyet 111 static int do_misc_opts(void); 112 #endif 113 static int do_ssf_ipv4(void); 114 static int do_ssf_pim(void); 115 static int do_ssm_ipv4(void); 116 static int do_ssm_pim(void); 117 static int open_and_bind_socket(sockunion_t *); 118 static int recv_loop_with_match(int, sockunion_t *, sockunion_t *); 119 static void signal_handler(int); 120 static void usage(void); 121 122 /* 123 * Test the IPv4 set/getipv4sourcefilter() libc API functions. 124 * Build a single socket. 125 * Join a source group. 126 * Repeatedly change the source filters via setipv4sourcefilter. 127 * Read it back with getipv4sourcefilter up to IP_MAX_SOURCES 128 * and check for inconsistency. 129 */ 130 static int 131 do_ssf_ipv4(void) 132 { 133 134 fprintf(stderr, "not yet implemented\n"); 135 return (0); 136 } 137 138 /* 139 * Test the protocol-independent set/getsourcefilter() functions. 140 */ 141 static int 142 do_ssf_pim(void) 143 { 144 145 fprintf(stderr, "not yet implemented\n"); 146 return (0); 147 } 148 149 /* 150 * Test the IPv4 ASM API. 151 * Repeatedly join, block sources, unblock and leave groups. 152 */ 153 static int 154 do_asm_ipv4(void) 155 { 156 int error; 157 char gaddrbuf[ADDRBUF_LEN]; 158 int i; 159 sockunion_t laddr; 160 struct ip_mreq mreq; 161 struct ip_mreq_source mreqs; 162 in_addr_t ngroupbase; 163 char saddrbuf[ADDRBUF_LEN]; 164 int sock; 165 sockunion_t tmpgroup; 166 sockunion_t tmpsource; 167 168 memset(&mreq, 0, sizeof(struct ip_mreq)); 169 memset(&mreqs, 0, sizeof(struct ip_mreq_source)); 170 memset(&laddr, 0, sizeof(sockunion_t)); 171 172 if (dobindaddr) { 173 laddr = ifaddr; 174 } else { 175 laddr.sin.sin_family = AF_INET; 176 laddr.sin.sin_len = sizeof(struct sockaddr_in); 177 laddr.sin.sin_addr.s_addr = INADDR_ANY; 178 } 179 laddr.sin.sin_port = htons(portno); 180 181 tmpgroup = basegroup; 182 ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr) + 1; /* XXX */ 183 tmpgroup.sin.sin_addr.s_addr = htonl(ngroupbase); 184 185 sock = open_and_bind_socket(&laddr); 186 if (sock == -1) 187 return (EX_OSERR); 188 189 for (i = 0; i < (signed)nmcastgroups; i++) { 190 mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); 191 mreq.imr_interface = ifaddr.sin.sin_addr; 192 if (doverbose) { 193 inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, 194 sizeof(gaddrbuf)); 195 fprintf(stderr, "IP_ADD_MEMBERSHIP %s %s\n", 196 gaddrbuf, inet_ntoa(mreq.imr_interface)); 197 } 198 error = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 199 &mreq, sizeof(struct ip_mreq)); 200 if (error < 0) { 201 warn("setsockopt IP_ADD_MEMBERSHIP"); 202 close(sock); 203 return (EX_OSERR); 204 } 205 } 206 207 /* 208 * If no test sources auto-generated or specified on command line, 209 * skip source filter portion of ASM test. 210 */ 211 if (nmcastsources == 0) 212 goto skipsources; 213 214 /* 215 * Begin blocking sources on the first group chosen. 216 */ 217 for (i = 0; i < (signed)nmcastsources; i++) { 218 mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; 219 mreqs.imr_interface = ifaddr.sin.sin_addr; 220 mreqs.imr_sourceaddr = ipv4_sources[i]; 221 if (doverbose) { 222 inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, 223 sizeof(gaddrbuf)); 224 inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, 225 sizeof(saddrbuf)); 226 fprintf(stderr, "IP_BLOCK_SOURCE %s %s %s\n", 227 gaddrbuf, inet_ntoa(mreqs.imr_interface), 228 saddrbuf); 229 } 230 error = setsockopt(sock, IPPROTO_IP, IP_BLOCK_SOURCE, &mreqs, 231 sizeof(struct ip_mreq_source)); 232 if (error < 0) { 233 warn("setsockopt IP_BLOCK_SOURCE"); 234 close(sock); 235 return (EX_OSERR); 236 } 237 } 238 239 /* 240 * Choose the first group and source for a match. 241 * Enter the I/O loop. 242 */ 243 memset(&tmpsource, 0, sizeof(sockunion_t)); 244 tmpsource.sin.sin_family = AF_INET; 245 tmpsource.sin.sin_len = sizeof(struct sockaddr_in); 246 tmpsource.sin.sin_addr = ipv4_sources[0]; 247 248 error = recv_loop_with_match(sock, &tmpgroup, &tmpsource); 249 250 /* 251 * Unblock sources. 252 */ 253 for (i = nmcastsources-1; i >= 0; i--) { 254 mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; 255 mreqs.imr_interface = ifaddr.sin.sin_addr; 256 mreqs.imr_sourceaddr = ipv4_sources[i]; 257 if (doverbose) { 258 inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, 259 sizeof(gaddrbuf)); 260 inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, 261 sizeof(saddrbuf)); 262 fprintf(stderr, "IP_UNBLOCK_SOURCE %s %s %s\n", 263 gaddrbuf, inet_ntoa(mreqs.imr_interface), 264 saddrbuf); 265 } 266 error = setsockopt(sock, IPPROTO_IP, IP_UNBLOCK_SOURCE, &mreqs, 267 sizeof(struct ip_mreq_source)); 268 if (error < 0) { 269 warn("setsockopt IP_UNBLOCK_SOURCE"); 270 close(sock); 271 return (EX_OSERR); 272 } 273 } 274 275 skipsources: 276 /* 277 * Leave groups. 278 */ 279 for (i = nmcastgroups-1; i >= 0; i--) { 280 mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); 281 mreq.imr_interface = ifaddr.sin.sin_addr; 282 if (doverbose) { 283 inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, 284 sizeof(gaddrbuf)); 285 fprintf(stderr, "IP_DROP_MEMBERSHIP %s %s\n", 286 gaddrbuf, inet_ntoa(mreq.imr_interface)); 287 } 288 error = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, 289 &mreq, sizeof(struct ip_mreq)); 290 if (error < 0) { 291 warn("setsockopt IP_DROP_MEMBERSHIP"); 292 close(sock); 293 return (EX_OSERR); 294 } 295 } 296 297 return (0); 298 } 299 300 static int 301 do_asm_pim(void) 302 { 303 304 fprintf(stderr, "not yet implemented\n"); 305 return (0); 306 } 307 308 #ifdef notyet 309 /* 310 * Test misceallaneous IPv4 options. 311 */ 312 static int 313 do_misc_opts(void) 314 { 315 int sock; 316 317 sock = open_and_bind_socket(NULL); 318 if (sock == -1) 319 return (EX_OSERR); 320 test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, 321 "IP_MULTICAST_TTL", 1); 322 close(sock); 323 324 sock = open_and_bind_socket(NULL); 325 if (sock == -1) 326 return (EX_OSERR); 327 test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, 328 "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); 329 close(sock); 330 331 return (0); 332 } 333 #endif 334 335 /* 336 * Test the IPv4 SSM API. 337 */ 338 static int 339 do_ssm_ipv4(void) 340 { 341 342 fprintf(stderr, "not yet implemented\n"); 343 return (0); 344 } 345 346 /* 347 * Test the protocol-independent SSM API with IPv4 addresses. 348 */ 349 static int 350 do_ssm_pim(void) 351 { 352 353 fprintf(stderr, "not yet implemented\n"); 354 return (0); 355 } 356 357 int 358 main(int argc, char *argv[]) 359 { 360 struct addrinfo aih; 361 struct addrinfo *aip; 362 int ch; 363 int error; 364 int exitval; 365 size_t i; 366 struct in_addr *pina; 367 struct sockaddr_storage *pbss; 368 369 ifname = DEFAULT_IFNAME; 370 ifaddr_str = DEFAULT_IFADDR_STR; 371 basegroup_str = DEFAULT_GROUP_STR; 372 ifname = DEFAULT_IFNAME; 373 portno = DEFAULT_PORT; 374 basegroup.ss.ss_family = AF_UNSPEC; 375 ifaddr.ss.ss_family = AF_UNSPEC; 376 377 progname = basename(argv[0]); 378 while ((ch = getopt(argc, argv, "4bg:i:I:mM:p:rsS:tT:v")) != -1) { 379 switch (ch) { 380 case '4': 381 doipv4 = 1; 382 break; 383 case 'b': 384 dobindaddr = 1; 385 break; 386 case 'g': 387 basegroup_str = optarg; 388 break; 389 case 'i': 390 ifname = optarg; 391 break; 392 case 'I': 393 ifaddr_str = optarg; 394 break; 395 case 'm': 396 usage(); /* notyet */ 397 /*NOTREACHED*/ 398 domiscopts = 1; 399 break; 400 case 'M': 401 nmcastgroups = atoi(optarg); 402 break; 403 case 'p': 404 portno = atoi(optarg); 405 break; 406 case 'r': 407 doreuseport = 1; 408 break; 409 case 'S': 410 nmcastsources = atoi(optarg); 411 break; 412 case 's': 413 dossm = 1; 414 break; 415 case 't': 416 dossf = 1; 417 break; 418 case 'T': 419 timeout = atoi(optarg); 420 break; 421 case 'v': 422 doverbose = 1; 423 break; 424 default: 425 usage(); 426 break; 427 /*NOTREACHED*/ 428 } 429 } 430 argc -= optind; 431 argv += optind; 432 433 memset(&aih, 0, sizeof(struct addrinfo)); 434 aih.ai_flags = AI_NUMERICHOST | AI_PASSIVE; 435 aih.ai_family = PF_INET; 436 aih.ai_socktype = SOCK_DGRAM; 437 aih.ai_protocol = IPPROTO_UDP; 438 439 /* 440 * Fill out base group. 441 */ 442 aip = NULL; 443 error = getaddrinfo(basegroup_str, NULL, &aih, &aip); 444 if (error != 0) { 445 fprintf(stderr, "%s: getaddrinfo: %s\n", progname, 446 gai_strerror(error)); 447 exit(EX_USAGE); 448 } 449 memcpy(&basegroup, aip->ai_addr, aip->ai_addrlen); 450 if (dodebug) { 451 fprintf(stderr, "debug: gai thinks %s is %s\n", 452 basegroup_str, inet_ntoa(basegroup.sin.sin_addr)); 453 } 454 freeaddrinfo(aip); 455 456 assert(basegroup.ss.ss_family == AF_INET); 457 458 /* 459 * If user specified interface as an address, and protocol 460 * specific APIs were selected, parse it. 461 * Otherwise, parse interface index from name if protocol 462 * independent APIs were selected (the default). 463 */ 464 if (doipv4) { 465 if (ifaddr_str == NULL) { 466 warnx("required argument missing: ifaddr"); 467 usage(); 468 /* NOTREACHED */ 469 } 470 aip = NULL; 471 error = getaddrinfo(ifaddr_str, NULL, &aih, &aip); 472 if (error != 0) { 473 fprintf(stderr, "%s: getaddrinfo: %s\n", progname, 474 gai_strerror(error)); 475 exit(EX_USAGE); 476 } 477 memcpy(&ifaddr, aip->ai_addr, aip->ai_addrlen); 478 if (dodebug) { 479 fprintf(stderr, "debug: gai thinks %s is %s\n", 480 ifaddr_str, inet_ntoa(ifaddr.sin.sin_addr)); 481 } 482 freeaddrinfo(aip); 483 } 484 485 if (!doipv4) { 486 if (ifname == NULL) { 487 warnx("required argument missing: ifname"); 488 usage(); 489 /* NOTREACHED */ 490 } 491 ifindex = if_nametoindex(ifname); 492 if (ifindex == 0) 493 err(EX_USAGE, "if_nametoindex"); 494 } 495 496 /* 497 * Introduce randomness into group base if specified. 498 */ 499 if (dorandom) { 500 in_addr_t ngroupbase; 501 502 srandomdev(); 503 ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr); 504 ngroupbase |= ((random() % ((1 << 11) - 1)) << 16); 505 basegroup.sin.sin_addr.s_addr = htonl(ngroupbase); 506 } 507 508 if (argc > 0) { 509 nmcastsources = argc; 510 if (doipv4) { 511 ipv4_sources = calloc(nmcastsources, 512 sizeof(struct in_addr)); 513 if (ipv4_sources == NULL) { 514 exitval = EX_OSERR; 515 goto out; 516 } 517 } else { 518 ss_sources = calloc(nmcastsources, 519 sizeof(struct sockaddr_storage)); 520 if (ss_sources == NULL) { 521 exitval = EX_OSERR; 522 goto out; 523 } 524 } 525 } 526 527 /* 528 * Parse source list, if any were specified on the command line. 529 */ 530 assert(aih.ai_family == PF_INET); 531 pbss = ss_sources; 532 pina = ipv4_sources; 533 for (i = 0; i < (size_t)argc; i++) { 534 aip = NULL; 535 error = getaddrinfo(argv[i], NULL, &aih, &aip); 536 if (error != 0) { 537 fprintf(stderr, "getaddrinfo: %s\n", 538 gai_strerror(error)); 539 exitval = EX_USAGE; 540 goto out; 541 } 542 if (doipv4) { 543 struct sockaddr_in *sin = 544 (struct sockaddr_in *)aip->ai_addr; 545 *pina++ = sin->sin_addr; 546 } else { 547 memcpy(pbss++, aip->ai_addr, aip->ai_addrlen); 548 } 549 freeaddrinfo(aip); 550 } 551 552 /* 553 * Perform the regression tests which the user requested. 554 */ 555 #ifdef notyet 556 if (domiscopts) { 557 exitval = do_misc_opts(); 558 if (exitval) 559 goto out; 560 } 561 #endif 562 if (doipv4) { 563 /* IPv4 protocol specific API tests */ 564 if (dossm) { 565 /* Source-specific multicast */ 566 exitval = do_ssm_ipv4(); 567 if (exitval) 568 goto out; 569 if (dossf) { 570 /* Do setipvsourcefilter() too */ 571 exitval = do_ssf_ipv4(); 572 } 573 } else { 574 /* Any-source multicast */ 575 exitval = do_asm_ipv4(); 576 } 577 } else { 578 /* Protocol independent API tests */ 579 if (dossm) { 580 /* Source-specific multicast */ 581 exitval = do_ssm_pim(); 582 if (exitval) 583 goto out; 584 if (dossf) { 585 /* Do setsourcefilter() too */ 586 exitval = do_ssf_pim(); 587 } 588 } else { 589 /* Any-source multicast */ 590 exitval = do_asm_pim(); 591 } 592 } 593 594 out: 595 if (ipv4_sources != NULL) 596 free(ipv4_sources); 597 598 if (ss_sources != NULL) 599 free(ss_sources); 600 601 exit(exitval); 602 } 603 604 static int 605 open_and_bind_socket(sockunion_t *bsu) 606 { 607 int error, optval, sock; 608 609 sock = -1; 610 611 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 612 if (sock == -1) { 613 warn("socket"); 614 return (-1); 615 } 616 617 if (doreuseport) { 618 optval = 1; 619 if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, 620 sizeof(optval)) < 0) { 621 warn("setsockopt SO_REUSEPORT"); 622 close(sock); 623 return (-1); 624 } 625 } 626 627 if (bsu != NULL) { 628 error = bind(sock, &bsu->sa, bsu->sa.sa_len); 629 if (error == -1) { 630 warn("bind"); 631 close(sock); 632 return (-1); 633 } 634 } 635 636 return (sock); 637 } 638 639 /* 640 * Protocol-agnostic multicast I/O loop. 641 * 642 * Wait for 'timeout' seconds looking for traffic on group, so that manual 643 * or automated regression tests (possibly running on another host) have an 644 * opportunity to transmit within the group to test source filters. 645 * 646 * If the filter failed, this loop will report if we received traffic 647 * from the source we elected to monitor. 648 */ 649 static int 650 recv_loop_with_match(int sock, sockunion_t *group, sockunion_t *source) 651 { 652 int error; 653 sockunion_t from; 654 char groupname[NI_MAXHOST]; 655 ssize_t len; 656 size_t npackets; 657 int jmpretval; 658 char rxbuf[RXBUFSIZE]; 659 char sourcename[NI_MAXHOST]; 660 661 assert(source->sa.sa_family == AF_INET); 662 663 /* 664 * Return immediately if we don't need to wait for traffic. 665 */ 666 if (timeout == 0) 667 return (0); 668 669 error = getnameinfo(&group->sa, group->sa.sa_len, groupname, 670 NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 671 if (error) { 672 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 673 return (error); 674 } 675 676 error = getnameinfo(&source->sa, source->sa.sa_len, sourcename, 677 NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 678 if (error) { 679 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 680 return (error); 681 } 682 683 fprintf(stdout, 684 "Waiting %d seconds for inbound traffic on group %s\n" 685 "Expecting no traffic from blocked source: %s\n", 686 (int)timeout, groupname, sourcename); 687 688 signal(SIGINT, signal_handler); 689 signal(SIGALRM, signal_handler); 690 691 error = 0; 692 npackets = 0; 693 alarm(timeout); 694 while (0 == (jmpretval = setjmp(jmpbuf))) { 695 len = recvfrom(sock, rxbuf, RXBUFSIZE, 0, &from.sa, 696 (socklen_t *)&from.sa.sa_len); 697 if (dodebug) { 698 fprintf(stderr, "debug: packet received from %s\n", 699 inet_ntoa(from.sin.sin_addr)); 700 } 701 if (source && 702 source->sin.sin_addr.s_addr == from.sin.sin_addr.s_addr) 703 break; 704 npackets++; 705 } 706 707 if (doverbose) { 708 fprintf(stderr, "Number of datagrams received from " 709 "non-blocked sources: %d\n", (int)npackets); 710 } 711 712 switch (jmpretval) { 713 case SIGALRM: /* ok */ 714 break; 715 case SIGINT: /* go bye bye */ 716 fprintf(stderr, "interrupted\n"); 717 error = 20; 718 break; 719 case 0: /* Broke out of loop; saw a bad source. */ 720 fprintf(stderr, "FAIL: got packet from blocked source\n"); 721 error = EX_IOERR; 722 break; 723 default: 724 warnx("recvfrom"); 725 error = EX_OSERR; 726 break; 727 } 728 729 signal(SIGINT, SIG_DFL); 730 signal(SIGALRM, SIG_DFL); 731 732 return (error); 733 } 734 735 static void 736 signal_handler(int signo) 737 { 738 739 longjmp(jmpbuf, signo); 740 } 741 742 static void 743 usage(void) 744 { 745 746 fprintf(stderr, "\nIP multicast regression test utility\n"); 747 fprintf(stderr, 748 "usage: %s [-4] [-b] [-g groupaddr] [-i ifname] [-I ifaddr] [-m]\n" 749 " [-M ngroups] [-p portno] [-r] [-R] [-s] [-S nsources] [-t] [-T timeout]\n" 750 " [-v] [blockaddr ...]\n\n", progname); 751 fprintf(stderr, "-4: Use IPv4 API " 752 "(default: Use protocol-independent API)\n"); 753 fprintf(stderr, "-b: bind listening socket to ifaddr " 754 "(default: INADDR_ANY)\n"); 755 fprintf(stderr, "-g: Base IPv4 multicast group to join (default: %s)\n", 756 DEFAULT_GROUP_STR); 757 fprintf(stderr, "-i: interface for multicast joins (default: %s)\n", 758 DEFAULT_IFNAME); 759 fprintf(stderr, "-I: IPv4 address to join groups on, if using IPv4 " 760 "API\n (default: %s)\n", DEFAULT_IFADDR_STR); 761 #ifdef notyet 762 fprintf(stderr, "-m: Test misc IPv4 multicast socket options " 763 "(default: off)\n"); 764 #endif 765 fprintf(stderr, "-M: Number of multicast groups to join " 766 "(default: %d)\n", (int)nmcastgroups); 767 fprintf(stderr, "-p: Set local and remote port (default: %d)\n", 768 DEFAULT_PORT); 769 fprintf(stderr, "-r: Set SO_REUSEPORT on (default: off)\n"); 770 fprintf(stderr, "-R: Randomize groups/sources (default: off)\n"); 771 fprintf(stderr, "-s: Test source-specific API " 772 "(default: test any-source API)\n"); 773 fprintf(stderr, "-S: Number of multicast sources to generate if\n" 774 " none specified on command line (default: %d)\n", 775 (int)nmcastsources); 776 fprintf(stderr, "-t: Test get/setNsourcefilter() (default: off)\n"); 777 fprintf(stderr, "-T: Timeout to wait for blocked traffic on first " 778 "group (default: %d)\n", DEFAULT_TIMEOUT); 779 fprintf(stderr, "-v: Be verbose (default: off)\n"); 780 fprintf(stderr, "\nRemaining arguments are treated as a list of IPv4 " 781 "sources to filter.\n\n"); 782 783 exit(EX_USAGE); 784 } 785