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