xref: /freebsd/usr.sbin/ifmcstat/ifmcstat.c (revision b3aaa0cc21c63d388230c7ef2a80abd631ff20d5)
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$");
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 
96 union 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 };
107 typedef union sockunion sockunion_t;
108 
109 uint32_t	ifindex = 0;
110 int		af = AF_UNSPEC;
111 int		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
130 static void		if_addrlist(struct ifaddr *);
131 static struct in_multi *
132 			in_multientry(struct in_multi *);
133 #ifdef HAVE_IGMPV3
134 static void		in_addr_slistentry(struct in_addr_slist *, char *);
135 #endif
136 #endif /* INET */
137 
138 #ifdef INET6
139 static void		if6_addrlist(struct ifaddr *);
140 static struct in6_multi *
141 			in6_multientry(struct in6_multi *);
142 #endif /* INET6 */
143 
144 static void		kread(u_long, void *, int);
145 static void		ll_addrlist(struct ifaddr *);
146 
147 static 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 
152 kvm_t	*kvmd;
153 struct	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 
161 static int		ifmcstat_getifmaddrs(void);
162 #ifdef INET6
163 static const char *	inet6_n2a(struct in6_addr *);
164 #endif
165 int			main(int, char **);
166 
167 static void
168 usage()
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 
181 int
182 main(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(EX_NOHOST);
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(EX_USAGE);
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(EX_OSERR);
257 
258 	exit(EX_OK);
259 	/*NOTREACHED*/
260 }
261 
262 #ifdef WITH_KVM
263 
264 static int
265 ifmcstat_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));
299 	next:
300 		ifp = nifp;
301 	}
302 
303 	return (0);
304 }
305 
306 static void
307 kread(u_long addr, void *buf, int len)
308 {
309 
310 	if (kvm_read(kvmd, addr, buf, len) != len) {
311 		perror("kvm_read");
312 		exit(EX_OSERR);
313 	}
314 }
315 
316 static void
317 ll_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 
377 static void
378 if6_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 
430 static struct in6_multi *
431 in6_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 
446 static void
447 if_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 
499 static struct in_multi *
500 in_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
560 static void
561 in_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 }
588 #endif /* HAVE_IGMPV3 */
589 
590 #endif /* INET */
591 
592 #endif /* WITH_KVM */
593 
594 #ifdef INET6
595 static const char *
596 inet6_n2a(struct in6_addr *p)
597 {
598 	static char buf[NI_MAXHOST];
599 	struct sockaddr_in6 sin6;
600 	u_int32_t scopeid;
601 	const int niflags = NI_NUMERICHOST;
602 
603 	memset(&sin6, 0, sizeof(sin6));
604 	sin6.sin6_family = AF_INET6;
605 	sin6.sin6_len = sizeof(struct sockaddr_in6);
606 	sin6.sin6_addr = *p;
607 	if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
608 	    IN6_IS_ADDR_MC_NODELOCAL(p)) {
609 		scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
610 		if (scopeid) {
611 			sin6.sin6_scope_id = scopeid;
612 			sin6.sin6_addr.s6_addr[2] = 0;
613 			sin6.sin6_addr.s6_addr[3] = 0;
614 		}
615 	}
616 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
617 	    buf, sizeof(buf), NULL, 0, niflags) == 0) {
618 		return (buf);
619 	} else {
620 		return ("(invalid)");
621 	}
622 }
623 #endif /* INET6 */
624 
625 static int
626 ifmcstat_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 	}
801 out:
802 	if (ifmap != NULL)
803 		freeifmaddrs(ifmap);
804 	if (ifap != NULL)
805 		freeifaddrs(ifap);
806 
807 	return (error);
808 }
809