Merge latest ifmcstat from HEAD.

Obtained from:	KAME (with many FreeBSD changes)
This commit is contained in:
bms 2007-04-03 00:39:34 +00:00
parent 144ec955e7
commit ecb163fc29
3 changed files with 389 additions and 80 deletions

@ -6,6 +6,7 @@ MAN= ifmcstat.8
BINMODE= 550
WARNS?= 2
CFLAGS+=-DINET6
DPADD= ${LIBKVM}
LDADD= -lkvm

@ -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 20, 2007
.Dt IFMCSTAT 8
.Os
.Sh NAME
@ -37,22 +37,41 @@
.Nd dump multicast group management statistics per interface
.Sh SYNOPSIS
.Nm
.Op Fl i Ar interface
.Op Fl f Ar address-family
.Op Fl M Ar core
.Op Fl N Ar system
.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 M Ar core
extracts values associated with the name list from the specified core,
instead of the default
.Pa /dev/kmem .
.It Fl N Ar system
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 system
This is the same as specifying
.Nm
.Fl N Ar system .
This usage is deprecated; it is supported only for backwards compatibility.
.Sh BUGS
.Nm
needs superuser privilege.

@ -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,50 @@
* 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$
*/
/* TODO: use sysctl. */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#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>
# include <netinet/igmp_var.h>
#undef _KERNEL
#ifdef INET6
# ifdef HAVE_MLDV2
# include <netinet6/in6_msf.h>
# endif
#include <netinet/icmp6.h>
#define _KERNEL
# include <netinet6/mld6_var.h>
#undef _KERNEL
#endif /* INET6 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
@ -36,26 +78,13 @@
#include <nlist.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#define _KERNEL
#include <netinet/if_ether.h>
#undef _KERNEL
#include <netinet/in_var.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
kvm_t *kvmd;
int ifindex = 0;
int af = AF_UNSPEC;
struct nlist nl[] = {
#define N_IFNET 0
@ -64,24 +93,27 @@ struct nlist nl[] = {
};
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));
#ifdef INET6
void if6_addrlist __P((struct ifaddr *));
void in6_multilist __P((struct in6_multi *));
struct in6_multi * in6_multientry __P((struct in6_multi *));
#endif
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))
#ifdef N_IN6_MK
struct multi6_kludge {
LIST_ENTRY(multi6_kludge) mk_entry;
struct ifnet *mk_ifp;
struct in6_multihead mk_head;
};
#endif
const char *inet6_n2a(p)
struct in6_addr *p;
{
@ -94,7 +126,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 +142,56 @@ 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;
const char *kernel = NULL;
const char *core = 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:M:N:")) != -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 'M':
core = strdup(optarg);
break;
case 'N':
kernel = strdup(optarg);
break;
default:
fprintf(stderr,
"usage: ifmcstat [-i interface] [-f address family] [-M core] [-N system]\n");
exit(1);
/*NOTREACHED*/
}
}
if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
NULL) {
perror("kvm_openfiles");
exit(1);
}
@ -129,13 +206,16 @@ int main()
KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
while (ifp) {
KREAD(ifp, &ifnet, struct ifnet);
nifp = ifnet.if_link.tqe_next;
if (ifindex && ifindex != ifnet.if_index)
goto next;
printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
#ifdef INET6
if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
nifp = TAILQ_NEXT(&ifnet, if_link);
/* not supported */
#endif
next:
ifp = nifp;
}
@ -165,6 +245,8 @@ void kread(addr, buf, len)
}
}
#ifdef INET6
void
if6_addrlist(ifap)
struct ifaddr *ifap;
@ -174,6 +256,8 @@ if6_addrlist(ifap)
struct in6_ifaddr if6a;
struct ifaddr *ifap0;
if (af && af != AF_INET6)
return;
ifap0 = ifap;
while (ifap) {
KREAD(ifap, &ifa, struct ifaddr);
@ -185,7 +269,7 @@ if6_addrlist(ifap)
KREAD(ifap, &if6a, struct in6_ifaddr);
printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
nextifap:
ifap = TAILQ_NEXT(&ifa, ifa_link);
ifap = ifa.ifa_link.tqe_next;
}
if (ifap0) {
struct ifnet ifnet;
@ -215,33 +299,6 @@ if6_addrlist(ifap)
ifmp = TAILQ_NEXT(&ifm, ifma_link);
}
}
#ifdef N_IN6_MK
if (nl[N_IN6_MK].n_value != 0) {
LIST_HEAD(in6_mktype, multi6_kludge) in6_mk;
struct multi6_kludge *mkp, mk;
char *nam;
KREAD(nl[N_IN6_MK].n_value, &in6_mk, struct in6_mktype);
KREAD(ifap0, &ifa, struct ifaddr);
nam = strdup(ifname(ifa.ifa_ifp));
if (!nam) {
fprintf(stderr, "ifmcstat: not enough core\n");
exit(1);
}
LIST_FOREACH(mkp, &in6_mk, mk_entry) {
KREAD(mkp, &mk, struct multi6_kludge);
if (strcmp(nam, ifname(mk.mk_ifp)) == 0 &&
LIST_FIRST(&mk.mk_head)) {
printf("\t(on kludge entry for %s)\n", nam);
in6_multilist(LIST_FIRST(&mk.mk_head));
}
}
free(nam);
}
#endif
}
struct in6_multi *
@ -249,13 +306,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 +395,160 @@ in6_multilist(mc)
while (mc)
mc = in6_multientry(mc);
}
#endif /* INET6 */
void
if_addrlist(ifap)
struct ifaddr *ifap;
{
struct ifaddr ifa;
struct sockaddr sa;
struct in_ifaddr ia;
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));
nextifap:
ifap = ifa.ifa_link.tqe_next;
}
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);
}
}
}
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
}
return (NULL);
}
#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