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