]>
git.saurik.com Git - apple/network_cmds.git/blob - netstat.tproj/mcast.c
3637f4de42ed024fbc6d7451b9a447a5039b668f
2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org>
30 * All rights reserved.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
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.
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
55 #include <sys/cdefs.h>
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.
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>
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>
99 struct sockaddr_storage ss
;
101 struct sockaddr_dl sdl
;
102 struct sockaddr_in sin
;
103 struct sockaddr_in6 sin6
;
105 typedef union sockunion sockunion_t
;
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.
111 #ifndef ifa_broadaddr
112 #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
116 // struct ifmaddrs *ifma_next;
117 // struct sockaddr *ifma_name;
118 // struct sockaddr *ifma_addr;
119 // struct sockaddr *ifma_lladdr;
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
);
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
*);
132 static void printb(const char *, unsigned int, const char *);
133 static const char *sdl_addr_to_hex(const struct sockaddr_dl
*, char *, int);
135 extern char *routename6(struct sockaddr_in6
*);
137 #define sa_equal(a1, a2) \
138 (bcmp((a1), (a2), ((a1))->sa_len) == 0)
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))
147 #define SALIGN (sizeof(uint32_t) - 1)
148 #define SA_RLEN(sa) (sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
150 #define MAX_SYSCTL_TRY 5
151 #define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
154 ifmalist_dump_af(const struct ifmaddrs
* const ifmap
, int const af
)
156 const struct ifmaddrs
*ifma
;
158 char myifname
[IFNAMSIZ
];
160 char *pafname
, *pifname
, *plladdr
= NULL
, *pgroup
= NULL
;
172 pafname
= "Link-layer";
178 fprintf(stdout
, "%s Multicast Group Memberships\n", pafname
);
179 fprintf(stdout
, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address",
182 for (ifma
= ifmap
; ifma
; ifma
= ifma
->ifma_next
) {
184 if (ifma
->ifma_name
== NULL
|| ifma
->ifma_addr
== NULL
)
188 psa
= (sockunion_t
*)ifma
->ifma_addr
;
189 if (psa
->sa
.sa_family
!= af
)
192 switch (psa
->sa
.sa_family
) {
194 pgroup
= inet_ntoa(psa
->sin
.sin_addr
);
198 pgroup
= routename6(&(psa
->sin6
));
202 if ((psa
->sdl
.sdl_alen
== ETHER_ADDR_LEN
) ||
203 (psa
->sdl
.sdl_type
== IFT_ETHER
)) {
205 ether_ntoa((struct ether_addr
*)&psa
->sdl
.sdl_data
);
208 pgroup
= addr2ascii(AF_LINK
,
210 sizeof(struct sockaddr_dl
),
219 /* Link-layer mapping, if any */
220 psa
= (sockunion_t
*)ifma
->ifma_lladdr
;
222 if (psa
->sa
.sa_family
== AF_LINK
) {
223 if ((psa
->sdl
.sdl_alen
== ETHER_ADDR_LEN
) ||
224 (psa
->sdl
.sdl_type
== IFT_ETHER
)) {
227 ether_ntoa((struct ether_addr
*)&psa
->sdl
.sdl_data
);
230 /* something more exotic */
231 plladdr
= addr2ascii(AF_LINK
,
233 sizeof(struct sockaddr_dl
),
240 /* not a link-layer address */
241 plladdr
= "<invalid>";
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
]);
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
), sizeof(myifname
));
255 pcolon
= strchr(myifname
, ':');
263 fprintf(stdout
, "%-20s\t%-16s\t%s\n", pgroup
, plladdr
, pifname
);
270 struct ifmaddrs
*ifmap
;
272 if (getifmaddrs(&ifmap
))
273 err(EX_OSERR
, "getifmaddrs");
275 ifmalist_dump_af(ifmap
, AF_LINK
);
277 ifmalist_dump_af(ifmap
, AF_INET
);
280 ifmalist_dump_af(ifmap
, AF_INET6
);
284 ifmalist_dump_mcstat(ifmap
);
291 ifmalist_dump_mcstat(struct ifmaddrs
*ifmap
)
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
;
301 uint32_t lastifindex
, thisifindex
;
303 uint32_t ifindex
= 0;
305 if (interface
!= NULL
)
306 ifindex
= if_nametoindex(interface
);
312 lastifasa
.ss
.ss_family
= AF_UNSPEC
;
314 if (getifaddrs(&ifap
) != 0) {
319 for (ifma
= ifmap
; ifma
; ifma
= ifma
->ifma_next
) {
321 if (ifma
->ifma_name
== NULL
|| ifma
->ifma_addr
== NULL
)
324 psa
= (sockunion_t
*)ifma
->ifma_name
;
325 if (psa
->sa
.sa_family
!= AF_LINK
) {
327 "WARNING: Kernel returned invalid data.\n");
332 /* Filter on interface name. */
333 thisifindex
= psa
->sdl
.sdl_index
;
334 if (ifindex
!= 0 && thisifindex
!= ifindex
)
337 /* Filter on address family. */
338 pgsa
= (sockunion_t
*)ifma
->ifma_addr
;
339 if (af
!= 0 && pgsa
->sa
.sa_family
!= af
)
342 strlcpy(thisifname
, link_ntoa(&psa
->sdl
), sizeof(thisifname
));
343 pcolon
= strchr(thisifname
, ':');
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
);
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
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
))
367 * For AF_INET6 only the link-local address should
368 * be returned. If built without IPv6 support,
369 * skip this address entirely.
371 pifasa
= (sockunion_t
*)ifa
->ifa_addr
;
372 if (pifasa
->sa
.sa_family
== AF_INET6
374 && !IN6_IS_ADDR_LINKLOCAL(&pifasa
->sin6
.sin6_addr
)
383 continue; /* primary address not found */
385 if (!vflag
&& pifasa
->sa
.sa_family
== AF_LINK
)
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
))) {
394 switch (pifasa
->sa
.sa_family
) {
409 switch (pifasa
->sa
.sa_family
) {
414 inet6_n2a(&pifasa
->sin6
.sin6_addr
);
415 strlcpy(addrbuf
, p
, sizeof(addrbuf
));
422 error
= getnameinfo(&pifasa
->sa
,
424 addrbuf
, sizeof(addrbuf
), NULL
, 0,
427 printf("getnameinfo: %s\n",
428 gai_strerror(error
));
431 (void) sdl_addr_to_hex(&pifasa
->sdl
, addrbuf
,
440 fprintf(stdout
, "\t%s %s\n", pafname
, addrbuf
);
442 * Print per-link IGMP information, if available.
444 if (pifasa
->sa
.sa_family
== AF_INET
) {
445 struct igmp_ifinfo igi
;
449 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
450 if (sysctlnametomib("net.inet.igmp.ifinfo",
451 mib
, &mibsize
) == -1) {
452 perror("sysctlnametomib");
455 mib
[mibsize
] = thisifindex
;
456 len
= sizeof(struct igmp_ifinfo
);
457 if (sysctl(mib
, mibsize
+ 1, &igi
, &len
, NULL
,
459 perror("sysctl net.inet.igmp.ifinfo");
466 * Print per-link MLD information, if available.
468 if (pifasa
->sa
.sa_family
== AF_INET6
) {
469 struct mld_ifinfo mli
;
473 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
474 if (sysctlnametomib("net.inet6.mld.ifinfo",
475 mib
, &mibsize
) == -1) {
476 perror("sysctlnametomib");
479 mib
[mibsize
] = thisifindex
;
480 len
= sizeof(struct mld_ifinfo
);
481 if (sysctl(mib
, mibsize
+ 1, &mli
, &len
, NULL
,
483 perror("sysctl net.inet6.mld.ifinfo");
495 /* Print this group address. */
497 if (pgsa
->sa
.sa_family
== AF_INET6
) {
498 const char *p
= inet6_n2a(&pgsa
->sin6
.sin6_addr
);
499 strlcpy(addrbuf
, p
, sizeof(addrbuf
));
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
);
506 printf("getnameinfo: %s\n",
507 gai_strerror(error
));
509 (void) sdl_addr_to_hex(&pgsa
->sdl
, addrbuf
,
513 fprintf(stdout
, "\t\tgroup %s", addrbuf
);
514 if (pgsa
->sa
.sa_family
== AF_INET
) {
515 inm_print_sources_sysctl(thisifindex
,
519 if (pgsa
->sa
.sa_family
== AF_INET6
) {
520 in6m_print_sources_sysctl(thisifindex
,
521 &pgsa
->sin6
.sin6_addr
);
524 fprintf(stdout
, "\n");
526 /* Link-layer mapping, if present. */
527 pllsa
= (sockunion_t
*)ifma
->ifma_lladdr
;
529 (void) sdl_addr_to_hex(&pllsa
->sdl
, addrbuf
,
531 fprintf(stdout
, "\t\t\tmcast-macaddr %s\n", addrbuf
);
542 in_ifinfo(struct igmp_ifinfo
*igi
)
546 switch (igi
->igi_version
) {
550 printf("igmpv%d", igi
->igi_version
);
553 printf("igmpv?(%d)", igi
->igi_version
);
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
);
562 printf(" v1timer %u v2timer %u v3timer %u",
563 igi
->igi_v1_timer
, igi
->igi_v2_timer
, igi
->igi_v3_timer
);
568 static const char *inm_modes
[] = {
578 if (mode
>= MCAST_UNDEFINED
&& mode
<= MCAST_EXCLUDE
)
579 return (inm_modes
[mode
]);
584 * Retrieve per-group source filter mode and lists via sysctl.
587 inm_print_sources_sysctl(uint32_t ifindex
, struct in_addr gina
)
589 #define MAX_SYSCTL_TRY 5
598 struct in_addr
*pina
;
603 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
604 if (sysctlnametomib("net.inet.ip.mcast.filters", mib
, &mibsize
) == -1) {
605 perror("sysctlnametomib");
611 mib
[6] = gina
.s_addr
; /* 32 bits wide */
612 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
614 if (sysctl(mib
, mibsize
, NULL
, &needed
, NULL
, 0) == -1) {
615 perror("sysctl net.inet.ip.mcast.filters");
618 if ((buf
= malloc(needed
)) == NULL
) {
622 if (sysctl(mib
, mibsize
, buf
, &needed
, NULL
, 0) == -1) {
623 if (errno
!= ENOMEM
|| ++ntry
>= MAX_SYSCTL_TRY
) {
630 } while (buf
== NULL
);
633 if (len
< sizeof(uint32_t)) {
640 len
-= sizeof(uint32_t);
642 modestr
= inm_mode(fmode
);
644 printf(" mode %s", modestr
);
646 printf(" mode (%u)", fmode
);
651 cnt
= len
/ sizeof(struct in_addr
);
652 pina
= (struct in_addr
*)p
;
654 for (i
= 0; i
< cnt
; i
++) {
657 fprintf(stdout
, "%s%s", (i
== 0 ? "" : ","),
659 len
-= sizeof(struct in_addr
);
662 fprintf(stderr
, "warning: %u trailing bytes from %s\n",
663 (unsigned int)len
, "net.inet.ip.mcast.filters");
668 #undef MAX_SYSCTL_TRY
674 in6_ifinfo(struct mld_ifinfo
*mli
)
678 switch (mli
->mli_version
) {
681 printf("mldv%d", mli
->mli_version
);
684 printf("mldv?(%d)", mli
->mli_version
);
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
);
693 printf(" v1timer %u v2timer %u", mli
->mli_v1_timer
,
700 * Retrieve MLD per-group source filter mode and lists via sysctl.
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.
710 in6m_print_sources_sysctl(uint32_t ifindex
, struct in6_addr
*pgroup
)
712 #define MAX_SYSCTL_TRY 5
713 char addrbuf
[INET6_ADDRSTRLEN
];
723 struct in6_addr
*pina
;
728 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
729 if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib
,
731 perror("sysctlnametomib");
738 for (i
= 0; i
< 4; i
++)
741 mibsize
= sizeof(mib
) / sizeof(mib
[0]);
743 if (sysctl(mib
, mibsize
, NULL
, &needed
, NULL
, 0) == -1) {
744 perror("sysctl net.inet6.ip6.mcast.filters");
747 if ((buf
= malloc(needed
)) == NULL
) {
751 if (sysctl(mib
, mibsize
, buf
, &needed
, NULL
, 0) == -1) {
752 if (errno
!= ENOMEM
|| ++ntry
>= MAX_SYSCTL_TRY
) {
759 } while (buf
== NULL
);
762 if (len
< sizeof(uint32_t)) {
769 len
-= sizeof(uint32_t);
771 modestr
= inm_mode(fmode
);
773 printf(" mode %s", modestr
);
775 printf(" mode (%u)", fmode
);
780 cnt
= len
/ sizeof(struct in6_addr
);
781 pina
= (struct in6_addr
*)p
;
783 for (i
= 0; i
< cnt
; i
++) {
786 inet_ntop(AF_INET6
, (const char *)pina
++, addrbuf
,
788 fprintf(stdout
, "%s%s", (i
== 0 ? "" : ","), addrbuf
);
789 len
-= sizeof(struct in6_addr
);
792 fprintf(stderr
, "warning: %u trailing bytes from %s\n",
793 (unsigned int)len
, "net.inet6.ip6.mcast.filters");
798 #undef MAX_SYSCTL_TRY
802 inet6_n2a(struct in6_addr
*p
)
804 static char buf
[NI_MAXHOST
];
805 struct sockaddr_in6 sin6
;
807 const int niflags
= NI_NUMERICHOST
;
809 memset(&sin6
, 0, sizeof(sin6
));
810 sin6
.sin6_family
= AF_INET6
;
811 sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
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]);
817 sin6
.sin6_scope_id
= scopeid
;
818 sin6
.sin6_addr
.s6_addr
[2] = 0;
819 sin6
.sin6_addr
.s6_addr
[3] = 0;
822 if (getnameinfo((struct sockaddr
*)&sin6
, sin6
.sin6_len
,
823 buf
, sizeof(buf
), NULL
, 0, niflags
) == 0) {
826 return ("(invalid)");
832 * Print a value a la the %b format of the kernel's printf
835 printb(const char *s
, unsigned int v
, const char *bits
)
840 if (bits
&& *bits
== 8)
841 printf("%s=%o", s
, v
);
843 printf("%s=%x", s
, v
);
847 while ((i
= *bits
++) != '\0') {
848 if (v
& (1 << (i
-1))) {
852 for (; (c
= *bits
) > 32; bits
++)
855 for (; *bits
> 32; bits
++)
863 * convert hardware address to hex string for logging errors.
866 sdl_addr_to_hex(const struct sockaddr_dl
*sdl
, char *orig_buf
, int buflen
)
868 char *buf
= orig_buf
;
870 const u_char
*lladdr
;
871 int maxbytes
= buflen
/ 3;
873 lladdr
= (u_char
*)(size_t)sdl
->sdl_data
+ sdl
->sdl_nlen
;
875 if (maxbytes
> sdl
->sdl_alen
) {
876 maxbytes
= sdl
->sdl_alen
;
879 for (i
= 0; i
< maxbytes
; i
++) {
880 snprintf(buf
, 3, "%02x", lladdr
[i
]);
882 *buf
= (i
== maxbytes
- 1) ? '\0' : ':';