]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/ifmcstat/ifmcstat.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / ifmcstat / ifmcstat.c
1 /*      $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $       */
2
3 /*
4  * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org>
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 # ifdef HAVE_MLDV2
65 #  include <netinet6/in6_msf.h>
66 # endif
67 #include <netinet/icmp6.h>
68 #define _KERNEL
69 # include <netinet6/mld6_var.h>
70 #undef _KERNEL
71 #endif /* INET6 */
72
73 #include <arpa/inet.h>
74 #include <netdb.h>
75
76 #include <stddef.h>
77 #include <stdarg.h>
78 #include <stdlib.h>
79 #include <stdint.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83
84 #include <ctype.h>
85 #include <err.h>
86 #include <fcntl.h>
87 #include <kvm.h>
88 #include <limits.h>
89 #include <ifaddrs.h>
90 #include <nlist.h>
91 #include <sysexits.h>
92 #include <unistd.h>
93
94 /* XXX: This file currently assumes INET and KVM support in the base system. */
95 #ifndef INET
96 #define INET
97 #endif
98
99 union sockunion {
100         struct sockaddr_storage ss;
101         struct sockaddr         sa;
102         struct sockaddr_dl      sdl;
103 #ifdef INET
104         struct sockaddr_in      sin;
105 #endif
106 #ifdef INET6
107         struct sockaddr_in6     sin6;
108 #endif
109 };
110 typedef union sockunion sockunion_t;
111
112 uint32_t        ifindex = 0;
113 int             af = AF_UNSPEC;
114
115 #define sa_equal(a1, a2)        \
116         (bcmp((a1), (a2), ((a1))->sa_len) == 0)
117
118 #define sa_dl_equal(a1, a2)     \
119         ((((struct sockaddr_dl *)(a1))->sdl_len ==                      \
120          ((struct sockaddr_dl *)(a2))->sdl_len) &&                      \
121          (bcmp(LLADDR((struct sockaddr_dl *)(a1)),                      \
122                LLADDR((struct sockaddr_dl *)(a2)),                      \
123                ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
124
125 /*
126  * Most of the code in this utility is to support the use of KVM for
127  * post-mortem debugging of the multicast code.
128  */
129 #ifdef WITH_KVM
130
131 #ifdef INET
132 static void             if_addrlist(struct ifaddr *);
133 static struct in_multi *
134                         in_multientry(struct in_multi *);
135 #ifdef HAVE_IGMPV3
136 static void             in_addr_slistentry(struct in_addr_slist *, char *);
137 #endif
138 #endif /* INET */
139
140 #ifdef INET6
141 static void             if6_addrlist(struct ifaddr *);
142 static struct in6_multi *
143                         in6_multientry(struct in6_multi *);
144 #ifdef HAVE_MLDV2
145 static void             in6_addr_slistentry(struct in6_addr_slist *, char *);
146 #endif
147 static const char *     inet6_n2a(struct in6_addr *);
148 #endif /* INET6 */
149
150 static void             kread(u_long, void *, int);
151 static int              ifmcstat_kvm(const char *kernel, const char *core);
152
153 #define KREAD(addr, buf, type) \
154         kread((u_long)addr, (void *)buf, sizeof(type))
155
156 kvm_t   *kvmd;
157 struct  nlist nl[] = {
158         { "_ifnet", 0, 0, 0, 0, },
159         { "", 0, 0, 0, 0, },
160 };
161 #define N_IFNET 0
162
163 #endif /* WITH_KVM */
164
165 static int              ifmcstat_getifmaddrs(void);
166 int                     main(int, char **);
167
168 int
169 main(int argc, char **argv)
170 {
171         int c, error;
172 #ifdef WITH_KVM
173         const char *kernel = NULL;
174         const char *core = NULL;
175
176         /* "ifmcstat [kernel]" format is supported for backward compatiblity */
177         if (argc == 2)
178                 kernel = argv[1];
179 #endif
180
181         while ((c = getopt(argc, argv, "i:f:M:N:")) != -1) {
182                 switch (c) {
183                 case 'i':
184                         if ((ifindex = if_nametoindex(optarg)) == 0) {
185                                 fprintf(stderr, "%s: unknown interface\n",
186                                     optarg);
187                                 exit(1);
188                         }
189                         break;
190
191                 case 'f':
192 #ifdef INET
193                         if (strcmp(optarg, "inet") == 0) {
194                                 af = AF_INET;
195                                 break;
196                         }
197 #endif
198 #ifdef INET6
199                         if (strcmp(optarg, "inet6") == 0) {
200                                 af = AF_INET6;
201                                 break;
202                         }
203 #endif
204                         fprintf(stderr, "%s: unknown address family\n", optarg);
205                         exit(1);
206                         /*NOTREACHED*/
207                         break;
208
209 #ifdef WITH_KVM
210                 case 'M':
211                         core = strdup(optarg);
212                         break;
213
214                 case 'N':
215                         kernel = strdup(optarg);
216                         break;
217 #endif
218
219                 default:
220                         fprintf(stderr,
221                             "usage: ifmcstat [-i interface] [-f address family]"
222 #ifdef WITH_KVM
223                             " [-M core] [-N system]"
224 #endif
225                             "\n");
226                         exit(1);
227                         break;
228                         /*NOTREACHED*/
229                 }
230         }
231
232 #ifdef WITH_KVM
233         error = ifmcstat_kvm(kernel, core);
234         /*
235          * If KVM failed, and user did not explicitly specify a core file,
236          * try the sysctl backend.
237          */
238         if (error != 0 && (core == NULL && kernel == NULL))
239 #endif
240         error = ifmcstat_getifmaddrs();
241         if (error != 0)
242                 exit(1);
243
244         exit(0);
245         /*NOTREACHED*/
246 }
247
248 #ifdef WITH_KVM
249
250 static int
251 ifmcstat_kvm(const char *kernel, const char *core)
252 {
253         char    buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
254         struct  ifnet   *ifp, *nifp, ifnet;
255
256         if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
257             NULL) {
258                 perror("kvm_openfiles");
259                 return (-1);
260         }
261         if (kvm_nlist(kvmd, nl) < 0) {
262                 perror("kvm_nlist");
263                 return (-1);
264         }
265         if (nl[N_IFNET].n_value == 0) {
266                 printf("symbol %s not found\n", nl[N_IFNET].n_name);
267                 return (-1);
268         }
269         KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
270         while (ifp) {
271                 KREAD(ifp, &ifnet, struct ifnet);
272                 nifp = ifnet.if_link.tqe_next;
273                 if (ifindex && ifindex != ifnet.if_index)
274                         goto next;
275         
276                 printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
277 #ifdef INET
278                 if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
279 #endif
280 #ifdef INET6
281                 if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
282 #endif
283 next:
284                 ifp = nifp;
285         }
286
287         return (0);
288 }
289
290 static void
291 kread(u_long addr, void *buf, int len)
292 {
293
294         if (kvm_read(kvmd, addr, buf, len) != len) {
295                 perror("kvm_read");
296                 exit(1);
297         }
298 }
299
300 #ifdef INET6
301
302 static const char *
303 inet6_n2a(struct in6_addr *p)
304 {
305         static char buf[NI_MAXHOST];
306         struct sockaddr_in6 sin6;
307         u_int32_t scopeid;
308         const int niflags = NI_NUMERICHOST;
309
310         memset(&sin6, 0, sizeof(sin6));
311         sin6.sin6_family = AF_INET6;
312         sin6.sin6_len = sizeof(struct sockaddr_in6);
313         sin6.sin6_addr = *p;
314         if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
315             IN6_IS_ADDR_MC_NODELOCAL(p)) {
316                 scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
317                 if (scopeid) {
318                         sin6.sin6_scope_id = scopeid;
319                         sin6.sin6_addr.s6_addr[2] = 0;
320                         sin6.sin6_addr.s6_addr[3] = 0;
321                 }
322         }
323         if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
324                         buf, sizeof(buf), NULL, 0, niflags) == 0)
325                 return buf;
326         else
327                 return "(invalid)";
328 }
329
330 static void
331 if6_addrlist(struct ifaddr *ifap)
332 {
333         struct ifaddr ifa;
334         struct sockaddr sa;
335         struct in6_ifaddr if6a;
336         struct ifaddr *ifap0;
337
338         if (af && af != AF_INET6)
339                 return;
340         ifap0 = ifap;
341         while (ifap) {
342                 KREAD(ifap, &ifa, struct ifaddr);
343                 if (ifa.ifa_addr == NULL)
344                         goto nextifap;
345                 KREAD(ifa.ifa_addr, &sa, struct sockaddr);
346                 if (sa.sa_family != PF_INET6)
347                         goto nextifap;
348                 KREAD(ifap, &if6a, struct in6_ifaddr);
349                 printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
350         nextifap:
351                 ifap = ifa.ifa_link.tqe_next;
352         }
353         if (ifap0) {
354                 struct ifnet ifnet;
355                 struct ifmultiaddr ifm, *ifmp = 0;
356                 struct sockaddr_dl sdl;
357
358                 KREAD(ifap0, &ifa, struct ifaddr);
359                 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
360                 if (TAILQ_FIRST(&ifnet.if_multiaddrs))
361                         ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
362                 while (ifmp) {
363                         KREAD(ifmp, &ifm, struct ifmultiaddr);
364                         if (ifm.ifma_addr == NULL)
365                                 goto nextmulti;
366                         KREAD(ifm.ifma_addr, &sa, struct sockaddr);
367                         if (sa.sa_family != AF_INET6)
368                                 goto nextmulti;
369                         (void)in6_multientry((struct in6_multi *)
370                                              ifm.ifma_protospec);
371                         if (ifm.ifma_lladdr == 0)
372                                 goto nextmulti;
373                         KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
374                         printf("\t\t\tmcast-macaddr %s refcnt %d\n",
375                                ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
376                                ifm.ifma_refcount);
377                     nextmulti:
378                         ifmp = TAILQ_NEXT(&ifm, ifma_link);
379                 }
380         }
381 }
382
383 static struct in6_multi *
384 in6_multientry(struct in6_multi *mc)
385 {
386         struct in6_multi multi;
387 #ifdef HAVE_MLDV2
388         struct in6_multi_source src;
389         struct router6_info rt6i;
390 #endif
391
392         KREAD(mc, &multi, struct in6_multi);
393         printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr));
394         printf(" refcnt %u\n", multi.in6m_refcount);
395
396 #ifdef HAVE_MLDV2
397         if (multi.in6m_rti != NULL) {
398                 KREAD(multi.in6m_rti, &rt6i, struct router_info);
399                 printf("\t\t\t");
400                 switch (rt6i.rt6i_type) {
401                 case MLD_V1_ROUTER:
402                         printf("mldv1");
403                         break;
404                 case MLD_V2_ROUTER:
405                         printf("mldv2");
406                         break;
407                 default:
408                         printf("mldv?(%d)", rt6i.rt6i_type);
409                         break;
410                 }
411
412                 if (multi.in6m_source == NULL) {
413                         printf("\n");
414                         return(multi.in6m_entry.le_next);
415                 }
416
417                 KREAD(multi.in6m_source, &src, struct in6_multi_source);
418                 printf(" mode=%s grpjoin=%d\n",
419                     src.i6ms_mode == MCAST_INCLUDE ? "include" :
420                     src.i6ms_mode == MCAST_EXCLUDE ? "exclude" :
421                     "???",
422                     src.i6ms_grpjoin);
423                 in6_addr_slistentry(src.i6ms_cur, "current");
424                 in6_addr_slistentry(src.i6ms_rec, "recorded");
425                 in6_addr_slistentry(src.i6ms_in, "included");
426                 in6_addr_slistentry(src.i6ms_ex, "excluded");
427                 in6_addr_slistentry(src.i6ms_alw, "allowed");
428                 in6_addr_slistentry(src.i6ms_blk, "blocked");
429                 in6_addr_slistentry(src.i6ms_toin, "to-include");
430                 in6_addr_slistentry(src.i6ms_ex, "to-exclude");
431         }
432 #endif
433         return(multi.in6m_entry.le_next);
434 }
435
436 #ifdef HAVE_MLDV2
437 static void
438 in6_addr_slistentry(struct in6_addr_slist *ias, char *heading)
439 {
440         struct in6_addr_slist slist;
441         struct i6as_head head;
442         struct in6_addr_source src;
443
444         if (ias == NULL) {
445                 printf("\t\t\t\t%s (none)\n", heading);
446                 return;
447         }
448         memset(&slist, 0, sizeof(slist));
449         KREAD(ias, &slist, struct in6_addr_source);
450         printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
451         if (slist.numsrc == 0) {
452                 return;
453         }
454         KREAD(slist.head, &head, struct i6as_head);
455
456         KREAD(head.lh_first, &src, struct in6_addr_source);
457         while (1) {
458                 printf("\t\t\t\t\tsource %s (ref=%d)\n",
459                         inet6_n2a(&src.i6as_addr.sin6_addr),
460                         src.i6as_refcount);
461                 if (src.i6as_list.le_next == NULL)
462                         break;
463                 KREAD(src.i6as_list.le_next, &src, struct in6_addr_source);
464         }
465         return;
466 }
467 #endif /* HAVE_MLDV2 */
468
469 #endif /* INET6 */
470
471 #ifdef INET
472
473 static void
474 if_addrlist(struct ifaddr *ifap)
475 {
476         struct ifaddr ifa;
477         struct sockaddr sa;
478         struct in_ifaddr ia;
479         struct ifaddr *ifap0;
480
481         if (af && af != AF_INET)
482                 return;
483         ifap0 = ifap;
484         while (ifap) {
485                 KREAD(ifap, &ifa, struct ifaddr);
486                 if (ifa.ifa_addr == NULL)
487                         goto nextifap;
488                 KREAD(ifa.ifa_addr, &sa, struct sockaddr);
489                 if (sa.sa_family != PF_INET)
490                         goto nextifap;
491                 KREAD(ifap, &ia, struct in_ifaddr);
492                 printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
493         nextifap:
494                 ifap = ifa.ifa_link.tqe_next;
495         }
496         if (ifap0) {
497                 struct ifnet ifnet;
498                 struct ifmultiaddr ifm, *ifmp = 0;
499                 struct sockaddr_dl sdl;
500
501                 KREAD(ifap0, &ifa, struct ifaddr);
502                 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
503                 if (TAILQ_FIRST(&ifnet.if_multiaddrs))
504                         ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
505                 while (ifmp) {
506                         KREAD(ifmp, &ifm, struct ifmultiaddr);
507                         if (ifm.ifma_addr == NULL)
508                                 goto nextmulti;
509                         KREAD(ifm.ifma_addr, &sa, struct sockaddr);
510                         if (sa.sa_family != AF_INET)
511                                 goto nextmulti;
512                         (void)in_multientry((struct in_multi *)
513                                             ifm.ifma_protospec);
514                         if (ifm.ifma_lladdr == 0)
515                                 goto nextmulti;
516                         KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
517                         printf("\t\t\tmcast-macaddr %s refcnt %d\n",
518                                ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
519                                ifm.ifma_refcount);
520                     nextmulti:
521                         ifmp = TAILQ_NEXT(&ifm, ifma_link);
522                 }
523         }
524 }
525
526 static struct in_multi *
527 in_multientry(struct in_multi *mc)
528 {
529         struct in_multi multi;
530         struct router_info rti;
531 #ifdef HAVE_IGMPV3
532         struct in_multi_source src;
533 #endif
534
535         KREAD(mc, &multi, struct in_multi);
536         printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr));
537
538         if (multi.inm_rti != NULL) {
539                 KREAD(multi.inm_rti, &rti, struct router_info);
540                 printf("\t\t\t");
541                 switch (rti.rti_type) {
542                 case IGMP_V1_ROUTER:
543                         printf("igmpv1");
544                         break;
545                 case IGMP_V2_ROUTER:
546                         printf("igmpv2");
547                         break;
548 #ifdef HAVE_IGMPV3
549                 case IGMP_V3_ROUTER:
550                         printf("igmpv3");
551                         break;
552 #endif
553                 default:
554                         printf("igmpv?(%d)", rti.rti_type);
555                         break;
556                 }
557
558 #ifdef HAVE_IGMPV3
559                 if (multi.inm_source == NULL) {
560                         printf("\n");
561                         return (multi.inm_list.le_next);
562                 }
563
564                 KREAD(multi.inm_source, &src, struct in_multi_source);
565                 printf(" mode=%s grpjoin=%d\n",
566                     src.ims_mode == MCAST_INCLUDE ? "include" :
567                     src.ims_mode == MCAST_EXCLUDE ? "exclude" :
568                     "???",
569                     src.ims_grpjoin);
570                 in_addr_slistentry(src.ims_cur, "current");
571                 in_addr_slistentry(src.ims_rec, "recorded");
572                 in_addr_slistentry(src.ims_in, "included");
573                 in_addr_slistentry(src.ims_ex, "excluded");
574                 in_addr_slistentry(src.ims_alw, "allowed");
575                 in_addr_slistentry(src.ims_blk, "blocked");
576                 in_addr_slistentry(src.ims_toin, "to-include");
577                 in_addr_slistentry(src.ims_ex, "to-exclude");
578 #else
579                 printf("\n");
580 #endif
581         }
582
583         return (NULL);
584 }
585
586 #ifdef HAVE_IGMPV3
587 static void
588 in_addr_slistentry(struct in_addr_slist *ias, char *heading)
589 {
590         struct in_addr_slist slist;
591         struct ias_head head;
592         struct in_addr_source src;
593
594         if (ias == NULL) {
595                 printf("\t\t\t\t%s (none)\n", heading);
596                 return;
597         }
598         memset(&slist, 0, sizeof(slist));
599         KREAD(ias, &slist, struct in_addr_source);
600         printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
601         if (slist.numsrc == 0) {
602                 return;
603         }
604         KREAD(slist.head, &head, struct ias_head);
605
606         KREAD(head.lh_first, &src, struct in_addr_source);
607         while (1) {
608                 printf("\t\t\t\t\tsource %s (ref=%d)\n",
609                         inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount);
610                 if (src.ias_list.le_next == NULL)
611                         break;
612                 KREAD(src.ias_list.le_next, &src, struct in_addr_source);
613         }
614         return;
615 }
616 #endif /* HAVE_IGMPV3 */
617
618 #endif /* INET */
619
620 #endif /* WITH_KVM */
621
622 static int
623 ifmcstat_getifmaddrs(void)
624 {
625         char                     thisifname[IFNAMSIZ];
626         char                     addrbuf[INET6_ADDRSTRLEN];
627         struct ifaddrs          *ifap, *ifa;
628         struct ifmaddrs         *ifmap, *ifma;
629         sockunion_t              lastifasa;
630         sockunion_t             *psa, *pgsa, *pllsa, *pifasa;
631         char                    *pcolon;
632         char                    *pafname;
633         uint32_t                 lastifindex, thisifindex;
634         int                      error;
635
636         error = 0;
637         ifap = NULL;
638         ifmap = NULL;
639         lastifindex = 0;
640         thisifindex = 0;
641         lastifasa.ss.ss_family = AF_UNSPEC;
642
643         if (getifaddrs(&ifap) != 0) {
644                 warn("getifmaddrs");
645                 return (-1);
646         }
647
648         if (getifmaddrs(&ifmap) != 0) {
649                 warn("getifmaddrs");
650                 error = -1;
651                 goto out;
652         }
653
654         for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
655                 error = 0;
656                 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
657                         continue;
658
659                 psa = (sockunion_t *)ifma->ifma_name;
660                 if (psa->sa.sa_family != AF_LINK) {
661                         fprintf(stderr,
662                             "WARNING: Kernel returned invalid data.\n");
663                         error = -1;
664                         break;
665                 }
666
667                 /* Filter on interface name. */
668                 thisifindex = psa->sdl.sdl_index;
669                 if (ifindex != 0 && thisifindex != ifindex)
670                         continue;
671
672                 /* Filter on address family. */
673                 pgsa = (sockunion_t *)ifma->ifma_addr;
674                 if (af != 0 && pgsa->sa.sa_family != af)
675                         continue;
676
677                 strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
678                 pcolon = strchr(thisifname, ':');
679                 if (pcolon)
680                         *pcolon = '\0';
681
682                 /* Only print the banner for the first ifmaddrs entry. */
683                 if (lastifindex == 0 || lastifindex != thisifindex) {
684                         lastifindex = thisifindex;
685                         fprintf(stdout, "%s:\n", thisifname);
686                 }
687
688                 /*
689                  * Currently, multicast joins only take place on the
690                  * primary IPv4 address, and only on the link-local IPv6
691                  * address, as per IGMPv2/3 and MLDv1/2 semantics.
692                  * Therefore, we only look up the primary address on
693                  * the first pass.
694                  */
695                 pifasa = NULL;
696                 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
697                         if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
698                             (ifa->ifa_addr == NULL) ||
699                             (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
700                                 continue;
701 #ifdef INET6
702                         /*
703                          * For AF_INET6 only the link-local address should
704                          * be returned.
705                          * XXX: ifmcstat actually prints all of the inet6
706                          * addresses, but never mind...
707                          */
708                         pifasa = (sockunion_t *)ifa->ifa_addr;
709                         if (pifasa->sa.sa_family == AF_INET6 &&
710                             !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)) {
711                                 pifasa = NULL;
712                                 continue;
713                         }
714 #endif
715                         break;
716                 }
717                 if (pifasa == NULL)
718                         continue;       /* primary address not found */
719
720                 /* Parse and print primary address, if not already printed. */
721                 if (lastifasa.ss.ss_family == AF_UNSPEC ||
722                     ((lastifasa.ss.ss_family == AF_LINK &&
723                       !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
724                      !sa_equal(&lastifasa.sa, &pifasa->sa))) {
725
726                         switch (pifasa->sa.sa_family) {
727                         case AF_INET:
728                                 pafname = "inet";
729                                 break;
730                         case AF_INET6:
731                                 pafname = "inet6";
732                                 break;
733                         case AF_LINK:
734                                 pafname = "link";
735                                 break;
736                         default:
737                                 pafname = "unknown";
738                                 break;
739                         }
740
741                         switch (pifasa->sa.sa_family) {
742                         case AF_INET:
743                         case AF_INET6:
744                         case AF_LINK:
745                                 error = getnameinfo(&pifasa->sa,
746                                     pifasa->sa.sa_len,
747                                     addrbuf, sizeof(addrbuf), NULL, 0,
748                                     NI_NUMERICHOST);
749                                 if (error)
750                                         perror("getnameinfo");
751                                 break;
752                         default:
753                                 addrbuf[0] = '\0';
754                                 break;
755                         }
756
757                         fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
758                         lastifasa = *pifasa;
759                 }
760
761                 /* Print this group address. */
762                 error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, addrbuf,
763                     sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
764                 if (error)
765                         perror("getnameinfo");
766                 fprintf(stdout, "\t\tgroup %s\n", addrbuf);
767
768                 /* Link-layer mapping, if present. */
769                 pllsa = (sockunion_t *)ifma->ifma_lladdr;
770                 if (pllsa != NULL) {
771                         error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
772                             addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
773                         fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
774                 }
775         }
776 out:
777         if (ifmap != NULL)
778                 freeifmaddrs(ifmap);
779         if (ifap != NULL)
780                 freeifaddrs(ifap);
781
782         return (error);
783 }