1116742Ssam/*	$KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $	*/
2116904Ssam
3139530Ssam/*
4116742Ssam * Copyright (c) 2007-2009 Bruce Simpson.
5116742Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6116742Ssam * All rights reserved.
7116742Ssam *
8116742Ssam * Redistribution and use in source and binary forms, with or without
9116742Ssam * modification, are permitted provided that the following conditions
10116742Ssam * are met:
11116742Ssam * 1. Redistributions of source code must retain the above copyright
12116742Ssam *    notice, this list of conditions and the following disclaimer.
13116742Ssam * 2. Redistributions in binary form must reproduce the above copyright
14116904Ssam *    notice, this list of conditions and the following disclaimer in the
15116904Ssam *    documentation and/or other materials provided with the distribution.
16116742Ssam * 3. Neither the name of the project nor the names of its contributors
17116904Ssam *    may be used to endorse or promote products derived from this software
18116904Ssam *    without specific prior written permission.
19116904Ssam *
20116742Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23116904Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26116904Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27116904Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28116904Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29116904Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30116904Ssam * SUCH DAMAGE.
31116904Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD$");
35116742Ssam
36116742Ssam#include <sys/types.h>
37116742Ssam#include <sys/param.h>
38116742Ssam#include <sys/sysctl.h>
39116742Ssam#include <sys/socket.h>
40116742Ssam#include <sys/queue.h>
41116742Ssam#include <sys/tree.h>
42117811Ssam
43117811Ssam#include <net/if.h>
44117811Ssam#include <net/if_var.h>
45117811Ssam#include <net/if_types.h>
46117811Ssam#include <net/if_dl.h>
47116742Ssam#include <net/route.h>
48117811Ssam
49116742Ssam#include <netinet/in.h>
50116742Ssam#include <netinet/in_var.h>
51116742Ssam#include <netinet/in_systm.h>
52116742Ssam#include <netinet/ip.h>
53116742Ssam#include <netinet/igmp.h>
54139502Ssam#define KERNEL
55116742Ssam# include <netinet/if_ether.h>
56144618Ssam#undef KERNEL
57144618Ssam#define _KERNEL
58116742Ssam#define SYSCTL_DECL(x)
59119150Ssam# include <netinet/igmp_var.h>
60144618Ssam#undef SYSCTL_DECL
61119150Ssam#undef _KERNEL
62148299Ssam
63148299Ssam#ifdef INET6
64144618Ssam#include <netinet/icmp6.h>
65119150Ssam#define _KERNEL
66144618Ssam# include <netinet6/mld6_var.h>
67144618Ssam#undef _KERNEL
68116742Ssam#endif /* INET6 */
69144618Ssam
70138568Ssam#include <arpa/inet.h>
71144618Ssam#include <netdb.h>
72138568Ssam
73144618Ssam#include <stddef.h>
74138568Ssam#include <stdarg.h>
75138568Ssam#include <stdint.h>
76144618Ssam#include <stdio.h>
77144618Ssam#include <stdlib.h>
78144618Ssam#include <string.h>
79138568Ssam
80144618Ssam#include <ctype.h>
81138568Ssam#include <err.h>
82138568Ssam#include <errno.h>
83138568Ssam#include <fcntl.h>
84138568Ssam#include <kvm.h>
85138568Ssam#include <limits.h>
86139525Ssam#include <ifaddrs.h>
87138568Ssam#include <nlist.h>
88138568Ssam#include <sysexits.h>
89138568Ssam#include <unistd.h>
90138568Ssam
91138568Ssam/* XXX: This file currently assumes INET and KVM support in the base system. */
92138568Ssam#ifndef INET
93138568Ssam#define INET
94138568Ssam#endif
95138568Ssam
96138568Ssamextern void	printb(const char *, unsigned int, const char *);
97138568Ssam
98138568Ssamunion sockunion {
99138568Ssam	struct sockaddr_storage	ss;
100138568Ssam	struct sockaddr		sa;
101138568Ssam	struct sockaddr_dl	sdl;
102138568Ssam#ifdef INET
103138568Ssam	struct sockaddr_in	sin;
104138568Ssam#endif
105139525Ssam#ifdef INET6
106138568Ssam	struct sockaddr_in6	sin6;
107138568Ssam#endif
108138568Ssam};
109138568Ssamtypedef union sockunion sockunion_t;
110138568Ssam
111138568Ssamuint32_t	ifindex = 0;
112138568Ssamint		af = AF_UNSPEC;
113138568Ssam#ifdef WITH_KVM
114138568Ssamint		Kflag = 0;
115138568Ssam#endif
116138568Ssamint		vflag = 0;
117138568Ssam
118138568Ssam#define	sa_equal(a1, a2)	\
119138568Ssam	(bcmp((a1), (a2), ((a1))->sa_len) == 0)
120138568Ssam
121138568Ssam#define	sa_dl_equal(a1, a2)	\
122138568Ssam	((((struct sockaddr_dl *)(a1))->sdl_len ==			\
123138568Ssam	 ((struct sockaddr_dl *)(a2))->sdl_len) &&			\
124138568Ssam	 (bcmp(LLADDR((struct sockaddr_dl *)(a1)),			\
125138568Ssam	       LLADDR((struct sockaddr_dl *)(a2)),			\
126138568Ssam	       ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
127138568Ssam
128138568Ssam/*
129138568Ssam * Most of the code in this utility is to support the use of KVM for
130138568Ssam * post-mortem debugging of the multicast code.
131138568Ssam */
132138568Ssam#ifdef WITH_KVM
133138568Ssam
134138568Ssam#ifdef INET
135144618Ssamstatic void		if_addrlist(struct ifaddr *);
136138568Ssamstatic struct in_multi *
137144618Ssam			in_multientry(struct in_multi *);
138144618Ssam#endif /* INET */
139138568Ssam
140138568Ssam#ifdef INET6
141138568Ssamstatic void		if6_addrlist(struct ifaddr *);
142138568Ssamstatic struct in6_multi *
143138568Ssam			in6_multientry(struct in6_multi *);
144138568Ssam#endif /* INET6 */
145138568Ssam
146138568Ssamstatic void		kread(u_long, void *, int);
147138568Ssamstatic void		ll_addrlist(struct ifaddr *);
148138568Ssam
149138568Ssamstatic int		ifmcstat_kvm(const char *kernel, const char *core);
150138568Ssam
151138568Ssam#define	KREAD(addr, buf, type) \
152138568Ssam	kread((u_long)addr, (void *)buf, sizeof(type))
153138568Ssam
154138568Ssamkvm_t	*kvmd;
155138568Ssamstruct	nlist nl[] = {
156138568Ssam	{ "_ifnet", 0, 0, 0, 0, },
157138568Ssam	{ "", 0, 0, 0, 0, },
158138568Ssam};
159144618Ssam#define	N_IFNET	0
160144618Ssam
161144618Ssam#endif /* WITH_KVM */
162138568Ssam
163138568Ssamstatic int		ifmcstat_getifmaddrs(void);
164138568Ssam#ifdef INET
165138568Ssamstatic void		in_ifinfo(struct igmp_ifinfo *);
166138568Ssamstatic const char *	inm_mode(u_int mode);
167138568Ssam#endif
168148299Ssam#ifdef INET6
169138568Ssamstatic void		in6_ifinfo(struct mld_ifinfo *);
170138568Ssamstatic const char *	inet6_n2a(struct in6_addr *, uint32_t);
171138568Ssam#endif
172138568Ssamint			main(int, char **);
173138568Ssam
174138568Ssamstatic void
175138568Ssamusage()
176138568Ssam{
177138568Ssam
178138568Ssam	fprintf(stderr,
179138568Ssam	    "usage: ifmcstat [-i interface] [-f address family]"
180138568Ssam	    " [-v]"
181140762Ssam#ifdef WITH_KVM
182140762Ssam	    " [-K] [-M core] [-N system]"
183138568Ssam#endif
184138568Ssam	    "\n");
185138568Ssam	exit(EX_USAGE);
186138568Ssam}
187138568Ssam
188138568Ssamstatic const char *options = "i:f:vM:N:"
189138568Ssam#ifdef WITH_KVM
190138568Ssam	"K"
191138568Ssam#endif
192138568Ssam	;
193138568Ssam
194138568Ssamint
195138568Ssammain(int argc, char **argv)
196138568Ssam{
197138568Ssam	int c, error;
198138568Ssam#ifdef WITH_KVM
199138568Ssam	const char *kernel = NULL;
200138568Ssam	const char *core = NULL;
201138568Ssam#endif
202138568Ssam
203138568Ssam	while ((c = getopt(argc, argv, options)) != -1) {
204138568Ssam		switch (c) {
205144618Ssam		case 'i':
206144618Ssam			if ((ifindex = if_nametoindex(optarg)) == 0) {
207144618Ssam				fprintf(stderr, "%s: unknown interface\n",
208138568Ssam				    optarg);
209117811Ssam				exit(EX_NOHOST);
210117811Ssam			}
211144618Ssam			break;
212144618Ssam
213117811Ssam		case 'f':
214117811Ssam#ifdef INET
215138568Ssam			if (strcmp(optarg, "inet") == 0) {
216138568Ssam				af = AF_INET;
217138568Ssam				break;
218138568Ssam			}
219138568Ssam#endif
220138568Ssam#ifdef INET6
221138568Ssam			if (strcmp(optarg, "inet6") == 0) {
222138568Ssam				af = AF_INET6;
223138568Ssam				break;
224138568Ssam			}
225138568Ssam#endif
226138568Ssam			if (strcmp(optarg, "link") == 0) {
227138568Ssam				af = AF_LINK;
228138568Ssam				break;
229138568Ssam			}
230144618Ssam			fprintf(stderr, "%s: unknown address family\n", optarg);
231138568Ssam			exit(EX_USAGE);
232144618Ssam			/*NOTREACHED*/
233138568Ssam			break;
234138568Ssam
235138568Ssam#ifdef WITH_KVM
236138568Ssam		case 'K':
237138568Ssam			++Kflag;
238138568Ssam			break;
239138568Ssam#endif
240138568Ssam
241144618Ssam		case 'v':
242138568Ssam			++vflag;
243144618Ssam			break;
244138568Ssam
245144618Ssam#ifdef WITH_KVM
246116742Ssam		case 'M':
247			core = strdup(optarg);
248			break;
249
250		case 'N':
251			kernel = strdup(optarg);
252			break;
253#endif
254
255		default:
256			usage();
257			break;
258			/*NOTREACHED*/
259		}
260	}
261
262	if (af == AF_LINK && vflag)
263		usage();
264
265#ifdef WITH_KVM
266	if (Kflag)
267		error = ifmcstat_kvm(kernel, core);
268	/*
269	 * If KVM failed, and user did not explicitly specify a core file,
270	 * or force KVM backend to be disabled, try the sysctl backend.
271	 */
272	if (!Kflag || (error != 0 && (core == NULL && kernel == NULL)))
273#endif
274	error = ifmcstat_getifmaddrs();
275	if (error != 0)
276		exit(EX_OSERR);
277
278	exit(EX_OK);
279	/*NOTREACHED*/
280}
281
282#ifdef INET
283
284static void
285in_ifinfo(struct igmp_ifinfo *igi)
286{
287
288	printf("\t");
289	switch (igi->igi_version) {
290	case IGMP_VERSION_1:
291	case IGMP_VERSION_2:
292	case IGMP_VERSION_3:
293		printf("igmpv%d", igi->igi_version);
294		break;
295	default:
296		printf("igmpv?(%d)", igi->igi_version);
297		break;
298	}
299	if (igi->igi_flags)
300		printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
301	if (igi->igi_version == IGMP_VERSION_3) {
302		printf(" rv %u qi %u qri %u uri %u",
303		    igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
304	}
305	if (vflag >= 2) {
306		printf(" v1timer %u v2timer %u v3timer %u",
307		    igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
308	}
309	printf("\n");
310}
311
312static const char *inm_modes[] = {
313	"undefined",
314	"include",
315	"exclude",
316};
317
318static const char *
319inm_mode(u_int mode)
320{
321
322	if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
323		return (inm_modes[mode]);
324	return (NULL);
325}
326
327#endif /* INET */
328
329#ifdef WITH_KVM
330
331static int
332ifmcstat_kvm(const char *kernel, const char *core)
333{
334	char	buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
335	struct	ifnet	*ifp, *nifp, ifnet;
336
337	if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
338	    NULL) {
339		perror("kvm_openfiles");
340		return (-1);
341	}
342	if (kvm_nlist(kvmd, nl) < 0) {
343		perror("kvm_nlist");
344		return (-1);
345	}
346	if (nl[N_IFNET].n_value == 0) {
347		printf("symbol %s not found\n", nl[N_IFNET].n_name);
348		return (-1);
349	}
350	KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
351	while (ifp) {
352		KREAD(ifp, &ifnet, struct ifnet);
353		nifp = ifnet.if_link.tqe_next;
354		if (ifindex && ifindex != ifnet.if_index)
355			goto next;
356
357		printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
358#ifdef INET
359		if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
360#endif
361#ifdef INET6
362		if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
363#endif
364		if (vflag)
365			ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
366	next:
367		ifp = nifp;
368	}
369
370	return (0);
371}
372
373static void
374kread(u_long addr, void *buf, int len)
375{
376
377	if (kvm_read(kvmd, addr, buf, len) != len) {
378		perror("kvm_read");
379		exit(EX_OSERR);
380	}
381}
382
383static void
384ll_addrlist(struct ifaddr *ifap)
385{
386	char addrbuf[NI_MAXHOST];
387	struct ifaddr ifa;
388	struct sockaddr sa;
389	struct sockaddr_dl sdl;
390	struct ifaddr *ifap0;
391
392	if (af && af != AF_LINK)
393		return;
394
395	ifap0 = ifap;
396	while (ifap) {
397		KREAD(ifap, &ifa, struct ifaddr);
398		if (ifa.ifa_addr == NULL)
399			goto nextifap;
400		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
401		if (sa.sa_family != PF_LINK)
402			goto nextifap;
403		KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl);
404		if (sdl.sdl_alen == 0)
405			goto nextifap;
406		addrbuf[0] = '\0';
407		getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len,
408		    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
409		printf("\tlink %s\n", addrbuf);
410	nextifap:
411		ifap = ifa.ifa_link.tqe_next;
412	}
413	if (ifap0) {
414		struct ifnet ifnet;
415		struct ifmultiaddr ifm, *ifmp = 0;
416
417		KREAD(ifap0, &ifa, struct ifaddr);
418		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
419		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
420			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
421		while (ifmp) {
422			KREAD(ifmp, &ifm, struct ifmultiaddr);
423			if (ifm.ifma_addr == NULL)
424				goto nextmulti;
425			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
426			if (sa.sa_family != AF_LINK)
427				goto nextmulti;
428			KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl);
429			addrbuf[0] = '\0';
430			getnameinfo((struct sockaddr *)&sdl,
431			    sdl.sdl_len, addrbuf, sizeof(addrbuf),
432			    NULL, 0, NI_NUMERICHOST);
433			printf("\t\tgroup %s refcnt %d\n",
434			    addrbuf, ifm.ifma_refcount);
435		nextmulti:
436			ifmp = TAILQ_NEXT(&ifm, ifma_link);
437		}
438	}
439}
440
441#ifdef INET6
442
443static void
444if6_addrlist(struct ifaddr *ifap)
445{
446	struct ifnet ifnet;
447	struct ifaddr ifa;
448	struct sockaddr sa;
449	struct in6_ifaddr if6a;
450	struct ifaddr *ifap0;
451
452	if (af && af != AF_INET6)
453		return;
454	ifap0 = ifap;
455	while (ifap) {
456		KREAD(ifap, &ifa, struct ifaddr);
457		if (ifa.ifa_addr == NULL)
458			goto nextifap;
459		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
460		if (sa.sa_family != PF_INET6)
461			goto nextifap;
462		KREAD(ifap, &if6a, struct in6_ifaddr);
463		printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr,
464		    if6a.ia_addr.sin6_scope_id));
465		/*
466		 * Print per-link MLD information, if available.
467		 */
468		if (ifa.ifa_ifp != NULL) {
469			struct in6_ifextra ie;
470			struct mld_ifinfo mli;
471
472			KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
473			KREAD(ifnet.if_afdata[AF_INET6], &ie,
474			    struct in6_ifextra);
475			if (ie.mld_ifinfo != NULL) {
476				KREAD(ie.mld_ifinfo, &mli, struct mld_ifinfo);
477				in6_ifinfo(&mli);
478			}
479		}
480	nextifap:
481		ifap = ifa.ifa_link.tqe_next;
482	}
483	if (ifap0) {
484		struct ifnet ifnet;
485		struct ifmultiaddr ifm, *ifmp = 0;
486		struct sockaddr_dl sdl;
487
488		KREAD(ifap0, &ifa, struct ifaddr);
489		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
490		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
491			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
492		while (ifmp) {
493			KREAD(ifmp, &ifm, struct ifmultiaddr);
494			if (ifm.ifma_addr == NULL)
495				goto nextmulti;
496			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
497			if (sa.sa_family != AF_INET6)
498				goto nextmulti;
499			(void)in6_multientry((struct in6_multi *)
500					     ifm.ifma_protospec);
501			if (ifm.ifma_lladdr == 0)
502				goto nextmulti;
503			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
504			printf("\t\t\tmcast-macaddr %s refcnt %d\n",
505			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
506			       ifm.ifma_refcount);
507		    nextmulti:
508			ifmp = TAILQ_NEXT(&ifm, ifma_link);
509		}
510	}
511}
512
513static struct in6_multi *
514in6_multientry(struct in6_multi *mc)
515{
516	struct in6_multi multi;
517
518	KREAD(mc, &multi, struct in6_multi);
519	printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr, 0));
520	printf(" refcnt %u\n", multi.in6m_refcount);
521
522	return (multi.in6m_entry.le_next);
523}
524
525#endif /* INET6 */
526
527#ifdef INET
528
529static void
530if_addrlist(struct ifaddr *ifap)
531{
532	struct ifaddr ifa;
533	struct ifnet ifnet;
534	struct sockaddr sa;
535	struct in_ifaddr ia;
536	struct ifaddr *ifap0;
537
538	if (af && af != AF_INET)
539		return;
540	ifap0 = ifap;
541	while (ifap) {
542		KREAD(ifap, &ifa, struct ifaddr);
543		if (ifa.ifa_addr == NULL)
544			goto nextifap;
545		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
546		if (sa.sa_family != PF_INET)
547			goto nextifap;
548		KREAD(ifap, &ia, struct in_ifaddr);
549		printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
550		/*
551		 * Print per-link IGMP information, if available.
552		 */
553		if (ifa.ifa_ifp != NULL) {
554			struct in_ifinfo ii;
555			struct igmp_ifinfo igi;
556
557			KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
558			KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo);
559			if (ii.ii_igmp != NULL) {
560				KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo);
561				in_ifinfo(&igi);
562			}
563		}
564	nextifap:
565		ifap = ifa.ifa_link.tqe_next;
566	}
567	if (ifap0) {
568		struct ifmultiaddr ifm, *ifmp = 0;
569		struct sockaddr_dl sdl;
570
571		KREAD(ifap0, &ifa, struct ifaddr);
572		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
573		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
574			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
575		while (ifmp) {
576			KREAD(ifmp, &ifm, struct ifmultiaddr);
577			if (ifm.ifma_addr == NULL)
578				goto nextmulti;
579			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
580			if (sa.sa_family != AF_INET)
581				goto nextmulti;
582			(void)in_multientry((struct in_multi *)
583					    ifm.ifma_protospec);
584			if (ifm.ifma_lladdr == 0)
585				goto nextmulti;
586			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
587			printf("\t\t\tmcast-macaddr %s refcnt %d\n",
588			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
589			       ifm.ifma_refcount);
590		    nextmulti:
591			ifmp = TAILQ_NEXT(&ifm, ifma_link);
592		}
593	}
594}
595
596static const char *inm_states[] = {
597	"not-member",
598	"silent",
599	"idle",
600	"lazy",
601	"sleeping",
602	"awakening",
603	"query-pending",
604	"sg-query-pending",
605	"leaving"
606};
607
608static const char *
609inm_state(u_int state)
610{
611
612	if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
613		return (inm_states[state]);
614	return (NULL);
615}
616
617#if 0
618static struct ip_msource *
619ims_min_kvm(struct in_multi *pinm)
620{
621	struct ip_msource ims0;
622	struct ip_msource *tmp, *parent;
623
624	parent = NULL;
625	tmp = RB_ROOT(&pinm->inm_srcs);
626	while (tmp) {
627		parent = tmp;
628		KREAD(tmp, &ims0, struct ip_msource);
629		tmp = RB_LEFT(&ims0, ims_link);
630	}
631	return (parent); /* kva */
632}
633
634/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */
635static struct ip_msource *
636ims_next_kvm(struct ip_msource *ims)
637{
638	struct ip_msource ims0, ims1;
639	struct ip_msource *tmp;
640
641	KREAD(ims, &ims0, struct ip_msource);
642	if (RB_RIGHT(&ims0, ims_link)) {
643		ims = RB_RIGHT(&ims0, ims_link);
644		KREAD(ims, &ims1, struct ip_msource);
645		while ((tmp = RB_LEFT(&ims1, ims_link))) {
646			KREAD(tmp, &ims0, struct ip_msource);
647			ims = RB_LEFT(&ims0, ims_link);
648		}
649	} else {
650		tmp = RB_PARENT(&ims0, ims_link);
651		if (tmp) {
652			KREAD(tmp, &ims1, struct ip_msource);
653			if (ims == RB_LEFT(&ims1, ims_link))
654				ims = tmp;
655		} else {
656			while ((tmp = RB_PARENT(&ims0, ims_link))) {
657				KREAD(tmp, &ims1, struct ip_msource);
658				if (ims == RB_RIGHT(&ims1, ims_link)) {
659					ims = tmp;
660					KREAD(ims, &ims0, struct ip_msource);
661				} else
662					break;
663			}
664			ims = RB_PARENT(&ims0, ims_link);
665		}
666	}
667	return (ims); /* kva */
668}
669
670static void
671inm_print_sources_kvm(struct in_multi *pinm)
672{
673	struct ip_msource ims0;
674	struct ip_msource *ims;
675	struct in_addr src;
676	int cnt;
677	uint8_t fmode;
678
679	cnt = 0;
680	fmode = pinm->inm_st[1].iss_fmode;
681	if (fmode == MCAST_UNDEFINED)
682		return;
683	for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) {
684		if (cnt == 0)
685			printf(" srcs ");
686		KREAD(ims, &ims0, struct ip_msource);
687		/* Only print sources in-mode at t1. */
688		if (fmode != ims_get_mode(pinm, ims, 1))
689			continue;
690		src.s_addr = htonl(ims0.ims_haddr);
691		printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src));
692	}
693}
694#endif
695
696static struct in_multi *
697in_multientry(struct in_multi *pinm)
698{
699	struct in_multi inm;
700	const char *state, *mode;
701
702	KREAD(pinm, &inm, struct in_multi);
703	printf("\t\tgroup %s", inet_ntoa(inm.inm_addr));
704	printf(" refcnt %u", inm.inm_refcount);
705
706	state = inm_state(inm.inm_state);
707	if (state)
708		printf(" state %s", state);
709	else
710		printf(" state (%d)", inm.inm_state);
711
712	mode = inm_mode(inm.inm_st[1].iss_fmode);
713	if (mode)
714		printf(" mode %s", mode);
715	else
716		printf(" mode (%d)", inm.inm_st[1].iss_fmode);
717
718	if (vflag >= 2) {
719		printf(" asm %u ex %u in %u rec %u",
720		    (u_int)inm.inm_st[1].iss_asm,
721		    (u_int)inm.inm_st[1].iss_ex,
722		    (u_int)inm.inm_st[1].iss_in,
723		    (u_int)inm.inm_st[1].iss_rec);
724	}
725
726#if 0
727	/* Buggy. */
728	if (vflag)
729		inm_print_sources_kvm(&inm);
730#endif
731
732	printf("\n");
733	return (NULL);
734}
735
736#endif /* INET */
737
738#endif /* WITH_KVM */
739
740#ifdef INET6
741
742static void
743in6_ifinfo(struct mld_ifinfo *mli)
744{
745
746	printf("\t");
747	switch (mli->mli_version) {
748	case MLD_VERSION_1:
749	case MLD_VERSION_2:
750		printf("mldv%d", mli->mli_version);
751		break;
752	default:
753		printf("mldv?(%d)", mli->mli_version);
754		break;
755	}
756	if (mli->mli_flags)
757		printb(" flags", mli->mli_flags, "\020\1SILENT\2USEALLOW");
758	if (mli->mli_version == MLD_VERSION_2) {
759		printf(" rv %u qi %u qri %u uri %u",
760		    mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
761	}
762	if (vflag >= 2) {
763		printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
764		   mli->mli_v2_timer);
765	}
766	printf("\n");
767}
768
769static const char *
770inet6_n2a(struct in6_addr *p, uint32_t scope_id)
771{
772	static char buf[NI_MAXHOST];
773	struct sockaddr_in6 sin6;
774	const int niflags = NI_NUMERICHOST;
775
776	memset(&sin6, 0, sizeof(sin6));
777	sin6.sin6_family = AF_INET6;
778	sin6.sin6_len = sizeof(struct sockaddr_in6);
779	sin6.sin6_addr = *p;
780	sin6.sin6_scope_id = scope_id;
781	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
782	    buf, sizeof(buf), NULL, 0, niflags) == 0) {
783		return (buf);
784	} else {
785		return ("(invalid)");
786	}
787}
788#endif /* INET6 */
789
790#ifdef INET
791/*
792 * Retrieve per-group source filter mode and lists via sysctl.
793 */
794static void
795inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
796{
797#define	MAX_SYSCTL_TRY	5
798	int mib[7];
799	int ntry = 0;
800	size_t mibsize;
801	size_t len;
802	size_t needed;
803	size_t cnt;
804	int i;
805	char *buf;
806	struct in_addr *pina;
807	uint32_t *p;
808	uint32_t fmode;
809	const char *modestr;
810
811	mibsize = sizeof(mib) / sizeof(mib[0]);
812	if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
813		perror("sysctlnametomib");
814		return;
815	}
816
817	needed = 0;
818	mib[5] = ifindex;
819	mib[6] = gina.s_addr;	/* 32 bits wide */
820	mibsize = sizeof(mib) / sizeof(mib[0]);
821	do {
822		if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
823			perror("sysctl net.inet.ip.mcast.filters");
824			return;
825		}
826		if ((buf = malloc(needed)) == NULL) {
827			perror("malloc");
828			return;
829		}
830		if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
831			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
832				perror("sysctl");
833				goto out_free;
834			}
835			free(buf);
836			buf = NULL;
837		}
838	} while (buf == NULL);
839
840	len = needed;
841	if (len < sizeof(uint32_t)) {
842		perror("sysctl");
843		goto out_free;
844	}
845
846	p = (uint32_t *)buf;
847	fmode = *p++;
848	len -= sizeof(uint32_t);
849
850	modestr = inm_mode(fmode);
851	if (modestr)
852		printf(" mode %s", modestr);
853	else
854		printf(" mode (%u)", fmode);
855
856	if (vflag == 0)
857		goto out_free;
858
859	cnt = len / sizeof(struct in_addr);
860	pina = (struct in_addr *)p;
861
862	for (i = 0; i < cnt; i++) {
863		if (i == 0)
864			printf(" srcs ");
865		fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
866		    inet_ntoa(*pina++));
867		len -= sizeof(struct in_addr);
868	}
869	if (len > 0) {
870		fprintf(stderr, "warning: %u trailing bytes from %s\n",
871		    (unsigned int)len, "net.inet.ip.mcast.filters");
872	}
873
874out_free:
875	free(buf);
876#undef	MAX_SYSCTL_TRY
877}
878
879#endif /* INET */
880
881#ifdef INET6
882/*
883 * Retrieve MLD per-group source filter mode and lists via sysctl.
884 *
885 * Note: The 128-bit IPv6 group address needs to be segmented into
886 * 32-bit pieces for marshaling to sysctl. So the MIB name ends
887 * up looking like this:
888 *  a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
889 * Assumes that pgroup originated from the kernel, so its components
890 * are already in network-byte order.
891 */
892static void
893in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
894{
895#define	MAX_SYSCTL_TRY	5
896	char addrbuf[INET6_ADDRSTRLEN];
897	int mib[10];
898	int ntry = 0;
899	int *pi;
900	size_t mibsize;
901	size_t len;
902	size_t needed;
903	size_t cnt;
904	int i;
905	char *buf;
906	struct in6_addr *pina;
907	uint32_t *p;
908	uint32_t fmode;
909	const char *modestr;
910
911	mibsize = sizeof(mib) / sizeof(mib[0]);
912	if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
913	    &mibsize) == -1) {
914		perror("sysctlnametomib");
915		return;
916	}
917
918	needed = 0;
919	mib[5] = ifindex;
920	pi = (int *)pgroup;
921	for (i = 0; i < 4; i++)
922		mib[6 + i] = *pi++;
923
924	mibsize = sizeof(mib) / sizeof(mib[0]);
925	do {
926		if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
927			perror("sysctl net.inet6.ip6.mcast.filters");
928			return;
929		}
930		if ((buf = malloc(needed)) == NULL) {
931			perror("malloc");
932			return;
933		}
934		if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
935			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
936				perror("sysctl");
937				goto out_free;
938			}
939			free(buf);
940			buf = NULL;
941		}
942	} while (buf == NULL);
943
944	len = needed;
945	if (len < sizeof(uint32_t)) {
946		perror("sysctl");
947		goto out_free;
948	}
949
950	p = (uint32_t *)buf;
951	fmode = *p++;
952	len -= sizeof(uint32_t);
953
954	modestr = inm_mode(fmode);
955	if (modestr)
956		printf(" mode %s", modestr);
957	else
958		printf(" mode (%u)", fmode);
959
960	if (vflag == 0)
961		goto out_free;
962
963	cnt = len / sizeof(struct in6_addr);
964	pina = (struct in6_addr *)p;
965
966	for (i = 0; i < cnt; i++) {
967		if (i == 0)
968			printf(" srcs ");
969		inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
970		    INET6_ADDRSTRLEN);
971		fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
972		len -= sizeof(struct in6_addr);
973	}
974	if (len > 0) {
975		fprintf(stderr, "warning: %u trailing bytes from %s\n",
976		    (unsigned int)len, "net.inet6.ip6.mcast.filters");
977	}
978
979out_free:
980	free(buf);
981#undef	MAX_SYSCTL_TRY
982}
983#endif /* INET6 */
984
985static int
986ifmcstat_getifmaddrs(void)
987{
988	char			 thisifname[IFNAMSIZ];
989	char			 addrbuf[NI_MAXHOST];
990	struct ifaddrs		*ifap, *ifa;
991	struct ifmaddrs		*ifmap, *ifma;
992	sockunion_t		 lastifasa;
993	sockunion_t		*psa, *pgsa, *pllsa, *pifasa;
994	char			*pcolon;
995	char			*pafname;
996	uint32_t		 lastifindex, thisifindex;
997	int			 error;
998
999	error = 0;
1000	ifap = NULL;
1001	ifmap = NULL;
1002	lastifindex = 0;
1003	thisifindex = 0;
1004	lastifasa.ss.ss_family = AF_UNSPEC;
1005
1006	if (getifaddrs(&ifap) != 0) {
1007		warn("getifmaddrs");
1008		return (-1);
1009	}
1010
1011	if (getifmaddrs(&ifmap) != 0) {
1012		warn("getifmaddrs");
1013		error = -1;
1014		goto out;
1015	}
1016
1017	for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
1018		error = 0;
1019		if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
1020			continue;
1021
1022		psa = (sockunion_t *)ifma->ifma_name;
1023		if (psa->sa.sa_family != AF_LINK) {
1024			fprintf(stderr,
1025			    "WARNING: Kernel returned invalid data.\n");
1026			error = -1;
1027			break;
1028		}
1029
1030		/* Filter on interface name. */
1031		thisifindex = psa->sdl.sdl_index;
1032		if (ifindex != 0 && thisifindex != ifindex)
1033			continue;
1034
1035		/* Filter on address family. */
1036		pgsa = (sockunion_t *)ifma->ifma_addr;
1037		if (af != 0 && pgsa->sa.sa_family != af)
1038			continue;
1039
1040		strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
1041		pcolon = strchr(thisifname, ':');
1042		if (pcolon)
1043			*pcolon = '\0';
1044
1045		/* Only print the banner for the first ifmaddrs entry. */
1046		if (lastifindex == 0 || lastifindex != thisifindex) {
1047			lastifindex = thisifindex;
1048			fprintf(stdout, "%s:\n", thisifname);
1049		}
1050
1051		/*
1052		 * Currently, multicast joins only take place on the
1053		 * primary IPv4 address, and only on the link-local IPv6
1054		 * address, as per IGMPv2/3 and MLDv1/2 semantics.
1055		 * Therefore, we only look up the primary address on
1056		 * the first pass.
1057		 */
1058		pifasa = NULL;
1059		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1060			if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
1061			    (ifa->ifa_addr == NULL) ||
1062			    (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
1063				continue;
1064			/*
1065			 * For AF_INET6 only the link-local address should
1066			 * be returned. If built without IPv6 support,
1067			 * skip this address entirely.
1068			 */
1069			pifasa = (sockunion_t *)ifa->ifa_addr;
1070			if (pifasa->sa.sa_family == AF_INET6
1071#ifdef INET6
1072			    && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
1073#endif
1074			) {
1075				pifasa = NULL;
1076				continue;
1077			}
1078			break;
1079		}
1080		if (pifasa == NULL)
1081			continue;	/* primary address not found */
1082
1083		if (!vflag && pifasa->sa.sa_family == AF_LINK)
1084			continue;
1085
1086		/* Parse and print primary address, if not already printed. */
1087		if (lastifasa.ss.ss_family == AF_UNSPEC ||
1088		    ((lastifasa.ss.ss_family == AF_LINK &&
1089		      !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
1090		     !sa_equal(&lastifasa.sa, &pifasa->sa))) {
1091
1092			switch (pifasa->sa.sa_family) {
1093			case AF_INET:
1094				pafname = "inet";
1095				break;
1096			case AF_INET6:
1097				pafname = "inet6";
1098				break;
1099			case AF_LINK:
1100				pafname = "link";
1101				break;
1102			default:
1103				pafname = "unknown";
1104				break;
1105			}
1106
1107			switch (pifasa->sa.sa_family) {
1108			case AF_INET6:
1109#ifdef INET6
1110			{
1111				const char *p =
1112				    inet6_n2a(&pifasa->sin6.sin6_addr,
1113					pifasa->sin6.sin6_scope_id);
1114				strlcpy(addrbuf, p, sizeof(addrbuf));
1115				break;
1116			}
1117#else
1118			/* FALLTHROUGH */
1119#endif
1120			case AF_INET:
1121			case AF_LINK:
1122				error = getnameinfo(&pifasa->sa,
1123				    pifasa->sa.sa_len,
1124				    addrbuf, sizeof(addrbuf), NULL, 0,
1125				    NI_NUMERICHOST);
1126				if (error)
1127					perror("getnameinfo");
1128				break;
1129			default:
1130				addrbuf[0] = '\0';
1131				break;
1132			}
1133
1134			fprintf(stdout, "\t%s %s", pafname, addrbuf);
1135#ifdef INET6
1136			if (pifasa->sa.sa_family == AF_INET6 &&
1137			    pifasa->sin6.sin6_scope_id)
1138				fprintf(stdout, " scopeid 0x%x",
1139				    pifasa->sin6.sin6_scope_id);
1140#endif
1141			fprintf(stdout, "\n");
1142#ifdef INET
1143			/*
1144			 * Print per-link IGMP information, if available.
1145			 */
1146			if (pifasa->sa.sa_family == AF_INET) {
1147				struct igmp_ifinfo igi;
1148				size_t mibsize, len;
1149				int mib[5];
1150
1151				mibsize = sizeof(mib) / sizeof(mib[0]);
1152				if (sysctlnametomib("net.inet.igmp.ifinfo",
1153				    mib, &mibsize) == -1) {
1154					perror("sysctlnametomib");
1155					goto next_ifnet;
1156				}
1157				mib[mibsize] = thisifindex;
1158				len = sizeof(struct igmp_ifinfo);
1159				if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
1160				    0) == -1) {
1161					perror("sysctl net.inet.igmp.ifinfo");
1162					goto next_ifnet;
1163				}
1164				in_ifinfo(&igi);
1165			}
1166#endif /* INET */
1167#ifdef INET6
1168			/*
1169			 * Print per-link MLD information, if available.
1170			 */
1171			if (pifasa->sa.sa_family == AF_INET6) {
1172				struct mld_ifinfo mli;
1173				size_t mibsize, len;
1174				int mib[5];
1175
1176				mibsize = sizeof(mib) / sizeof(mib[0]);
1177				if (sysctlnametomib("net.inet6.mld.ifinfo",
1178				    mib, &mibsize) == -1) {
1179					perror("sysctlnametomib");
1180					goto next_ifnet;
1181				}
1182				mib[mibsize] = thisifindex;
1183				len = sizeof(struct mld_ifinfo);
1184				if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
1185				    0) == -1) {
1186					perror("sysctl net.inet6.mld.ifinfo");
1187					goto next_ifnet;
1188				}
1189				in6_ifinfo(&mli);
1190			}
1191#endif /* INET6 */
1192#if defined(INET) || defined(INET6)
1193next_ifnet:
1194#endif
1195			lastifasa = *pifasa;
1196		}
1197
1198		/* Print this group address. */
1199#ifdef INET6
1200		if (pgsa->sa.sa_family == AF_INET6) {
1201			const char *p = inet6_n2a(&pgsa->sin6.sin6_addr,
1202			    pgsa->sin6.sin6_scope_id);
1203			strlcpy(addrbuf, p, sizeof(addrbuf));
1204		} else
1205#endif
1206		{
1207			error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
1208			    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
1209			if (error)
1210				perror("getnameinfo");
1211		}
1212
1213		fprintf(stdout, "\t\tgroup %s", addrbuf);
1214#ifdef INET6
1215		if (pgsa->sa.sa_family == AF_INET6 &&
1216		    pgsa->sin6.sin6_scope_id)
1217			fprintf(stdout, " scopeid 0x%x",
1218			    pgsa->sin6.sin6_scope_id);
1219#endif
1220#ifdef INET
1221		if (pgsa->sa.sa_family == AF_INET) {
1222			inm_print_sources_sysctl(thisifindex,
1223			    pgsa->sin.sin_addr);
1224		}
1225#endif
1226#ifdef INET6
1227		if (pgsa->sa.sa_family == AF_INET6) {
1228			in6m_print_sources_sysctl(thisifindex,
1229			    &pgsa->sin6.sin6_addr);
1230		}
1231#endif
1232		fprintf(stdout, "\n");
1233
1234		/* Link-layer mapping, if present. */
1235		pllsa = (sockunion_t *)ifma->ifma_lladdr;
1236		if (pllsa != NULL) {
1237			error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
1238			    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
1239			fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
1240		}
1241	}
1242out:
1243	if (ifmap != NULL)
1244		freeifmaddrs(ifmap);
1245	if (ifap != NULL)
1246		freeifaddrs(ifap);
1247
1248	return (error);
1249}
1250