diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8 index fc02b44df369..93221e15d854 100644 --- a/usr.sbin/ifmcstat/ifmcstat.8 +++ b/usr.sbin/ifmcstat/ifmcstat.8 @@ -1,4 +1,4 @@ -.\" $KAME: ifmcstat.8,v 1.3 2000/10/15 11:43:57 itojun Exp $ +.\" $KAME: ifmcstat.8,v 1.6 2002/10/31 04:23:43 suz Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. .\" All rights reserved. @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 21, 1998 +.Dd March 19, 2007 .Dt IFMCSTAT 8 .Os .Sh NAME @@ -37,22 +37,36 @@ .Nd dump multicast group management statistics per interface .Sh SYNOPSIS .Nm +.Op Fl i Ar interface +.Op Fl f Ar address-family +.Op Fl k Ar kernel +.Nm +.Op Ar kernel +.\" .Sh DESCRIPTION The .Nm -utility dumps multicast group information from the kernel. -It is similar but not identical to the output from -.Nm netstat Fl ina . +command dumps multicast group information from the kernel. .Pp -There are no command line options. +The following options are supported: +.Bl -tag -width Fl +.It Fl i Ar interface +specifies the interface to be displayed. .Pp -Only root can use -.Nm . -.Sh SEE ALSO -.Xr netstat 1 -.Rs -.%A G. Malkin -.%A R. Minnear -.%R IPng for IPv6 -.%N RFC2080 -.Re +.It Fl f Ar address-family +specifies the address-family to be displayed; currently only +.Ar inet +and +.Ar inet6 +are supported. +.It Fl k Ar kernel +extracts the name list from the specified kernel instead of the +default, which is the kernel image the system has booted from. +.It Nm Ar kernel +This is the same as specifying +.Nm +.Fl k Ar kernel . +This usage is deprecated; it is supported only for backwards compatibility. +.Sh BUGS +.Nm +needs superuser privilege. diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c index feb289d90ae5..33036118c2bd 100644 --- a/usr.sbin/ifmcstat/ifmcstat.c +++ b/usr.sbin/ifmcstat/ifmcstat.c @@ -1,3 +1,5 @@ +/* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +27,13 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#ifdef __FreeBSD__ +#include +__FBSDID("$FreeBSD$"); +#endif + #include #include #include @@ -36,40 +41,94 @@ #include #include #include +#include #include +#include #include #include -#include #include -#include +#ifdef __FreeBSD__ +# include +#endif #include #include +#include #include -#define _KERNEL -#include -#undef _KERNEL +#include +#include +#include +#ifdef HAVE_IGMPV3 +#include +#endif +#ifdef HAVE_MLDV2 +#include +#include +#endif +#ifndef __NetBSD__ +# ifdef __FreeBSD__ +# define KERNEL +# endif +# include +# ifdef __FreeBSD__ +# undef KERNEL +# endif +#else +# include +#endif #include +#include +#define _KERNEL +/* defined _KERNEL only to define IGMP_v?_ROUTER and MLD_V?_ROUTER */ +#ifdef __FreeBSD__ +#include +#endif +#include +#include +#ifdef __FreeBSD__ +#define IGMP_v1_ROUTER IGMP_V1_ROUTER +#define IGMP_v2_ROUTER IGMP_V2_ROUTER +#define IGMP_v3_ROUTER IGMP_V3_ROUTER +#endif +#undef _KERNEL #include #include kvm_t *kvmd; +int ifindex = 0; +int af = AF_UNSPEC; struct nlist nl[] = { #define N_IFNET 0 { "_ifnet" }, +#ifndef __FreeBSD__ +#define N_IN6_MK 1 + { "_in6_mk" }, +#endif { "" }, }; const char *inet6_n2a __P((struct in6_addr *)); -int main __P((void)); +int main __P((int, char **)); char *ifname __P((struct ifnet *)); void kread __P((u_long, void *, int)); +#ifndef __FreeBSD__ +void acmc __P((struct ether_multi *)); +#endif void if6_addrlist __P((struct ifaddr *)); void in6_multilist __P((struct in6_multi *)); struct in6_multi * in6_multientry __P((struct in6_multi *)); +void if_addrlist(struct ifaddr *); +void in_multilist(struct in_multi *); +struct in_multi * in_multientry(struct in_multi *); +#ifdef HAVE_IGMPV3 +void in_addr_slistentry(struct in_addr_slist *ias, char *heading); +#endif +#ifdef HAVE_MLDV2 +void in6_addr_slistentry(struct in6_addr_slist *ias, char *heading); +#endif #define KREAD(addr, buf, type) \ kread((u_long)addr, (void *)buf, sizeof(type)) @@ -94,7 +153,8 @@ const char *inet6_n2a(p) sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_addr = *p; - if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p)) { + if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) || + IN6_IS_ADDR_MC_NODELOCAL(p)) { scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); if (scopeid) { sin6.sin6_scope_id = scopeid; @@ -109,12 +169,58 @@ const char *inet6_n2a(p) return "(invalid)"; } -int main() +int main(argc, argv) + int argc; + char **argv; { char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ]; + int c; struct ifnet *ifp, *nifp, ifnet; +#ifndef __FreeBSD__ +#ifndef __NetBSD__ + struct arpcom arpcom; +#else + struct ethercom ec; + struct sockaddr_dl sdl; +#endif +#endif + const char *kernel = NULL; - if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) { + /* "ifmcstat [kernel]" format is supported for backward compatiblity */ + if (argc == 2) + kernel = argv[1]; + + while ((c = getopt(argc, argv, "i:f:k:")) != -1) { + switch (c) { + case 'i': + if ((ifindex = if_nametoindex(optarg)) == 0) { + fprintf(stderr, "%s: unknown interface\n", optarg); + exit(1); + } + break; + case 'f': + if (strcmp(optarg, "inet") == 0) { + af = AF_INET; + break; + } + if (strcmp(optarg, "inet6") == 0) { + af = AF_INET6; + break; + } + fprintf(stderr, "%s: unknown address family\n", optarg); + exit(1); + /*NOTREACHED*/ + case 'k': + kernel = strdup(optarg); + break; + default: + fprintf(stderr, "usage: ifmcstat [-i interface] [-f address family] [-k kernel]\n"); + exit(1); + /*NOTREACHED*/ + } + } + + if ((kvmd = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, buf)) == NULL) { perror("kvm_openfiles"); exit(1); } @@ -129,13 +235,48 @@ int main() KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); while (ifp) { KREAD(ifp, &ifnet, struct ifnet); +#if defined(__NetBSD__) || defined(__OpenBSD__) + nifp = ifnet.if_list.tqe_next; +#else + nifp = ifnet.if_link.tqe_next; +#endif + if (ifindex && ifindex != ifnet.if_index) + goto next; + printf("%s:\n", if_indextoname(ifnet.if_index, ifname)); - +#if defined(__NetBSD__) || defined(__OpenBSD__) + if_addrlist(ifnet.if_addrlist.tqh_first); + if6_addrlist(ifnet.if_addrlist.tqh_first); +#else + if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); - nifp = TAILQ_NEXT(&ifnet, if_link); +#endif +#ifdef __NetBSD__ + KREAD(ifnet.if_sadl, &sdl, struct sockaddr_dl); + if (sdl.sdl_type == IFT_ETHER) { + printf("\tenaddr %s", + ether_ntoa((struct ether_addr *)LLADDR(&sdl))); + KREAD(ifp, &ec, struct ethercom); + printf(" multicnt %d", ec.ec_multicnt); + acmc(ec.ec_multiaddrs.lh_first); + printf("\n"); + } +#elif defined(__FreeBSD__) /* not supported */ +#else /* __OpenBSD__ */ + if (ifnet.if_type == IFT_ETHER) { + KREAD(ifp, &arpcom, struct arpcom); + printf("\tenaddr %s", + ether_ntoa((struct ether_addr *)arpcom.ac_enaddr)); + KREAD(ifp, &arpcom, struct arpcom); + printf(" multicnt %d", arpcom.ac_multicnt); + acmc(arpcom.ac_multiaddrs.lh_first); + printf("\n"); + } +#endif +next: ifp = nifp; } @@ -165,6 +306,28 @@ void kread(addr, buf, len) } } +#ifndef __FreeBSD__ +void acmc(am) + struct ether_multi *am; +{ + struct ether_multi em; + + while (am) { + KREAD(am, &em, struct ether_multi); + + printf("\n\t\t"); + printf("%s -- ", ether_ntoa((struct ether_addr *)em.enm_addrlo)); + printf("%s ", ether_ntoa((struct ether_addr *)&em.enm_addrhi)); + printf("%d", em.enm_refcount); +#if !defined(__NetBSD__) && !defined(__OpenBSD__) + am = em.enm_next; +#else + am = em.enm_list.le_next; +#endif + } +} +#endif + void if6_addrlist(ifap) struct ifaddr *ifap; @@ -172,8 +335,13 @@ if6_addrlist(ifap) struct ifaddr ifa; struct sockaddr sa; struct in6_ifaddr if6a; +#ifndef __FreeBSD__ + struct in6_multi *mc = 0; +#endif struct ifaddr *ifap0; + if (af && af != AF_INET6) + return; ifap0 = ifap; while (ifap) { KREAD(ifap, &ifa, struct ifaddr); @@ -184,9 +352,17 @@ if6_addrlist(ifap) goto nextifap; KREAD(ifap, &if6a, struct in6_ifaddr); printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr)); +#ifndef __FreeBSD__ + mc = mc ? mc : if6a.ia6_multiaddrs.lh_first; +#endif nextifap: - ifap = TAILQ_NEXT(&ifa, ifa_link); +#if defined(__NetBSD__) || defined(__OpenBSD__) + ifap = ifa.ifa_list.tqe_next; +#else + ifap = ifa.ifa_link.tqe_next; +#endif } +#ifdef __FreeBSD__ if (ifap0) { struct ifnet ifnet; struct ifmultiaddr ifm, *ifmp = 0; @@ -215,6 +391,10 @@ if6_addrlist(ifap) ifmp = TAILQ_NEXT(&ifm, ifma_link); } } +#else + if (mc) + in6_multilist(mc); +#endif #ifdef N_IN6_MK if (nl[N_IN6_MK].n_value != 0) { LIST_HEAD(in6_mktype, multi6_kludge) in6_mk; @@ -230,12 +410,12 @@ if6_addrlist(ifap) exit(1); } - LIST_FOREACH(mkp, &in6_mk, mk_entry) { + for (mkp = in6_mk.lh_first; mkp; mkp = mk.mk_entry.le_next) { KREAD(mkp, &mk, struct multi6_kludge); if (strcmp(nam, ifname(mk.mk_ifp)) == 0 && - LIST_FIRST(&mk.mk_head)) { + mk.mk_head.lh_first) { printf("\t(on kludge entry for %s)\n", nam); - in6_multilist(LIST_FIRST(&mk.mk_head)); + in6_multilist(mk.mk_head.lh_first); } } @@ -249,13 +429,88 @@ in6_multientry(mc) struct in6_multi *mc; { struct in6_multi multi; +#ifdef HAVE_MLDV2 + struct in6_multi_source src; + struct router6_info rt6i; +#endif KREAD(mc, &multi, struct in6_multi); printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr)); printf(" refcnt %u\n", multi.in6m_refcount); - return(LIST_NEXT(&multi, in6m_entry)); + +#ifdef HAVE_MLDV2 + if (multi.in6m_rti != NULL) { + KREAD(multi.in6m_rti, &rt6i, struct router_info); + printf("\t\t\t"); + switch (rt6i.rt6i_type) { + case MLD_V1_ROUTER: + printf("mldv1"); + break; + case MLD_V2_ROUTER: + printf("mldv2"); + break; + default: + printf("mldv?(%d)", rt6i.rt6i_type); + break; + } + + if (multi.in6m_source == NULL) { + printf("\n"); + return(multi.in6m_entry.le_next); + } + + KREAD(multi.in6m_source, &src, struct in6_multi_source); + printf(" mode=%s grpjoin=%d\n", + src.i6ms_mode == MCAST_INCLUDE ? "include" : + src.i6ms_mode == MCAST_EXCLUDE ? "exclude" : + "???", + src.i6ms_grpjoin); + in6_addr_slistentry(src.i6ms_cur, "current"); + in6_addr_slistentry(src.i6ms_rec, "recorded"); + in6_addr_slistentry(src.i6ms_in, "included"); + in6_addr_slistentry(src.i6ms_ex, "excluded"); + in6_addr_slistentry(src.i6ms_alw, "allowed"); + in6_addr_slistentry(src.i6ms_blk, "blocked"); + in6_addr_slistentry(src.i6ms_toin, "to-include"); + in6_addr_slistentry(src.i6ms_ex, "to-exclude"); + } +#endif + return(multi.in6m_entry.le_next); } +#ifdef HAVE_MLDV2 +void +in6_addr_slistentry(struct in6_addr_slist *ias, char *heading) +{ + struct in6_addr_slist slist; + struct i6as_head head; + struct in6_addr_source src; + + if (ias == NULL) { + printf("\t\t\t\t%s (none)\n", heading); + return; + } + memset(&slist, 0, sizeof(slist)); + KREAD(ias, &slist, struct in6_addr_source); + printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc); + if (slist.numsrc == 0) { + return; + } + KREAD(slist.head, &head, struct i6as_head); + + KREAD(head.lh_first, &src, struct in6_addr_source); + while (1) { + printf("\t\t\t\t\tsource %s (ref=%d)\n", + inet6_n2a(&src.i6as_addr.sin6_addr), + src.i6as_refcount); + if (src.i6as_list.le_next == NULL) + break; + KREAD(src.i6as_list.le_next, &src, struct in6_addr_source); + } + return; +} +#endif + void in6_multilist(mc) struct in6_multi *mc; @@ -263,3 +518,177 @@ in6_multilist(mc) while (mc) mc = in6_multientry(mc); } + +void +if_addrlist(ifap) + struct ifaddr *ifap; +{ + struct ifaddr ifa; + struct sockaddr sa; + struct in_ifaddr ia; +#ifndef __FreeBSD__ + struct in_multi *mc = 0; +#endif + struct ifaddr *ifap0; + + if (af && af != AF_INET) + return; + ifap0 = ifap; + while (ifap) { + KREAD(ifap, &ifa, struct ifaddr); + if (ifa.ifa_addr == NULL) + goto nextifap; + KREAD(ifa.ifa_addr, &sa, struct sockaddr); + if (sa.sa_family != PF_INET) + goto nextifap; + KREAD(ifap, &ia, struct in_ifaddr); + printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr)); +#ifndef __FreeBSD__ + mc = mc ? mc : ia.ia_multiaddrs.lh_first; +#endif + nextifap: +#if defined(__NetBSD__) || defined(__OpenBSD__) + ifap = ifa.ifa_list.tqe_next; +#else + ifap = ifa.ifa_link.tqe_next; +#endif + } +#ifdef __FreeBSD__ + if (ifap0) { + struct ifnet ifnet; + struct ifmultiaddr ifm, *ifmp = 0; + struct sockaddr_dl sdl; + + KREAD(ifap0, &ifa, struct ifaddr); + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + if (TAILQ_FIRST(&ifnet.if_multiaddrs)) + ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); + while (ifmp) { + KREAD(ifmp, &ifm, struct ifmultiaddr); + if (ifm.ifma_addr == NULL) + goto nextmulti; + KREAD(ifm.ifma_addr, &sa, struct sockaddr); + if (sa.sa_family != AF_INET) + goto nextmulti; + (void)in_multientry((struct in_multi *) + ifm.ifma_protospec); + if (ifm.ifma_lladdr == 0) + goto nextmulti; + KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); + printf("\t\t\tmcast-macaddr %s multicnt %d\n", + ether_ntoa((struct ether_addr *)LLADDR(&sdl)), + ifm.ifma_refcount); + nextmulti: + ifmp = TAILQ_NEXT(&ifm, ifma_link); + } + } +#else /* !FreeBSD */ + if (mc) + in_multilist(mc); +#endif +} + +void +in_multilist(mc) + struct in_multi *mc; +{ + while (mc) + mc = in_multientry(mc); +} + +struct in_multi * +in_multientry(mc) + struct in_multi *mc; +{ + struct in_multi multi; + struct router_info rti; +#ifdef HAVE_IGMPV3 + struct in_multi_source src; +#endif + + KREAD(mc, &multi, struct in_multi); + printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr)); + + if (multi.inm_rti != NULL) { + KREAD(multi.inm_rti, &rti, struct router_info); + printf("\t\t\t"); + switch (rti.rti_type) { + case IGMP_v1_ROUTER: + printf("igmpv1"); + break; + case IGMP_v2_ROUTER: + printf("igmpv2"); + break; +#ifdef HAVE_IGMPV3 + case IGMP_v3_ROUTER: + printf("igmpv3"); + break; +#endif + default: + printf("igmpv?(%d)", rti.rti_type); + break; + } + +#ifdef HAVE_IGMPV3 + if (multi.inm_source == NULL) { + printf("\n"); + return (multi.inm_list.le_next); + } + + KREAD(multi.inm_source, &src, struct in_multi_source); + printf(" mode=%s grpjoin=%d\n", + src.ims_mode == MCAST_INCLUDE ? "include" : + src.ims_mode == MCAST_EXCLUDE ? "exclude" : + "???", + src.ims_grpjoin); + in_addr_slistentry(src.ims_cur, "current"); + in_addr_slistentry(src.ims_rec, "recorded"); + in_addr_slistentry(src.ims_in, "included"); + in_addr_slistentry(src.ims_ex, "excluded"); + in_addr_slistentry(src.ims_alw, "allowed"); + in_addr_slistentry(src.ims_blk, "blocked"); + in_addr_slistentry(src.ims_toin, "to-include"); + in_addr_slistentry(src.ims_ex, "to-exclude"); +#else + printf("\n"); +#endif + } + +#ifdef __FreeBSD__ + return (NULL); +#else + return (multi.inm_list.le_next); +#endif +} + +#ifdef HAVE_IGMPV3 +void +in_addr_slistentry(struct in_addr_slist *ias, char *heading) +{ + struct in_addr_slist slist; + struct ias_head head; + struct in_addr_source src; + + if (ias == NULL) { + printf("\t\t\t\t%s (none)\n", heading); + return; + } + memset(&slist, 0, sizeof(slist)); + KREAD(ias, &slist, struct in_addr_source); + printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc); + if (slist.numsrc == 0) { + return; + } + KREAD(slist.head, &head, struct ias_head); + + KREAD(head.lh_first, &src, struct in_addr_source); + while (1) { + printf("\t\t\t\t\tsource %s (ref=%d)\n", + inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount); + if (src.ias_list.le_next == NULL) + break; + KREAD(src.ias_list.le_next, &src, struct in_addr_source); + } + return; +} +#endif