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