ifmcstat.c revision 167731
1/*	$KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/* TODO: use sysctl. */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/usr.sbin/ifmcstat/ifmcstat.c 167731 2007-03-20 02:08:28Z bms $");
36
37#include <sys/types.h>
38#include <sys/param.h>
39#include <sys/socket.h>
40#include <sys/queue.h>
41
42#include <net/if.h>
43#include <net/if_var.h>
44#include <net/if_types.h>
45#include <net/if_dl.h>
46#include <net/route.h>
47
48#include <netinet/in.h>
49#include <netinet/in_var.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52#include <netinet/igmp.h>
53#ifdef HAVE_IGMPV3
54# include <netinet/in_msf.h>
55#endif
56#define KERNEL
57# include <netinet/if_ether.h>
58#undef KERNEL
59#define _KERNEL
60# include <sys/sysctl.h>
61# include <netinet/igmp_var.h>
62#undef _KERNEL
63
64#ifdef INET6
65# ifdef HAVE_MLDV2
66#  include <netinet6/in6_msf.h>
67# endif
68#include <netinet/icmp6.h>
69#define _KERNEL
70# include <netinet6/mld6_var.h>
71#undef _KERNEL
72#endif /* INET6 */
73
74#include <stdio.h>
75#include <stdlib.h>
76#include <fcntl.h>
77#include <kvm.h>
78#include <nlist.h>
79#include <string.h>
80#include <limits.h>
81#include <unistd.h>
82#include <arpa/inet.h>
83#include <netdb.h>
84
85kvm_t	*kvmd;
86int ifindex = 0;
87int af = AF_UNSPEC;
88
89struct	nlist nl[] = {
90#define	N_IFNET	0
91	{ "_ifnet" },
92	{ "" },
93};
94
95const char *inet6_n2a __P((struct in6_addr *));
96int main __P((int, char **));
97char *ifname __P((struct ifnet *));
98void kread __P((u_long, void *, int));
99#ifdef INET6
100void if6_addrlist __P((struct ifaddr *));
101void in6_multilist __P((struct in6_multi *));
102struct in6_multi * in6_multientry __P((struct in6_multi *));
103#endif
104void if_addrlist(struct ifaddr *);
105void in_multilist(struct in_multi *);
106struct in_multi * in_multientry(struct in_multi *);
107#ifdef HAVE_IGMPV3
108void in_addr_slistentry(struct in_addr_slist *ias, char *heading);
109#endif
110#ifdef HAVE_MLDV2
111void in6_addr_slistentry(struct in6_addr_slist *ias, char *heading);
112#endif
113
114#define	KREAD(addr, buf, type) \
115	kread((u_long)addr, (void *)buf, sizeof(type))
116
117const char *inet6_n2a(p)
118	struct in6_addr *p;
119{
120	static char buf[NI_MAXHOST];
121	struct sockaddr_in6 sin6;
122	u_int32_t scopeid;
123	const int niflags = NI_NUMERICHOST;
124
125	memset(&sin6, 0, sizeof(sin6));
126	sin6.sin6_family = AF_INET6;
127	sin6.sin6_len = sizeof(struct sockaddr_in6);
128	sin6.sin6_addr = *p;
129	if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
130	    IN6_IS_ADDR_MC_NODELOCAL(p)) {
131		scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
132		if (scopeid) {
133			sin6.sin6_scope_id = scopeid;
134			sin6.sin6_addr.s6_addr[2] = 0;
135			sin6.sin6_addr.s6_addr[3] = 0;
136		}
137	}
138	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
139			buf, sizeof(buf), NULL, 0, niflags) == 0)
140		return buf;
141	else
142		return "(invalid)";
143}
144
145int main(argc, argv)
146	int argc;
147	char **argv;
148{
149	char	buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
150	int c;
151	struct	ifnet	*ifp, *nifp, ifnet;
152	const char *kernel = NULL;
153	const char *core = NULL;
154
155	/* "ifmcstat [kernel]" format is supported for backward compatiblity */
156	if (argc == 2)
157		kernel = argv[1];
158
159	while ((c = getopt(argc, argv, "i:f:M:N:")) != -1) {
160		switch (c) {
161		case 'i':
162			if ((ifindex = if_nametoindex(optarg)) == 0) {
163				fprintf(stderr, "%s: unknown interface\n", optarg);
164				exit(1);
165			}
166			break;
167		case 'f':
168			if (strcmp(optarg, "inet") == 0) {
169				af = AF_INET;
170				break;
171			}
172			if (strcmp(optarg, "inet6") == 0) {
173				af = AF_INET6;
174				break;
175			}
176			fprintf(stderr, "%s: unknown address family\n", optarg);
177			exit(1);
178			/*NOTREACHED*/
179		case 'M':
180			core = strdup(optarg);
181			break;
182		case 'N':
183			kernel = strdup(optarg);
184			break;
185		default:
186			fprintf(stderr,
187"usage: ifmcstat [-i interface] [-f address family] [-M core] [-N system]\n");
188			exit(1);
189			/*NOTREACHED*/
190		}
191	}
192
193	if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
194	    NULL) {
195		perror("kvm_openfiles");
196		exit(1);
197	}
198	if (kvm_nlist(kvmd, nl) < 0) {
199		perror("kvm_nlist");
200		exit(1);
201	}
202	if (nl[N_IFNET].n_value == 0) {
203		printf("symbol %s not found\n", nl[N_IFNET].n_name);
204		exit(1);
205	}
206	KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
207	while (ifp) {
208		KREAD(ifp, &ifnet, struct ifnet);
209		nifp = ifnet.if_link.tqe_next;
210		if (ifindex && ifindex != ifnet.if_index)
211			goto next;
212
213		printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
214		if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
215#ifdef INET6
216		if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
217#endif
218next:
219		ifp = nifp;
220	}
221
222	exit(0);
223	/*NOTREACHED*/
224}
225
226char *ifname(ifp)
227	struct ifnet *ifp;
228{
229	static char buf[BUFSIZ];
230	struct ifnet ifnet;
231
232	KREAD(ifp, &ifnet, struct ifnet);
233	strlcpy(buf, ifnet.if_xname, sizeof(buf));
234	return buf;
235}
236
237void kread(addr, buf, len)
238	u_long addr;
239	void *buf;
240	int len;
241{
242	if (kvm_read(kvmd, addr, buf, len) != len) {
243		perror("kvm_read");
244		exit(1);
245	}
246}
247
248#ifdef INET6
249
250void
251if6_addrlist(ifap)
252	struct ifaddr *ifap;
253{
254	struct ifaddr ifa;
255	struct sockaddr sa;
256	struct in6_ifaddr if6a;
257	struct ifaddr *ifap0;
258
259	if (af && af != AF_INET6)
260		return;
261	ifap0 = ifap;
262	while (ifap) {
263		KREAD(ifap, &ifa, struct ifaddr);
264		if (ifa.ifa_addr == NULL)
265			goto nextifap;
266		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
267		if (sa.sa_family != PF_INET6)
268			goto nextifap;
269		KREAD(ifap, &if6a, struct in6_ifaddr);
270		printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
271	nextifap:
272		ifap = ifa.ifa_link.tqe_next;
273	}
274	if (ifap0) {
275		struct ifnet ifnet;
276		struct ifmultiaddr ifm, *ifmp = 0;
277		struct sockaddr_dl sdl;
278
279		KREAD(ifap0, &ifa, struct ifaddr);
280		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
281		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
282			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
283		while (ifmp) {
284			KREAD(ifmp, &ifm, struct ifmultiaddr);
285			if (ifm.ifma_addr == NULL)
286				goto nextmulti;
287			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
288			if (sa.sa_family != AF_INET6)
289				goto nextmulti;
290			(void)in6_multientry((struct in6_multi *)
291					     ifm.ifma_protospec);
292			if (ifm.ifma_lladdr == 0)
293				goto nextmulti;
294			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
295			printf("\t\t\tmcast-macaddr %s multicnt %d\n",
296			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
297			       ifm.ifma_refcount);
298		    nextmulti:
299			ifmp = TAILQ_NEXT(&ifm, ifma_link);
300		}
301	}
302}
303
304struct in6_multi *
305in6_multientry(mc)
306	struct in6_multi *mc;
307{
308	struct in6_multi multi;
309#ifdef HAVE_MLDV2
310	struct in6_multi_source src;
311	struct router6_info rt6i;
312#endif
313
314	KREAD(mc, &multi, struct in6_multi);
315	printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr));
316	printf(" refcnt %u\n", multi.in6m_refcount);
317
318#ifdef HAVE_MLDV2
319	if (multi.in6m_rti != NULL) {
320		KREAD(multi.in6m_rti, &rt6i, struct router_info);
321		printf("\t\t\t");
322		switch (rt6i.rt6i_type) {
323		case MLD_V1_ROUTER:
324			printf("mldv1");
325			break;
326		case MLD_V2_ROUTER:
327			printf("mldv2");
328			break;
329		default:
330			printf("mldv?(%d)", rt6i.rt6i_type);
331			break;
332		}
333
334		if (multi.in6m_source == NULL) {
335			printf("\n");
336			return(multi.in6m_entry.le_next);
337		}
338
339		KREAD(multi.in6m_source, &src, struct in6_multi_source);
340		printf(" mode=%s grpjoin=%d\n",
341		    src.i6ms_mode == MCAST_INCLUDE ? "include" :
342		    src.i6ms_mode == MCAST_EXCLUDE ? "exclude" :
343		    "???",
344		    src.i6ms_grpjoin);
345		in6_addr_slistentry(src.i6ms_cur, "current");
346		in6_addr_slistentry(src.i6ms_rec, "recorded");
347		in6_addr_slistentry(src.i6ms_in, "included");
348		in6_addr_slistentry(src.i6ms_ex, "excluded");
349		in6_addr_slistentry(src.i6ms_alw, "allowed");
350		in6_addr_slistentry(src.i6ms_blk, "blocked");
351		in6_addr_slistentry(src.i6ms_toin, "to-include");
352		in6_addr_slistentry(src.i6ms_ex, "to-exclude");
353	}
354#endif
355	return(multi.in6m_entry.le_next);
356}
357
358#ifdef HAVE_MLDV2
359void
360in6_addr_slistentry(struct in6_addr_slist *ias, char *heading)
361{
362	struct in6_addr_slist slist;
363	struct i6as_head head;
364	struct in6_addr_source src;
365
366	if (ias == NULL) {
367		printf("\t\t\t\t%s (none)\n", heading);
368		return;
369	}
370	memset(&slist, 0, sizeof(slist));
371	KREAD(ias, &slist, struct in6_addr_source);
372	printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
373	if (slist.numsrc == 0) {
374		return;
375	}
376	KREAD(slist.head, &head, struct i6as_head);
377
378	KREAD(head.lh_first, &src, struct in6_addr_source);
379	while (1) {
380		printf("\t\t\t\t\tsource %s (ref=%d)\n",
381			inet6_n2a(&src.i6as_addr.sin6_addr),
382			src.i6as_refcount);
383		if (src.i6as_list.le_next == NULL)
384			break;
385		KREAD(src.i6as_list.le_next, &src, struct in6_addr_source);
386	}
387	return;
388}
389#endif
390
391void
392in6_multilist(mc)
393	struct in6_multi *mc;
394{
395	while (mc)
396		mc = in6_multientry(mc);
397}
398
399#endif /* INET6 */
400
401void
402if_addrlist(ifap)
403	struct ifaddr *ifap;
404{
405	struct ifaddr ifa;
406	struct sockaddr sa;
407	struct in_ifaddr ia;
408	struct ifaddr *ifap0;
409
410	if (af && af != AF_INET)
411		return;
412	ifap0 = ifap;
413	while (ifap) {
414		KREAD(ifap, &ifa, struct ifaddr);
415		if (ifa.ifa_addr == NULL)
416			goto nextifap;
417		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
418		if (sa.sa_family != PF_INET)
419			goto nextifap;
420		KREAD(ifap, &ia, struct in_ifaddr);
421		printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
422	nextifap:
423		ifap = ifa.ifa_link.tqe_next;
424	}
425	if (ifap0) {
426		struct ifnet ifnet;
427		struct ifmultiaddr ifm, *ifmp = 0;
428		struct sockaddr_dl sdl;
429
430		KREAD(ifap0, &ifa, struct ifaddr);
431		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
432		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
433			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
434		while (ifmp) {
435			KREAD(ifmp, &ifm, struct ifmultiaddr);
436			if (ifm.ifma_addr == NULL)
437				goto nextmulti;
438			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
439			if (sa.sa_family != AF_INET)
440				goto nextmulti;
441			(void)in_multientry((struct in_multi *)
442					    ifm.ifma_protospec);
443			if (ifm.ifma_lladdr == 0)
444				goto nextmulti;
445			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
446			printf("\t\t\tmcast-macaddr %s multicnt %d\n",
447			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
448			       ifm.ifma_refcount);
449		    nextmulti:
450			ifmp = TAILQ_NEXT(&ifm, ifma_link);
451		}
452	}
453}
454
455void
456in_multilist(mc)
457	struct in_multi *mc;
458{
459	while (mc)
460		mc = in_multientry(mc);
461}
462
463struct in_multi *
464in_multientry(mc)
465	struct in_multi *mc;
466{
467	struct in_multi multi;
468	struct router_info rti;
469#ifdef HAVE_IGMPV3
470	struct in_multi_source src;
471#endif
472
473	KREAD(mc, &multi, struct in_multi);
474	printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr));
475
476	if (multi.inm_rti != NULL) {
477		KREAD(multi.inm_rti, &rti, struct router_info);
478		printf("\t\t\t");
479		switch (rti.rti_type) {
480		case IGMP_V1_ROUTER:
481			printf("igmpv1");
482			break;
483		case IGMP_V2_ROUTER:
484			printf("igmpv2");
485			break;
486#ifdef HAVE_IGMPV3
487		case IGMP_V3_ROUTER:
488			printf("igmpv3");
489			break;
490#endif
491		default:
492			printf("igmpv?(%d)", rti.rti_type);
493			break;
494		}
495
496#ifdef HAVE_IGMPV3
497		if (multi.inm_source == NULL) {
498			printf("\n");
499			return (multi.inm_list.le_next);
500		}
501
502		KREAD(multi.inm_source, &src, struct in_multi_source);
503		printf(" mode=%s grpjoin=%d\n",
504		    src.ims_mode == MCAST_INCLUDE ? "include" :
505		    src.ims_mode == MCAST_EXCLUDE ? "exclude" :
506		    "???",
507		    src.ims_grpjoin);
508		in_addr_slistentry(src.ims_cur, "current");
509		in_addr_slistentry(src.ims_rec, "recorded");
510		in_addr_slistentry(src.ims_in, "included");
511		in_addr_slistentry(src.ims_ex, "excluded");
512		in_addr_slistentry(src.ims_alw, "allowed");
513		in_addr_slistentry(src.ims_blk, "blocked");
514		in_addr_slistentry(src.ims_toin, "to-include");
515		in_addr_slistentry(src.ims_ex, "to-exclude");
516#else
517		printf("\n");
518#endif
519	}
520
521	return (NULL);
522}
523
524#ifdef HAVE_IGMPV3
525void
526in_addr_slistentry(struct in_addr_slist *ias, char *heading)
527{
528	struct in_addr_slist slist;
529	struct ias_head head;
530	struct in_addr_source src;
531
532	if (ias == NULL) {
533		printf("\t\t\t\t%s (none)\n", heading);
534		return;
535	}
536	memset(&slist, 0, sizeof(slist));
537	KREAD(ias, &slist, struct in_addr_source);
538	printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
539	if (slist.numsrc == 0) {
540		return;
541	}
542	KREAD(slist.head, &head, struct ias_head);
543
544	KREAD(head.lh_first, &src, struct in_addr_source);
545	while (1) {
546		printf("\t\t\t\t\tsource %s (ref=%d)\n",
547			inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount);
548		if (src.ias_list.le_next == NULL)
549			break;
550		KREAD(src.ias_list.le_next, &src, struct in_addr_source);
551	}
552	return;
553}
554#endif
555