]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mtest/mtest.c
ident(1): Normalizing date format
[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         size_t                   j;
392         int                      af, error, f, flags, i, level, n, optname;
393
394         af = AF_UNSPEC;
395         su.sa.sa_family = AF_UNSPEC;
396         su2.sa.sa_family = AF_UNSPEC;
397
398         line = cmd;
399         while (isblank(*++line))
400                 ;       /* Skip whitespace. */
401
402         n = 0;
403         switch (*cmd) {
404         case '?':
405                 usage();
406                 break;
407
408         case 'q':
409                 close(s);
410                 exit(0);
411
412         case 's':
413                 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
414                         printf("-1\n");
415                         break;
416                 }
417                 sleep(n);
418                 printf("ok\n");
419                 break;
420
421         case 'j':
422         case 'l':
423                 str3[0] = '\0';
424                 toptname = "";
425                 sscanf(line, "%s %s %s", str1, str2, str3);
426                 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
427                 if (ifindex == 0) {
428                         printf("-1\n");
429                         break;
430                 }
431                 af = su.sa.sa_family;
432 #ifdef INET
433                 if (af == AF_INET) {
434                         struct in_addr ina;
435
436                         error = __ifindex_to_primary_ip(ifindex, &ina);
437                         if (error != 0) {
438                                 warn("primary_ip_lookup %s", str2);
439                                 printf("-1\n");
440                                 break;
441                         }
442                         level = IPPROTO_IP;
443
444                         if (su2.sa.sa_family != AF_UNSPEC) {
445                                 mr.mrs.imr_multiaddr = su.sin.sin_addr;
446                                 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
447                                 mr.mrs.imr_interface = ina;
448                                 optname = (*cmd == 'j') ?
449                                     IP_ADD_SOURCE_MEMBERSHIP :
450                                     IP_DROP_SOURCE_MEMBERSHIP;
451                                 toptname = (*cmd == 'j') ?
452                                     "IP_ADD_SOURCE_MEMBERSHIP" :
453                                     "IP_DROP_SOURCE_MEMBERSHIP";
454                                 optval = (void *)&mr.mrs;
455                                 optlen = sizeof(mr.mrs);
456                         } else {
457                                 mr.mr.imr_multiaddr = su.sin.sin_addr;
458                                 mr.mr.imr_interface = ina;
459                                 optname = (*cmd == 'j') ?
460                                     IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
461                                 toptname = (*cmd == 'j') ?
462                                     "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
463                                 optval = (void *)&mr.mr;
464                                 optlen = sizeof(mr.mr);
465                         }
466                         if (s < 0) {
467                                 warnc(EPROTONOSUPPORT, "setsockopt %s",
468                                     toptname);
469                         } else if (setsockopt(s, level, optname, optval,
470                             optlen) == 0) {
471                                 printf("ok\n");
472                                 break;
473                         } else {
474                                 warn("setsockopt %s", toptname);
475                         }
476                 }
477 #ifdef INET6
478                 else
479 #endif /* INET with INET6 */
480 #endif /* INET */
481 #ifdef INET6
482                 if (af == AF_INET6) {
483                         level = IPPROTO_IPV6;
484                         if (su2.sa.sa_family != AF_UNSPEC) {
485                                 mr.gr.gsr_interface = ifindex;
486                                 mr.gr.gsr_group = su.ss;
487                                 mr.gr.gsr_source = su2.ss;
488                                 optname = (*cmd == 'j') ?
489                                     MCAST_JOIN_SOURCE_GROUP:
490                                     MCAST_LEAVE_SOURCE_GROUP;
491                                 toptname = (*cmd == 'j') ?
492                                     "MCAST_JOIN_SOURCE_GROUP":
493                                     "MCAST_LEAVE_SOURCE_GROUP";
494                                 optval = (void *)&mr.gr;
495                                 optlen = sizeof(mr.gr);
496                         } else {
497                                 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
498                                 mr.mr6.ipv6mr_interface = ifindex;
499                                 optname = (*cmd == 'j') ?
500                                     IPV6_JOIN_GROUP :
501                                     IPV6_LEAVE_GROUP;
502                                 toptname = (*cmd == 'j') ?
503                                     "IPV6_JOIN_GROUP" :
504                                     "IPV6_LEAVE_GROUP";
505                                 optval = (void *)&mr.mr6;
506                                 optlen = sizeof(mr.mr6);
507                         }
508                         if (s6 < 0) {
509                                 warnc(EPROTONOSUPPORT, "setsockopt %s",
510                                     toptname);
511                         } else if (setsockopt(s6, level, optname, optval,
512                             optlen) == 0) {
513                                 printf("ok\n");
514                                 break;
515                         } else {
516                                 warn("setsockopt %s", toptname);
517                         }
518                 }
519 #endif /* INET6 */
520                 /* FALLTHROUGH */
521                 printf("-1\n");
522                 break;
523
524         /*
525          * Set the socket to include or exclude filter mode, and
526          * add some sources to the filterlist, using the full-state API.
527          */
528         case 'i':
529         case 'e': {
530                 sockunion_t      sources[MAX_ADDRS];
531                 struct addrinfo  hints;
532                 struct addrinfo *res;
533                 char            *cp;
534                 int              af1;
535
536                 n = 0;
537                 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
538                 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
539                         printf("-1\n");
540                         break;
541                 }
542
543                 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
544                 if (ifindex == 0 || n < 0 || n > MAX_ADDRS) {
545                         printf("-1\n");
546                         break;
547                 }
548                 af = su.sa.sa_family;
549                 if (af2sock(af, s, s6) == -1) {
550                         warnc(EPROTONOSUPPORT, "setsourcefilter");
551                         break;
552                 }
553
554                 memset(&hints, 0, sizeof(struct addrinfo));
555                 hints.ai_flags = AI_NUMERICHOST;
556                 hints.ai_family = af;
557                 hints.ai_socktype = SOCK_DGRAM;
558
559                 for (i = 0; i < n; i++) {
560                         sockunion_t *psu = (sockunion_t *)&sources[i];
561                         /*
562                          * Trim trailing whitespace, as getaddrinfo()
563                          * can't cope with it.
564                          */
565                         fgets(str1, sizeof(str1), fp);
566                         cp = strchr(str1, '\n');
567                         if (cp != NULL)
568                                 *cp = '\0';
569
570                         res = NULL;
571                         error = getaddrinfo(str1, "0", &hints, &res);
572                         if (error)
573                                 break;
574                         assert(res != NULL);
575
576                         memset(psu, 0, sizeof(sockunion_t));
577                         af1 = res->ai_family;
578                         if (af1 == af)
579                                 memcpy(psu, res->ai_addr, res->ai_addrlen);
580                         freeaddrinfo(res);
581                         if (af1 != af)
582                                 break;
583                 }
584                 if (i < n) {
585                         if (error)
586                                 warnx("getaddrinfo: %s", gai_strerror(error));
587                         printf("-1\n");
588                         break;
589                 }
590                 if (setsourcefilter(af2sock(af, s, s6), ifindex,
591                     &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0)
592                         warn("setsourcefilter");
593                 else
594                         printf("ok\n");
595         } break;
596
597         /*
598          * Allow or block traffic from a source, using the
599          * delta based api.
600          */
601         case 't':
602         case 'b': {
603                 str3[0] = '\0';
604                 toptname = "";
605                 sscanf(line, "%s %s %s", str1, str2, str3);
606                 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
607                 if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) {
608                         printf("-1\n");
609                         break;
610                 }
611                 af = su.sa.sa_family;
612                 if (af2sock(af, s, s6) == -1) {
613                         warnc(EPROTONOSUPPORT, "getsourcefilter");
614                         break;
615                 }
616
617                 /* First determine our current filter mode. */
618                 if (getsourcefilter(af2sock(af, s, s6), ifindex,
619                     &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) {
620                         warn("getsourcefilter");
621                         break;
622                 }
623 #ifdef INET
624                 if (af == AF_INET) {
625                         struct in_addr ina;
626
627                         error = __ifindex_to_primary_ip(ifindex, &ina);
628                         if (error != 0) {
629                                 warn("primary_ip_lookup %s", str2);
630                                 printf("-1\n");
631                                 break;
632                         }
633                         level = IPPROTO_IP;
634                         optval = (void *)&mr.mrs;
635                         optlen = sizeof(mr.mrs);
636                         mr.mrs.imr_multiaddr = su.sin.sin_addr;
637                         mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
638                         mr.mrs.imr_interface = ina;
639                         if (fmode == MCAST_EXCLUDE) {
640                                 /* Any-source mode socket membership. */
641                                 optname = (*cmd == 't') ?
642                                     IP_UNBLOCK_SOURCE :
643                                     IP_BLOCK_SOURCE;
644                                 toptname = (*cmd == 't') ?
645                                     "IP_UNBLOCK_SOURCE" :
646                                     "IP_BLOCK_SOURCE";
647                         } else {
648                                 /* Source-specific mode socket membership. */
649                                 optname = (*cmd == 't') ?
650                                     IP_ADD_SOURCE_MEMBERSHIP :
651                                     IP_DROP_SOURCE_MEMBERSHIP;
652                                 toptname = (*cmd == 't') ?
653                                     "IP_ADD_SOURCE_MEMBERSHIP" :
654                                     "IP_DROP_SOURCE_MEMBERSHIP";
655                         }
656                         if (setsockopt(s, level, optname, optval,
657                             optlen) == 0) {
658                                 printf("ok\n");
659                                 break;
660                         } else {
661                                 warn("setsockopt %s", toptname);
662                         }
663                 }
664 #ifdef INET6
665                 else
666 #endif /* INET with INET6 */
667 #endif /* INET */
668 #ifdef INET6
669                 if (af == AF_INET6) {
670                         level = IPPROTO_IPV6;
671                         mr.gr.gsr_interface = ifindex;
672                         mr.gr.gsr_group = su.ss;
673                         mr.gr.gsr_source = su2.ss;
674                         if (fmode == MCAST_EXCLUDE) {
675                                 /* Any-source mode socket membership. */
676                                 optname = (*cmd == 't') ?
677                                     MCAST_UNBLOCK_SOURCE :
678                                     MCAST_BLOCK_SOURCE;
679                                 toptname = (*cmd == 't') ?
680                                     "MCAST_UNBLOCK_SOURCE" :
681                                     "MCAST_BLOCK_SOURCE";
682                         } else {
683                                 /* Source-specific mode socket membership. */
684                                 optname = (*cmd == 't') ?
685                                     MCAST_JOIN_SOURCE_GROUP :
686                                     MCAST_LEAVE_SOURCE_GROUP;
687                                 toptname = (*cmd == 't') ?
688                                     "MCAST_JOIN_SOURCE_GROUP":
689                                     "MCAST_LEAVE_SOURCE_GROUP";
690                         }
691                         optval = (void *)&mr.gr;
692                         optlen = sizeof(mr.gr);
693                         if (setsockopt(s6, level, optname, optval,
694                             optlen) == 0) {
695                                 printf("ok\n");
696                                 break;
697                         } else {
698                                 warn("setsockopt %s", toptname);
699                         }
700                 }
701 #endif /* INET6 */
702                 /* FALLTHROUGH */
703                 printf("-1\n");
704         } break;
705
706         case 'g': {
707                 sockunion_t      sources[MAX_ADDRS];
708                 char             addrbuf[NI_MAXHOST];
709                 int              nreqsrc, nsrc;
710
711                 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
712                         printf("-1\n");
713                         break;
714                 }
715                 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
716                 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
717                         printf("-1\n");
718                         break;
719                 }
720
721                 af = su.sa.sa_family;
722                 if (af2sock(af, s, s6) == -1) {
723                         warnc(EPROTONOSUPPORT, "getsourcefilter");
724                         break;
725                 }
726                 nsrc = nreqsrc;
727                 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
728                     su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) {
729                         warn("getsourcefilter");
730                         printf("-1\n");
731                         break;
732                 }
733                 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
734                     "exclude");
735                 printf("%d\n", nsrc);
736
737                 nsrc = MIN(nreqsrc, nsrc);
738                 fprintf(stderr, "hexdump of sources:\n");
739                 uint8_t *bp = (uint8_t *)&sources[0];
740                 for (j = 0; j < (nsrc * sizeof(sources[0])); j++) {
741                         fprintf(stderr, "%02x", bp[j]);
742                 }
743                 fprintf(stderr, "\nend hexdump\n");
744
745                 qsort(sources, nsrc, af2socklen(af), su_cmp);
746                 for (i = 0; i < nsrc; i++) {
747                         sockunion_t *psu = (sockunion_t *)&sources[i];
748                         addrbuf[0] = '\0';
749                         error = getnameinfo(&psu->sa, psu->sa.sa_len,
750                             addrbuf, sizeof(addrbuf), NULL, 0,
751                             NI_NUMERICHOST);
752                         if (error)
753                                 warnx("getnameinfo: %s", gai_strerror(error));
754                         else
755                                 printf("%s\n", addrbuf);
756                 }
757                 printf("ok\n");
758         } break;
759
760         /* link-layer stuff follows. */
761
762         case 'a':
763         case 'd': {
764                 struct sockaddr_dl      *dlp;
765                 struct ether_addr       *ep;
766
767                 memset(&ifr, 0, sizeof(struct ifreq));
768                 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
769                 dlp->sdl_len = sizeof(struct sockaddr_dl);
770                 dlp->sdl_family = AF_LINK;
771                 dlp->sdl_index = 0;
772                 dlp->sdl_nlen = 0;
773                 dlp->sdl_alen = ETHER_ADDR_LEN;
774                 dlp->sdl_slen = 0;
775                 if (sscanf(line, "%s %s", str1, str2) != 2) {
776                         warnc(EINVAL, "sscanf");
777                         break;
778                 }
779                 ep = ether_aton(str2);
780                 if (ep == NULL) {
781                         warnc(EINVAL, "ether_aton");
782                         break;
783                 }
784                 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
785                 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
786                 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
787                     &ifr) == -1) {
788                         warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
789                         printf("-1\n");
790                 } else
791                         printf("ok\n");
792                 break;
793         }
794
795         case 'm':
796                 fprintf(stderr,
797                     "warning: IFF_ALLMULTI cannot be set from userland "
798                     "in FreeBSD; command ignored.\n");
799                 printf("-1\n");
800                 break;
801
802         case 'p':
803                 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
804                         printf("-1\n");
805                         break;
806                 }
807                 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
808                         warn("ioctl SIOCGIFFLAGS");
809                         break;
810                 }
811                 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
812                 if (f == 0) {
813                         flags &= ~IFF_PPROMISC;
814                 } else {
815                         flags |= IFF_PPROMISC;
816                 }
817                 ifr.ifr_flags = flags & 0xffff;
818                 ifr.ifr_flagshigh = flags >> 16;
819                 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
820                         warn("ioctl SIOCGIFFLAGS");
821                 else
822                         printf( "changed to 0x%08x\n", flags );
823                 break;
824
825         case '\n':
826                 break;
827         default:
828                 printf("invalid command\n");
829                 break;
830         }
831 }
832
833 static void
834 usage(void)
835 {
836
837         printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
838         printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
839         printf(
840 "i mcast-addr ifname n          - set n include mode src filter\n");
841         printf(
842 "e mcast-addr ifname n          - set n exclude mode src filter\n");
843         printf("t mcast-addr ifname src-addr  - allow traffic from src\n");
844         printf("b mcast-addr ifname src-addr  - block traffic from src\n");
845         printf("g mcast-addr ifname n        - get and show n src filters\n");
846         printf("a ifname mac-addr          - add link multicast filter\n");
847         printf("d ifname mac-addr          - delete link multicast filter\n");
848         printf("m ifname 1/0               - set/clear ether allmulti flag\n");
849         printf("p ifname 1/0               - set/clear ether promisc flag\n");
850         printf("f filename                 - read command(s) from file\n");
851         printf("s seconds                  - sleep for some time\n");
852         printf("q                          - quit\n");
853 }
854