ifmcstat.c revision 168560
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: head/usr.sbin/ifmcstat/ifmcstat.c 168560 2007-04-10 00:26:12Z bms $"); 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 99union 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}; 110typedef union sockunion sockunion_t; 111 112uint32_t ifindex = 0; 113int 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 132static void if_addrlist(struct ifaddr *); 133static struct in_multi * 134 in_multientry(struct in_multi *); 135#ifdef HAVE_IGMPV3 136static void in_addr_slistentry(struct in_addr_slist *, char *); 137#endif 138#endif /* INET */ 139 140#ifdef INET6 141static void if6_addrlist(struct ifaddr *); 142static struct in6_multi * 143 in6_multientry(struct in6_multi *); 144#ifdef HAVE_MLDV2 145static void in6_addr_slistentry(struct in6_addr_slist *, char *); 146#endif 147static const char * inet6_n2a(struct in6_addr *); 148#endif /* INET6 */ 149 150static void kread(u_long, void *, int); 151static 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 156kvm_t *kvmd; 157struct 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 165static int ifmcstat_getifmaddrs(void); 166int main(int, char **); 167 168int 169main(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 250static int 251ifmcstat_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 283next: 284 ifp = nifp; 285 } 286 287 return (0); 288} 289 290static void 291kread(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 302static const char * 303inet6_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 330static void 331if6_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 383static struct in6_multi * 384in6_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 437static void 438in6_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 473static void 474if_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 526static struct in_multi * 527in_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 587static void 588in_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 622static int 623ifmcstat_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 } 776out: 777 if (ifmap != NULL) 778 freeifmaddrs(ifmap); 779 if (ifap != NULL) 780 freeifaddrs(ifap); 781 782 return (error); 783} 784