1/*
2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org>
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 */
54
55#include <sys/cdefs.h>
56
57/*
58 * Print the running system's current multicast group memberships.
59 * As this relies on getifmaddrs(), it may not be used with a core file.
60 */
61
62#include <sys/types.h>
63#include <sys/param.h>
64#include <sys/sysctl.h>
65#include <sys/ioctl.h>
66#include <sys/socket.h>
67#include <sys/errno.h>
68
69#include <net/if.h>
70#include <net/if_var.h>
71#include <net/if_mib.h>
72#include <net/if_types.h>
73#include <net/if_dl.h>
74#include <net/route.h>
75#include <netinet/in.h>
76#include <netinet/if_ether.h>
77#include <netinet/igmp_var.h>
78#include <netinet6/mld6_var.h>
79#include <arpa/inet.h>
80#include <netdb.h>
81
82#include <ctype.h>
83#include <err.h>
84#include <ifaddrs.h>
85#include <sysexits.h>
86
87#include <stddef.h>
88#include <stdarg.h>
89#include <stdlib.h>
90#include <stdint.h>
91#include <stdio.h>
92#include <string.h>
93#include <ifaddrs.h>
94
95
96#include "netstat.h"
97
98union sockunion {
99	struct sockaddr_storage	ss;
100	struct sockaddr		sa;
101	struct sockaddr_dl	sdl;
102	struct sockaddr_in	sin;
103	struct sockaddr_in6	sin6;
104};
105typedef union sockunion sockunion_t;
106
107/*
108 * This may have been defined in <net/if.h>.  Note that if <net/if.h> is
109 * to be included it must be included before this header file.
110 */
111#ifndef	ifa_broadaddr
112#define	ifa_broadaddr	ifa_dstaddr	/* broadcast address interface */
113#endif
114
115//struct ifmaddrs {
116//	struct ifmaddrs	*ifma_next;
117//	struct sockaddr	*ifma_name;
118//	struct sockaddr	*ifma_addr;
119//	struct sockaddr	*ifma_lladdr;
120//};
121
122void ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af);
123static int ifmalist_dump_mcstat(struct ifmaddrs *);
124static void in_ifinfo(struct igmp_ifinfo *);
125static const char *inm_mode(u_int);
126static void inm_print_sources_sysctl(uint32_t, struct in_addr);
127#ifdef INET6
128static void in6_ifinfo(struct mld_ifinfo *);
129static void in6m_print_sources_sysctl(uint32_t, struct in6_addr *);
130static const char *inet6_n2a(struct in6_addr *);
131#endif
132static void printb(const char *, unsigned int, const char *);
133static const char *sdl_addr_to_hex(const struct sockaddr_dl *, char *, int);
134
135extern char *routename6(struct sockaddr_in6 *);
136
137#define	sa_equal(a1, a2)	\
138	(bcmp((a1), (a2), ((a1))->sa_len) == 0)
139
140#define	sa_dl_equal(a1, a2)	\
141	((((struct sockaddr_dl *)(a1))->sdl_len ==			\
142	 ((struct sockaddr_dl *)(a2))->sdl_len) &&			\
143	 (bcmp(LLADDR((struct sockaddr_dl *)(a1)),			\
144	       LLADDR((struct sockaddr_dl *)(a2)),			\
145	       ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
146
147#define	SALIGN	(sizeof(uint32_t) - 1)
148#define	SA_RLEN(sa)	(sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
149			    (SALIGN + 1)) : 0)
150#define	MAX_SYSCTL_TRY	5
151#define	RTA_MASKS	(RTA_GATEWAY | RTA_IFP | RTA_IFA)
152
153void
154ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af)
155{
156	const struct ifmaddrs *ifma;
157	sockunion_t *psa;
158	char myifname[IFNAMSIZ];
159	char *pcolon;
160	char *pafname, *pifname, *plladdr = NULL, *pgroup = NULL;
161
162	switch (af) {
163	case AF_INET:
164		pafname = "IPv4";
165		break;
166#ifdef INET6
167	case AF_INET6:
168		pafname = "IPv6";
169		break;
170#endif
171	case AF_LINK:
172		pafname = "Link-layer";
173		break;
174	default:
175		return;		/* XXX */
176	}
177
178	fprintf(stdout, "%s Multicast Group Memberships\n", pafname);
179	fprintf(stdout, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address",
180	    "Netif");
181
182	for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
183
184		if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
185			continue;
186
187		/* Group address */
188		psa = (sockunion_t *)ifma->ifma_addr;
189		if (psa->sa.sa_family != af)
190			continue;
191
192		switch (psa->sa.sa_family) {
193		case AF_INET:
194			pgroup = inet_ntoa(psa->sin.sin_addr);
195			break;
196#ifdef INET6
197		case AF_INET6:
198			pgroup = routename6(&(psa->sin6));
199			break;
200#endif
201		case AF_LINK:
202			if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) ||
203			    (psa->sdl.sdl_type == IFT_ETHER)) {
204				pgroup =
205ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data);
206#ifdef notyet
207			} else {
208				pgroup = addr2ascii(AF_LINK,
209				    &psa->sdl,
210				    sizeof(struct sockaddr_dl),
211				    addrbuf);
212#endif
213			}
214			break;
215		default:
216			continue;	/* XXX */
217		}
218
219		/* Link-layer mapping, if any */
220		psa = (sockunion_t *)ifma->ifma_lladdr;
221		if (psa != NULL) {
222			if (psa->sa.sa_family == AF_LINK) {
223				if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) ||
224				    (psa->sdl.sdl_type == IFT_ETHER)) {
225					/* IEEE 802 */
226					plladdr =
227ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data);
228#ifdef notyet
229				} else {
230					/* something more exotic */
231					plladdr = addr2ascii(AF_LINK,
232					    &psa->sdl,
233					    sizeof(struct sockaddr_dl),
234					    addrbuf);
235#endif
236				}
237			} else {
238				int i;
239
240				/* not a link-layer address */
241				plladdr = "<invalid>";
242
243				for (i = 0; psa->sa.sa_len > 2 && i < psa->sa.sa_len - 2; i++)
244					printf("0x%x ", psa->sa.sa_data[i]);
245				printf("\n");
246			}
247		} else {
248			plladdr = "<none>";
249		}
250
251		/* Interface upon which the membership exists */
252		psa = (sockunion_t *)ifma->ifma_name;
253		if (psa != NULL && psa->sa.sa_family == AF_LINK) {
254			strlcpy(myifname, link_ntoa(&psa->sdl), IFNAMSIZ);
255			pcolon = strchr(myifname, ':');
256			if (pcolon)
257				*pcolon = '\0';
258			pifname = myifname;
259		} else {
260			pifname = "";
261		}
262
263		fprintf(stdout, "%-20s\t%-16s\t%s\n", pgroup, plladdr, pifname);
264	}
265}
266
267void
268ifmalist_dump(void)
269{
270	struct ifmaddrs *ifmap;
271
272	if (getifmaddrs(&ifmap))
273		err(EX_OSERR, "getifmaddrs");
274
275	ifmalist_dump_af(ifmap, AF_LINK);
276	fputs("\n", stdout);
277	ifmalist_dump_af(ifmap, AF_INET);
278#ifdef INET6
279	fputs("\n", stdout);
280	ifmalist_dump_af(ifmap, AF_INET6);
281#endif
282	if (sflag) {
283		fputs("\n", stdout);
284		ifmalist_dump_mcstat(ifmap);
285	}
286
287	freeifmaddrs(ifmap);
288}
289
290static int
291ifmalist_dump_mcstat(struct ifmaddrs *ifmap)
292{
293	char			 thisifname[IFNAMSIZ];
294	char			 addrbuf[NI_MAXHOST];
295	struct ifaddrs		*ifap, *ifa;
296	struct ifmaddrs		*ifma;
297	sockunion_t		 lastifasa;
298	sockunion_t		*psa, *pgsa, *pllsa, *pifasa;
299	char			*pcolon;
300	char			*pafname;
301	uint32_t		 lastifindex, thisifindex;
302	int			 error;
303	uint32_t		ifindex = 0;
304
305	if (interface != NULL)
306		ifindex = if_nametoindex(interface);
307
308	error = 0;
309	ifap = NULL;
310	lastifindex = 0;
311	thisifindex = 0;
312	lastifasa.ss.ss_family = AF_UNSPEC;
313
314	if (getifaddrs(&ifap) != 0) {
315		warn("getifmaddrs");
316		return (-1);
317	}
318
319	for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
320		error = 0;
321		if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
322			continue;
323
324		psa = (sockunion_t *)ifma->ifma_name;
325		if (psa->sa.sa_family != AF_LINK) {
326			fprintf(stderr,
327			    "WARNING: Kernel returned invalid data.\n");
328			error = -1;
329			break;
330		}
331
332		/* Filter on interface name. */
333		thisifindex = psa->sdl.sdl_index;
334		if (ifindex != 0 && thisifindex != ifindex)
335			continue;
336
337		/* Filter on address family. */
338		pgsa = (sockunion_t *)ifma->ifma_addr;
339		if (af != 0 && pgsa->sa.sa_family != af)
340			continue;
341
342		strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
343		pcolon = strchr(thisifname, ':');
344		if (pcolon)
345			*pcolon = '\0';
346
347		/* Only print the banner for the first ifmaddrs entry. */
348		if (lastifindex == 0 || lastifindex != thisifindex) {
349			lastifindex = thisifindex;
350			fprintf(stdout, "%s:\n", thisifname);
351		}
352
353		/*
354		 * Currently, multicast joins only take place on the
355		 * primary IPv4 address, and only on the link-local IPv6
356		 * address, as per IGMPv2/3 and MLDv1/2 semantics.
357		 * Therefore, we only look up the primary address on
358		 * the first pass.
359		 */
360		pifasa = NULL;
361		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
362			if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
363			    (ifa->ifa_addr == NULL) ||
364			    (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
365				continue;
366			/*
367			 * For AF_INET6 only the link-local address should
368			 * be returned. If built without IPv6 support,
369			 * skip this address entirely.
370			 */
371			pifasa = (sockunion_t *)ifa->ifa_addr;
372			if (pifasa->sa.sa_family == AF_INET6
373#ifdef INET6
374			    && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
375#endif
376			) {
377				pifasa = NULL;
378				continue;
379			}
380			break;
381		}
382		if (pifasa == NULL)
383			continue;	/* primary address not found */
384
385		if (!vflag && pifasa->sa.sa_family == AF_LINK)
386			continue;
387
388		/* Parse and print primary address, if not already printed. */
389		if (lastifasa.ss.ss_family == AF_UNSPEC ||
390		    ((lastifasa.ss.ss_family == AF_LINK &&
391		      !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
392		     !sa_equal(&lastifasa.sa, &pifasa->sa))) {
393
394			switch (pifasa->sa.sa_family) {
395			case AF_INET:
396				pafname = "inet";
397				break;
398			case AF_INET6:
399				pafname = "inet6";
400				break;
401			case AF_LINK:
402				pafname = "link";
403				break;
404			default:
405				pafname = "unknown";
406				break;
407			}
408
409			switch (pifasa->sa.sa_family) {
410			case AF_INET6:
411#ifdef INET6
412			{
413				const char *p =
414				    inet6_n2a(&pifasa->sin6.sin6_addr);
415				strlcpy(addrbuf, p, sizeof(addrbuf));
416				break;
417			}
418#else
419			/* FALLTHROUGH */
420#endif
421			case AF_INET:
422				error = getnameinfo(&pifasa->sa,
423				    pifasa->sa.sa_len,
424				    addrbuf, sizeof(addrbuf), NULL, 0,
425				    NI_NUMERICHOST);
426				if (error)
427					printf("getnameinfo: %s\n",
428					    gai_strerror(error));
429				break;
430			case AF_LINK: {
431				(void) sdl_addr_to_hex(&pifasa->sdl, addrbuf,
432				    sizeof (addrbuf));
433				break;
434			}
435			default:
436				addrbuf[0] = '\0';
437				break;
438			}
439
440			fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
441			/*
442			 * Print per-link IGMP information, if available.
443			 */
444			if (pifasa->sa.sa_family == AF_INET) {
445				struct igmp_ifinfo igi;
446				size_t mibsize, len;
447				int mib[5];
448
449				mibsize = sizeof(mib) / sizeof(mib[0]);
450				if (sysctlnametomib("net.inet.igmp.ifinfo",
451				    mib, &mibsize) == -1) {
452					perror("sysctlnametomib");
453					goto next_ifnet;
454				}
455				mib[mibsize] = thisifindex;
456				len = sizeof(struct igmp_ifinfo);
457				if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
458				    0) == -1) {
459					perror("sysctl net.inet.igmp.ifinfo");
460					goto next_ifnet;
461				}
462				in_ifinfo(&igi);
463			}
464#ifdef INET6
465			/*
466			 * Print per-link MLD information, if available.
467			 */
468			if (pifasa->sa.sa_family == AF_INET6) {
469				struct mld_ifinfo mli;
470				size_t mibsize, len;
471				int mib[5];
472
473				mibsize = sizeof(mib) / sizeof(mib[0]);
474				if (sysctlnametomib("net.inet6.mld.ifinfo",
475				    mib, &mibsize) == -1) {
476					perror("sysctlnametomib");
477					goto next_ifnet;
478				}
479				mib[mibsize] = thisifindex;
480				len = sizeof(struct mld_ifinfo);
481				if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
482				    0) == -1) {
483					perror("sysctl net.inet6.mld.ifinfo");
484					goto next_ifnet;
485				}
486				in6_ifinfo(&mli);
487			}
488#endif /* INET6 */
489#if defined(INET6)
490next_ifnet:
491#endif
492			lastifasa = *pifasa;
493		}
494
495		/* Print this group address. */
496#ifdef INET6
497		if (pgsa->sa.sa_family == AF_INET6) {
498			const char *p = inet6_n2a(&pgsa->sin6.sin6_addr);
499			strlcpy(addrbuf, p, sizeof(addrbuf));
500		} else
501#endif
502		if (pgsa->sa.sa_family == AF_INET) {
503			error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
504			    addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
505			if (error)
506				printf("getnameinfo: %s\n",
507				    gai_strerror(error));
508		} else {
509			(void) sdl_addr_to_hex(&pgsa->sdl, addrbuf,
510			    sizeof (addrbuf));
511		}
512
513		fprintf(stdout, "\t\tgroup %s", addrbuf);
514		if (pgsa->sa.sa_family == AF_INET) {
515			inm_print_sources_sysctl(thisifindex,
516			    pgsa->sin.sin_addr);
517		}
518#ifdef INET6
519		if (pgsa->sa.sa_family == AF_INET6) {
520			in6m_print_sources_sysctl(thisifindex,
521			    &pgsa->sin6.sin6_addr);
522		}
523#endif
524		fprintf(stdout, "\n");
525
526		/* Link-layer mapping, if present. */
527		pllsa = (sockunion_t *)ifma->ifma_lladdr;
528		if (pllsa != NULL) {
529			(void) sdl_addr_to_hex(&pllsa->sdl, addrbuf,
530			    sizeof (addrbuf));
531			fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
532		}
533	}
534
535	if (ifap != NULL)
536		freeifaddrs(ifap);
537
538	return (error);
539}
540
541static void
542in_ifinfo(struct igmp_ifinfo *igi)
543{
544
545	printf("\t");
546	switch (igi->igi_version) {
547	case IGMP_VERSION_1:
548	case IGMP_VERSION_2:
549	case IGMP_VERSION_3:
550		printf("igmpv%d", igi->igi_version);
551		break;
552	default:
553		printf("igmpv?(%d)", igi->igi_version);
554		break;
555	}
556	printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
557	if (igi->igi_version == IGMP_VERSION_3) {
558		printf(" rv %u qi %u qri %u uri %u",
559		    igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
560	}
561	if (vflag >= 2) {
562		printf(" v1timer %u v2timer %u v3timer %u",
563		    igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
564	}
565	printf("\n");
566}
567
568static const char *inm_modes[] = {
569	"undefined",
570	"include",
571	"exclude",
572};
573
574static const char *
575inm_mode(u_int mode)
576{
577
578	if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
579		return (inm_modes[mode]);
580	return (NULL);
581}
582
583/*
584 * Retrieve per-group source filter mode and lists via sysctl.
585 */
586static void
587inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
588{
589#define	MAX_SYSCTL_TRY	5
590	int mib[7];
591	int ntry = 0;
592	size_t mibsize;
593	size_t len;
594	size_t needed;
595	size_t cnt;
596	int i;
597	char *buf;
598	struct in_addr *pina;
599	uint32_t *p;
600	uint32_t fmode;
601	const char *modestr;
602
603	mibsize = sizeof(mib) / sizeof(mib[0]);
604	if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
605		perror("sysctlnametomib");
606		return;
607	}
608
609	needed = 0;
610	mib[5] = ifindex;
611	mib[6] = gina.s_addr;	/* 32 bits wide */
612	mibsize = sizeof(mib) / sizeof(mib[0]);
613	do {
614		if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
615			perror("sysctl net.inet.ip.mcast.filters");
616			return;
617		}
618		if ((buf = malloc(needed)) == NULL) {
619			perror("malloc");
620			return;
621		}
622		if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
623			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
624				perror("sysctl");
625				goto out_free;
626			}
627			free(buf);
628			buf = NULL;
629		}
630	} while (buf == NULL);
631
632	len = needed;
633	if (len < sizeof(uint32_t)) {
634		perror("sysctl");
635		goto out_free;
636	}
637
638	p = (uint32_t *)buf;
639	fmode = *p++;
640	len -= sizeof(uint32_t);
641
642	modestr = inm_mode(fmode);
643	if (modestr)
644		printf(" mode %s", modestr);
645	else
646		printf(" mode (%u)", fmode);
647
648	if (vflag == 0)
649		goto out_free;
650
651	cnt = len / sizeof(struct in_addr);
652	pina = (struct in_addr *)p;
653
654	for (i = 0; i < cnt; i++) {
655		if (i == 0)
656			printf(" srcs ");
657		fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
658		    inet_ntoa(*pina++));
659		len -= sizeof(struct in_addr);
660	}
661	if (len > 0) {
662		fprintf(stderr, "warning: %u trailing bytes from %s\n",
663		    (unsigned int)len, "net.inet.ip.mcast.filters");
664	}
665
666out_free:
667	free(buf);
668#undef	MAX_SYSCTL_TRY
669}
670
671#ifdef INET6
672
673static void
674in6_ifinfo(struct mld_ifinfo *mli)
675{
676
677	printf("\t");
678	switch (mli->mli_version) {
679	case MLD_VERSION_1:
680	case MLD_VERSION_2:
681		printf("mldv%d", mli->mli_version);
682		break;
683	default:
684		printf("mldv?(%d)", mli->mli_version);
685		break;
686	}
687	printb(" flags", mli->mli_flags, "\020\1SILENT");
688	if (mli->mli_version == MLD_VERSION_2) {
689		printf(" rv %u qi %u qri %u uri %u",
690		    mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
691	}
692	if (vflag >= 2) {
693		printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
694		   mli->mli_v2_timer);
695	}
696	printf("\n");
697}
698
699/*
700 * Retrieve MLD per-group source filter mode and lists via sysctl.
701 *
702 * Note: The 128-bit IPv6 group addres needs to be segmented into
703 * 32-bit pieces for marshaling to sysctl. So the MIB name ends
704 * up looking like this:
705 *  a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
706 * Assumes that pgroup originated from the kernel, so its components
707 * are already in network-byte order.
708 */
709static void
710in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
711{
712#define	MAX_SYSCTL_TRY	5
713	char addrbuf[INET6_ADDRSTRLEN];
714	int mib[10];
715	int ntry = 0;
716	int *pi;
717	size_t mibsize;
718	size_t len;
719	size_t needed;
720	size_t cnt;
721	int i;
722	char *buf;
723	struct in6_addr *pina;
724	uint32_t *p;
725	uint32_t fmode;
726	const char *modestr;
727
728	mibsize = sizeof(mib) / sizeof(mib[0]);
729	if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
730	    &mibsize) == -1) {
731		perror("sysctlnametomib");
732		return;
733	}
734
735	needed = 0;
736	mib[5] = ifindex;
737	pi = (int *)pgroup;
738	for (i = 0; i < 4; i++)
739		mib[6 + i] = *pi++;
740
741	mibsize = sizeof(mib) / sizeof(mib[0]);
742	do {
743		if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
744			perror("sysctl net.inet6.ip6.mcast.filters");
745			return;
746		}
747		if ((buf = malloc(needed)) == NULL) {
748			perror("malloc");
749			return;
750		}
751		if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
752			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
753				perror("sysctl");
754				goto out_free;
755			}
756			free(buf);
757			buf = NULL;
758		}
759	} while (buf == NULL);
760
761	len = needed;
762	if (len < sizeof(uint32_t)) {
763		perror("sysctl");
764		goto out_free;
765	}
766
767	p = (uint32_t *)buf;
768	fmode = *p++;
769	len -= sizeof(uint32_t);
770
771	modestr = inm_mode(fmode);
772	if (modestr)
773		printf(" mode %s", modestr);
774	else
775		printf(" mode (%u)", fmode);
776
777	if (vflag == 0)
778		goto out_free;
779
780	cnt = len / sizeof(struct in6_addr);
781	pina = (struct in6_addr *)p;
782
783	for (i = 0; i < cnt; i++) {
784		if (i == 0)
785			printf(" srcs ");
786		inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
787		    INET6_ADDRSTRLEN);
788		fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
789		len -= sizeof(struct in6_addr);
790	}
791	if (len > 0) {
792		fprintf(stderr, "warning: %u trailing bytes from %s\n",
793		    (unsigned int)len, "net.inet6.ip6.mcast.filters");
794	}
795
796out_free:
797	free(buf);
798#undef	MAX_SYSCTL_TRY
799}
800
801static const char *
802inet6_n2a(struct in6_addr *p)
803{
804	static char buf[NI_MAXHOST];
805	struct sockaddr_in6 sin6;
806	u_int32_t scopeid;
807	const int niflags = NI_NUMERICHOST;
808
809	memset(&sin6, 0, sizeof(sin6));
810	sin6.sin6_family = AF_INET6;
811	sin6.sin6_len = sizeof(struct sockaddr_in6);
812	sin6.sin6_addr = *p;
813	if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
814	    IN6_IS_ADDR_MC_NODELOCAL(p)) {
815		scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
816		if (scopeid) {
817			sin6.sin6_scope_id = scopeid;
818			sin6.sin6_addr.s6_addr[2] = 0;
819			sin6.sin6_addr.s6_addr[3] = 0;
820		}
821	}
822	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
823	    buf, sizeof(buf), NULL, 0, niflags) == 0) {
824		return (buf);
825	} else {
826		return ("(invalid)");
827	}
828}
829#endif /* INET6 */
830
831/*
832 * Print a value a la the %b format of the kernel's printf
833 */
834void
835printb(const char *s, unsigned int v, const char *bits)
836{
837	int i, any = 0;
838	char c;
839
840	if (bits && *bits == 8)
841		printf("%s=%o", s, v);
842	else
843		printf("%s=%x", s, v);
844	bits++;
845	if (bits) {
846		putchar('<');
847		while ((i = *bits++) != '\0') {
848			if (v & (1 << (i-1))) {
849				if (any)
850					putchar(',');
851				any = 1;
852				for (; (c = *bits) > 32; bits++)
853					putchar(c);
854			} else
855				for (; *bits > 32; bits++)
856					;
857		}
858		putchar('>');
859	}
860}
861
862/*
863 * convert hardware address to hex string for logging errors.
864  */
865static const char *
866sdl_addr_to_hex(const struct sockaddr_dl *sdl, char *orig_buf, int buflen)
867{
868	char *buf = orig_buf;
869	int i;
870	const u_char *lladdr;
871	int maxbytes = buflen / 3;
872
873	lladdr = (u_char *)(size_t)sdl->sdl_data + sdl->sdl_nlen;
874
875	if (maxbytes > sdl->sdl_alen) {
876		maxbytes = sdl->sdl_alen;
877	}
878	*buf = '\0';
879	for (i = 0; i < maxbytes; i++) {
880		snprintf(buf, 3, "%02x", lladdr[i]);
881		buf += 2;
882		*buf = (i == maxbytes - 1) ? '\0' : ':';
883		buf++;
884	}
885	return (orig_buf);
886}
887
888