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