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