1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2007-2026 Bruce Simpson.
5 * Copyright (c) 2000 Wilbert De Graaf.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Diagnostic and test utility for multicast sockets.
35 * XXX: This file currently assumes INET support in the base system.
36 * TODO: Support embedded KAME Scope ID in IPv6 group addresses.
37 * TODO: Use IPv4 link-local address when source address selection
38 * is implemented; use MCAST_JOIN_SOURCE for IPv4.
39 */
40
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/errno.h>
44 #include <sys/socket.h>
45 #include <sys/time.h>
46 #include <sys/ioctl.h>
47
48 #include <net/if.h>
49 #include <net/if_dl.h>
50 #include <net/ethernet.h>
51 #ifdef INET
52 #include <netinet/in.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_var.h>
56 #endif
57 #ifdef INET6
58 #include <netinet/in.h>
59 #include <netinet/ip6.h>
60 #endif
61
62 #include <assert.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <ctype.h>
67 #include <errno.h>
68 #include <err.h>
69 #include <unistd.h>
70
71 #include <arpa/inet.h>
72 #include <netdb.h>
73 #include <ifaddrs.h>
74
75 union sockunion {
76 struct sockaddr_storage ss;
77 struct sockaddr sa;
78 struct sockaddr_dl sdl;
79 #ifdef INET
80 struct sockaddr_in sin;
81 #endif
82 #ifdef INET6
83 struct sockaddr_in6 sin6;
84 #endif
85 };
86 typedef union sockunion sockunion_t;
87
88 union mrequnion {
89 #ifdef INET
90 struct ip_mreq mr;
91 struct ip_mreq_source mrs;
92 #endif
93 #ifdef INET6
94 struct ipv6_mreq mr6;
95 struct group_source_req gr;
96 #endif
97 };
98 typedef union mrequnion mrequnion_t;
99
100 #define MAX_ADDRS 20
101 #define STR_SIZE 20
102 #define LINE_LENGTH 80
103
104 #ifdef INET
105 static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *);
106 #endif
107 #if defined(INET) && defined(INET6)
108 static struct in6_addr *
109 __in6_v4_to_v4mapped(struct in6_addr *, struct in_addr *);
110 #endif
111 static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *,
112 const char *, const char *, const char *);
113 static void process_file(char *, int, int);
114 static void process_cmd(char*, int, int, FILE *);
115 static int su_cmp(const void *, const void *);
116 static void usage(void);
117
118 /*
119 * Ordering predicate for qsort().
120 */
121 static int
su_cmp(const void * a,const void * b)122 su_cmp(const void *a, const void *b)
123 {
124 const sockunion_t *sua = (const sockunion_t *)a;
125 const sockunion_t *sub = (const sockunion_t *)b;
126
127 assert(sua->sa.sa_family == sub->sa.sa_family);
128
129 switch (sua->sa.sa_family) {
130 #ifdef INET
131 case AF_INET:
132 return ((int)(sua->sin.sin_addr.s_addr -
133 sub->sin.sin_addr.s_addr));
134 break;
135 #endif
136 #ifdef INET6
137 case AF_INET6:
138 return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr,
139 sizeof(struct in6_addr)));
140 break;
141 #endif
142 default:
143 break;
144 }
145
146 assert(sua->sa.sa_len == sub->sa.sa_len);
147 return (memcmp(sua, sub, sua->sa.sa_len));
148 }
149
150 #if defined(INET) && defined(INET6)
151 /*
152 * Internal: Express an IPv4 address within IPv6 as IPv4-mapped.
153 * Converse of IN6_IS_ADDR_V4MAPPED(a).
154 */
155 static struct in6_addr *
__in6_v4_to_v4mapped(struct in6_addr * a6,struct in_addr * a4)156 __in6_v4_to_v4mapped(struct in6_addr *a6 , struct in_addr *a4)
157 {
158
159 a6->s6_addr32[0] = 0;
160 a6->s6_addr32[1] = 0;
161 a6->s6_addr32[2] = ntohl(0x0000ffff);
162 a6->s6_addr32[3] = a4->s_addr;
163 return (a6);
164 }
165 #endif /* defined(INET) && defined(INET6) */
166
167 #ifdef INET
168 /*
169 * Internal: Map an interface index to primary IPv4 address.
170 * This is somewhat inefficient. This is a useful enough operation
171 * that it probably belongs in the C library.
172 * Return zero if found, -1 on error, 1 on not found.
173 */
174 static int
__ifindex_to_primary_ip(const uint32_t ifindex,struct in_addr * pina)175 __ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina)
176 {
177 char ifname[IFNAMSIZ];
178 struct ifaddrs *ifa;
179 struct ifaddrs *ifaddrs;
180 sockunion_t *psu;
181 int retval;
182
183 assert(ifindex != 0);
184
185 retval = -1;
186 if (if_indextoname(ifindex, ifname) == NULL)
187 return (retval);
188 if (getifaddrs(&ifaddrs) < 0)
189 return (retval);
190
191 /*
192 * Find the ifaddr entry corresponding to the interface name,
193 * and return the first matching IPv4 address.
194 */
195 retval = 1;
196 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
197 if (strcmp(ifa->ifa_name, ifname) != 0)
198 continue;
199 psu = (sockunion_t *)ifa->ifa_addr;
200 if (psu && psu->sa.sa_family == AF_INET) {
201 retval = 0;
202 memcpy(pina, &psu->sin.sin_addr,
203 sizeof(struct in_addr));
204 break;
205 }
206 }
207
208 if (retval != 0)
209 errno = EADDRNOTAVAIL; /* XXX */
210
211 freeifaddrs(ifaddrs);
212 return (retval);
213 }
214 #endif /* INET */
215
216 int
main(int argc,char ** argv)217 main(int argc, char **argv)
218 {
219 char line[LINE_LENGTH];
220 char *p;
221 int i, s, s6;
222
223 s = -1;
224 s6 = -1;
225 #ifdef INET
226 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
227 if (s == -1 && errno != EAFNOSUPPORT)
228 err(1, "can't open IPv4 socket");
229 #endif
230 #ifdef INET6
231 s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
232 if (s6 == -1 && errno != EAFNOSUPPORT)
233 err(1, "can't open IPv6 socket");
234 #endif
235 if (s == -1 && s6 == -1)
236 errc(1, EPROTONOSUPPORT, "can't open socket");
237
238 if (argc < 2) {
239 if (isatty(STDIN_FILENO)) {
240 printf("multicast membership test program; "
241 "enter ? for list of commands\n");
242 }
243 do {
244 if (fgets(line, sizeof(line), stdin) != NULL) {
245 if (line[0] != 'f')
246 process_cmd(line, s, s6, stdin);
247 else {
248 /* Get the filename */
249 for (i = 1; isblank(line[i]); i++);
250 if ((p = (char*)strchr(line, '\n'))
251 != NULL)
252 *p = '\0';
253 process_file(&line[i], s, s6);
254 }
255 }
256 } while (!feof(stdin));
257 } else {
258 for (i = 1; i < argc; i++) {
259 process_file(argv[i], s, s6);
260 }
261 }
262
263 if (s != -1)
264 close(s);
265 if (s6 != -1)
266 close(s6);
267
268 exit (0);
269 }
270
271 static void
process_file(char * fname,int s,int s6)272 process_file(char *fname, int s, int s6)
273 {
274 char line[80];
275 FILE *fp;
276 char *lineptr;
277
278 fp = fopen(fname, "r");
279 if (fp == NULL) {
280 warn("fopen");
281 return;
282 }
283
284 /* Skip comments and empty lines. */
285 while (fgets(line, sizeof(line), fp) != NULL) {
286 lineptr = line;
287 while (isblank(*lineptr))
288 lineptr++;
289 if (*lineptr != '#' && *lineptr != '\n')
290 process_cmd(lineptr, s, s6, fp);
291 }
292
293 fclose(fp);
294 }
295
296 /*
297 * Parse join/leave/allow/block arguments, given:
298 * str1: group (as AF_INET or AF_INET6 printable)
299 * str2: ifname
300 * str3: optional source address (may be NULL).
301 * This argument must have the same parsed address family as str1.
302 * Return the ifindex of ifname, or 0 if any parse element failed.
303 */
304 static uint32_t
parse_cmd_args(sockunion_t * psu,sockunion_t * psu2,const char * str1,const char * str2,const char * str3)305 parse_cmd_args(sockunion_t *psu, sockunion_t *psu2,
306 const char *str1, const char *str2, const char *str3)
307 {
308 struct addrinfo hints;
309 struct addrinfo *res;
310 uint32_t ifindex;
311 int af, error;
312
313 assert(psu != NULL);
314 assert(str1 != NULL);
315 assert(str2 != NULL);
316
317 af = AF_UNSPEC;
318
319 ifindex = if_nametoindex(str2);
320 if (ifindex == 0)
321 return (0);
322
323 memset(&hints, 0, sizeof(struct addrinfo));
324 hints.ai_flags = AI_NUMERICHOST;
325 hints.ai_family = PF_UNSPEC;
326 hints.ai_socktype = SOCK_DGRAM;
327
328 memset(psu, 0, sizeof(sockunion_t));
329 psu->sa.sa_family = AF_UNSPEC;
330
331 error = getaddrinfo(str1, "0", &hints, &res);
332 if (error) {
333 warnx("getaddrinfo: %s", gai_strerror(error));
334 return (0);
335 }
336 assert(res != NULL);
337 af = res->ai_family;
338 memcpy(psu, res->ai_addr, res->ai_addrlen);
339 freeaddrinfo(res);
340
341 /* sscanf() may pass the empty string. */
342 if (psu2 != NULL && str3 != NULL && *str3 != '\0') {
343 memset(psu2, 0, sizeof(sockunion_t));
344 psu2->sa.sa_family = AF_UNSPEC;
345
346 /* look for following address family; str3 is *optional*. */
347 hints.ai_family = af;
348 error = getaddrinfo(str3, "0", &hints, &res);
349 if (error) {
350 warnx("getaddrinfo: %s", gai_strerror(error));
351 ifindex = 0;
352 } else {
353 if (af != res->ai_family) {
354 errno = EINVAL; /* XXX */
355 ifindex = 0;
356 }
357 memcpy(psu2, res->ai_addr, res->ai_addrlen);
358 freeaddrinfo(res);
359 }
360 }
361
362 return (ifindex);
363 }
364
365 static __inline int
af2sock(const int af,int s,int s6)366 af2sock(const int af, int s, int s6)
367 {
368
369 #ifdef INET
370 if (af == AF_INET)
371 return (s);
372 #endif
373 #ifdef INET6
374 if (af == AF_INET6)
375 return (s6);
376 #endif
377 return (-1);
378 }
379
380 static __inline int
af2socklen(const int af)381 af2socklen(const int af)
382 {
383
384 #ifdef INET
385 if (af == AF_INET)
386 return (sizeof(struct sockaddr_in));
387 #endif
388 #ifdef INET6
389 if (af == AF_INET6)
390 return (sizeof(struct sockaddr_in6));
391 #endif
392 return (-1);
393 }
394
395 static void
process_cmd(char * cmd,int s,int s6,FILE * fp __unused)396 process_cmd(char *cmd, int s, int s6, FILE *fp __unused)
397 {
398 char str1[STR_SIZE];
399 char str2[STR_SIZE];
400 char str3[STR_SIZE];
401 mrequnion_t mr;
402 sockunion_t su, su2;
403 struct ifreq ifr;
404 char *line;
405 char *toptname;
406 void *optval;
407 uint32_t fmode, ifindex;
408 socklen_t optlen;
409 size_t j;
410 int af, error, f, flags, i, level, n, optname;
411
412 af = AF_UNSPEC;
413 su.sa.sa_family = AF_UNSPEC;
414 su2.sa.sa_family = AF_UNSPEC;
415
416 line = cmd;
417 while (isblank(*++line))
418 ; /* Skip whitespace. */
419
420 n = 0;
421 switch (*cmd) {
422 case '?':
423 usage();
424 break;
425
426 case 'q':
427 close(s);
428 exit(0);
429
430 case 's':
431 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
432 printf("-1\n");
433 break;
434 }
435 sleep(n);
436 printf("ok\n");
437 break;
438
439 case 'j':
440 case 'l':
441 str3[0] = '\0';
442 toptname = "";
443 sscanf(line, "%s %s %s", str1, str2, str3);
444 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
445 if (ifindex == 0) {
446 printf("-1\n");
447 break;
448 }
449 af = su.sa.sa_family;
450 #ifdef INET
451 if (af == AF_INET) {
452 struct in_addr ina;
453
454 error = __ifindex_to_primary_ip(ifindex, &ina);
455 if (error != 0) {
456 warn("primary_ip_lookup %s", str2);
457 printf("-1\n");
458 break;
459 }
460 level = IPPROTO_IP;
461
462 if (su2.sa.sa_family != AF_UNSPEC) {
463 mr.mrs.imr_multiaddr = su.sin.sin_addr;
464 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
465 mr.mrs.imr_interface = ina;
466 optname = (*cmd == 'j') ?
467 IP_ADD_SOURCE_MEMBERSHIP :
468 IP_DROP_SOURCE_MEMBERSHIP;
469 toptname = (*cmd == 'j') ?
470 "IP_ADD_SOURCE_MEMBERSHIP" :
471 "IP_DROP_SOURCE_MEMBERSHIP";
472 optval = (void *)&mr.mrs;
473 optlen = sizeof(mr.mrs);
474 } else {
475 mr.mr.imr_multiaddr = su.sin.sin_addr;
476 mr.mr.imr_interface = ina;
477 optname = (*cmd == 'j') ?
478 IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
479 toptname = (*cmd == 'j') ?
480 "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
481 optval = (void *)&mr.mr;
482 optlen = sizeof(mr.mr);
483 }
484 if (s < 0) {
485 warnc(EPROTONOSUPPORT, "setsockopt %s",
486 toptname);
487 } else if (setsockopt(s, level, optname, optval,
488 optlen) == 0) {
489 printf("ok\n");
490 break;
491 } else {
492 warn("setsockopt %s", toptname);
493 }
494 }
495 #ifdef INET6
496 else
497 #endif /* INET with INET6 */
498 #endif /* INET */
499 #ifdef INET6
500 if (af == AF_INET6) {
501 level = IPPROTO_IPV6;
502 if (su2.sa.sa_family != AF_UNSPEC) {
503 mr.gr.gsr_interface = ifindex;
504 mr.gr.gsr_group = su.ss;
505 mr.gr.gsr_source = su2.ss;
506 optname = (*cmd == 'j') ?
507 MCAST_JOIN_SOURCE_GROUP:
508 MCAST_LEAVE_SOURCE_GROUP;
509 toptname = (*cmd == 'j') ?
510 "MCAST_JOIN_SOURCE_GROUP":
511 "MCAST_LEAVE_SOURCE_GROUP";
512 optval = (void *)&mr.gr;
513 optlen = sizeof(mr.gr);
514 } else {
515 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
516 mr.mr6.ipv6mr_interface = ifindex;
517 optname = (*cmd == 'j') ?
518 IPV6_JOIN_GROUP :
519 IPV6_LEAVE_GROUP;
520 toptname = (*cmd == 'j') ?
521 "IPV6_JOIN_GROUP" :
522 "IPV6_LEAVE_GROUP";
523 optval = (void *)&mr.mr6;
524 optlen = sizeof(mr.mr6);
525 }
526 if (s6 < 0) {
527 warnc(EPROTONOSUPPORT, "setsockopt %s",
528 toptname);
529 } else if (setsockopt(s6, level, optname, optval,
530 optlen) == 0) {
531 printf("ok\n");
532 break;
533 } else {
534 warn("setsockopt %s", toptname);
535 }
536 }
537 #endif /* INET6 */
538 /* FALLTHROUGH */
539 printf("-1\n");
540 break;
541
542 /*
543 * Exercise the non-IETF-ratified extension to RFC 3493 to
544 * join IPv4 groups as IPv4 mapped addresses on IPv6 sockets.
545 * Only ASM joins are supported; further use of the full-state API
546 * may result in undefined behaviour.
547 */
548 case 'u':
549 case 'v':
550 toptname = "";
551 sscanf(line, "%s %s", str1, str2);
552 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
553 if (ifindex == 0) {
554 printf("-1\n");
555 break;
556 }
557 af = su.sa.sa_family;
558 #if defined(INET) && defined(INET6)
559 if (af == AF_INET) {
560 struct in_addr gaddr;
561
562 gaddr = su.sin.sin_addr;
563 __in6_v4_to_v4mapped(&su.sin6.sin6_addr, &gaddr);
564 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
565 mr.mr6.ipv6mr_interface = ifindex;
566 optname = (*cmd == 'u') ?
567 IPV6_JOIN_GROUP :
568 IPV6_LEAVE_GROUP;
569 toptname = (*cmd == 'u') ?
570 "IPV6_JOIN_GROUP" :
571 "IPV6_LEAVE_GROUP";
572 level = IPPROTO_IPV6;
573 optval = (void *)&mr.mr6;
574 optlen = sizeof(mr.mr6);
575 if (s6 < 0) {
576 warnc(EPROTONOSUPPORT, "setsockopt %s",
577 toptname);
578 } else if (setsockopt(s6, level, optname, optval,
579 optlen) == 0) {
580 printf("ok\n");
581 break;
582 } else {
583 warn("setsockopt %s", toptname);
584 }
585 }
586 #endif /* defined(INET) && defined(INET6) */
587 /* FALLTHROUGH */
588 printf("-1\n");
589 break;
590
591 /*
592 * Set the socket to include or exclude filter mode, and
593 * add some sources to the filterlist, using the full-state API.
594 */
595 case 'i':
596 case 'e': {
597 sockunion_t sources[MAX_ADDRS];
598 struct addrinfo hints;
599 struct addrinfo *res;
600 char *cp;
601 int af1;
602
603 n = 0;
604 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
605 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
606 printf("-1\n");
607 break;
608 }
609
610 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
611 if (ifindex == 0 || n < 0 || n > MAX_ADDRS) {
612 printf("-1\n");
613 break;
614 }
615 af = su.sa.sa_family;
616 if (af2sock(af, s, s6) == -1) {
617 warnc(EPROTONOSUPPORT, "setsourcefilter");
618 break;
619 }
620
621 memset(&hints, 0, sizeof(struct addrinfo));
622 hints.ai_flags = AI_NUMERICHOST;
623 hints.ai_family = af;
624 hints.ai_socktype = SOCK_DGRAM;
625
626 for (i = 0; i < n; i++) {
627 sockunion_t *psu = (sockunion_t *)&sources[i];
628 /*
629 * Trim trailing whitespace, as getaddrinfo()
630 * can't cope with it.
631 */
632 fgets(str1, sizeof(str1), fp);
633 cp = strchr(str1, '\n');
634 if (cp != NULL)
635 *cp = '\0';
636
637 res = NULL;
638 error = getaddrinfo(str1, "0", &hints, &res);
639 if (error)
640 break;
641 assert(res != NULL);
642
643 memset(psu, 0, sizeof(sockunion_t));
644 af1 = res->ai_family;
645 if (af1 == af)
646 memcpy(psu, res->ai_addr, res->ai_addrlen);
647 freeaddrinfo(res);
648 if (af1 != af)
649 break;
650 }
651 if (i < n) {
652 if (error)
653 warnx("getaddrinfo: %s", gai_strerror(error));
654 printf("-1\n");
655 break;
656 }
657 if (setsourcefilter(af2sock(af, s, s6), ifindex,
658 &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0)
659 warn("setsourcefilter");
660 else
661 printf("ok\n");
662 } break;
663
664 /*
665 * Allow or block traffic from a source, using the
666 * delta based api.
667 */
668 case 't':
669 case 'b': {
670 str3[0] = '\0';
671 toptname = "";
672 sscanf(line, "%s %s %s", str1, str2, str3);
673 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
674 if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) {
675 printf("-1\n");
676 break;
677 }
678 af = su.sa.sa_family;
679 if (af2sock(af, s, s6) == -1) {
680 warnc(EPROTONOSUPPORT, "getsourcefilter");
681 break;
682 }
683
684 /* First determine our current filter mode. */
685 if (getsourcefilter(af2sock(af, s, s6), ifindex,
686 &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) {
687 warn("getsourcefilter");
688 break;
689 }
690 #ifdef INET
691 if (af == AF_INET) {
692 struct in_addr ina;
693
694 error = __ifindex_to_primary_ip(ifindex, &ina);
695 if (error != 0) {
696 warn("primary_ip_lookup %s", str2);
697 printf("-1\n");
698 break;
699 }
700 level = IPPROTO_IP;
701 optval = (void *)&mr.mrs;
702 optlen = sizeof(mr.mrs);
703 mr.mrs.imr_multiaddr = su.sin.sin_addr;
704 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
705 mr.mrs.imr_interface = ina;
706 if (fmode == MCAST_EXCLUDE) {
707 /* Any-source mode socket membership. */
708 optname = (*cmd == 't') ?
709 IP_UNBLOCK_SOURCE :
710 IP_BLOCK_SOURCE;
711 toptname = (*cmd == 't') ?
712 "IP_UNBLOCK_SOURCE" :
713 "IP_BLOCK_SOURCE";
714 } else {
715 /* Source-specific mode socket membership. */
716 optname = (*cmd == 't') ?
717 IP_ADD_SOURCE_MEMBERSHIP :
718 IP_DROP_SOURCE_MEMBERSHIP;
719 toptname = (*cmd == 't') ?
720 "IP_ADD_SOURCE_MEMBERSHIP" :
721 "IP_DROP_SOURCE_MEMBERSHIP";
722 }
723 if (setsockopt(s, level, optname, optval,
724 optlen) == 0) {
725 printf("ok\n");
726 break;
727 } else {
728 warn("setsockopt %s", toptname);
729 }
730 }
731 #ifdef INET6
732 else
733 #endif /* INET with INET6 */
734 #endif /* INET */
735 #ifdef INET6
736 if (af == AF_INET6) {
737 level = IPPROTO_IPV6;
738 mr.gr.gsr_interface = ifindex;
739 mr.gr.gsr_group = su.ss;
740 mr.gr.gsr_source = su2.ss;
741 if (fmode == MCAST_EXCLUDE) {
742 /* Any-source mode socket membership. */
743 optname = (*cmd == 't') ?
744 MCAST_UNBLOCK_SOURCE :
745 MCAST_BLOCK_SOURCE;
746 toptname = (*cmd == 't') ?
747 "MCAST_UNBLOCK_SOURCE" :
748 "MCAST_BLOCK_SOURCE";
749 } else {
750 /* Source-specific mode socket membership. */
751 optname = (*cmd == 't') ?
752 MCAST_JOIN_SOURCE_GROUP :
753 MCAST_LEAVE_SOURCE_GROUP;
754 toptname = (*cmd == 't') ?
755 "MCAST_JOIN_SOURCE_GROUP":
756 "MCAST_LEAVE_SOURCE_GROUP";
757 }
758 optval = (void *)&mr.gr;
759 optlen = sizeof(mr.gr);
760 if (setsockopt(s6, level, optname, optval,
761 optlen) == 0) {
762 printf("ok\n");
763 break;
764 } else {
765 warn("setsockopt %s", toptname);
766 }
767 }
768 #endif /* INET6 */
769 /* FALLTHROUGH */
770 printf("-1\n");
771 } break;
772
773 case 'g': {
774 sockunion_t sources[MAX_ADDRS];
775 char addrbuf[NI_MAXHOST];
776 int nreqsrc, nsrc;
777
778 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
779 printf("-1\n");
780 break;
781 }
782 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
783 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
784 printf("-1\n");
785 break;
786 }
787
788 af = su.sa.sa_family;
789 if (af2sock(af, s, s6) == -1) {
790 warnc(EPROTONOSUPPORT, "getsourcefilter");
791 break;
792 }
793 nsrc = nreqsrc;
794 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
795 su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) {
796 warn("getsourcefilter");
797 printf("-1\n");
798 break;
799 }
800 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
801 "exclude");
802 printf("%d\n", nsrc);
803
804 nsrc = MIN(nreqsrc, nsrc);
805 fprintf(stderr, "hexdump of sources:\n");
806 uint8_t *bp = (uint8_t *)&sources[0];
807 for (j = 0; j < (nsrc * sizeof(sources[0])); j++) {
808 fprintf(stderr, "%02x", bp[j]);
809 }
810 fprintf(stderr, "\nend hexdump\n");
811
812 qsort(sources, nsrc, af2socklen(af), su_cmp);
813 for (i = 0; i < nsrc; i++) {
814 sockunion_t *psu = (sockunion_t *)&sources[i];
815 addrbuf[0] = '\0';
816 error = getnameinfo(&psu->sa, psu->sa.sa_len,
817 addrbuf, sizeof(addrbuf), NULL, 0,
818 NI_NUMERICHOST);
819 if (error)
820 warnx("getnameinfo: %s", gai_strerror(error));
821 else
822 printf("%s\n", addrbuf);
823 }
824 printf("ok\n");
825 } break;
826
827 /* link-layer stuff follows. */
828
829 case 'a':
830 case 'd': {
831 struct sockaddr_dl *dlp;
832 struct ether_addr *ep;
833
834 memset(&ifr, 0, sizeof(struct ifreq));
835 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
836 dlp->sdl_len = sizeof(struct sockaddr_dl);
837 dlp->sdl_family = AF_LINK;
838 dlp->sdl_index = 0;
839 dlp->sdl_nlen = 0;
840 dlp->sdl_alen = ETHER_ADDR_LEN;
841 dlp->sdl_slen = 0;
842 if (sscanf(line, "%s %s", str1, str2) != 2) {
843 warnc(EINVAL, "sscanf");
844 break;
845 }
846 ep = ether_aton(str2);
847 if (ep == NULL) {
848 warnc(EINVAL, "ether_aton");
849 break;
850 }
851 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
852 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
853 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
854 &ifr) == -1) {
855 warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
856 printf("-1\n");
857 } else
858 printf("ok\n");
859 break;
860 }
861
862 case 'm':
863 fprintf(stderr,
864 "warning: IFF_ALLMULTI cannot be set from userland "
865 "in FreeBSD; command ignored.\n");
866 printf("-1\n");
867 break;
868
869 case 'p':
870 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
871 printf("-1\n");
872 break;
873 }
874 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
875 warn("ioctl SIOCGIFFLAGS");
876 break;
877 }
878 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
879 if (f == 0) {
880 flags &= ~IFF_PPROMISC;
881 } else {
882 flags |= IFF_PPROMISC;
883 }
884 ifr.ifr_flags = flags & 0xffff;
885 ifr.ifr_flagshigh = flags >> 16;
886 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
887 warn("ioctl SIOCGIFFLAGS");
888 else
889 printf( "changed to 0x%08x\n", flags );
890 break;
891
892 case '\n':
893 break;
894 default:
895 printf("invalid command\n");
896 break;
897 }
898 }
899
900 static void
usage(void)901 usage(void)
902 {
903
904 printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
905 printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
906 printf("u mcast-addr ifname - join IPv4-mapped group on IPv6 socket\n");
907 printf("v mcast-addr ifname - leave IPv4-mapped group on IPv6 socket\n");
908 printf(
909 "i mcast-addr ifname n - set n include mode src filter\n");
910 printf(
911 "e mcast-addr ifname n - set n exclude mode src filter\n");
912 printf("t mcast-addr ifname src-addr - allow traffic from src\n");
913 printf("b mcast-addr ifname src-addr - block traffic from src\n");
914 printf("g mcast-addr ifname n - get and show n src filters\n");
915 printf("a ifname mac-addr - add link multicast filter\n");
916 printf("d ifname mac-addr - delete link multicast filter\n");
917 printf("m ifname 1/0 - set/clear ether allmulti flag\n");
918 printf("p ifname 1/0 - set/clear ether promisc flag\n");
919 printf("f filename - read command(s) from file\n");
920 printf("s seconds - sleep for some time\n");
921 printf("q - quit\n");
922 }
923
924