ifmcstat.c revision 188646
1/*	$KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $	*/
2
3/*
4 * Copyright (c) 2007-2009 Bruce Simpson.
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/usr.sbin/ifmcstat/ifmcstat.c 188646 2009-02-15 15:21:34Z bms $");
35
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/queue.h>
40
41#include <net/if.h>
42#include <net/if_var.h>
43#include <net/if_types.h>
44#include <net/if_dl.h>
45#include <net/route.h>
46
47#include <netinet/in.h>
48#include <netinet/in_var.h>
49#include <netinet/in_systm.h>
50#include <netinet/ip.h>
51#include <netinet/igmp.h>
52#ifdef HAVE_IGMPV3
53# include <netinet/in_msf.h>
54#endif
55#define KERNEL
56# include <netinet/if_ether.h>
57#undef KERNEL
58#define _KERNEL
59# include <sys/sysctl.h>
60# include <netinet/igmp_var.h>
61#undef _KERNEL
62
63#ifdef INET6
64#include <netinet/icmp6.h>
65#define _KERNEL
66# include <netinet6/mld6_var.h>
67#undef _KERNEL
68#endif /* INET6 */
69
70#include <arpa/inet.h>
71#include <netdb.h>
72
73#include <stddef.h>
74#include <stdarg.h>
75#include <stdlib.h>
76#include <stdint.h>
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80
81#include <ctype.h>
82#include <err.h>
83#include <fcntl.h>
84#include <kvm.h>
85#include <limits.h>
86#include <ifaddrs.h>
87#include <nlist.h>
88#include <sysexits.h>
89#include <unistd.h>
90
91/* XXX: This file currently assumes INET and KVM support in the base system. */
92#ifndef INET
93#define INET
94#endif
95
96union sockunion {
97	struct sockaddr_storage	ss;
98	struct sockaddr		sa;
99	struct sockaddr_dl	sdl;
100#ifdef INET
101	struct sockaddr_in	sin;
102#endif
103#ifdef INET6
104	struct sockaddr_in6	sin6;
105#endif
106};
107typedef union sockunion sockunion_t;
108
109uint32_t	ifindex = 0;
110int		af = AF_UNSPEC;
111int		vflag = 0;
112
113#define	sa_equal(a1, a2)	\
114	(bcmp((a1), (a2), ((a1))->sa_len) == 0)
115
116#define	sa_dl_equal(a1, a2)	\
117	((((struct sockaddr_dl *)(a1))->sdl_len ==			\
118	 ((struct sockaddr_dl *)(a2))->sdl_len) &&			\
119	 (bcmp(LLADDR((struct sockaddr_dl *)(a1)),			\
120	       LLADDR((struct sockaddr_dl *)(a2)),			\
121	       ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
122
123/*
124 * Most of the code in this utility is to support the use of KVM for
125 * post-mortem debugging of the multicast code.
126 */
127#ifdef WITH_KVM
128
129#ifdef INET
130static void		if_addrlist(struct ifaddr *);
131static struct in_multi *
132			in_multientry(struct in_multi *);
133#ifdef HAVE_IGMPV3
134static void		in_addr_slistentry(struct in_addr_slist *, char *);
135#endif
136#endif /* INET */
137
138#ifdef INET6
139static void		if6_addrlist(struct ifaddr *);
140static struct in6_multi *
141			in6_multientry(struct in6_multi *);
142#endif /* INET6 */
143
144static void		kread(u_long, void *, int);
145static void		ll_addrlist(struct ifaddr *);
146
147static int		ifmcstat_kvm(const char *kernel, const char *core);
148
149#define	KREAD(addr, buf, type) \
150	kread((u_long)addr, (void *)buf, sizeof(type))
151
152kvm_t	*kvmd;
153struct	nlist nl[] = {
154	{ "_ifnet", 0, 0, 0, 0, },
155	{ "", 0, 0, 0, 0, },
156};
157#define	N_IFNET	0
158
159#endif /* WITH_KVM */
160
161static int		ifmcstat_getifmaddrs(void);
162#ifdef INET6
163static const char *	inet6_n2a(struct in6_addr *);
164#endif
165int			main(int, char **);
166
167static void
168usage()
169{
170
171	fprintf(stderr,
172	    "usage: ifmcstat [-i interface] [-f address family]"
173	    " [-v]"
174#ifdef WITH_KVM
175	    " [-M core] [-N system]"
176#endif
177	    "\n");
178	exit(EX_USAGE);
179}
180
181int
182main(int argc, char **argv)
183{
184	int c, error;
185#ifdef WITH_KVM
186	const char *kernel = NULL;
187	const char *core = NULL;
188#endif
189
190	while ((c = getopt(argc, argv, "i:f:vM:N:")) != -1) {
191		switch (c) {
192		case 'i':
193			if ((ifindex = if_nametoindex(optarg)) == 0) {
194				fprintf(stderr, "%s: unknown interface\n",
195				    optarg);
196				exit(1);
197			}
198			break;
199
200		case 'f':
201#ifdef INET
202			if (strcmp(optarg, "inet") == 0) {
203				af = AF_INET;
204				break;
205			}
206#endif
207#ifdef INET6
208			if (strcmp(optarg, "inet6") == 0) {
209				af = AF_INET6;
210				break;
211			}
212#endif
213			if (strcmp(optarg, "link") == 0) {
214				af = AF_LINK;
215				break;
216			}
217			fprintf(stderr, "%s: unknown address family\n", optarg);
218			exit(1);
219			/*NOTREACHED*/
220			break;
221
222		case 'v':
223			vflag = 1;
224			break;
225
226#ifdef WITH_KVM
227		case 'M':
228			core = strdup(optarg);
229			break;
230
231		case 'N':
232			kernel = strdup(optarg);
233			break;
234#endif
235
236		default:
237			usage();
238			break;
239			/*NOTREACHED*/
240		}
241	}
242
243	if (af == AF_LINK && vflag)
244		usage();
245
246#ifdef WITH_KVM
247	error = ifmcstat_kvm(kernel, core);
248	/*
249	 * If KVM failed, and user did not explicitly specify a core file,
250	 * try the sysctl backend.
251	 */
252	if (error != 0 && (core == NULL && kernel == NULL))
253#endif
254	error = ifmcstat_getifmaddrs();
255	if (error != 0)
256		exit(1);
257
258	exit(0);
259	/*NOTREACHED*/
260}
261
262#ifdef WITH_KVM
263
264static int
265ifmcstat_kvm(const char *kernel, const char *core)
266{
267	char	buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
268	struct	ifnet	*ifp, *nifp, ifnet;
269
270	if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
271	    NULL) {
272		perror("kvm_openfiles");
273		return (-1);
274	}
275	if (kvm_nlist(kvmd, nl) < 0) {
276		perror("kvm_nlist");
277		return (-1);
278	}
279	if (nl[N_IFNET].n_value == 0) {
280		printf("symbol %s not found\n", nl[N_IFNET].n_name);
281		return (-1);
282	}
283	KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
284	while (ifp) {
285		KREAD(ifp, &ifnet, struct ifnet);
286		nifp = ifnet.if_link.tqe_next;
287		if (ifindex && ifindex != ifnet.if_index)
288			goto next;
289
290		printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
291#ifdef INET
292		if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
293#endif
294#ifdef INET6
295		if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
296#endif
297		if (vflag)
298			ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
299next:
300		ifp = nifp;
301	}
302
303	return (0);
304}
305
306static void
307kread(u_long addr, void *buf, int len)
308{
309
310	if (kvm_read(kvmd, addr, buf, len) != len) {
311		perror("kvm_read");
312		exit(1);
313	}
314}
315
316static void
317ll_addrlist(struct ifaddr *ifap)
318{
319	char addrbuf[NI_MAXHOST];
320	struct ifaddr ifa;
321	struct sockaddr sa;
322	struct sockaddr_dl sdl;
323	struct ifaddr *ifap0;
324	int error;
325
326	if (af && af != AF_LINK)
327		return;
328
329	ifap0 = ifap;
330	while (ifap) {
331		KREAD(ifap, &ifa, struct ifaddr);
332		if (ifa.ifa_addr == NULL)
333			goto nextifap;
334		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
335		if (sa.sa_family != PF_LINK)
336			goto nextifap;
337		KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl);
338		if (sdl.sdl_alen == 0)
339			goto nextifap;
340		addrbuf[0] = '\0';
341		error = getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len,
342		    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
343		printf("\tlink %s\n", addrbuf);
344	nextifap:
345		ifap = ifa.ifa_link.tqe_next;
346	}
347	if (ifap0) {
348		struct ifnet ifnet;
349		struct ifmultiaddr ifm, *ifmp = 0;
350
351		KREAD(ifap0, &ifa, struct ifaddr);
352		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
353		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
354			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
355		while (ifmp) {
356			KREAD(ifmp, &ifm, struct ifmultiaddr);
357			if (ifm.ifma_addr == NULL)
358				goto nextmulti;
359			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
360			if (sa.sa_family != AF_LINK)
361				goto nextmulti;
362			KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl);
363			addrbuf[0] = '\0';
364			error = getnameinfo((struct sockaddr *)&sdl,
365			    sdl.sdl_len, addrbuf, sizeof(addrbuf),
366			    NULL, 0, NI_NUMERICHOST);
367			printf("\t\tgroup %s refcnt %d\n",
368			    addrbuf, ifm.ifma_refcount);
369		nextmulti:
370			ifmp = TAILQ_NEXT(&ifm, ifma_link);
371		}
372	}
373}
374
375#ifdef INET6
376
377static void
378if6_addrlist(struct ifaddr *ifap)
379{
380	struct ifaddr ifa;
381	struct sockaddr sa;
382	struct in6_ifaddr if6a;
383	struct ifaddr *ifap0;
384
385	if (af && af != AF_INET6)
386		return;
387	ifap0 = ifap;
388	while (ifap) {
389		KREAD(ifap, &ifa, struct ifaddr);
390		if (ifa.ifa_addr == NULL)
391			goto nextifap;
392		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
393		if (sa.sa_family != PF_INET6)
394			goto nextifap;
395		KREAD(ifap, &if6a, struct in6_ifaddr);
396		printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
397	nextifap:
398		ifap = ifa.ifa_link.tqe_next;
399	}
400	if (ifap0) {
401		struct ifnet ifnet;
402		struct ifmultiaddr ifm, *ifmp = 0;
403		struct sockaddr_dl sdl;
404
405		KREAD(ifap0, &ifa, struct ifaddr);
406		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
407		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
408			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
409		while (ifmp) {
410			KREAD(ifmp, &ifm, struct ifmultiaddr);
411			if (ifm.ifma_addr == NULL)
412				goto nextmulti;
413			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
414			if (sa.sa_family != AF_INET6)
415				goto nextmulti;
416			(void)in6_multientry((struct in6_multi *)
417					     ifm.ifma_protospec);
418			if (ifm.ifma_lladdr == 0)
419				goto nextmulti;
420			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
421			printf("\t\t\tmcast-macaddr %s refcnt %d\n",
422			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
423			       ifm.ifma_refcount);
424		    nextmulti:
425			ifmp = TAILQ_NEXT(&ifm, ifma_link);
426		}
427	}
428}
429
430static struct in6_multi *
431in6_multientry(struct in6_multi *mc)
432{
433	struct in6_multi multi;
434
435	KREAD(mc, &multi, struct in6_multi);
436	printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr));
437	printf(" refcnt %u\n", multi.in6m_refcount);
438
439	return(multi.in6m_entry.le_next);
440}
441
442#endif /* INET6 */
443
444#ifdef INET
445
446static void
447if_addrlist(struct ifaddr *ifap)
448{
449	struct ifaddr ifa;
450	struct sockaddr sa;
451	struct in_ifaddr ia;
452	struct ifaddr *ifap0;
453
454	if (af && af != AF_INET)
455		return;
456	ifap0 = ifap;
457	while (ifap) {
458		KREAD(ifap, &ifa, struct ifaddr);
459		if (ifa.ifa_addr == NULL)
460			goto nextifap;
461		KREAD(ifa.ifa_addr, &sa, struct sockaddr);
462		if (sa.sa_family != PF_INET)
463			goto nextifap;
464		KREAD(ifap, &ia, struct in_ifaddr);
465		printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
466	nextifap:
467		ifap = ifa.ifa_link.tqe_next;
468	}
469	if (ifap0) {
470		struct ifnet ifnet;
471		struct ifmultiaddr ifm, *ifmp = 0;
472		struct sockaddr_dl sdl;
473
474		KREAD(ifap0, &ifa, struct ifaddr);
475		KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
476		if (TAILQ_FIRST(&ifnet.if_multiaddrs))
477			ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
478		while (ifmp) {
479			KREAD(ifmp, &ifm, struct ifmultiaddr);
480			if (ifm.ifma_addr == NULL)
481				goto nextmulti;
482			KREAD(ifm.ifma_addr, &sa, struct sockaddr);
483			if (sa.sa_family != AF_INET)
484				goto nextmulti;
485			(void)in_multientry((struct in_multi *)
486					    ifm.ifma_protospec);
487			if (ifm.ifma_lladdr == 0)
488				goto nextmulti;
489			KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
490			printf("\t\t\tmcast-macaddr %s refcnt %d\n",
491			       ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
492			       ifm.ifma_refcount);
493		    nextmulti:
494			ifmp = TAILQ_NEXT(&ifm, ifma_link);
495		}
496	}
497}
498
499static struct in_multi *
500in_multientry(struct in_multi *mc)
501{
502	struct in_multi multi;
503	struct router_info rti;
504#ifdef HAVE_IGMPV3
505	struct in_multi_source src;
506#endif
507
508	KREAD(mc, &multi, struct in_multi);
509	printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr));
510
511	if (multi.inm_rti != NULL) {
512		KREAD(multi.inm_rti, &rti, struct router_info);
513		printf("\t\t\t");
514		switch (rti.rti_type) {
515		case IGMP_V1_ROUTER:
516			printf("igmpv1");
517			break;
518		case IGMP_V2_ROUTER:
519			printf("igmpv2");
520			break;
521#ifdef HAVE_IGMPV3
522		case IGMP_V3_ROUTER:
523			printf("igmpv3");
524			break;
525#endif
526		default:
527			printf("igmpv?(%d)", rti.rti_type);
528			break;
529		}
530
531#ifdef HAVE_IGMPV3
532		if (multi.inm_source == NULL) {
533			printf("\n");
534			return (multi.inm_list.le_next);
535		}
536
537		KREAD(multi.inm_source, &src, struct in_multi_source);
538		printf(" mode=%s grpjoin=%d\n",
539		    src.ims_mode == MCAST_INCLUDE ? "include" :
540		    src.ims_mode == MCAST_EXCLUDE ? "exclude" :
541		    "???",
542		    src.ims_grpjoin);
543		in_addr_slistentry(src.ims_cur, "current");
544		in_addr_slistentry(src.ims_rec, "recorded");
545		in_addr_slistentry(src.ims_in, "included");
546		in_addr_slistentry(src.ims_ex, "excluded");
547		in_addr_slistentry(src.ims_alw, "allowed");
548		in_addr_slistentry(src.ims_blk, "blocked");
549		in_addr_slistentry(src.ims_toin, "to-include");
550		in_addr_slistentry(src.ims_ex, "to-exclude");
551#else
552		printf("\n");
553#endif
554	}
555
556	return (NULL);
557}
558
559#ifdef HAVE_IGMPV3
560static void
561in_addr_slistentry(struct in_addr_slist *ias, char *heading)
562{
563	struct in_addr_slist slist;
564	struct ias_head head;
565	struct in_addr_source src;
566
567	if (ias == NULL) {
568		printf("\t\t\t\t%s (none)\n", heading);
569		return;
570	}
571	memset(&slist, 0, sizeof(slist));
572	KREAD(ias, &slist, struct in_addr_source);
573	printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
574	if (slist.numsrc == 0) {
575		return;
576	}
577	KREAD(slist.head, &head, struct ias_head);
578
579	KREAD(head.lh_first, &src, struct in_addr_source);
580	while (1) {
581		printf("\t\t\t\t\tsource %s (ref=%d)\n",
582			inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount);
583		if (src.ias_list.le_next == NULL)
584			break;
585		KREAD(src.ias_list.le_next, &src, struct in_addr_source);
586	}
587	return;
588}
589#endif /* HAVE_IGMPV3 */
590
591#endif /* INET */
592
593#endif /* WITH_KVM */
594
595#ifdef INET6
596static const char *
597inet6_n2a(struct in6_addr *p)
598{
599	static char buf[NI_MAXHOST];
600	struct sockaddr_in6 sin6;
601	u_int32_t scopeid;
602	const int niflags = NI_NUMERICHOST;
603
604	memset(&sin6, 0, sizeof(sin6));
605	sin6.sin6_family = AF_INET6;
606	sin6.sin6_len = sizeof(struct sockaddr_in6);
607	sin6.sin6_addr = *p;
608	if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
609	    IN6_IS_ADDR_MC_NODELOCAL(p)) {
610		scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
611		if (scopeid) {
612			sin6.sin6_scope_id = scopeid;
613			sin6.sin6_addr.s6_addr[2] = 0;
614			sin6.sin6_addr.s6_addr[3] = 0;
615		}
616	}
617	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
618			buf, sizeof(buf), NULL, 0, niflags) == 0)
619		return buf;
620	else
621		return "(invalid)";
622}
623#endif /* INET6 */
624
625static int
626ifmcstat_getifmaddrs(void)
627{
628	char			 thisifname[IFNAMSIZ];
629	char			 addrbuf[NI_MAXHOST];
630	struct ifaddrs		*ifap, *ifa;
631	struct ifmaddrs		*ifmap, *ifma;
632	sockunion_t		 lastifasa;
633	sockunion_t		*psa, *pgsa, *pllsa, *pifasa;
634	char			*pcolon;
635	char			*pafname;
636	uint32_t		 lastifindex, thisifindex;
637	int			 error;
638
639	error = 0;
640	ifap = NULL;
641	ifmap = NULL;
642	lastifindex = 0;
643	thisifindex = 0;
644	lastifasa.ss.ss_family = AF_UNSPEC;
645
646	if (getifaddrs(&ifap) != 0) {
647		warn("getifmaddrs");
648		return (-1);
649	}
650
651	if (getifmaddrs(&ifmap) != 0) {
652		warn("getifmaddrs");
653		error = -1;
654		goto out;
655	}
656
657	for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
658		error = 0;
659		if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
660			continue;
661
662		psa = (sockunion_t *)ifma->ifma_name;
663		if (psa->sa.sa_family != AF_LINK) {
664			fprintf(stderr,
665			    "WARNING: Kernel returned invalid data.\n");
666			error = -1;
667			break;
668		}
669
670		/* Filter on interface name. */
671		thisifindex = psa->sdl.sdl_index;
672		if (ifindex != 0 && thisifindex != ifindex)
673			continue;
674
675		/* Filter on address family. */
676		pgsa = (sockunion_t *)ifma->ifma_addr;
677		if (af != 0 && pgsa->sa.sa_family != af)
678			continue;
679
680		strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
681		pcolon = strchr(thisifname, ':');
682		if (pcolon)
683			*pcolon = '\0';
684
685		/* Only print the banner for the first ifmaddrs entry. */
686		if (lastifindex == 0 || lastifindex != thisifindex) {
687			lastifindex = thisifindex;
688			fprintf(stdout, "%s:\n", thisifname);
689		}
690
691		/*
692		 * Currently, multicast joins only take place on the
693		 * primary IPv4 address, and only on the link-local IPv6
694		 * address, as per IGMPv2/3 and MLDv1/2 semantics.
695		 * Therefore, we only look up the primary address on
696		 * the first pass.
697		 */
698		pifasa = NULL;
699		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
700			if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
701			    (ifa->ifa_addr == NULL) ||
702			    (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
703				continue;
704			/*
705			 * For AF_INET6 only the link-local address should
706			 * be returned. If built without IPv6 support,
707			 * skip this address entirely.
708			 */
709			pifasa = (sockunion_t *)ifa->ifa_addr;
710			if (pifasa->sa.sa_family == AF_INET6
711#ifdef INET6
712			    && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
713#endif
714			) {
715				pifasa = NULL;
716				continue;
717			}
718			break;
719		}
720		if (pifasa == NULL)
721			continue;	/* primary address not found */
722
723		if (!vflag && pifasa->sa.sa_family == AF_LINK)
724			continue;
725
726		/* Parse and print primary address, if not already printed. */
727		if (lastifasa.ss.ss_family == AF_UNSPEC ||
728		    ((lastifasa.ss.ss_family == AF_LINK &&
729		      !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
730		     !sa_equal(&lastifasa.sa, &pifasa->sa))) {
731
732			switch (pifasa->sa.sa_family) {
733			case AF_INET:
734				pafname = "inet";
735				break;
736			case AF_INET6:
737				pafname = "inet6";
738				break;
739			case AF_LINK:
740				pafname = "link";
741				break;
742			default:
743				pafname = "unknown";
744				break;
745			}
746
747			switch (pifasa->sa.sa_family) {
748			case AF_INET6:
749#ifdef INET6
750			{
751				const char *p =
752				    inet6_n2a(&pifasa->sin6.sin6_addr);
753				strlcpy(addrbuf, p, sizeof(addrbuf));
754				break;
755			}
756#else
757			/* FALLTHROUGH */
758#endif
759			case AF_INET:
760			case AF_LINK:
761				error = getnameinfo(&pifasa->sa,
762				    pifasa->sa.sa_len,
763				    addrbuf, sizeof(addrbuf), NULL, 0,
764				    NI_NUMERICHOST);
765				if (error)
766					perror("getnameinfo");
767				break;
768			default:
769				addrbuf[0] = '\0';
770				break;
771			}
772
773			fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
774			lastifasa = *pifasa;
775		}
776
777		/* Print this group address. */
778#ifdef INET6
779		if (pgsa->sa.sa_family == AF_INET6) {
780			const char *p = inet6_n2a(&pgsa->sin6.sin6_addr);
781			strlcpy(addrbuf, p, sizeof(addrbuf));
782		} else
783#endif
784		{
785			error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
786			    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
787			if (error)
788				perror("getnameinfo");
789		}
790
791		fprintf(stdout, "\t\tgroup %s\n", addrbuf);
792
793		/* Link-layer mapping, if present. */
794		pllsa = (sockunion_t *)ifma->ifma_lladdr;
795		if (pllsa != NULL) {
796			error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
797			    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
798			fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
799		}
800	}
801out:
802	if (ifmap != NULL)
803		freeifmaddrs(ifmap);
804	if (ifap != NULL)
805		freeifaddrs(ifap);
806
807	return (error);
808}
809