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 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:vM:N:"; 131 132 int 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 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 * 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 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 * 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 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 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 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