xref: /freebsd/usr.sbin/ifmcstat/ifmcstat.c (revision f0a75d274af375d15b97b830966b99a02b7db911)
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$");
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 
85 kvm_t	*kvmd;
86 int ifindex = 0;
87 int af = AF_UNSPEC;
88 
89 struct	nlist nl[] = {
90 #define	N_IFNET	0
91 	{ "_ifnet" },
92 	{ "" },
93 };
94 
95 const char *inet6_n2a __P((struct in6_addr *));
96 int main __P((int, char **));
97 char *ifname __P((struct ifnet *));
98 void kread __P((u_long, void *, int));
99 #ifdef INET6
100 void if6_addrlist __P((struct ifaddr *));
101 void in6_multilist __P((struct in6_multi *));
102 struct in6_multi * in6_multientry __P((struct in6_multi *));
103 #endif
104 void if_addrlist(struct ifaddr *);
105 void in_multilist(struct in_multi *);
106 struct in_multi * in_multientry(struct in_multi *);
107 #ifdef HAVE_IGMPV3
108 void in_addr_slistentry(struct in_addr_slist *ias, char *heading);
109 #endif
110 #ifdef HAVE_MLDV2
111 void 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 
117 const 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 
145 int 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
218 next:
219 		ifp = nifp;
220 	}
221 
222 	exit(0);
223 	/*NOTREACHED*/
224 }
225 
226 char *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 
237 void 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 
250 void
251 if6_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 
304 struct in6_multi *
305 in6_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
359 void
360 in6_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 
391 void
392 in6_multilist(mc)
393 	struct in6_multi *mc;
394 {
395 	while (mc)
396 		mc = in6_multientry(mc);
397 }
398 
399 #endif /* INET6 */
400 
401 void
402 if_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 
455 void
456 in_multilist(mc)
457 	struct in_multi *mc;
458 {
459 	while (mc)
460 		mc = in_multientry(mc);
461 }
462 
463 struct in_multi *
464 in_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
525 void
526 in_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