summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ifmcstat
diff options
context:
space:
mode:
authorbms <bms@FreeBSD.org>2009-03-09 17:53:05 +0000
committerbms <bms@FreeBSD.org>2009-03-09 17:53:05 +0000
commit71233409ea6a2f4d751847c05e7aad9375278d94 (patch)
tree3d95180b6661648cd67dba62e6daefc7c8661793 /usr.sbin/ifmcstat
parent2173e9258f5b3400a58f42bd91fd01c1efc4441d (diff)
downloadFreeBSD-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')
-rw-r--r--usr.sbin/ifmcstat/Makefile4
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.822
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.c433
3 files changed, 365 insertions, 94 deletions
diff --git a/usr.sbin/ifmcstat/Makefile b/usr.sbin/ifmcstat/Makefile
index 62de0b9..fab7ea6 100644
--- a/usr.sbin/ifmcstat/Makefile
+++ b/usr.sbin/ifmcstat/Makefile
@@ -4,8 +4,10 @@
.include <bsd.own.mk>
PROG= ifmcstat
+SRCS= ifmcstat.c printb.c
+
MAN= ifmcstat.8
-BINMODE= 550
+BINMODE= 555
WARNS?= 2
diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8
index 78eb39c..5805183 100644
--- a/usr.sbin/ifmcstat/ifmcstat.8
+++ b/usr.sbin/ifmcstat/ifmcstat.8
@@ -30,7 +30,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 15, 2009
+.Dd February 28, 2009
.Dt IFMCSTAT 8
.Os
.Sh NAME
@@ -41,6 +41,7 @@
.Op Fl i Ar interface
.Op Fl f Ar address-family
.Op Fl v
+.Op Fl K
.Op Fl M Ar core
.Op Fl N Ar system
.\"
@@ -66,6 +67,13 @@ specifies that link-layer memberships should be printed;
they are suppressed by default.
It may not be specified for
.Fl f Ar link .
+Source lists for each group will also be printed.
+.Pp
+If specified twice, and
+.Xr kvm 3
+is in use, the IGMP timers for each interface
+and the IGMP source list counters for each group
+will also be printed.
.El
.Pp
The following options are only available if
@@ -73,6 +81,10 @@ The following options are only available if
has been built with support for
.Xr kvm 3 :
.Bl -tag -width Fl
+.It Fl K
+forces the use of
+.Xr kvm 3
+to be disabled.
.It Fl M Ar core
extracts values associated with the name list from the specified core,
instead of the default
@@ -106,10 +118,14 @@ support, the information displayed by
is more limited.
This support is recommended for debugging purposes.
It requires super-user privilege if used to inspect a running kernel.
+.Pp
+The
.Xr kvm 3
-will be used by default if
+back-end will be used by default if
.Nm
-is run with super-user privileges.
+is run with super-user privileges, unless the
+.Fl K
+option is specified.
.Sh SEE ALSO
.Xr getifaddrs 3 ,
.Xr getifmaddrs 3 ,
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;
OpenPOWER on IntegriCloud