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(&pifasa->sa, pifasa->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 } 784