]> git.saurik.com Git - apple/network_cmds.git/blob - netstat.tproj/mcast.c
network_cmds-356.9.tar.gz
[apple/network_cmds.git] / netstat.tproj / mcast.c
1 /*
2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org>
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 */
54
55 #include <sys/cdefs.h>
56
57 /*
58 * Print the running system's current multicast group memberships.
59 * As this relies on getifmaddrs(), it may not be used with a core file.
60 */
61
62 #include <sys/types.h>
63 #include <sys/param.h>
64 #include <sys/sysctl.h>
65 #include <sys/ioctl.h>
66 #include <sys/socket.h>
67 #include <sys/errno.h>
68
69 #include <net/if.h>
70 #include <net/if_var.h>
71 #include <net/if_mib.h>
72 #include <net/if_types.h>
73 #include <net/if_dl.h>
74 #include <net/route.h>
75 #include <netinet/in.h>
76 #include <netinet/if_ether.h>
77 #include <netinet/igmp_var.h>
78 #include <netinet6/mld6_var.h>
79 #include <arpa/inet.h>
80 #include <netdb.h>
81
82 #include <ctype.h>
83 #include <err.h>
84 #include <ifaddrs.h>
85 #include <sysexits.h>
86
87 #include <stddef.h>
88 #include <stdarg.h>
89 #include <stdlib.h>
90 #include <stdint.h>
91 #include <stdio.h>
92 #include <string.h>
93 #include <ifaddrs.h>
94
95
96 #include "netstat.h"
97
98 union sockunion {
99 struct sockaddr_storage ss;
100 struct sockaddr sa;
101 struct sockaddr_dl sdl;
102 struct sockaddr_in sin;
103 struct sockaddr_in6 sin6;
104 };
105 typedef union sockunion sockunion_t;
106
107 /*
108 * This may have been defined in <net/if.h>. Note that if <net/if.h> is
109 * to be included it must be included before this header file.
110 */
111 #ifndef ifa_broadaddr
112 #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
113 #endif
114
115 //struct ifmaddrs {
116 // struct ifmaddrs *ifma_next;
117 // struct sockaddr *ifma_name;
118 // struct sockaddr *ifma_addr;
119 // struct sockaddr *ifma_lladdr;
120 //};
121
122 void ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af);
123 static int ifmalist_dump_mcstat(struct ifmaddrs *);
124 static void in_ifinfo(struct igmp_ifinfo *);
125 static const char *inm_mode(u_int);
126 static void inm_print_sources_sysctl(uint32_t, struct in_addr);
127 #ifdef INET6
128 static void in6_ifinfo(struct mld_ifinfo *);
129 static void in6m_print_sources_sysctl(uint32_t, struct in6_addr *);
130 static const char *inet6_n2a(struct in6_addr *);
131 #endif
132 static void printb(const char *, unsigned int, const char *);
133 static const char *sdl_addr_to_hex(const struct sockaddr_dl *, char *, int);
134
135 extern char *routename6(struct sockaddr_in6 *);
136
137 #define sa_equal(a1, a2) \
138 (bcmp((a1), (a2), ((a1))->sa_len) == 0)
139
140 #define sa_dl_equal(a1, a2) \
141 ((((struct sockaddr_dl *)(a1))->sdl_len == \
142 ((struct sockaddr_dl *)(a2))->sdl_len) && \
143 (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \
144 LLADDR((struct sockaddr_dl *)(a2)), \
145 ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
146
147 #define SALIGN (sizeof(uint32_t) - 1)
148 #define SA_RLEN(sa) (sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
149 (SALIGN + 1)) : 0)
150 #define MAX_SYSCTL_TRY 5
151 #define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
152
153 void
154 ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af)
155 {
156 const struct ifmaddrs *ifma;
157 sockunion_t *psa;
158 char myifname[IFNAMSIZ];
159 char *pcolon;
160 char *pafname, *pifname, *plladdr = NULL, *pgroup = NULL;
161
162 switch (af) {
163 case AF_INET:
164 pafname = "IPv4";
165 break;
166 #ifdef INET6
167 case AF_INET6:
168 pafname = "IPv6";
169 break;
170 #endif
171 case AF_LINK:
172 pafname = "Link-layer";
173 break;
174 default:
175 return; /* XXX */
176 }
177
178 fprintf(stdout, "%s Multicast Group Memberships\n", pafname);
179 fprintf(stdout, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address",
180 "Netif");
181
182 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
183
184 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
185 continue;
186
187 /* Group address */
188 psa = (sockunion_t *)ifma->ifma_addr;
189 if (psa->sa.sa_family != af)
190 continue;
191
192 switch (psa->sa.sa_family) {
193 case AF_INET:
194 pgroup = inet_ntoa(psa->sin.sin_addr);
195 break;
196 #ifdef INET6
197 case AF_INET6:
198 pgroup = routename6(&(psa->sin6));
199 break;
200 #endif
201 case AF_LINK:
202 if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) ||
203 (psa->sdl.sdl_type == IFT_ETHER)) {
204 pgroup =
205 ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data);
206 #ifdef notyet
207 } else {
208 pgroup = addr2ascii(AF_LINK,
209 &psa->sdl,
210 sizeof(struct sockaddr_dl),
211 addrbuf);
212 #endif
213 }
214 break;
215 default:
216 continue; /* XXX */
217 }
218
219 /* Link-layer mapping, if any */
220 psa = (sockunion_t *)ifma->ifma_lladdr;
221 if (psa != NULL) {
222 if (psa->sa.sa_family == AF_LINK) {
223 if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) ||
224 (psa->sdl.sdl_type == IFT_ETHER)) {
225 /* IEEE 802 */
226 plladdr =
227 ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data);
228 #ifdef notyet
229 } else {
230 /* something more exotic */
231 plladdr = addr2ascii(AF_LINK,
232 &psa->sdl,
233 sizeof(struct sockaddr_dl),
234 addrbuf);
235 #endif
236 }
237 } else {
238 int i;
239
240 /* not a link-layer address */
241 plladdr = "<invalid>";
242
243 for (i = 0; psa->sa.sa_len > 2 && i < psa->sa.sa_len - 2; i++)
244 printf("0x%x ", psa->sa.sa_data[i]);
245 printf("\n");
246 }
247 } else {
248 plladdr = "<none>";
249 }
250
251 /* Interface upon which the membership exists */
252 psa = (sockunion_t *)ifma->ifma_name;
253 if (psa != NULL && psa->sa.sa_family == AF_LINK) {
254 strlcpy(myifname, link_ntoa(&psa->sdl), IFNAMSIZ);
255 pcolon = strchr(myifname, ':');
256 if (pcolon)
257 *pcolon = '\0';
258 pifname = myifname;
259 } else {
260 pifname = "";
261 }
262
263 fprintf(stdout, "%-20s\t%-16s\t%s\n", pgroup, plladdr, pifname);
264 }
265 }
266
267 void
268 ifmalist_dump(void)
269 {
270 struct ifmaddrs *ifmap;
271
272 if (getifmaddrs(&ifmap))
273 err(EX_OSERR, "getifmaddrs");
274
275 ifmalist_dump_af(ifmap, AF_LINK);
276 fputs("\n", stdout);
277 ifmalist_dump_af(ifmap, AF_INET);
278 #ifdef INET6
279 fputs("\n", stdout);
280 ifmalist_dump_af(ifmap, AF_INET6);
281 #endif
282 if (sflag) {
283 fputs("\n", stdout);
284 ifmalist_dump_mcstat(ifmap);
285 }
286
287 freeifmaddrs(ifmap);
288 }
289
290 static int
291 ifmalist_dump_mcstat(struct ifmaddrs *ifmap)
292 {
293 char thisifname[IFNAMSIZ];
294 char addrbuf[NI_MAXHOST];
295 struct ifaddrs *ifap, *ifa;
296 struct ifmaddrs *ifma;
297 sockunion_t lastifasa;
298 sockunion_t *psa, *pgsa, *pllsa, *pifasa;
299 char *pcolon;
300 char *pafname;
301 uint32_t lastifindex, thisifindex;
302 int error;
303 uint32_t ifindex = 0;
304
305 if (interface != NULL)
306 ifindex = if_nametoindex(interface);
307
308 error = 0;
309 ifap = NULL;
310 lastifindex = 0;
311 thisifindex = 0;
312 lastifasa.ss.ss_family = AF_UNSPEC;
313
314 if (getifaddrs(&ifap) != 0) {
315 warn("getifmaddrs");
316 return (-1);
317 }
318
319 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
320 error = 0;
321 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
322 continue;
323
324 psa = (sockunion_t *)ifma->ifma_name;
325 if (psa->sa.sa_family != AF_LINK) {
326 fprintf(stderr,
327 "WARNING: Kernel returned invalid data.\n");
328 error = -1;
329 break;
330 }
331
332 /* Filter on interface name. */
333 thisifindex = psa->sdl.sdl_index;
334 if (ifindex != 0 && thisifindex != ifindex)
335 continue;
336
337 /* Filter on address family. */
338 pgsa = (sockunion_t *)ifma->ifma_addr;
339 if (af != 0 && pgsa->sa.sa_family != af)
340 continue;
341
342 strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
343 pcolon = strchr(thisifname, ':');
344 if (pcolon)
345 *pcolon = '\0';
346
347 /* Only print the banner for the first ifmaddrs entry. */
348 if (lastifindex == 0 || lastifindex != thisifindex) {
349 lastifindex = thisifindex;
350 fprintf(stdout, "%s:\n", thisifname);
351 }
352
353 /*
354 * Currently, multicast joins only take place on the
355 * primary IPv4 address, and only on the link-local IPv6
356 * address, as per IGMPv2/3 and MLDv1/2 semantics.
357 * Therefore, we only look up the primary address on
358 * the first pass.
359 */
360 pifasa = NULL;
361 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
362 if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
363 (ifa->ifa_addr == NULL) ||
364 (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
365 continue;
366 /*
367 * For AF_INET6 only the link-local address should
368 * be returned. If built without IPv6 support,
369 * skip this address entirely.
370 */
371 pifasa = (sockunion_t *)ifa->ifa_addr;
372 if (pifasa->sa.sa_family == AF_INET6
373 #ifdef INET6
374 && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
375 #endif
376 ) {
377 pifasa = NULL;
378 continue;
379 }
380 break;
381 }
382 if (pifasa == NULL)
383 continue; /* primary address not found */
384
385 if (!vflag && pifasa->sa.sa_family == AF_LINK)
386 continue;
387
388 /* Parse and print primary address, if not already printed. */
389 if (lastifasa.ss.ss_family == AF_UNSPEC ||
390 ((lastifasa.ss.ss_family == AF_LINK &&
391 !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
392 !sa_equal(&lastifasa.sa, &pifasa->sa))) {
393
394 switch (pifasa->sa.sa_family) {
395 case AF_INET:
396 pafname = "inet";
397 break;
398 case AF_INET6:
399 pafname = "inet6";
400 break;
401 case AF_LINK:
402 pafname = "link";
403 break;
404 default:
405 pafname = "unknown";
406 break;
407 }
408
409 switch (pifasa->sa.sa_family) {
410 case AF_INET6:
411 #ifdef INET6
412 {
413 const char *p =
414 inet6_n2a(&pifasa->sin6.sin6_addr);
415 strlcpy(addrbuf, p, sizeof(addrbuf));
416 break;
417 }
418 #else
419 /* FALLTHROUGH */
420 #endif
421 case AF_INET:
422 error = getnameinfo(&pifasa->sa,
423 pifasa->sa.sa_len,
424 addrbuf, sizeof(addrbuf), NULL, 0,
425 NI_NUMERICHOST);
426 if (error)
427 printf("getnameinfo: %s\n",
428 gai_strerror(error));
429 break;
430 case AF_LINK: {
431 (void) sdl_addr_to_hex(&pifasa->sdl, addrbuf,
432 sizeof (addrbuf));
433 break;
434 }
435 default:
436 addrbuf[0] = '\0';
437 break;
438 }
439
440 fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
441 /*
442 * Print per-link IGMP information, if available.
443 */
444 if (pifasa->sa.sa_family == AF_INET) {
445 struct igmp_ifinfo igi;
446 size_t mibsize, len;
447 int mib[5];
448
449 mibsize = sizeof(mib) / sizeof(mib[0]);
450 if (sysctlnametomib("net.inet.igmp.ifinfo",
451 mib, &mibsize) == -1) {
452 perror("sysctlnametomib");
453 goto next_ifnet;
454 }
455 mib[mibsize] = thisifindex;
456 len = sizeof(struct igmp_ifinfo);
457 if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
458 0) == -1) {
459 perror("sysctl net.inet.igmp.ifinfo");
460 goto next_ifnet;
461 }
462 in_ifinfo(&igi);
463 }
464 #ifdef INET6
465 /*
466 * Print per-link MLD information, if available.
467 */
468 if (pifasa->sa.sa_family == AF_INET6) {
469 struct mld_ifinfo mli;
470 size_t mibsize, len;
471 int mib[5];
472
473 mibsize = sizeof(mib) / sizeof(mib[0]);
474 if (sysctlnametomib("net.inet6.mld.ifinfo",
475 mib, &mibsize) == -1) {
476 perror("sysctlnametomib");
477 goto next_ifnet;
478 }
479 mib[mibsize] = thisifindex;
480 len = sizeof(struct mld_ifinfo);
481 if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
482 0) == -1) {
483 perror("sysctl net.inet6.mld.ifinfo");
484 goto next_ifnet;
485 }
486 in6_ifinfo(&mli);
487 }
488 #endif /* INET6 */
489 #if defined(INET6)
490 next_ifnet:
491 #endif
492 lastifasa = *pifasa;
493 }
494
495 /* Print this group address. */
496 #ifdef INET6
497 if (pgsa->sa.sa_family == AF_INET6) {
498 const char *p = inet6_n2a(&pgsa->sin6.sin6_addr);
499 strlcpy(addrbuf, p, sizeof(addrbuf));
500 } else
501 #endif
502 if (pgsa->sa.sa_family == AF_INET) {
503 error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
504 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
505 if (error)
506 printf("getnameinfo: %s\n",
507 gai_strerror(error));
508 } else {
509 (void) sdl_addr_to_hex(&pgsa->sdl, addrbuf,
510 sizeof (addrbuf));
511 }
512
513 fprintf(stdout, "\t\tgroup %s", addrbuf);
514 if (pgsa->sa.sa_family == AF_INET) {
515 inm_print_sources_sysctl(thisifindex,
516 pgsa->sin.sin_addr);
517 }
518 #ifdef INET6
519 if (pgsa->sa.sa_family == AF_INET6) {
520 in6m_print_sources_sysctl(thisifindex,
521 &pgsa->sin6.sin6_addr);
522 }
523 #endif
524 fprintf(stdout, "\n");
525
526 /* Link-layer mapping, if present. */
527 pllsa = (sockunion_t *)ifma->ifma_lladdr;
528 if (pllsa != NULL) {
529 (void) sdl_addr_to_hex(&pllsa->sdl, addrbuf,
530 sizeof (addrbuf));
531 fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
532 }
533 }
534
535 if (ifap != NULL)
536 freeifaddrs(ifap);
537
538 return (error);
539 }
540
541 static void
542 in_ifinfo(struct igmp_ifinfo *igi)
543 {
544
545 printf("\t");
546 switch (igi->igi_version) {
547 case IGMP_VERSION_1:
548 case IGMP_VERSION_2:
549 case IGMP_VERSION_3:
550 printf("igmpv%d", igi->igi_version);
551 break;
552 default:
553 printf("igmpv?(%d)", igi->igi_version);
554 break;
555 }
556 printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
557 if (igi->igi_version == IGMP_VERSION_3) {
558 printf(" rv %u qi %u qri %u uri %u",
559 igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
560 }
561 if (vflag >= 2) {
562 printf(" v1timer %u v2timer %u v3timer %u",
563 igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
564 }
565 printf("\n");
566 }
567
568 static const char *inm_modes[] = {
569 "undefined",
570 "include",
571 "exclude",
572 };
573
574 static const char *
575 inm_mode(u_int mode)
576 {
577
578 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
579 return (inm_modes[mode]);
580 return (NULL);
581 }
582
583 /*
584 * Retrieve per-group source filter mode and lists via sysctl.
585 */
586 static void
587 inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
588 {
589 #define MAX_SYSCTL_TRY 5
590 int mib[7];
591 int ntry = 0;
592 size_t mibsize;
593 size_t len;
594 size_t needed;
595 size_t cnt;
596 int i;
597 char *buf;
598 struct in_addr *pina;
599 uint32_t *p;
600 uint32_t fmode;
601 const char *modestr;
602
603 mibsize = sizeof(mib) / sizeof(mib[0]);
604 if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
605 perror("sysctlnametomib");
606 return;
607 }
608
609 needed = 0;
610 mib[5] = ifindex;
611 mib[6] = gina.s_addr; /* 32 bits wide */
612 mibsize = sizeof(mib) / sizeof(mib[0]);
613 do {
614 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
615 perror("sysctl net.inet.ip.mcast.filters");
616 return;
617 }
618 if ((buf = malloc(needed)) == NULL) {
619 perror("malloc");
620 return;
621 }
622 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
623 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
624 perror("sysctl");
625 goto out_free;
626 }
627 free(buf);
628 buf = NULL;
629 }
630 } while (buf == NULL);
631
632 len = needed;
633 if (len < sizeof(uint32_t)) {
634 perror("sysctl");
635 goto out_free;
636 }
637
638 p = (uint32_t *)buf;
639 fmode = *p++;
640 len -= sizeof(uint32_t);
641
642 modestr = inm_mode(fmode);
643 if (modestr)
644 printf(" mode %s", modestr);
645 else
646 printf(" mode (%u)", fmode);
647
648 if (vflag == 0)
649 goto out_free;
650
651 cnt = len / sizeof(struct in_addr);
652 pina = (struct in_addr *)p;
653
654 for (i = 0; i < cnt; i++) {
655 if (i == 0)
656 printf(" srcs ");
657 fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
658 inet_ntoa(*pina++));
659 len -= sizeof(struct in_addr);
660 }
661 if (len > 0) {
662 fprintf(stderr, "warning: %u trailing bytes from %s\n",
663 (unsigned int)len, "net.inet.ip.mcast.filters");
664 }
665
666 out_free:
667 free(buf);
668 #undef MAX_SYSCTL_TRY
669 }
670
671 #ifdef INET6
672
673 static void
674 in6_ifinfo(struct mld_ifinfo *mli)
675 {
676
677 printf("\t");
678 switch (mli->mli_version) {
679 case MLD_VERSION_1:
680 case MLD_VERSION_2:
681 printf("mldv%d", mli->mli_version);
682 break;
683 default:
684 printf("mldv?(%d)", mli->mli_version);
685 break;
686 }
687 printb(" flags", mli->mli_flags, "\020\1SILENT");
688 if (mli->mli_version == MLD_VERSION_2) {
689 printf(" rv %u qi %u qri %u uri %u",
690 mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
691 }
692 if (vflag >= 2) {
693 printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
694 mli->mli_v2_timer);
695 }
696 printf("\n");
697 }
698
699 /*
700 * Retrieve MLD per-group source filter mode and lists via sysctl.
701 *
702 * Note: The 128-bit IPv6 group addres needs to be segmented into
703 * 32-bit pieces for marshaling to sysctl. So the MIB name ends
704 * up looking like this:
705 * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
706 * Assumes that pgroup originated from the kernel, so its components
707 * are already in network-byte order.
708 */
709 static void
710 in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
711 {
712 #define MAX_SYSCTL_TRY 5
713 char addrbuf[INET6_ADDRSTRLEN];
714 int mib[10];
715 int ntry = 0;
716 int *pi;
717 size_t mibsize;
718 size_t len;
719 size_t needed;
720 size_t cnt;
721 int i;
722 char *buf;
723 struct in6_addr *pina;
724 uint32_t *p;
725 uint32_t fmode;
726 const char *modestr;
727
728 mibsize = sizeof(mib) / sizeof(mib[0]);
729 if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
730 &mibsize) == -1) {
731 perror("sysctlnametomib");
732 return;
733 }
734
735 needed = 0;
736 mib[5] = ifindex;
737 pi = (int *)pgroup;
738 for (i = 0; i < 4; i++)
739 mib[6 + i] = *pi++;
740
741 mibsize = sizeof(mib) / sizeof(mib[0]);
742 do {
743 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
744 perror("sysctl net.inet6.ip6.mcast.filters");
745 return;
746 }
747 if ((buf = malloc(needed)) == NULL) {
748 perror("malloc");
749 return;
750 }
751 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
752 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
753 perror("sysctl");
754 goto out_free;
755 }
756 free(buf);
757 buf = NULL;
758 }
759 } while (buf == NULL);
760
761 len = needed;
762 if (len < sizeof(uint32_t)) {
763 perror("sysctl");
764 goto out_free;
765 }
766
767 p = (uint32_t *)buf;
768 fmode = *p++;
769 len -= sizeof(uint32_t);
770
771 modestr = inm_mode(fmode);
772 if (modestr)
773 printf(" mode %s", modestr);
774 else
775 printf(" mode (%u)", fmode);
776
777 if (vflag == 0)
778 goto out_free;
779
780 cnt = len / sizeof(struct in6_addr);
781 pina = (struct in6_addr *)p;
782
783 for (i = 0; i < cnt; i++) {
784 if (i == 0)
785 printf(" srcs ");
786 inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
787 INET6_ADDRSTRLEN);
788 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
789 len -= sizeof(struct in6_addr);
790 }
791 if (len > 0) {
792 fprintf(stderr, "warning: %u trailing bytes from %s\n",
793 (unsigned int)len, "net.inet6.ip6.mcast.filters");
794 }
795
796 out_free:
797 free(buf);
798 #undef MAX_SYSCTL_TRY
799 }
800
801 static const char *
802 inet6_n2a(struct in6_addr *p)
803 {
804 static char buf[NI_MAXHOST];
805 struct sockaddr_in6 sin6;
806 u_int32_t scopeid;
807 const int niflags = NI_NUMERICHOST;
808
809 memset(&sin6, 0, sizeof(sin6));
810 sin6.sin6_family = AF_INET6;
811 sin6.sin6_len = sizeof(struct sockaddr_in6);
812 sin6.sin6_addr = *p;
813 if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
814 IN6_IS_ADDR_MC_NODELOCAL(p)) {
815 scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
816 if (scopeid) {
817 sin6.sin6_scope_id = scopeid;
818 sin6.sin6_addr.s6_addr[2] = 0;
819 sin6.sin6_addr.s6_addr[3] = 0;
820 }
821 }
822 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
823 buf, sizeof(buf), NULL, 0, niflags) == 0) {
824 return (buf);
825 } else {
826 return ("(invalid)");
827 }
828 }
829 #endif /* INET6 */
830
831 /*
832 * Print a value a la the %b format of the kernel's printf
833 */
834 void
835 printb(const char *s, unsigned int v, const char *bits)
836 {
837 int i, any = 0;
838 char c;
839
840 if (bits && *bits == 8)
841 printf("%s=%o", s, v);
842 else
843 printf("%s=%x", s, v);
844 bits++;
845 if (bits) {
846 putchar('<');
847 while ((i = *bits++) != '\0') {
848 if (v & (1 << (i-1))) {
849 if (any)
850 putchar(',');
851 any = 1;
852 for (; (c = *bits) > 32; bits++)
853 putchar(c);
854 } else
855 for (; *bits > 32; bits++)
856 ;
857 }
858 putchar('>');
859 }
860 }
861
862 /*
863 * convert hardware address to hex string for logging errors.
864 */
865 static const char *
866 sdl_addr_to_hex(const struct sockaddr_dl *sdl, char *orig_buf, int buflen)
867 {
868 char *buf = orig_buf;
869 int i;
870 const u_char *lladdr;
871 int maxbytes = buflen / 3;
872
873 lladdr = (u_char *)(size_t)sdl->sdl_data + sdl->sdl_nlen;
874
875 if (maxbytes > sdl->sdl_alen) {
876 maxbytes = sdl->sdl_alen;
877 }
878 *buf = '\0';
879 for (i = 0; i < maxbytes; i++) {
880 snprintf(buf, 3, "%02x", lladdr[i]);
881 buf += 2;
882 *buf = (i == maxbytes - 1) ? '\0' : ':';
883 buf++;
884 }
885 return (orig_buf);
886 }
887