1 /* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 2007-2009 Bruce Simpson.
7 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the project nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <sys/socket.h>
39 #include <sys/queue.h>
40 #include <sys/tree.h>
41
42 #include <net/if.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 #include <netinet/if_ether.h>
53 #include <netinet/igmp_var.h>
54
55 #ifdef INET6
56 #include <netinet/icmp6.h>
57 #include <netinet6/mld6_var.h>
58 #endif /* INET6 */
59
60 #include <arpa/inet.h>
61 #include <netdb.h>
62
63 #include <stddef.h>
64 #include <stdarg.h>
65 #include <stdint.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 #include <ctype.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <limits.h>
75 #include <ifaddrs.h>
76 #include <sysexits.h>
77 #include <unistd.h>
78
79 /* XXX: This file currently assumes INET support in the base system. */
80 #ifndef INET
81 #define INET
82 #endif
83
84 extern void printb(const char *, unsigned int, const char *);
85
86 union sockunion {
87 struct sockaddr_storage ss;
88 struct sockaddr sa;
89 struct sockaddr_dl sdl;
90 #ifdef INET
91 struct sockaddr_in sin;
92 #endif
93 #ifdef INET6
94 struct sockaddr_in6 sin6;
95 #endif
96 };
97 typedef union sockunion sockunion_t;
98
99 uint32_t ifindex = 0;
100 int af = AF_UNSPEC;
101 int vflag = 0;
102
103 #define sa_dl_equal(a1, a2) \
104 ((((struct sockaddr_dl *)(a1))->sdl_len == \
105 ((struct sockaddr_dl *)(a2))->sdl_len) && \
106 (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \
107 LLADDR((struct sockaddr_dl *)(a2)), \
108 ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
109
110 static int ifmcstat_getifmaddrs(void);
111 #ifdef INET
112 static void in_ifinfo(struct igmp_ifinfo *);
113 static const char * inm_mode(u_int mode);
114 #endif
115 #ifdef INET6
116 static void in6_ifinfo(struct mld_ifinfo *);
117 static const char * inet6_n2a(struct in6_addr *, uint32_t);
118 #endif
119 int main(int, char **);
120
121 static void
usage()122 usage()
123 {
124
125 fprintf(stderr,
126 "usage: ifmcstat [-i interface] [-f address family] [-v]\n");
127 exit(EX_USAGE);
128 }
129
130 static const char *options = "i:f:v";
131
132 int
main(int argc,char ** argv)133 main(int argc, char **argv)
134 {
135 int c, error;
136
137 while ((c = getopt(argc, argv, options)) != -1) {
138 switch (c) {
139 case 'i':
140 if ((ifindex = if_nametoindex(optarg)) == 0) {
141 fprintf(stderr, "%s: unknown interface\n",
142 optarg);
143 exit(EX_NOHOST);
144 }
145 break;
146
147 case 'f':
148 #ifdef INET
149 if (strcmp(optarg, "inet") == 0) {
150 af = AF_INET;
151 break;
152 }
153 #endif
154 #ifdef INET6
155 if (strcmp(optarg, "inet6") == 0) {
156 af = AF_INET6;
157 break;
158 }
159 #endif
160 if (strcmp(optarg, "link") == 0) {
161 af = AF_LINK;
162 break;
163 }
164 fprintf(stderr, "%s: unknown address family\n", optarg);
165 exit(EX_USAGE);
166 /*NOTREACHED*/
167 break;
168
169 case 'v':
170 ++vflag;
171 break;
172
173 default:
174 usage();
175 break;
176 /*NOTREACHED*/
177 }
178 }
179
180 if (af == AF_LINK && vflag)
181 usage();
182
183 error = ifmcstat_getifmaddrs();
184 if (error != 0)
185 exit(EX_OSERR);
186
187 exit(EX_OK);
188 /*NOTREACHED*/
189 }
190
191 #ifdef INET
192
193 static void
in_ifinfo(struct igmp_ifinfo * igi)194 in_ifinfo(struct igmp_ifinfo *igi)
195 {
196
197 printf("\t");
198 switch (igi->igi_version) {
199 case IGMP_VERSION_1:
200 case IGMP_VERSION_2:
201 case IGMP_VERSION_3:
202 printf("igmpv%d", igi->igi_version);
203 break;
204 default:
205 printf("igmpv?(%d)", igi->igi_version);
206 break;
207 }
208 if (igi->igi_flags)
209 printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
210 if (igi->igi_version == IGMP_VERSION_3) {
211 printf(" rv %u qi %u qri %u uri %u",
212 igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
213 }
214 if (vflag >= 2) {
215 printf(" v1timer %u v2timer %u v3timer %u",
216 igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
217 }
218 printf("\n");
219 }
220
221 static const char *inm_modes[] = {
222 "undefined",
223 "include",
224 "exclude",
225 };
226
227 static const char *
inm_mode(u_int mode)228 inm_mode(u_int mode)
229 {
230
231 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
232 return (inm_modes[mode]);
233 return (NULL);
234 }
235
236 #endif /* INET */
237
238 #ifdef INET6
239
240 static void
in6_ifinfo(struct mld_ifinfo * mli)241 in6_ifinfo(struct mld_ifinfo *mli)
242 {
243
244 printf("\t");
245 switch (mli->mli_version) {
246 case MLD_VERSION_1:
247 case MLD_VERSION_2:
248 printf("mldv%d", mli->mli_version);
249 break;
250 default:
251 printf("mldv?(%d)", mli->mli_version);
252 break;
253 }
254 if (mli->mli_flags)
255 printb(" flags", mli->mli_flags, "\020\1SILENT\2USEALLOW");
256 if (mli->mli_version == MLD_VERSION_2) {
257 printf(" rv %u qi %u qri %u uri %u",
258 mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
259 }
260 if (vflag >= 2) {
261 printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
262 mli->mli_v2_timer);
263 }
264 printf("\n");
265 }
266
267 static const char *
inet6_n2a(struct in6_addr * p,uint32_t scope_id)268 inet6_n2a(struct in6_addr *p, uint32_t scope_id)
269 {
270 static char buf[NI_MAXHOST];
271 struct sockaddr_in6 sin6;
272 const int niflags = NI_NUMERICHOST;
273
274 memset(&sin6, 0, sizeof(sin6));
275 sin6.sin6_family = AF_INET6;
276 sin6.sin6_len = sizeof(struct sockaddr_in6);
277 sin6.sin6_addr = *p;
278 sin6.sin6_scope_id = scope_id;
279 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
280 buf, sizeof(buf), NULL, 0, niflags) == 0) {
281 return (buf);
282 } else {
283 return ("(invalid)");
284 }
285 }
286 #endif /* INET6 */
287
288 #ifdef INET
289 /*
290 * Retrieve per-group source filter mode and lists via sysctl.
291 */
292 static void
inm_print_sources_sysctl(uint32_t ifindex,struct in_addr gina)293 inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
294 {
295 #define MAX_SYSCTL_TRY 5
296 int mib[7];
297 int ntry = 0;
298 size_t mibsize;
299 size_t len;
300 size_t needed;
301 size_t cnt;
302 int i;
303 char *buf;
304 struct in_addr *pina;
305 uint32_t *p;
306 uint32_t fmode;
307 const char *modestr;
308
309 mibsize = nitems(mib);
310 if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
311 perror("sysctlnametomib");
312 return;
313 }
314
315 needed = 0;
316 mib[5] = ifindex;
317 mib[6] = gina.s_addr; /* 32 bits wide */
318 mibsize = nitems(mib);
319 do {
320 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
321 perror("sysctl net.inet.ip.mcast.filters");
322 return;
323 }
324 if ((buf = malloc(needed)) == NULL) {
325 perror("malloc");
326 return;
327 }
328 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
329 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
330 perror("sysctl");
331 goto out_free;
332 }
333 free(buf);
334 buf = NULL;
335 }
336 } while (buf == NULL);
337
338 len = needed;
339 if (len < sizeof(uint32_t)) {
340 perror("sysctl");
341 goto out_free;
342 }
343
344 p = (uint32_t *)buf;
345 fmode = *p++;
346 len -= sizeof(uint32_t);
347
348 modestr = inm_mode(fmode);
349 if (modestr)
350 printf(" mode %s", modestr);
351 else
352 printf(" mode (%u)", fmode);
353
354 if (vflag == 0)
355 goto out_free;
356
357 cnt = len / sizeof(struct in_addr);
358 pina = (struct in_addr *)p;
359
360 for (i = 0; i < cnt; i++) {
361 if (i == 0)
362 printf(" srcs ");
363 fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
364 inet_ntoa(*pina++));
365 len -= sizeof(struct in_addr);
366 }
367 if (len > 0) {
368 fprintf(stderr, "warning: %u trailing bytes from %s\n",
369 (unsigned int)len, "net.inet.ip.mcast.filters");
370 }
371
372 out_free:
373 free(buf);
374 #undef MAX_SYSCTL_TRY
375 }
376
377 #endif /* INET */
378
379 #ifdef INET6
380 /*
381 * Retrieve MLD per-group source filter mode and lists via sysctl.
382 *
383 * Note: The 128-bit IPv6 group address needs to be segmented into
384 * 32-bit pieces for marshaling to sysctl. So the MIB name ends
385 * up looking like this:
386 * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
387 * Assumes that pgroup originated from the kernel, so its components
388 * are already in network-byte order.
389 */
390 static void
in6m_print_sources_sysctl(uint32_t ifindex,struct in6_addr * pgroup)391 in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
392 {
393 #define MAX_SYSCTL_TRY 5
394 char addrbuf[INET6_ADDRSTRLEN];
395 int mib[10];
396 int ntry = 0;
397 int *pi;
398 size_t mibsize;
399 size_t len;
400 size_t needed;
401 size_t cnt;
402 int i;
403 char *buf;
404 struct in6_addr *pina;
405 uint32_t *p;
406 uint32_t fmode;
407 const char *modestr;
408
409 mibsize = nitems(mib);
410 if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
411 &mibsize) == -1) {
412 perror("sysctlnametomib");
413 return;
414 }
415
416 needed = 0;
417 mib[5] = ifindex;
418 pi = (int *)pgroup;
419 for (i = 0; i < 4; i++)
420 mib[6 + i] = *pi++;
421
422 mibsize = nitems(mib);
423 do {
424 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
425 perror("sysctl net.inet6.ip6.mcast.filters");
426 return;
427 }
428 if ((buf = malloc(needed)) == NULL) {
429 perror("malloc");
430 return;
431 }
432 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
433 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
434 perror("sysctl");
435 goto out_free;
436 }
437 free(buf);
438 buf = NULL;
439 }
440 } while (buf == NULL);
441
442 len = needed;
443 if (len < sizeof(uint32_t)) {
444 perror("sysctl");
445 goto out_free;
446 }
447
448 p = (uint32_t *)buf;
449 fmode = *p++;
450 len -= sizeof(uint32_t);
451
452 modestr = inm_mode(fmode);
453 if (modestr)
454 printf(" mode %s", modestr);
455 else
456 printf(" mode (%u)", fmode);
457
458 if (vflag == 0)
459 goto out_free;
460
461 cnt = len / sizeof(struct in6_addr);
462 pina = (struct in6_addr *)p;
463
464 for (i = 0; i < cnt; i++) {
465 if (i == 0)
466 printf(" srcs ");
467 inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
468 INET6_ADDRSTRLEN);
469 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
470 len -= sizeof(struct in6_addr);
471 }
472 if (len > 0) {
473 fprintf(stderr, "warning: %u trailing bytes from %s\n",
474 (unsigned int)len, "net.inet6.ip6.mcast.filters");
475 }
476
477 out_free:
478 free(buf);
479 #undef MAX_SYSCTL_TRY
480 }
481 #endif /* INET6 */
482
483 static int
ifmcstat_getifmaddrs(void)484 ifmcstat_getifmaddrs(void)
485 {
486 char thisifname[IFNAMSIZ];
487 char addrbuf[NI_MAXHOST];
488 struct ifaddrs *ifap, *ifa;
489 struct ifmaddrs *ifmap, *ifma;
490 sockunion_t lastifasa;
491 sockunion_t *psa, *pgsa, *pllsa, *pifasa;
492 char *pcolon;
493 char *pafname;
494 uint32_t lastifindex, thisifindex;
495 int error;
496
497 error = 0;
498 ifap = NULL;
499 ifmap = NULL;
500 lastifindex = 0;
501 thisifindex = 0;
502 lastifasa.ss.ss_family = AF_UNSPEC;
503
504 if (getifaddrs(&ifap) != 0) {
505 warn("getifmaddrs");
506 return (-1);
507 }
508
509 if (getifmaddrs(&ifmap) != 0) {
510 warn("getifmaddrs");
511 error = -1;
512 goto out;
513 }
514
515 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
516 error = 0;
517 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
518 continue;
519
520 psa = (sockunion_t *)ifma->ifma_name;
521 if (psa->sa.sa_family != AF_LINK) {
522 fprintf(stderr,
523 "WARNING: Kernel returned invalid data.\n");
524 error = -1;
525 break;
526 }
527
528 /* Filter on interface name. */
529 thisifindex = psa->sdl.sdl_index;
530 if (ifindex != 0 && thisifindex != ifindex)
531 continue;
532
533 /* Filter on address family. */
534 pgsa = (sockunion_t *)ifma->ifma_addr;
535 if (af != 0 && pgsa->sa.sa_family != af)
536 continue;
537
538 strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
539 pcolon = strchr(thisifname, ':');
540 if (pcolon)
541 *pcolon = '\0';
542
543 /* Only print the banner for the first ifmaddrs entry. */
544 if (lastifindex == 0 || lastifindex != thisifindex) {
545 lastifindex = thisifindex;
546 fprintf(stdout, "%s:\n", thisifname);
547 }
548
549 /*
550 * Currently, multicast joins only take place on the
551 * primary IPv4 address, and only on the link-local IPv6
552 * address, as per IGMPv2/3 and MLDv1/2 semantics.
553 * Therefore, we only look up the primary address on
554 * the first pass.
555 */
556 pifasa = NULL;
557 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
558 if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
559 (ifa->ifa_addr == NULL) ||
560 (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
561 continue;
562 /*
563 * For AF_INET6 only the link-local address should
564 * be returned. If built without IPv6 support,
565 * skip this address entirely.
566 */
567 pifasa = (sockunion_t *)ifa->ifa_addr;
568 if (pifasa->sa.sa_family == AF_INET6
569 #ifdef INET6
570 && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
571 #endif
572 ) {
573 pifasa = NULL;
574 continue;
575 }
576 break;
577 }
578 if (pifasa == NULL)
579 continue; /* primary address not found */
580
581 if (!vflag && pifasa->sa.sa_family == AF_LINK)
582 continue;
583
584 /* Parse and print primary address, if not already printed. */
585 if (lastifasa.ss.ss_family == AF_UNSPEC ||
586 ((lastifasa.ss.ss_family == AF_LINK &&
587 !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
588 !sa_equal(&lastifasa.sa, &pifasa->sa))) {
589
590 switch (pifasa->sa.sa_family) {
591 case AF_INET:
592 pafname = "inet";
593 break;
594 case AF_INET6:
595 pafname = "inet6";
596 break;
597 case AF_LINK:
598 pafname = "link";
599 break;
600 default:
601 pafname = "unknown";
602 break;
603 }
604
605 switch (pifasa->sa.sa_family) {
606 case AF_INET6:
607 #ifdef INET6
608 {
609 const char *p =
610 inet6_n2a(&pifasa->sin6.sin6_addr,
611 pifasa->sin6.sin6_scope_id);
612 strlcpy(addrbuf, p, sizeof(addrbuf));
613 break;
614 }
615 #else
616 /* FALLTHROUGH */
617 #endif
618 case AF_INET:
619 case AF_LINK:
620 error = getnameinfo(&pifasa->sa,
621 pifasa->sa.sa_len,
622 addrbuf, sizeof(addrbuf), NULL, 0,
623 NI_NUMERICHOST);
624 if (error)
625 perror("getnameinfo");
626 break;
627 default:
628 addrbuf[0] = '\0';
629 break;
630 }
631
632 fprintf(stdout, "\t%s %s", pafname, addrbuf);
633 #ifdef INET6
634 if (pifasa->sa.sa_family == AF_INET6 &&
635 pifasa->sin6.sin6_scope_id)
636 fprintf(stdout, " scopeid 0x%x",
637 pifasa->sin6.sin6_scope_id);
638 #endif
639 fprintf(stdout, "\n");
640 #ifdef INET
641 /*
642 * Print per-link IGMP information, if available.
643 */
644 if (pifasa->sa.sa_family == AF_INET) {
645 struct igmp_ifinfo igi;
646 size_t mibsize, len;
647 int mib[5];
648
649 mibsize = nitems(mib);
650 if (sysctlnametomib("net.inet.igmp.ifinfo",
651 mib, &mibsize) == -1) {
652 perror("sysctlnametomib");
653 goto next_ifnet;
654 }
655 mib[mibsize] = thisifindex;
656 len = sizeof(struct igmp_ifinfo);
657 if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
658 0) == -1) {
659 perror("sysctl net.inet.igmp.ifinfo");
660 goto next_ifnet;
661 }
662 in_ifinfo(&igi);
663 }
664 #endif /* INET */
665 #ifdef INET6
666 /*
667 * Print per-link MLD information, if available.
668 */
669 if (pifasa->sa.sa_family == AF_INET6) {
670 struct mld_ifinfo mli;
671 size_t mibsize, len;
672 int mib[5];
673
674 mibsize = nitems(mib);
675 if (sysctlnametomib("net.inet6.mld.ifinfo",
676 mib, &mibsize) == -1) {
677 perror("sysctlnametomib");
678 goto next_ifnet;
679 }
680 mib[mibsize] = thisifindex;
681 len = sizeof(struct mld_ifinfo);
682 if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
683 0) == -1) {
684 perror("sysctl net.inet6.mld.ifinfo");
685 goto next_ifnet;
686 }
687 in6_ifinfo(&mli);
688 }
689 #endif /* INET6 */
690 #if defined(INET) || defined(INET6)
691 next_ifnet:
692 #endif
693 lastifasa = *pifasa;
694 }
695
696 /* Print this group address. */
697 #ifdef INET6
698 if (pgsa->sa.sa_family == AF_INET6) {
699 const char *p = inet6_n2a(&pgsa->sin6.sin6_addr,
700 pgsa->sin6.sin6_scope_id);
701 strlcpy(addrbuf, p, sizeof(addrbuf));
702 } else
703 #endif
704 {
705 error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
706 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
707 if (error)
708 perror("getnameinfo");
709 }
710
711 fprintf(stdout, "\t\tgroup %s", addrbuf);
712 #ifdef INET6
713 if (pgsa->sa.sa_family == AF_INET6 &&
714 pgsa->sin6.sin6_scope_id)
715 fprintf(stdout, " scopeid 0x%x",
716 pgsa->sin6.sin6_scope_id);
717 #endif
718 #ifdef INET
719 if (pgsa->sa.sa_family == AF_INET) {
720 inm_print_sources_sysctl(thisifindex,
721 pgsa->sin.sin_addr);
722 }
723 #endif
724 #ifdef INET6
725 if (pgsa->sa.sa_family == AF_INET6) {
726 in6m_print_sources_sysctl(thisifindex,
727 &pgsa->sin6.sin6_addr);
728 }
729 #endif
730 fprintf(stdout, "\n");
731
732 /* Link-layer mapping, if present. */
733 pllsa = (sockunion_t *)ifma->ifma_lladdr;
734 if (pllsa != NULL) {
735 error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
736 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
737 fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
738 }
739 }
740 out:
741 if (ifmap != NULL)
742 freeifmaddrs(ifmap);
743 if (ifap != NULL)
744 freeifaddrs(ifap);
745
746 return (error);
747 }
748