diff options
author | bms <bms@FreeBSD.org> | 2009-03-09 17:53:05 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2009-03-09 17:53:05 +0000 |
commit | 71233409ea6a2f4d751847c05e7aad9375278d94 (patch) | |
tree | 3d95180b6661648cd67dba62e6daefc7c8661793 /usr.sbin/ifmcstat/ifmcstat.c | |
parent | 2173e9258f5b3400a58f42bd91fd01c1efc4441d (diff) | |
download | FreeBSD-src-71233409ea6a2f4d751847c05e7aad9375278d94.zip FreeBSD-src-71233409ea6a2f4d751847c05e7aad9375278d94.tar.gz |
Merge IGMPv3 and Source-Specific Multicast (SSM) to the FreeBSD
IPv4 stack.
Diffs are minimized against p4.
PCS has been used for some protocol verification, more widespread
testing of recorded sources in Group-and-Source queries is needed.
sizeof(struct igmpstat) has changed.
__FreeBSD_version is bumped to 800070.
Diffstat (limited to 'usr.sbin/ifmcstat/ifmcstat.c')
-rw-r--r-- | usr.sbin/ifmcstat/ifmcstat.c | 433 |
1 files changed, 343 insertions, 90 deletions
diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c index c8f57d4..34068ea 100644 --- a/usr.sbin/ifmcstat/ifmcstat.c +++ b/usr.sbin/ifmcstat/ifmcstat.c @@ -35,8 +35,10 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/param.h> +#include <sys/sysctl.h> #include <sys/socket.h> #include <sys/queue.h> +#include <sys/tree.h> #include <net/if.h> #include <net/if_var.h> @@ -49,15 +51,13 @@ __FBSDID("$FreeBSD$"); #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/igmp.h> -#ifdef HAVE_IGMPV3 -# include <netinet/in_msf.h> -#endif #define KERNEL # include <netinet/if_ether.h> #undef KERNEL #define _KERNEL -# include <sys/sysctl.h> +#define SYSCTL_DECL(x) # include <netinet/igmp_var.h> +#undef SYSCTL_DECL #undef _KERNEL #ifdef INET6 @@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$"); #include <ctype.h> #include <err.h> +#include <errno.h> #include <fcntl.h> #include <kvm.h> #include <limits.h> @@ -93,6 +94,8 @@ __FBSDID("$FreeBSD$"); #define INET #endif +extern void printb(const char *, unsigned int, const char *); + union sockunion { struct sockaddr_storage ss; struct sockaddr sa; @@ -108,6 +111,9 @@ typedef union sockunion sockunion_t; uint32_t ifindex = 0; int af = AF_UNSPEC; +#ifdef WITH_KVM +int Kflag = 0; +#endif int vflag = 0; #define sa_equal(a1, a2) \ @@ -130,9 +136,6 @@ int vflag = 0; static void if_addrlist(struct ifaddr *); static struct in_multi * in_multientry(struct in_multi *); -#ifdef HAVE_IGMPV3 -static void in_addr_slistentry(struct in_addr_slist *, char *); -#endif #endif /* INET */ #ifdef INET6 @@ -159,6 +162,10 @@ struct nlist nl[] = { #endif /* WITH_KVM */ static int ifmcstat_getifmaddrs(void); +#ifdef INET +static void in_ifinfo(struct igmp_ifinfo *); +static const char * inm_mode(u_int mode); +#endif #ifdef INET6 static const char * inet6_n2a(struct in6_addr *); #endif @@ -172,12 +179,18 @@ usage() "usage: ifmcstat [-i interface] [-f address family]" " [-v]" #ifdef WITH_KVM - " [-M core] [-N system]" + " [-K] [-M core] [-N system]" #endif "\n"); exit(EX_USAGE); } +static const char *options = "i:f:vM:N:" +#ifdef WITH_KVM + "K" +#endif + ; + int main(int argc, char **argv) { @@ -187,7 +200,7 @@ main(int argc, char **argv) const char *core = NULL; #endif - while ((c = getopt(argc, argv, "i:f:vM:N:")) != -1) { + while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'i': if ((ifindex = if_nametoindex(optarg)) == 0) { @@ -219,8 +232,14 @@ main(int argc, char **argv) /*NOTREACHED*/ break; +#ifdef WITH_KVM + case 'K': + ++Kflag; + break; +#endif + case 'v': - vflag = 1; + ++vflag; break; #ifdef WITH_KVM @@ -244,12 +263,13 @@ main(int argc, char **argv) usage(); #ifdef WITH_KVM - error = ifmcstat_kvm(kernel, core); + if (!Kflag) + error = ifmcstat_kvm(kernel, core); /* * If KVM failed, and user did not explicitly specify a core file, - * try the sysctl backend. + * or force KVM backend to be disabled, try the sysctl backend. */ - if (error != 0 && (core == NULL && kernel == NULL)) + if (Kflag || (error != 0 && (core == NULL && kernel == NULL))) #endif error = ifmcstat_getifmaddrs(); if (error != 0) @@ -259,6 +279,52 @@ main(int argc, char **argv) /*NOTREACHED*/ } +#ifdef INET + +static void +in_ifinfo(struct igmp_ifinfo *igi) +{ + + printf("\t"); + switch (igi->igi_version) { + case IGMP_VERSION_1: + case IGMP_VERSION_2: + case IGMP_VERSION_3: + printf("igmpv%d", igi->igi_version); + break; + default: + printf("igmpv?(%d)", igi->igi_version); + break; + } + printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); + if (igi->igi_version == IGMP_VERSION_3) { + printf(" rv %u qi %u qri %u uri %u", + igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u v3timer %u", + igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); + } + printf("\n"); +} + +static const char *inm_modes[] = { + "undefined", + "include", + "exclude", +}; + +static const char * +inm_mode(u_int mode) +{ + + if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) + return (inm_modes[mode]); + return (NULL); +} + +#endif /* INET */ + #ifdef WITH_KVM static int @@ -447,6 +513,7 @@ static void if_addrlist(struct ifaddr *ifap) { struct ifaddr ifa; + struct ifnet ifnet; struct sockaddr sa; struct in_ifaddr ia; struct ifaddr *ifap0; @@ -463,11 +530,24 @@ if_addrlist(struct ifaddr *ifap) goto nextifap; KREAD(ifap, &ia, struct in_ifaddr); printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr)); + /* + * Print per-link IGMP information, if available. + */ + if (ifa.ifa_ifp != NULL) { + struct in_ifinfo ii; + struct igmp_ifinfo igi; + + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo); + if (ii.ii_igmp != NULL) { + KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo); + in_ifinfo(&igi); + } + } nextifap: ifap = ifa.ifa_link.tqe_next; } if (ifap0) { - struct ifnet ifnet; struct ifmultiaddr ifm, *ifmp = 0; struct sockaddr_dl sdl; @@ -496,96 +576,145 @@ if_addrlist(struct ifaddr *ifap) } } -static struct in_multi * -in_multientry(struct in_multi *mc) +static const char *inm_states[] = { + "not-member", + "silent", + "idle", + "lazy", + "sleeping", + "awakening", + "query-pending", + "sg-query-pending", + "leaving" +}; + +static const char * +inm_state(u_int state) { - 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 (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) + return (inm_states[state]); + return (NULL); +} - 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; - } +#if 0 +static struct ip_msource * +ims_min_kvm(struct in_multi *pinm) +{ + struct ip_msource ims0; + struct ip_msource *tmp, *parent; + + parent = NULL; + tmp = RB_ROOT(&pinm->inm_srcs); + while (tmp) { + parent = tmp; + KREAD(tmp, &ims0, struct ip_msource); + tmp = RB_LEFT(&ims0, ims_link); + } + return (parent); /* kva */ +} -#ifdef HAVE_IGMPV3 - if (multi.inm_source == NULL) { - printf("\n"); - return (multi.inm_list.le_next); +/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */ +static struct ip_msource * +ims_next_kvm(struct ip_msource *ims) +{ + struct ip_msource ims0, ims1; + struct ip_msource *tmp; + + KREAD(ims, &ims0, struct ip_msource); + if (RB_RIGHT(&ims0, ims_link)) { + ims = RB_RIGHT(&ims0, ims_link); + KREAD(ims, &ims1, struct ip_msource); + while ((tmp = RB_LEFT(&ims1, ims_link))) { + KREAD(tmp, &ims0, struct ip_msource); + ims = RB_LEFT(&ims0, ims_link); + } + } else { + tmp = RB_PARENT(&ims0, ims_link); + if (tmp) { + KREAD(tmp, &ims1, struct ip_msource); + if (ims == RB_LEFT(&ims1, ims_link)) + ims = tmp; + } else { + while ((tmp = RB_PARENT(&ims0, ims_link))) { + KREAD(tmp, &ims1, struct ip_msource); + if (ims == RB_RIGHT(&ims1, ims_link)) { + ims = tmp; + KREAD(ims, &ims0, struct ip_msource); + } else + break; + } + ims = RB_PARENT(&ims0, ims_link); } - - 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 } - - return (NULL); + return (ims); /* kva */ } -#ifdef HAVE_IGMPV3 static void -in_addr_slistentry(struct in_addr_slist *ias, char *heading) +inm_print_sources_kvm(struct in_multi *pinm) { - 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) { + struct ip_msource ims0; + struct ip_msource *ims; + struct in_addr src; + int cnt; + uint8_t fmode; + + cnt = 0; + fmode = pinm->inm_st[1].iss_fmode; + if (fmode == MCAST_UNDEFINED) return; + for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) { + if (cnt == 0) + printf(" srcs "); + KREAD(ims, &ims0, struct ip_msource); + /* Only print sources in-mode at t1. */ + if (fmode != ims_get_mode(pinm, ims, 1)) + continue; + src.s_addr = htonl(ims0.ims_haddr); + printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src)); } - KREAD(slist.head, &head, struct ias_head); +} +#endif - 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); +static struct in_multi * +in_multientry(struct in_multi *pinm) +{ + struct in_multi inm; + const char *state, *mode; + + KREAD(pinm, &inm, struct in_multi); + printf("\t\tgroup %s", inet_ntoa(inm.inm_addr)); + printf(" refcnt %u", inm.inm_refcount); + + state = inm_state(inm.inm_state); + if (state) + printf(" state %s", state); + else + printf(" state (%d)", inm.inm_state); + + mode = inm_mode(inm.inm_st[1].iss_fmode); + if (mode) + printf(" mode %s", mode); + else + printf(" mode (%d)", inm.inm_st[1].iss_fmode); + + if (vflag >= 2) { + printf(" asm %u ex %u in %u rec %u", + (u_int)inm.inm_st[1].iss_asm, + (u_int)inm.inm_st[1].iss_ex, + (u_int)inm.inm_st[1].iss_in, + (u_int)inm.inm_st[1].iss_rec); } + +#if 0 + /* Buggy. */ + if (vflag) + inm_print_sources_kvm(&inm); +#endif + + printf("\n"); + return (NULL); } -#endif /* HAVE_IGMPV3 */ #endif /* INET */ @@ -622,6 +751,97 @@ inet6_n2a(struct in6_addr *p) } #endif /* INET6 */ +#ifdef INET +/* + * Retrieve per-group source filter mode and lists via sysctl. + */ +static void +inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) +{ +#define MAX_SYSCTL_TRY 5 + int mib[7]; + int ntry = 0; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + mib[6] = gina.s_addr; /* 32 bits wide */ + mibsize = sizeof(mib) / sizeof(mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet.ip.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in_addr); + pina = (struct in_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), + inet_ntoa(*pina++)); + len -= sizeof(struct in_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet.ip.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +#endif /* INET */ + static int ifmcstat_getifmaddrs(void) { @@ -771,6 +991,32 @@ ifmcstat_getifmaddrs(void) } fprintf(stdout, "\t%s %s\n", pafname, addrbuf); +#ifdef INET + /* + * Print per-link IGMP information, if available. + */ + if (pifasa->sa.sa_family == AF_INET) { + struct igmp_ifinfo igi; + size_t mibsize, len; + int mib[5]; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.igmp.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct igmp_ifinfo); + if (sysctl(mib, mibsize + 1, &igi, &len, NULL, + 0) == -1) { + perror("sysctl net.inet.igmp.ifinfo"); + goto next_ifnet; + } + in_ifinfo(&igi); + } +next_ifnet: +#endif lastifasa = *pifasa; } @@ -788,7 +1034,14 @@ ifmcstat_getifmaddrs(void) perror("getnameinfo"); } - fprintf(stdout, "\t\tgroup %s\n", addrbuf); + fprintf(stdout, "\t\tgroup %s", addrbuf); +#ifdef INET + if (pgsa->sa.sa_family == AF_INET) { + inm_print_sources_sysctl(thisifindex, + pgsa->sin.sin_addr); + } +#endif + fprintf(stdout, "\n"); /* Link-layer mapping, if present. */ pllsa = (sockunion_t *)ifma->ifma_lladdr; |