1 /* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */
4 * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org>
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/queue.h>
42 #include <net/if_var.h>
43 #include <net/if_types.h>
44 #include <net/if_dl.h>
45 #include <net/route.h>
47 #include <netinet/in.h>
48 #include <netinet/in_var.h>
49 #include <netinet/in_systm.h>
50 #include <netinet/ip.h>
51 #include <netinet/igmp.h>
53 # include <netinet/in_msf.h>
56 # include <netinet/if_ether.h>
59 # include <sys/sysctl.h>
60 # include <netinet/igmp_var.h>
65 # include <netinet6/in6_msf.h>
67 #include <netinet/icmp6.h>
69 # include <netinet6/mld6_var.h>
73 #include <arpa/inet.h>
94 /* XXX: This file currently assumes INET and KVM support in the base system. */
100 struct sockaddr_storage ss
;
102 struct sockaddr_dl sdl
;
104 struct sockaddr_in sin
;
107 struct sockaddr_in6 sin6
;
110 typedef union sockunion sockunion_t
;
112 uint32_t ifindex
= 0;
115 #define sa_equal(a1, a2) \
116 (bcmp((a1), (a2), ((a1))->sa_len) == 0)
118 #define sa_dl_equal(a1, a2) \
119 ((((struct sockaddr_dl *)(a1))->sdl_len == \
120 ((struct sockaddr_dl *)(a2))->sdl_len) && \
121 (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \
122 LLADDR((struct sockaddr_dl *)(a2)), \
123 ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
126 * Most of the code in this utility is to support the use of KVM for
127 * post-mortem debugging of the multicast code.
132 static void if_addrlist(struct ifaddr
*);
133 static struct in_multi
*
134 in_multientry(struct in_multi
*);
136 static void in_addr_slistentry(struct in_addr_slist
*, char *);
141 static void if6_addrlist(struct ifaddr
*);
142 static struct in6_multi
*
143 in6_multientry(struct in6_multi
*);
145 static void in6_addr_slistentry(struct in6_addr_slist
*, char *);
147 static const char * inet6_n2a(struct in6_addr
*);
150 static void kread(u_long
, void *, int);
151 static int ifmcstat_kvm(const char *kernel
, const char *core
);
153 #define KREAD(addr, buf, type) \
154 kread((u_long)addr, (void *)buf, sizeof(type))
157 struct nlist nl
[] = {
158 { "_ifnet", 0, 0, 0, 0, },
163 #endif /* WITH_KVM */
165 static int ifmcstat_getifmaddrs(void);
166 int main(int, char **);
169 main(int argc
, char **argv
)
173 const char *kernel
= NULL
;
174 const char *core
= NULL
;
176 /* "ifmcstat [kernel]" format is supported for backward compatiblity */
181 while ((c
= getopt(argc
, argv
, "i:f:M:N:")) != -1) {
184 if ((ifindex
= if_nametoindex(optarg
)) == 0) {
185 fprintf(stderr
, "%s: unknown interface\n",
193 if (strcmp(optarg
, "inet") == 0) {
199 if (strcmp(optarg
, "inet6") == 0) {
204 fprintf(stderr
, "%s: unknown address family\n", optarg
);
211 core
= strdup(optarg
);
215 kernel
= strdup(optarg
);
221 "usage: ifmcstat [-i interface] [-f address family]"
223 " [-M core] [-N system]"
233 error
= ifmcstat_kvm(kernel
, core
);
235 * If KVM failed, and user did not explicitly specify a core file,
236 * try the sysctl backend.
238 if (error
!= 0 && (core
== NULL
&& kernel
== NULL
))
240 error
= ifmcstat_getifmaddrs();
251 ifmcstat_kvm(const char *kernel
, const char *core
)
253 char buf
[_POSIX2_LINE_MAX
], ifname
[IFNAMSIZ
];
254 struct ifnet
*ifp
, *nifp
, ifnet
;
256 if ((kvmd
= kvm_openfiles(kernel
, core
, NULL
, O_RDONLY
, buf
)) ==
258 perror("kvm_openfiles");
261 if (kvm_nlist(kvmd
, nl
) < 0) {
265 if (nl
[N_IFNET
].n_value
== 0) {
266 printf("symbol %s not found\n", nl
[N_IFNET
].n_name
);
269 KREAD(nl
[N_IFNET
].n_value
, &ifp
, struct ifnet
*);
271 KREAD(ifp
, &ifnet
, struct ifnet
);
272 nifp
= ifnet
.if_link
.tqe_next
;
273 if (ifindex
&& ifindex
!= ifnet
.if_index
)
276 printf("%s:\n", if_indextoname(ifnet
.if_index
, ifname
));
278 if_addrlist(TAILQ_FIRST(&ifnet
.if_addrhead
));
281 if6_addrlist(TAILQ_FIRST(&ifnet
.if_addrhead
));
291 kread(u_long addr
, void *buf
, int len
)
294 if (kvm_read(kvmd
, addr
, buf
, len
) != len
) {
303 inet6_n2a(struct in6_addr
*p
)
305 static char buf
[NI_MAXHOST
];
306 struct sockaddr_in6 sin6
;
308 const int niflags
= NI_NUMERICHOST
;
310 memset(&sin6
, 0, sizeof(sin6
));
311 sin6
.sin6_family
= AF_INET6
;
312 sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
314 if (IN6_IS_ADDR_LINKLOCAL(p
) || IN6_IS_ADDR_MC_LINKLOCAL(p
) ||
315 IN6_IS_ADDR_MC_NODELOCAL(p
)) {
316 scopeid
= ntohs(*(u_int16_t
*)&sin6
.sin6_addr
.s6_addr
[2]);
318 sin6
.sin6_scope_id
= scopeid
;
319 sin6
.sin6_addr
.s6_addr
[2] = 0;
320 sin6
.sin6_addr
.s6_addr
[3] = 0;
323 if (getnameinfo((struct sockaddr
*)&sin6
, sin6
.sin6_len
,
324 buf
, sizeof(buf
), NULL
, 0, niflags
) == 0)
331 if6_addrlist(struct ifaddr
*ifap
)
335 struct in6_ifaddr if6a
;
336 struct ifaddr
*ifap0
;
338 if (af
&& af
!= AF_INET6
)
342 KREAD(ifap
, &ifa
, struct ifaddr
);
343 if (ifa
.ifa_addr
== NULL
)
345 KREAD(ifa
.ifa_addr
, &sa
, struct sockaddr
);
346 if (sa
.sa_family
!= PF_INET6
)
348 KREAD(ifap
, &if6a
, struct in6_ifaddr
);
349 printf("\tinet6 %s\n", inet6_n2a(&if6a
.ia_addr
.sin6_addr
));
351 ifap
= ifa
.ifa_link
.tqe_next
;
355 struct ifmultiaddr ifm
, *ifmp
= 0;
356 struct sockaddr_dl sdl
;
358 KREAD(ifap0
, &ifa
, struct ifaddr
);
359 KREAD(ifa
.ifa_ifp
, &ifnet
, struct ifnet
);
360 if (TAILQ_FIRST(&ifnet
.if_multiaddrs
))
361 ifmp
= TAILQ_FIRST(&ifnet
.if_multiaddrs
);
363 KREAD(ifmp
, &ifm
, struct ifmultiaddr
);
364 if (ifm
.ifma_addr
== NULL
)
366 KREAD(ifm
.ifma_addr
, &sa
, struct sockaddr
);
367 if (sa
.sa_family
!= AF_INET6
)
369 (void)in6_multientry((struct in6_multi
*)
371 if (ifm
.ifma_lladdr
== 0)
373 KREAD(ifm
.ifma_lladdr
, &sdl
, struct sockaddr_dl
);
374 printf("\t\t\tmcast-macaddr %s refcnt %d\n",
375 ether_ntoa((struct ether_addr
*)LLADDR(&sdl
)),
378 ifmp
= TAILQ_NEXT(&ifm
, ifma_link
);
383 static struct in6_multi
*
384 in6_multientry(struct in6_multi
*mc
)
386 struct in6_multi multi
;
388 struct in6_multi_source src
;
389 struct router6_info rt6i
;
392 KREAD(mc
, &multi
, struct in6_multi
);
393 printf("\t\tgroup %s", inet6_n2a(&multi
.in6m_addr
));
394 printf(" refcnt %u\n", multi
.in6m_refcount
);
397 if (multi
.in6m_rti
!= NULL
) {
398 KREAD(multi
.in6m_rti
, &rt6i
, struct router_info
);
400 switch (rt6i
.rt6i_type
) {
408 printf("mldv?(%d)", rt6i
.rt6i_type
);
412 if (multi
.in6m_source
== NULL
) {
414 return(multi
.in6m_entry
.le_next
);
417 KREAD(multi
.in6m_source
, &src
, struct in6_multi_source
);
418 printf(" mode=%s grpjoin=%d\n",
419 src
.i6ms_mode
== MCAST_INCLUDE
? "include" :
420 src
.i6ms_mode
== MCAST_EXCLUDE
? "exclude" :
423 in6_addr_slistentry(src
.i6ms_cur
, "current");
424 in6_addr_slistentry(src
.i6ms_rec
, "recorded");
425 in6_addr_slistentry(src
.i6ms_in
, "included");
426 in6_addr_slistentry(src
.i6ms_ex
, "excluded");
427 in6_addr_slistentry(src
.i6ms_alw
, "allowed");
428 in6_addr_slistentry(src
.i6ms_blk
, "blocked");
429 in6_addr_slistentry(src
.i6ms_toin
, "to-include");
430 in6_addr_slistentry(src
.i6ms_ex
, "to-exclude");
433 return(multi
.in6m_entry
.le_next
);
438 in6_addr_slistentry(struct in6_addr_slist
*ias
, char *heading
)
440 struct in6_addr_slist slist
;
441 struct i6as_head head
;
442 struct in6_addr_source src
;
445 printf("\t\t\t\t%s (none)\n", heading
);
448 memset(&slist
, 0, sizeof(slist
));
449 KREAD(ias
, &slist
, struct in6_addr_source
);
450 printf("\t\t\t\t%s (entry num=%d)\n", heading
, slist
.numsrc
);
451 if (slist
.numsrc
== 0) {
454 KREAD(slist
.head
, &head
, struct i6as_head
);
456 KREAD(head
.lh_first
, &src
, struct in6_addr_source
);
458 printf("\t\t\t\t\tsource %s (ref=%d)\n",
459 inet6_n2a(&src
.i6as_addr
.sin6_addr
),
461 if (src
.i6as_list
.le_next
== NULL
)
463 KREAD(src
.i6as_list
.le_next
, &src
, struct in6_addr_source
);
467 #endif /* HAVE_MLDV2 */
474 if_addrlist(struct ifaddr
*ifap
)
479 struct ifaddr
*ifap0
;
481 if (af
&& af
!= AF_INET
)
485 KREAD(ifap
, &ifa
, struct ifaddr
);
486 if (ifa
.ifa_addr
== NULL
)
488 KREAD(ifa
.ifa_addr
, &sa
, struct sockaddr
);
489 if (sa
.sa_family
!= PF_INET
)
491 KREAD(ifap
, &ia
, struct in_ifaddr
);
492 printf("\tinet %s\n", inet_ntoa(ia
.ia_addr
.sin_addr
));
494 ifap
= ifa
.ifa_link
.tqe_next
;
498 struct ifmultiaddr ifm
, *ifmp
= 0;
499 struct sockaddr_dl sdl
;
501 KREAD(ifap0
, &ifa
, struct ifaddr
);
502 KREAD(ifa
.ifa_ifp
, &ifnet
, struct ifnet
);
503 if (TAILQ_FIRST(&ifnet
.if_multiaddrs
))
504 ifmp
= TAILQ_FIRST(&ifnet
.if_multiaddrs
);
506 KREAD(ifmp
, &ifm
, struct ifmultiaddr
);
507 if (ifm
.ifma_addr
== NULL
)
509 KREAD(ifm
.ifma_addr
, &sa
, struct sockaddr
);
510 if (sa
.sa_family
!= AF_INET
)
512 (void)in_multientry((struct in_multi
*)
514 if (ifm
.ifma_lladdr
== 0)
516 KREAD(ifm
.ifma_lladdr
, &sdl
, struct sockaddr_dl
);
517 printf("\t\t\tmcast-macaddr %s refcnt %d\n",
518 ether_ntoa((struct ether_addr
*)LLADDR(&sdl
)),
521 ifmp
= TAILQ_NEXT(&ifm
, ifma_link
);
526 static struct in_multi
*
527 in_multientry(struct in_multi
*mc
)
529 struct in_multi multi
;
530 struct router_info rti
;
532 struct in_multi_source src
;
535 KREAD(mc
, &multi
, struct in_multi
);
536 printf("\t\tgroup %s\n", inet_ntoa(multi
.inm_addr
));
538 if (multi
.inm_rti
!= NULL
) {
539 KREAD(multi
.inm_rti
, &rti
, struct router_info
);
541 switch (rti
.rti_type
) {
554 printf("igmpv?(%d)", rti
.rti_type
);
559 if (multi
.inm_source
== NULL
) {
561 return (multi
.inm_list
.le_next
);
564 KREAD(multi
.inm_source
, &src
, struct in_multi_source
);
565 printf(" mode=%s grpjoin=%d\n",
566 src
.ims_mode
== MCAST_INCLUDE
? "include" :
567 src
.ims_mode
== MCAST_EXCLUDE
? "exclude" :
570 in_addr_slistentry(src
.ims_cur
, "current");
571 in_addr_slistentry(src
.ims_rec
, "recorded");
572 in_addr_slistentry(src
.ims_in
, "included");
573 in_addr_slistentry(src
.ims_ex
, "excluded");
574 in_addr_slistentry(src
.ims_alw
, "allowed");
575 in_addr_slistentry(src
.ims_blk
, "blocked");
576 in_addr_slistentry(src
.ims_toin
, "to-include");
577 in_addr_slistentry(src
.ims_ex
, "to-exclude");
588 in_addr_slistentry(struct in_addr_slist
*ias
, char *heading
)
590 struct in_addr_slist slist
;
591 struct ias_head head
;
592 struct in_addr_source src
;
595 printf("\t\t\t\t%s (none)\n", heading
);
598 memset(&slist
, 0, sizeof(slist
));
599 KREAD(ias
, &slist
, struct in_addr_source
);
600 printf("\t\t\t\t%s (entry num=%d)\n", heading
, slist
.numsrc
);
601 if (slist
.numsrc
== 0) {
604 KREAD(slist
.head
, &head
, struct ias_head
);
606 KREAD(head
.lh_first
, &src
, struct in_addr_source
);
608 printf("\t\t\t\t\tsource %s (ref=%d)\n",
609 inet_ntoa(src
.ias_addr
.sin_addr
), src
.ias_refcount
);
610 if (src
.ias_list
.le_next
== NULL
)
612 KREAD(src
.ias_list
.le_next
, &src
, struct in_addr_source
);
616 #endif /* HAVE_IGMPV3 */
620 #endif /* WITH_KVM */
623 ifmcstat_getifmaddrs(void)
625 char thisifname
[IFNAMSIZ
];
626 char addrbuf
[INET6_ADDRSTRLEN
];
627 struct ifaddrs
*ifap
, *ifa
;
628 struct ifmaddrs
*ifmap
, *ifma
;
629 sockunion_t lastifasa
;
630 sockunion_t
*psa
, *pgsa
, *pllsa
, *pifasa
;
633 uint32_t lastifindex
, thisifindex
;
641 lastifasa
.ss
.ss_family
= AF_UNSPEC
;
643 if (getifaddrs(&ifap
) != 0) {
648 if (getifmaddrs(&ifmap
) != 0) {
654 for (ifma
= ifmap
; ifma
; ifma
= ifma
->ifma_next
) {
656 if (ifma
->ifma_name
== NULL
|| ifma
->ifma_addr
== NULL
)
659 psa
= (sockunion_t
*)ifma
->ifma_name
;
660 if (psa
->sa
.sa_family
!= AF_LINK
) {
662 "WARNING: Kernel returned invalid data.\n");
667 /* Filter on interface name. */
668 thisifindex
= psa
->sdl
.sdl_index
;
669 if (ifindex
!= 0 && thisifindex
!= ifindex
)
672 /* Filter on address family. */
673 pgsa
= (sockunion_t
*)ifma
->ifma_addr
;
674 if (af
!= 0 && pgsa
->sa
.sa_family
!= af
)
677 strlcpy(thisifname
, link_ntoa(&psa
->sdl
), IFNAMSIZ
);
678 pcolon
= strchr(thisifname
, ':');
682 /* Only print the banner for the first ifmaddrs entry. */
683 if (lastifindex
== 0 || lastifindex
!= thisifindex
) {
684 lastifindex
= thisifindex
;
685 fprintf(stdout
, "%s:\n", thisifname
);
689 * Currently, multicast joins only take place on the
690 * primary IPv4 address, and only on the link-local IPv6
691 * address, as per IGMPv2/3 and MLDv1/2 semantics.
692 * Therefore, we only look up the primary address on
696 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
697 if ((strcmp(ifa
->ifa_name
, thisifname
) != 0) ||
698 (ifa
->ifa_addr
== NULL
) ||
699 (ifa
->ifa_addr
->sa_family
!= pgsa
->sa
.sa_family
))
703 * For AF_INET6 only the link-local address should
705 * XXX: ifmcstat actually prints all of the inet6
706 * addresses, but never mind...
708 pifasa
= (sockunion_t
*)ifa
->ifa_addr
;
709 if (pifasa
->sa
.sa_family
== AF_INET6
&&
710 !IN6_IS_ADDR_LINKLOCAL(&pifasa
->sin6
.sin6_addr
)) {
718 continue; /* primary address not found */
720 /* Parse and print primary address, if not already printed. */
721 if (lastifasa
.ss
.ss_family
== AF_UNSPEC
||
722 ((lastifasa
.ss
.ss_family
== AF_LINK
&&
723 !sa_dl_equal(&lastifasa
.sa
, &pifasa
->sa
)) ||
724 !sa_equal(&lastifasa
.sa
, &pifasa
->sa
))) {
726 switch (pifasa
->sa
.sa_family
) {
741 switch (pifasa
->sa
.sa_family
) {
745 error
= getnameinfo(&pifasa
->sa
,
747 addrbuf
, sizeof(addrbuf
), NULL
, 0,
750 perror("getnameinfo");
757 fprintf(stdout
, "\t%s %s\n", pafname
, addrbuf
);
761 /* Print this group address. */
762 error
= getnameinfo(&pgsa
->sa
, pgsa
->sa
.sa_len
, addrbuf
,
763 sizeof(addrbuf
), NULL
, 0, NI_NUMERICHOST
);
765 perror("getnameinfo");
766 fprintf(stdout
, "\t\tgroup %s\n", addrbuf
);
768 /* Link-layer mapping, if present. */
769 pllsa
= (sockunion_t
*)ifma
->ifma_lladdr
;
771 error
= getnameinfo(&pifasa
->sa
, pifasa
->sa
.sa_len
,
772 addrbuf
, sizeof(addrbuf
), NULL
, 0, NI_NUMERICHOST
);
773 fprintf(stdout
, "\t\t\tmcast-macaddr %s\n", addrbuf
);