]> git.saurik.com Git - apple/network_cmds.git/blob - mtest.tproj/mtest.c
network_cmds-356.9.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 __inline int
351 af2socklen(const int af)
352 {
353
354 if (af == AF_INET)
355 return (sizeof(struct sockaddr_in));
356 #ifdef INET6
357 if (af == AF_INET6)
358 return (sizeof(struct sockaddr_in6));
359 #endif
360 return (-1);
361 }
362
363 static void
364 process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused)
365 {
366 char str1[STR_SIZE];
367 char str2[STR_SIZE];
368 char str3[STR_SIZE];
369 mrequnion_t mr;
370 sockunion_t su, su2;
371 struct ifreq ifr;
372 char *line;
373 char *toptname;
374 void *optval;
375 uint32_t fmode, ifindex;
376 socklen_t optlen;
377 int af, error, i, level, n, optname;
378 #ifndef __APPLE__
379 int f, flags;
380 #endif /* __APPLE__ */
381
382 af = AF_UNSPEC;
383 su.sa.sa_family = AF_UNSPEC;
384 su2.sa.sa_family = AF_UNSPEC;
385
386 line = cmd;
387 while (isblank(*++line))
388 ; /* Skip whitespace. */
389
390 switch (*cmd) {
391 case '?':
392 usage();
393 break;
394
395 case 'q':
396 close(s);
397 exit(0);
398
399 case 's':
400 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
401 printf("-1\n");
402 break;
403 }
404 sleep(n);
405 printf("ok\n");
406 break;
407
408 case 'j':
409 case 'l':
410 str3[0] = '\0';
411 toptname = "";
412 sscanf(line, "%s %s %s", str1, str2, str3);
413 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
414 if (ifindex == 0) {
415 printf("-1\n");
416 break;
417 }
418 af = su.sa.sa_family;
419 if (af == AF_INET) {
420 struct in_addr ina;
421
422 error = __ifindex_to_primary_ip(ifindex, &ina);
423 if (error != 0) {
424 warn("primary_ip_lookup %s", str2);
425 printf("-1\n");
426 break;
427 }
428 level = IPPROTO_IP;
429
430 #ifdef HAS_SSM
431 if (su2.sa.sa_family != AF_UNSPEC) {
432 mr.mrs.imr_multiaddr = su.sin.sin_addr;
433 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
434 mr.mrs.imr_interface = ina;
435 optname = (*cmd == 'j') ?
436 IP_ADD_SOURCE_MEMBERSHIP :
437 IP_DROP_SOURCE_MEMBERSHIP;
438 toptname = (*cmd == 'j') ?
439 "IP_ADD_SOURCE_MEMBERSHIP" :
440 "IP_DROP_SOURCE_MEMBERSHIP";
441 optval = (void *)&mr.mrs;
442 optlen = sizeof(mr.mrs);
443 } else {
444 #endif
445 mr.mr.imr_multiaddr = su.sin.sin_addr;
446 mr.mr.imr_interface = ina;
447 optname = (*cmd == 'j') ?
448 IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
449 toptname = (*cmd == 'j') ?
450 "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
451 optval = (void *)&mr.mr;
452 optlen = sizeof(mr.mr);
453 #ifdef HAS_SSM
454 }
455 #endif
456 if (setsockopt(s, level, optname, optval,
457 optlen) == 0) {
458 printf("ok\n");
459 break;
460 } else {
461 warn("setsockopt %s", toptname);
462 }
463 }
464 #ifdef INET6
465 else
466 #endif /* INET6 */
467 #ifdef INET6
468 if (af == AF_INET6) {
469 level = IPPROTO_IPV6;
470 #ifdef HAS_SSM
471 if (su2.sa.sa_family != AF_UNSPEC) {
472 mr.gr.gsr_interface = ifindex;
473 mr.gr.gsr_group = su.ss;
474 mr.gr.gsr_source = su2.ss;
475 optname = (*cmd == 'j') ?
476 MCAST_JOIN_SOURCE_GROUP:
477 MCAST_LEAVE_SOURCE_GROUP;
478 toptname = (*cmd == 'j') ?
479 "MCAST_JOIN_SOURCE_GROUP":
480 "MCAST_LEAVE_SOURCE_GROUP";
481 optval = (void *)&mr.gr;
482 optlen = sizeof(mr.gr);
483 } else {
484 #endif
485 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
486 mr.mr6.ipv6mr_interface = ifindex;
487 optname = (*cmd == 'j') ?
488 IPV6_JOIN_GROUP :
489 IPV6_LEAVE_GROUP;
490 toptname = (*cmd == 'j') ?
491 "IPV6_JOIN_GROUP" :
492 "IPV6_LEAVE_GROUP";
493 optval = (void *)&mr.mr6;
494 optlen = sizeof(mr.mr6);
495 #ifdef HAS_SSM
496 }
497 #endif
498 if (setsockopt(s6, level, optname, optval,
499 optlen) == 0) {
500 printf("ok\n");
501 break;
502 } else {
503 warn("setsockopt %s", toptname);
504 }
505 }
506 #endif /* INET6 */
507 /* FALLTHROUGH */
508 printf("-1\n");
509 break;
510
511 #ifdef HAS_SSM
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, (uint32_t *)&n, NULL) != 0) {
601 warn("getsourcefilter");
602 break;
603 }
604 if (af == AF_INET) {
605 struct in_addr ina;
606
607 error = __ifindex_to_primary_ip(ifindex, &ina);
608 if (error != 0) {
609 warn("primary_ip_lookup %s", str2);
610 printf("-1\n");
611 break;
612 }
613 level = IPPROTO_IP;
614 optval = (void *)&mr.mrs;
615 optlen = sizeof(mr.mrs);
616 mr.mrs.imr_multiaddr = su.sin.sin_addr;
617 mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
618 mr.mrs.imr_interface = ina;
619 if (fmode == MCAST_EXCLUDE) {
620 /* Any-source mode socket membership. */
621 optname = (*cmd == 't') ?
622 IP_UNBLOCK_SOURCE :
623 IP_BLOCK_SOURCE;
624 toptname = (*cmd == 't') ?
625 "IP_UNBLOCK_SOURCE" :
626 "IP_BLOCK_SOURCE";
627 } else {
628 /* Source-specific mode socket membership. */
629 optname = (*cmd == 't') ?
630 IP_ADD_SOURCE_MEMBERSHIP :
631 IP_DROP_SOURCE_MEMBERSHIP;
632 toptname = (*cmd == 't') ?
633 "IP_ADD_SOURCE_MEMBERSHIP" :
634 "IP_DROP_SOURCE_MEMBERSHIP";
635 }
636 if (setsockopt(s, level, optname, optval,
637 optlen) == 0) {
638 printf("ok\n");
639 break;
640 } else {
641 warn("setsockopt %s", toptname);
642 }
643 }
644 #ifdef INET6
645 else
646 #endif /* INET6 */
647 #ifdef INET6
648 if (af == AF_INET6) {
649 level = IPPROTO_IPV6;
650 mr.gr.gsr_interface = ifindex;
651 mr.gr.gsr_group = su.ss;
652 mr.gr.gsr_source = su2.ss;
653 if (fmode == MCAST_EXCLUDE) {
654 /* Any-source mode socket membership. */
655 optname = (*cmd == 't') ?
656 MCAST_UNBLOCK_SOURCE :
657 MCAST_BLOCK_SOURCE;
658 toptname = (*cmd == 't') ?
659 "MCAST_UNBLOCK_SOURCE" :
660 "MCAST_BLOCK_SOURCE";
661 } else {
662 /* Source-specific mode socket membership. */
663 optname = (*cmd == 't') ?
664 MCAST_JOIN_SOURCE_GROUP :
665 MCAST_LEAVE_SOURCE_GROUP;
666 toptname = (*cmd == 't') ?
667 "MCAST_JOIN_SOURCE_GROUP":
668 "MCAST_LEAVE_SOURCE_GROUP";
669 }
670 optval = (void *)&mr.gr;
671 optlen = sizeof(mr.gr);
672 if (setsockopt(s6, level, optname, optval,
673 optlen) == 0) {
674 printf("ok\n");
675 break;
676 } else {
677 warn("setsockopt %s", toptname);
678 }
679 }
680 #endif /* INET6 */
681 /* FALLTHROUGH */
682 printf("-1\n");
683 } break;
684
685 case 'g': {
686 sockunion_t sources[MAX_ADDRS];
687 char addrbuf[NI_MAXHOST];
688 int nreqsrc, nsrc;
689
690 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
691 printf("-1\n");
692 break;
693 }
694 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
695 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
696 printf("-1\n");
697 break;
698 }
699
700 af = su.sa.sa_family;
701 nsrc = nreqsrc;
702 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
703 su.sa.sa_len, &fmode, (uint32_t *)&nsrc,
704 &sources[0].ss) != 0) {
705 warn("getsourcefilter");
706 printf("-1\n");
707 break;
708 }
709 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
710 "exclude");
711 printf("%d\n", nsrc);
712
713 nsrc = MIN(nreqsrc, nsrc);
714 fprintf(stderr, "hexdump of sources:\n");
715 uint8_t *bp = (uint8_t *)&sources[0];
716 for (i = 0; i < (nsrc * sizeof(sources[0])); i++) {
717 fprintf(stderr, "%02x", bp[i]);
718 }
719 fprintf(stderr, "\nend hexdump\n");
720
721 qsort(sources, nsrc, sizeof (sockunion_t), su_cmp);
722 for (i = 0; i < nsrc; i++) {
723 sockunion_t *psu = (sockunion_t *)&sources[i];
724 addrbuf[0] = '\0';
725 error = getnameinfo(&psu->sa, psu->sa.sa_len,
726 addrbuf, sizeof(addrbuf), NULL, 0,
727 NI_NUMERICHOST);
728 if (error)
729 warnx("getnameinfo: %s", gai_strerror(error));
730 else
731 printf("%s\n", addrbuf);
732 }
733 printf("ok\n");
734 } break;
735 #endif
736 /* link-layer stuff follows. */
737
738 case 'a':
739 case 'd': {
740 struct sockaddr_dl *dlp;
741 struct ether_addr *ep;
742
743 memset(&ifr, 0, sizeof(struct ifreq));
744 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
745 dlp->sdl_len = sizeof(struct sockaddr_dl);
746 dlp->sdl_family = AF_LINK;
747 dlp->sdl_index = 0;
748 dlp->sdl_nlen = 0;
749 dlp->sdl_alen = ETHER_ADDR_LEN;
750 dlp->sdl_slen = 0;
751 if (sscanf(line, "%s %s", str1, str2) != 2) {
752 warnc(EINVAL, "sscanf");
753 break;
754 }
755 ep = ether_aton(str2);
756 if (ep == NULL) {
757 warnc(EINVAL, "ether_aton");
758 break;
759 }
760 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
761 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
762 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
763 &ifr) == -1) {
764 warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
765 printf("-1\n");
766 } else
767 printf("ok\n");
768 break;
769 }
770
771 case 'm':
772 fprintf(stderr,
773 "warning: IFF_ALLMULTI cannot be set from userland "
774 "in Darwin; command ignored.\n");
775 printf("-1\n");
776 break;
777
778 #ifndef __APPLE__
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 #endif /* __APPLE__ */
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 #ifndef HAS_SSM
815 printf("j mcast-addr ifname - join IP multicast group\n");
816 printf("l mcast-addr ifname - leave IP multicast group\n");
817 #else /* HAS_SSM */
818 printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
819 printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
820 printf(
821 "i mcast-addr ifname n - set n include mode src filter\n");
822 printf(
823 "e mcast-addr ifname n - set n exclude mode src filter\n");
824 printf("t mcast-addr ifname src-addr - allow traffic from src\n");
825 printf("b mcast-addr ifname src-addr - block traffic from src\n");
826 printf("g mcast-addr ifname n - get and show n src filters\n");
827 #endif
828 printf("a ifname mac-addr - add link multicast filter\n");
829 printf("d ifname mac-addr - delete link multicast filter\n");
830 printf("m ifname 1/0 - set/clear ether allmulti flag\n");
831 #ifndef __APPLE__
832 printf("p ifname 1/0 - set/clear ether promisc flag\n");
833 #endif /* __APPLE__ */
834 printf("f filename - read command(s) from file\n");
835 printf("s seconds - sleep for some time\n");
836 printf("q - quit\n");
837 }
838