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