]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/mtest/mtest.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / mtest / mtest.c
1 /*-
2  * Copyright (c) 2007-2009 Bruce Simpson.
3  * Copyright (c) 2000 Wilbert De Graaf.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 /*
32  * Diagnostic and test utility for multicast sockets.
33  * XXX: This file currently assumes INET support in the base system.
34  * TODO: Support embedded KAME Scope ID in IPv6 group addresses.
35  * TODO: Use IPv4 link-local address when source address selection
36  * is implemented; use MCAST_JOIN_SOURCE for IPv4.
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
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)
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)
213                 err(1, "can't open IPv6 socket");
214 #endif
215
216         if (argc < 2) {
217                 if (isatty(STDIN_FILENO)) {
218                         printf("multicast membership test program; "
219                             "enter ? for list of commands\n");
220                 }
221                 do {
222                         if (fgets(line, sizeof(line), stdin) != NULL) {
223                                 if (line[0] != 'f')
224                                         process_cmd(line, s, s6, stdin);
225                                 else {
226                                         /* Get the filename */
227                                         for (i = 1; isblank(line[i]); i++);
228                                         if ((p = (char*)strchr(line, '\n'))
229                                             != NULL)
230                                                 *p = '\0';
231                                         process_file(&line[i], s, s6);
232                                 }
233                         }
234                 } while (!feof(stdin));
235         } else {
236                 for (i = 1; i < argc; i++) {
237                         process_file(argv[i], s, s6);
238                 }
239         }
240
241         if (s != -1)
242                 close(s);
243         if (s6 != -1)
244                 close(s6);
245
246         exit (0);
247 }
248
249 static void
250 process_file(char *fname, int s, int s6)
251 {
252         char line[80];
253         FILE *fp;
254         char *lineptr;
255
256         fp = fopen(fname, "r");
257         if (fp == NULL) {
258                 warn("fopen");
259                 return;
260         }
261
262         /* Skip comments and empty lines. */
263         while (fgets(line, sizeof(line), fp) != NULL) {
264                 lineptr = line;
265                 while (isblank(*lineptr))
266                         lineptr++;
267                 if (*lineptr != '#' && *lineptr != '\n')
268                         process_cmd(lineptr, s, s6, fp);
269         }
270
271         fclose(fp);
272 }
273
274 /*
275  * Parse join/leave/allow/block arguments, given:
276  *  str1: group (as AF_INET or AF_INET6 printable)
277  *  str2: ifname
278  *  str3: optional source address (may be NULL).
279  *   This argument must have the same parsed address family as str1.
280  * Return the ifindex of ifname, or 0 if any parse element failed.
281  */
282 static uint32_t
283 parse_cmd_args(sockunion_t *psu, sockunion_t *psu2,
284     const char *str1, const char *str2, const char *str3)
285 {
286         struct addrinfo          hints;
287         struct addrinfo         *res;
288         uint32_t                 ifindex;
289         int                      af, error;
290
291         assert(psu != NULL);
292         assert(str1 != NULL);
293         assert(str2 != NULL);
294
295         af = AF_UNSPEC;
296
297         ifindex = if_nametoindex(str2);
298         if (ifindex == 0)
299                 return (0);
300
301         memset(&hints, 0, sizeof(struct addrinfo));
302         hints.ai_flags = AI_NUMERICHOST;
303         hints.ai_family = PF_UNSPEC;
304         hints.ai_socktype = SOCK_DGRAM;
305
306         memset(psu, 0, sizeof(sockunion_t));
307         psu->sa.sa_family = AF_UNSPEC;
308
309         error = getaddrinfo(str1, "0", &hints, &res);
310         if (error) {
311                 warnx("getaddrinfo: %s", gai_strerror(error));
312                 return (0);
313         }
314         assert(res != NULL);
315         af = res->ai_family;
316         memcpy(psu, res->ai_addr, res->ai_addrlen);
317         freeaddrinfo(res);
318
319         /* sscanf() may pass the empty string. */
320         if (psu2 != NULL && str3 != NULL && *str3 != '\0') {
321                 memset(psu2, 0, sizeof(sockunion_t));
322                 psu2->sa.sa_family = AF_UNSPEC;
323
324                 /* look for following address family; str3 is *optional*. */
325                 hints.ai_family = af;
326                 error = getaddrinfo(str3, "0", &hints, &res);
327                 if (error) {
328                         warnx("getaddrinfo: %s", gai_strerror(error));
329                         ifindex = 0;
330                 } else {
331                         if (af != res->ai_family) {
332                                 errno = EINVAL; /* XXX */
333                                 ifindex = 0;
334                         }
335                         memcpy(psu2, res->ai_addr, res->ai_addrlen);
336                         freeaddrinfo(res);
337                 }
338         }
339
340         return (ifindex);
341 }
342
343 static __inline int
344 af2sock(const int af, int s, int s6)
345 {
346
347 #ifdef INET
348         if (af == AF_INET)
349                 return (s);
350 #endif
351 #ifdef INET6
352         if (af == AF_INET6)
353                 return (s6);
354 #endif
355         return (-1);
356 }
357
358 static __inline int
359 af2socklen(const int af)
360 {
361
362 #ifdef INET
363         if (af == AF_INET)
364                 return (sizeof(struct sockaddr_in));
365 #endif
366 #ifdef INET6
367         if (af == AF_INET6)
368                 return (sizeof(struct sockaddr_in6));
369 #endif
370         return (-1);
371 }
372
373 static void
374 process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused)
375 {
376         char                     str1[STR_SIZE];
377         char                     str2[STR_SIZE];
378         char                     str3[STR_SIZE];
379         mrequnion_t              mr;
380         sockunion_t              su, su2;
381         struct ifreq             ifr;
382         char                    *line;
383         char                    *toptname;
384         void                    *optval;
385         uint32_t                 fmode, ifindex;
386         socklen_t                optlen;
387         int                      af, error, f, flags, i, level, n, optname;
388
389         af = AF_UNSPEC;
390         su.sa.sa_family = AF_UNSPEC;
391         su2.sa.sa_family = AF_UNSPEC;
392
393         line = cmd;
394         while (isblank(*++line))
395                 ;       /* Skip whitespace. */
396
397         switch (*cmd) {
398         case '?':
399                 usage();
400                 break;
401
402         case 'q':
403                 close(s);
404                 exit(0);
405
406         case 's':
407                 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
408                         printf("-1\n");
409                         break;
410                 }
411                 sleep(n);
412                 printf("ok\n");
413                 break;
414
415         case 'j':
416         case 'l':
417                 str3[0] = '\0';
418                 toptname = "";
419                 sscanf(line, "%s %s %s", str1, str2, str3);
420                 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
421                 if (ifindex == 0) {
422                         printf("-1\n");
423                         break;
424                 }
425                 af = su.sa.sa_family;
426 #ifdef INET
427                 if (af == AF_INET) {
428                         struct in_addr ina;
429
430                         error = __ifindex_to_primary_ip(ifindex, &ina);
431                         if (error != 0) {
432                                 warn("primary_ip_lookup %s", str2);
433                                 printf("-1\n");
434                                 break;
435                         }
436                         level = IPPROTO_IP;
437
438                         if (su2.sa.sa_family != AF_UNSPEC) {
439                                 mr.mrs.imr_multiaddr = su.sin.sin_addr;
440                                 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
441                                 mr.mrs.imr_interface = ina;
442                                 optname = (*cmd == 'j') ?
443                                     IP_ADD_SOURCE_MEMBERSHIP :
444                                     IP_DROP_SOURCE_MEMBERSHIP;
445                                 toptname = (*cmd == 'j') ?
446                                     "IP_ADD_SOURCE_MEMBERSHIP" :
447                                     "IP_DROP_SOURCE_MEMBERSHIP";
448                                 optval = (void *)&mr.mrs;
449                                 optlen = sizeof(mr.mrs);
450                         } else {
451                                 mr.mr.imr_multiaddr = su.sin.sin_addr;
452                                 mr.mr.imr_interface = ina;
453                                 optname = (*cmd == 'j') ?
454                                     IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
455                                 toptname = (*cmd == 'j') ?
456                                     "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
457                                 optval = (void *)&mr.mr;
458                                 optlen = sizeof(mr.mr);
459                         }
460                         if (setsockopt(s, level, optname, optval,
461                             optlen) == 0) {
462                                 printf("ok\n");
463                                 break;
464                         } else {
465                                 warn("setsockopt %s", toptname);
466                         }
467                 }
468 #ifdef INET6
469                 else
470 #endif /* INET with INET6 */
471 #endif /* INET */
472 #ifdef INET6
473                 if (af == AF_INET6) {
474                         level = IPPROTO_IPV6;
475                         if (su2.sa.sa_family != AF_UNSPEC) {
476                                 mr.gr.gsr_interface = ifindex;
477                                 mr.gr.gsr_group = su.ss;
478                                 mr.gr.gsr_source = su2.ss;
479                                 optname = (*cmd == 'j') ?
480                                     MCAST_JOIN_SOURCE_GROUP:
481                                     MCAST_LEAVE_SOURCE_GROUP;
482                                 toptname = (*cmd == 'j') ?
483                                     "MCAST_JOIN_SOURCE_GROUP":
484                                     "MCAST_LEAVE_SOURCE_GROUP";
485                                 optval = (void *)&mr.gr;
486                                 optlen = sizeof(mr.gr);
487                         } else {
488                                 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
489                                 mr.mr6.ipv6mr_interface = ifindex;
490                                 optname = (*cmd == 'j') ?
491                                     IPV6_JOIN_GROUP :
492                                     IPV6_LEAVE_GROUP;
493                                 toptname = (*cmd == 'j') ?
494                                     "IPV6_JOIN_GROUP" :
495                                     "IPV6_LEAVE_GROUP";
496                                 optval = (void *)&mr.mr6;
497                                 optlen = sizeof(mr.mr6);
498                         }
499                         if (setsockopt(s6, level, optname, optval,
500                             optlen) == 0) {
501                                 printf("ok\n");
502                                 break;
503                         } else {
504                                 warn("setsockopt %s", toptname);
505                         }
506                 }
507 #endif /* INET6 */
508                 /* FALLTHROUGH */
509                 printf("-1\n");
510                 break;
511
512         /*
513          * Set the socket to include or exclude filter mode, and
514          * add some sources to the filterlist, using the full-state API.
515          */
516         case 'i':
517         case 'e': {
518                 sockunion_t      sources[MAX_ADDRS];
519                 struct addrinfo  hints;
520                 struct addrinfo *res;
521                 char            *cp;
522                 int              af1;
523
524                 n = 0;
525                 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
526                 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
527                         printf("-1\n");
528                         break;
529                 }
530
531                 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
532                 if (ifindex == 0 || n < 0 || n > MAX_ADDRS) {
533                         printf("-1\n");
534                         break;
535                 }
536                 af = su.sa.sa_family;
537
538                 memset(&hints, 0, sizeof(struct addrinfo));
539                 hints.ai_flags = AI_NUMERICHOST;
540                 hints.ai_family = af;
541                 hints.ai_socktype = SOCK_DGRAM;
542
543                 for (i = 0; i < n; i++) {
544                         sockunion_t *psu = (sockunion_t *)&sources[i];
545                         /*
546                          * Trim trailing whitespace, as getaddrinfo()
547                          * can't cope with it.
548                          */
549                         fgets(str1, sizeof(str1), fp);
550                         cp = strchr(str1, '\n');
551                         if (cp != NULL)
552                                 *cp = '\0';
553
554                         res = NULL;
555                         error = getaddrinfo(str1, "0", &hints, &res);
556                         if (error)
557                                 break;
558                         assert(res != NULL);
559
560                         memset(psu, 0, sizeof(sockunion_t));
561                         af1 = res->ai_family;
562                         if (af1 == af)
563                                 memcpy(psu, res->ai_addr, res->ai_addrlen);
564                         freeaddrinfo(res);
565                         if (af1 != af)
566                                 break;
567                 }
568                 if (i < n) {
569                         if (error)
570                                 warnx("getaddrinfo: %s", gai_strerror(error));
571                         printf("-1\n");
572                         break;
573                 }
574                 if (setsourcefilter(af2sock(af, s, s6), ifindex,
575                     &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0)
576                         warn("setsourcefilter");
577                 else
578                         printf("ok\n");
579         } break;
580
581         /*
582          * Allow or block traffic from a source, using the
583          * delta based api.
584          */
585         case 't':
586         case 'b': {
587                 str3[0] = '\0';
588                 toptname = "";
589                 sscanf(line, "%s %s %s", str1, str2, str3);
590                 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
591                 if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) {
592                         printf("-1\n");
593                         break;
594                 }
595                 af = su.sa.sa_family;
596
597                 /* First determine our current filter mode. */
598                 n = 0;
599                 if (getsourcefilter(af2sock(af, s, s6), ifindex,
600                     &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) {
601                         warn("getsourcefilter");
602                         break;
603                 }
604 #ifdef INET
605                 if (af == AF_INET) {
606                         struct in_addr ina;
607
608                         error = __ifindex_to_primary_ip(ifindex, &ina);
609                         if (error != 0) {
610                                 warn("primary_ip_lookup %s", str2);
611                                 printf("-1\n");
612                                 break;
613                         }
614                         level = IPPROTO_IP;
615                         optval = (void *)&mr.mrs;
616                         optlen = sizeof(mr.mrs);
617                         mr.mrs.imr_multiaddr = su.sin.sin_addr;
618                         mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
619                         mr.mrs.imr_interface = ina;
620                         if (fmode == MCAST_EXCLUDE) {
621                                 /* Any-source mode socket membership. */
622                                 optname = (*cmd == 't') ?
623                                     IP_UNBLOCK_SOURCE :
624                                     IP_BLOCK_SOURCE;
625                                 toptname = (*cmd == 't') ?
626                                     "IP_UNBLOCK_SOURCE" :
627                                     "IP_BLOCK_SOURCE";
628                         } else {
629                                 /* Source-specific mode socket membership. */
630                                 optname = (*cmd == 't') ?
631                                     IP_ADD_SOURCE_MEMBERSHIP :
632                                     IP_DROP_SOURCE_MEMBERSHIP;
633                                 toptname = (*cmd == 't') ?
634                                     "IP_ADD_SOURCE_MEMBERSHIP" :
635                                     "IP_DROP_SOURCE_MEMBERSHIP";
636                         }
637                         if (setsockopt(s, level, optname, optval,
638                             optlen) == 0) {
639                                 printf("ok\n");
640                                 break;
641                         } else {
642                                 warn("setsockopt %s", toptname);
643                         }
644                 }
645 #ifdef INET6
646                 else
647 #endif /* INET with INET6 */
648 #endif /* INET */
649 #ifdef INET6
650                 if (af == AF_INET6) {
651                         level = IPPROTO_IPV6;
652                         mr.gr.gsr_interface = ifindex;
653                         mr.gr.gsr_group = su.ss;
654                         mr.gr.gsr_source = su2.ss;
655                         if (fmode == MCAST_EXCLUDE) {
656                                 /* Any-source mode socket membership. */
657                                 optname = (*cmd == 't') ?
658                                     MCAST_UNBLOCK_SOURCE :
659                                     MCAST_BLOCK_SOURCE;
660                                 toptname = (*cmd == 't') ?
661                                     "MCAST_UNBLOCK_SOURCE" :
662                                     "MCAST_BLOCK_SOURCE";
663                         } else {
664                                 /* Source-specific mode socket membership. */
665                                 optname = (*cmd == 't') ?
666                                     MCAST_JOIN_SOURCE_GROUP :
667                                     MCAST_LEAVE_SOURCE_GROUP;
668                                 toptname = (*cmd == 't') ?
669                                     "MCAST_JOIN_SOURCE_GROUP":
670                                     "MCAST_LEAVE_SOURCE_GROUP";
671                         }
672                         optval = (void *)&mr.gr;
673                         optlen = sizeof(mr.gr);
674                         if (setsockopt(s6, level, optname, optval,
675                             optlen) == 0) {
676                                 printf("ok\n");
677                                 break;
678                         } else {
679                                 warn("setsockopt %s", toptname);
680                         }
681                 }
682 #endif /* INET6 */
683                 /* FALLTHROUGH */
684                 printf("-1\n");
685         } break;
686
687         case 'g': {
688                 sockunion_t      sources[MAX_ADDRS];
689                 char             addrbuf[NI_MAXHOST];
690                 int              nreqsrc, nsrc;
691
692                 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
693                         printf("-1\n");
694                         break;
695                 }
696                 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
697                 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
698                         printf("-1\n");
699                         break;
700                 }
701
702                 af = su.sa.sa_family;
703                 nsrc = nreqsrc;
704                 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
705                     su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) {
706                         warn("getsourcefilter");
707                         printf("-1\n");
708                         break;
709                 }
710                 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
711                     "exclude");
712                 printf("%d\n", nsrc);
713
714                 nsrc = MIN(nreqsrc, nsrc);
715                 fprintf(stderr, "hexdump of sources:\n");
716                 uint8_t *bp = (uint8_t *)&sources[0];
717                 for (i = 0; i < (nsrc * sizeof(sources[0])); i++) {
718                         fprintf(stderr, "%02x", bp[i]);
719                 }
720                 fprintf(stderr, "\nend hexdump\n");
721
722                 qsort(sources, nsrc, af2socklen(af), su_cmp);
723                 for (i = 0; i < nsrc; i++) {
724                         sockunion_t *psu = (sockunion_t *)&sources[i];
725                         addrbuf[0] = '\0';
726                         error = getnameinfo(&psu->sa, psu->sa.sa_len,
727                             addrbuf, sizeof(addrbuf), NULL, 0,
728                             NI_NUMERICHOST);
729                         if (error)
730                                 warnx("getnameinfo: %s", gai_strerror(error));
731                         else
732                                 printf("%s\n", addrbuf);
733                 }
734                 printf("ok\n");
735         } break;
736
737         /* link-layer stuff follows. */
738
739         case 'a':
740         case 'd': {
741                 struct sockaddr_dl      *dlp;
742                 struct ether_addr       *ep;
743
744                 memset(&ifr, 0, sizeof(struct ifreq));
745                 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
746                 dlp->sdl_len = sizeof(struct sockaddr_dl);
747                 dlp->sdl_family = AF_LINK;
748                 dlp->sdl_index = 0;
749                 dlp->sdl_nlen = 0;
750                 dlp->sdl_alen = ETHER_ADDR_LEN;
751                 dlp->sdl_slen = 0;
752                 if (sscanf(line, "%s %s", str1, str2) != 2) {
753                         warnc(EINVAL, "sscanf");
754                         break;
755                 }
756                 ep = ether_aton(str2);
757                 if (ep == NULL) {
758                         warnc(EINVAL, "ether_aton");
759                         break;
760                 }
761                 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
762                 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
763                 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
764                     &ifr) == -1) {
765                         warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
766                         printf("-1\n");
767                 } else
768                         printf("ok\n");
769                 break;
770         }
771
772         case 'm':
773                 fprintf(stderr,
774                     "warning: IFF_ALLMULTI cannot be set from userland "
775                     "in FreeBSD; command ignored.\n");
776                 printf("-1\n");
777                 break;
778
779         case 'p':
780                 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
781                         printf("-1\n");
782                         break;
783                 }
784                 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
785                         warn("ioctl SIOCGIFFLAGS");
786                         break;
787                 }
788                 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
789                 if (f == 0) {
790                         flags &= ~IFF_PPROMISC;
791                 } else {
792                         flags |= IFF_PPROMISC;
793                 }
794                 ifr.ifr_flags = flags & 0xffff;
795                 ifr.ifr_flagshigh = flags >> 16;
796                 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
797                         warn("ioctl SIOCGIFFLAGS");
798                 else
799                         printf( "changed to 0x%08x\n", flags );
800                 break;
801
802         case '\n':
803                 break;
804         default:
805                 printf("invalid command\n");
806                 break;
807         }
808 }
809
810 static void
811 usage(void)
812 {
813
814         printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
815         printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
816         printf(
817 "i mcast-addr ifname n          - set n include mode src filter\n");
818         printf(
819 "e mcast-addr ifname n          - set n exclude mode src filter\n");
820         printf("t mcast-addr ifname src-addr  - allow traffic from src\n");
821         printf("b mcast-addr ifname src-addr  - block traffic from src\n");
822         printf("g mcast-addr ifname n        - get and show n src filters\n");
823         printf("a ifname mac-addr          - add link multicast filter\n");
824         printf("d ifname mac-addr          - delete link multicast filter\n");
825         printf("m ifname 1/0               - set/clear ether allmulti flag\n");
826         printf("p ifname 1/0               - set/clear ether promisc flag\n");
827         printf("f filename                 - read command(s) from file\n");
828         printf("s seconds                  - sleep for some time\n");
829         printf("q                          - quit\n");
830 }
831