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
do_ssf_ipv4(void)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
do_ssf_pim(void)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
do_asm_ipv4(void)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
do_asm_pim(void)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
do_misc_opts(void)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
do_ssm_ipv4(void)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
do_ssm_pim(void)350 do_ssm_pim(void)
351 {
352
353 fprintf(stderr, "not yet implemented\n");
354 return (0);
355 }
356
357 int
main(int argc,char * argv[])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
open_and_bind_socket(sockunion_t * bsu)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
recv_loop_with_match(int sock,sockunion_t * group,sockunion_t * source)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
signal_handler(int signo)736 signal_handler(int signo)
737 {
738
739 longjmp(jmpbuf, signo);
740 }
741
742 static void
usage(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