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/sysctl.h> 39 #include <sys/socket.h> 40 #include <sys/queue.h> 41 #include <sys/tree.h> 42 43 #include <net/if.h> 44 #include <net/if_types.h> 45 #include <net/if_dl.h> 46 #include <net/route.h> 47 48 #include <netinet/in.h> 49 #include <netinet/in_var.h> 50 #include <netinet/in_systm.h> 51 #include <netinet/ip.h> 52 #include <netinet/igmp.h> 53 #include <netinet/if_ether.h> 54 #include <netinet/igmp_var.h> 55 56 #ifdef INET6 57 #include <netinet/icmp6.h> 58 #include <netinet6/mld6_var.h> 59 #endif /* INET6 */ 60 61 #include <arpa/inet.h> 62 #include <netdb.h> 63 64 #include <stddef.h> 65 #include <stdarg.h> 66 #include <stdint.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 71 #include <ctype.h> 72 #include <err.h> 73 #include <errno.h> 74 #include <fcntl.h> 75 #include <limits.h> 76 #include <ifaddrs.h> 77 #include <sysexits.h> 78 #include <unistd.h> 79 80 #ifdef KVM 81 /* 82 * Currently the KVM build is broken. To be fixed it requires uncovering 83 * large amount of _KERNEL code in include files, and it is also very 84 * tentative to internal kernel ABI changes. If anyone wishes to restore 85 * it, please move it out of src/usr.sbin to src/tools/tools. 86 */ 87 #include <kvm.h> 88 #include <nlist.h> 89 #endif 90 91 /* XXX: This file currently assumes INET support in the base system. */ 92 #ifndef INET 93 #define INET 94 #endif 95 96 extern void printb(const char *, unsigned int, const char *); 97 98 union sockunion { 99 struct sockaddr_storage ss; 100 struct sockaddr sa; 101 struct sockaddr_dl sdl; 102 #ifdef INET 103 struct sockaddr_in sin; 104 #endif 105 #ifdef INET6 106 struct sockaddr_in6 sin6; 107 #endif 108 }; 109 typedef union sockunion sockunion_t; 110 111 uint32_t ifindex = 0; 112 int af = AF_UNSPEC; 113 #ifdef WITH_KVM 114 int Kflag = 0; 115 #endif 116 int vflag = 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 #endif /* INET */ 136 137 #ifdef INET6 138 static void if6_addrlist(struct ifaddr *); 139 static struct in6_multi * 140 in6_multientry(struct in6_multi *); 141 #endif /* INET6 */ 142 143 static void kread(u_long, void *, int); 144 static void ll_addrlist(struct ifaddr *); 145 146 static int ifmcstat_kvm(const char *kernel, const char *core); 147 148 #define KREAD(addr, buf, type) \ 149 kread((u_long)addr, (void *)buf, sizeof(type)) 150 151 kvm_t *kvmd; 152 struct nlist nl[] = { 153 { "_ifnet", 0, 0, 0, 0, }, 154 { "", 0, 0, 0, 0, }, 155 }; 156 #define N_IFNET 0 157 158 #endif /* WITH_KVM */ 159 160 static int ifmcstat_getifmaddrs(void); 161 #ifdef INET 162 static void in_ifinfo(struct igmp_ifinfo *); 163 static const char * inm_mode(u_int mode); 164 #endif 165 #ifdef INET6 166 static void in6_ifinfo(struct mld_ifinfo *); 167 static const char * inet6_n2a(struct in6_addr *, uint32_t); 168 #endif 169 int main(int, char **); 170 171 static void 172 usage() 173 { 174 175 fprintf(stderr, 176 "usage: ifmcstat [-i interface] [-f address family]" 177 " [-v]" 178 #ifdef WITH_KVM 179 " [-K] [-M core] [-N system]" 180 #endif 181 "\n"); 182 exit(EX_USAGE); 183 } 184 185 static const char *options = "i:f:vM:N:" 186 #ifdef WITH_KVM 187 "K" 188 #endif 189 ; 190 191 int 192 main(int argc, char **argv) 193 { 194 int c, error; 195 #ifdef WITH_KVM 196 const char *kernel = NULL; 197 const char *core = NULL; 198 #endif 199 200 while ((c = getopt(argc, argv, options)) != -1) { 201 switch (c) { 202 case 'i': 203 if ((ifindex = if_nametoindex(optarg)) == 0) { 204 fprintf(stderr, "%s: unknown interface\n", 205 optarg); 206 exit(EX_NOHOST); 207 } 208 break; 209 210 case 'f': 211 #ifdef INET 212 if (strcmp(optarg, "inet") == 0) { 213 af = AF_INET; 214 break; 215 } 216 #endif 217 #ifdef INET6 218 if (strcmp(optarg, "inet6") == 0) { 219 af = AF_INET6; 220 break; 221 } 222 #endif 223 if (strcmp(optarg, "link") == 0) { 224 af = AF_LINK; 225 break; 226 } 227 fprintf(stderr, "%s: unknown address family\n", optarg); 228 exit(EX_USAGE); 229 /*NOTREACHED*/ 230 break; 231 232 #ifdef WITH_KVM 233 case 'K': 234 ++Kflag; 235 break; 236 #endif 237 238 case 'v': 239 ++vflag; 240 break; 241 242 #ifdef WITH_KVM 243 case 'M': 244 core = strdup(optarg); 245 break; 246 247 case 'N': 248 kernel = strdup(optarg); 249 break; 250 #endif 251 252 default: 253 usage(); 254 break; 255 /*NOTREACHED*/ 256 } 257 } 258 259 if (af == AF_LINK && vflag) 260 usage(); 261 262 #ifdef WITH_KVM 263 if (Kflag) 264 error = ifmcstat_kvm(kernel, core); 265 /* 266 * If KVM failed, and user did not explicitly specify a core file, 267 * or force KVM backend to be disabled, try the sysctl backend. 268 */ 269 if (!Kflag || (error != 0 && (core == NULL && kernel == NULL))) 270 #endif 271 error = ifmcstat_getifmaddrs(); 272 if (error != 0) 273 exit(EX_OSERR); 274 275 exit(EX_OK); 276 /*NOTREACHED*/ 277 } 278 279 #ifdef INET 280 281 static void 282 in_ifinfo(struct igmp_ifinfo *igi) 283 { 284 285 printf("\t"); 286 switch (igi->igi_version) { 287 case IGMP_VERSION_1: 288 case IGMP_VERSION_2: 289 case IGMP_VERSION_3: 290 printf("igmpv%d", igi->igi_version); 291 break; 292 default: 293 printf("igmpv?(%d)", igi->igi_version); 294 break; 295 } 296 if (igi->igi_flags) 297 printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); 298 if (igi->igi_version == IGMP_VERSION_3) { 299 printf(" rv %u qi %u qri %u uri %u", 300 igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); 301 } 302 if (vflag >= 2) { 303 printf(" v1timer %u v2timer %u v3timer %u", 304 igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); 305 } 306 printf("\n"); 307 } 308 309 static const char *inm_modes[] = { 310 "undefined", 311 "include", 312 "exclude", 313 }; 314 315 static const char * 316 inm_mode(u_int mode) 317 { 318 319 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 320 return (inm_modes[mode]); 321 return (NULL); 322 } 323 324 #endif /* INET */ 325 326 #ifdef WITH_KVM 327 328 static int 329 ifmcstat_kvm(const char *kernel, const char *core) 330 { 331 char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ]; 332 struct ifnet *ifp, *nifp, ifnet; 333 334 if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) == 335 NULL) { 336 perror("kvm_openfiles"); 337 return (-1); 338 } 339 if (kvm_nlist(kvmd, nl) < 0) { 340 perror("kvm_nlist"); 341 return (-1); 342 } 343 if (nl[N_IFNET].n_value == 0) { 344 printf("symbol %s not found\n", nl[N_IFNET].n_name); 345 return (-1); 346 } 347 KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); 348 while (ifp) { 349 KREAD(ifp, &ifnet, struct ifnet); 350 nifp = ifnet.if_link.tqe_next; 351 if (ifindex && ifindex != ifnet.if_index) 352 goto next; 353 354 printf("%s:\n", if_indextoname(ifnet.if_index, ifname)); 355 #ifdef INET 356 if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); 357 #endif 358 #ifdef INET6 359 if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); 360 #endif 361 if (vflag) 362 ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); 363 next: 364 ifp = nifp; 365 } 366 367 return (0); 368 } 369 370 static void 371 kread(u_long addr, void *buf, int len) 372 { 373 374 if (kvm_read(kvmd, addr, buf, len) != len) { 375 perror("kvm_read"); 376 exit(EX_OSERR); 377 } 378 } 379 380 static void 381 ll_addrlist(struct ifaddr *ifap) 382 { 383 char addrbuf[NI_MAXHOST]; 384 struct ifaddr ifa; 385 struct sockaddr sa; 386 struct sockaddr_dl sdl; 387 struct ifaddr *ifap0; 388 389 if (af && af != AF_LINK) 390 return; 391 392 ifap0 = ifap; 393 while (ifap) { 394 KREAD(ifap, &ifa, struct ifaddr); 395 if (ifa.ifa_addr == NULL) 396 goto nextifap; 397 KREAD(ifa.ifa_addr, &sa, struct sockaddr); 398 if (sa.sa_family != PF_LINK) 399 goto nextifap; 400 KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl); 401 if (sdl.sdl_alen == 0) 402 goto nextifap; 403 addrbuf[0] = '\0'; 404 getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len, 405 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 406 printf("\tlink %s\n", addrbuf); 407 nextifap: 408 ifap = ifa.ifa_link.tqe_next; 409 } 410 if (ifap0) { 411 struct ifnet ifnet; 412 struct ifmultiaddr ifm, *ifmp = 0; 413 414 KREAD(ifap0, &ifa, struct ifaddr); 415 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 416 if (TAILQ_FIRST(&ifnet.if_multiaddrs)) 417 ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); 418 while (ifmp) { 419 KREAD(ifmp, &ifm, struct ifmultiaddr); 420 if (ifm.ifma_addr == NULL) 421 goto nextmulti; 422 KREAD(ifm.ifma_addr, &sa, struct sockaddr); 423 if (sa.sa_family != AF_LINK) 424 goto nextmulti; 425 KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl); 426 addrbuf[0] = '\0'; 427 getnameinfo((struct sockaddr *)&sdl, 428 sdl.sdl_len, addrbuf, sizeof(addrbuf), 429 NULL, 0, NI_NUMERICHOST); 430 printf("\t\tgroup %s refcnt %d\n", 431 addrbuf, ifm.ifma_refcount); 432 nextmulti: 433 ifmp = TAILQ_NEXT(&ifm, ifma_link); 434 } 435 } 436 } 437 438 #ifdef INET6 439 440 static void 441 if6_addrlist(struct ifaddr *ifap) 442 { 443 struct ifnet ifnet; 444 struct ifaddr ifa; 445 struct sockaddr sa; 446 struct in6_ifaddr if6a; 447 struct ifaddr *ifap0; 448 449 if (af && af != AF_INET6) 450 return; 451 ifap0 = ifap; 452 while (ifap) { 453 KREAD(ifap, &ifa, struct ifaddr); 454 if (ifa.ifa_addr == NULL) 455 goto nextifap; 456 KREAD(ifa.ifa_addr, &sa, struct sockaddr); 457 if (sa.sa_family != PF_INET6) 458 goto nextifap; 459 KREAD(ifap, &if6a, struct in6_ifaddr); 460 printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr, 461 if6a.ia_addr.sin6_scope_id)); 462 /* 463 * Print per-link MLD information, if available. 464 */ 465 if (ifa.ifa_ifp != NULL) { 466 struct in6_ifextra ie; 467 struct mld_ifinfo mli; 468 469 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 470 KREAD(ifnet.if_afdata[AF_INET6], &ie, 471 struct in6_ifextra); 472 if (ie.mld_ifinfo != NULL) { 473 KREAD(ie.mld_ifinfo, &mli, struct mld_ifinfo); 474 in6_ifinfo(&mli); 475 } 476 } 477 nextifap: 478 ifap = ifa.ifa_link.tqe_next; 479 } 480 if (ifap0) { 481 struct ifnet ifnet; 482 struct ifmultiaddr ifm, *ifmp = 0; 483 struct sockaddr_dl sdl; 484 485 KREAD(ifap0, &ifa, struct ifaddr); 486 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 487 if (TAILQ_FIRST(&ifnet.if_multiaddrs)) 488 ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); 489 while (ifmp) { 490 KREAD(ifmp, &ifm, struct ifmultiaddr); 491 if (ifm.ifma_addr == NULL) 492 goto nextmulti; 493 KREAD(ifm.ifma_addr, &sa, struct sockaddr); 494 if (sa.sa_family != AF_INET6) 495 goto nextmulti; 496 (void)in6_multientry((struct in6_multi *) 497 ifm.ifma_protospec); 498 if (ifm.ifma_lladdr == 0) 499 goto nextmulti; 500 KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); 501 printf("\t\t\tmcast-macaddr %s refcnt %d\n", 502 ether_ntoa((struct ether_addr *)LLADDR(&sdl)), 503 ifm.ifma_refcount); 504 nextmulti: 505 ifmp = TAILQ_NEXT(&ifm, ifma_link); 506 } 507 } 508 } 509 510 static struct in6_multi * 511 in6_multientry(struct in6_multi *mc) 512 { 513 struct in6_multi multi; 514 515 KREAD(mc, &multi, struct in6_multi); 516 printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr, 0)); 517 printf(" refcnt %u\n", multi.in6m_refcount); 518 519 return (multi.in6m_entry.le_next); 520 } 521 522 #endif /* INET6 */ 523 524 #ifdef INET 525 526 static void 527 if_addrlist(struct ifaddr *ifap) 528 { 529 struct ifaddr ifa; 530 struct ifnet ifnet; 531 struct sockaddr sa; 532 struct in_ifaddr ia; 533 struct ifaddr *ifap0; 534 535 if (af && af != AF_INET) 536 return; 537 ifap0 = ifap; 538 while (ifap) { 539 KREAD(ifap, &ifa, struct ifaddr); 540 if (ifa.ifa_addr == NULL) 541 goto nextifap; 542 KREAD(ifa.ifa_addr, &sa, struct sockaddr); 543 if (sa.sa_family != PF_INET) 544 goto nextifap; 545 KREAD(ifap, &ia, struct in_ifaddr); 546 printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr)); 547 /* 548 * Print per-link IGMP information, if available. 549 */ 550 if (ifa.ifa_ifp != NULL) { 551 struct in_ifinfo ii; 552 struct igmp_ifinfo igi; 553 554 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 555 KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo); 556 if (ii.ii_igmp != NULL) { 557 KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo); 558 in_ifinfo(&igi); 559 } 560 } 561 nextifap: 562 ifap = ifa.ifa_link.tqe_next; 563 } 564 if (ifap0) { 565 struct ifmultiaddr ifm, *ifmp = 0; 566 struct sockaddr_dl sdl; 567 568 KREAD(ifap0, &ifa, struct ifaddr); 569 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 570 if (TAILQ_FIRST(&ifnet.if_multiaddrs)) 571 ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); 572 while (ifmp) { 573 KREAD(ifmp, &ifm, struct ifmultiaddr); 574 if (ifm.ifma_addr == NULL) 575 goto nextmulti; 576 KREAD(ifm.ifma_addr, &sa, struct sockaddr); 577 if (sa.sa_family != AF_INET) 578 goto nextmulti; 579 (void)in_multientry((struct in_multi *) 580 ifm.ifma_protospec); 581 if (ifm.ifma_lladdr == 0) 582 goto nextmulti; 583 KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); 584 printf("\t\t\tmcast-macaddr %s refcnt %d\n", 585 ether_ntoa((struct ether_addr *)LLADDR(&sdl)), 586 ifm.ifma_refcount); 587 nextmulti: 588 ifmp = TAILQ_NEXT(&ifm, ifma_link); 589 } 590 } 591 } 592 593 static const char *inm_states[] = { 594 "not-member", 595 "silent", 596 "idle", 597 "lazy", 598 "sleeping", 599 "awakening", 600 "query-pending", 601 "sg-query-pending", 602 "leaving" 603 }; 604 605 static const char * 606 inm_state(u_int state) 607 { 608 609 if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) 610 return (inm_states[state]); 611 return (NULL); 612 } 613 614 #if 0 615 static struct ip_msource * 616 ims_min_kvm(struct in_multi *pinm) 617 { 618 struct ip_msource ims0; 619 struct ip_msource *tmp, *parent; 620 621 parent = NULL; 622 tmp = RB_ROOT(&pinm->inm_srcs); 623 while (tmp) { 624 parent = tmp; 625 KREAD(tmp, &ims0, struct ip_msource); 626 tmp = RB_LEFT(&ims0, ims_link); 627 } 628 return (parent); /* kva */ 629 } 630 631 /* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */ 632 static struct ip_msource * 633 ims_next_kvm(struct ip_msource *ims) 634 { 635 struct ip_msource ims0, ims1; 636 struct ip_msource *tmp; 637 638 KREAD(ims, &ims0, struct ip_msource); 639 if (RB_RIGHT(&ims0, ims_link)) { 640 ims = RB_RIGHT(&ims0, ims_link); 641 KREAD(ims, &ims1, struct ip_msource); 642 while ((tmp = RB_LEFT(&ims1, ims_link))) { 643 KREAD(tmp, &ims0, struct ip_msource); 644 ims = RB_LEFT(&ims0, ims_link); 645 } 646 } else { 647 tmp = RB_PARENT(&ims0, ims_link); 648 if (tmp) { 649 KREAD(tmp, &ims1, struct ip_msource); 650 if (ims == RB_LEFT(&ims1, ims_link)) 651 ims = tmp; 652 } else { 653 while ((tmp = RB_PARENT(&ims0, ims_link))) { 654 KREAD(tmp, &ims1, struct ip_msource); 655 if (ims == RB_RIGHT(&ims1, ims_link)) { 656 ims = tmp; 657 KREAD(ims, &ims0, struct ip_msource); 658 } else 659 break; 660 } 661 ims = RB_PARENT(&ims0, ims_link); 662 } 663 } 664 return (ims); /* kva */ 665 } 666 667 static void 668 inm_print_sources_kvm(struct in_multi *pinm) 669 { 670 struct ip_msource ims0; 671 struct ip_msource *ims; 672 struct in_addr src; 673 int cnt; 674 uint8_t fmode; 675 676 cnt = 0; 677 fmode = pinm->inm_st[1].iss_fmode; 678 if (fmode == MCAST_UNDEFINED) 679 return; 680 for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) { 681 if (cnt == 0) 682 printf(" srcs "); 683 KREAD(ims, &ims0, struct ip_msource); 684 /* Only print sources in-mode at t1. */ 685 if (fmode != ims_get_mode(pinm, ims, 1)) 686 continue; 687 src.s_addr = htonl(ims0.ims_haddr); 688 printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src)); 689 } 690 } 691 #endif 692 693 static struct in_multi * 694 in_multientry(struct in_multi *pinm) 695 { 696 struct in_multi inm; 697 const char *state, *mode; 698 699 KREAD(pinm, &inm, struct in_multi); 700 printf("\t\tgroup %s", inet_ntoa(inm.inm_addr)); 701 printf(" refcnt %u", inm.inm_refcount); 702 703 state = inm_state(inm.inm_state); 704 if (state) 705 printf(" state %s", state); 706 else 707 printf(" state (%d)", inm.inm_state); 708 709 mode = inm_mode(inm.inm_st[1].iss_fmode); 710 if (mode) 711 printf(" mode %s", mode); 712 else 713 printf(" mode (%d)", inm.inm_st[1].iss_fmode); 714 715 if (vflag >= 2) { 716 printf(" asm %u ex %u in %u rec %u", 717 (u_int)inm.inm_st[1].iss_asm, 718 (u_int)inm.inm_st[1].iss_ex, 719 (u_int)inm.inm_st[1].iss_in, 720 (u_int)inm.inm_st[1].iss_rec); 721 } 722 723 #if 0 724 /* Buggy. */ 725 if (vflag) 726 inm_print_sources_kvm(&inm); 727 #endif 728 729 printf("\n"); 730 return (NULL); 731 } 732 733 #endif /* INET */ 734 735 #endif /* WITH_KVM */ 736 737 #ifdef INET6 738 739 static void 740 in6_ifinfo(struct mld_ifinfo *mli) 741 { 742 743 printf("\t"); 744 switch (mli->mli_version) { 745 case MLD_VERSION_1: 746 case MLD_VERSION_2: 747 printf("mldv%d", mli->mli_version); 748 break; 749 default: 750 printf("mldv?(%d)", mli->mli_version); 751 break; 752 } 753 if (mli->mli_flags) 754 printb(" flags", mli->mli_flags, "\020\1SILENT\2USEALLOW"); 755 if (mli->mli_version == MLD_VERSION_2) { 756 printf(" rv %u qi %u qri %u uri %u", 757 mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri); 758 } 759 if (vflag >= 2) { 760 printf(" v1timer %u v2timer %u", mli->mli_v1_timer, 761 mli->mli_v2_timer); 762 } 763 printf("\n"); 764 } 765 766 static const char * 767 inet6_n2a(struct in6_addr *p, uint32_t scope_id) 768 { 769 static char buf[NI_MAXHOST]; 770 struct sockaddr_in6 sin6; 771 const int niflags = NI_NUMERICHOST; 772 773 memset(&sin6, 0, sizeof(sin6)); 774 sin6.sin6_family = AF_INET6; 775 sin6.sin6_len = sizeof(struct sockaddr_in6); 776 sin6.sin6_addr = *p; 777 sin6.sin6_scope_id = scope_id; 778 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 779 buf, sizeof(buf), NULL, 0, niflags) == 0) { 780 return (buf); 781 } else { 782 return ("(invalid)"); 783 } 784 } 785 #endif /* INET6 */ 786 787 #ifdef INET 788 /* 789 * Retrieve per-group source filter mode and lists via sysctl. 790 */ 791 static void 792 inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) 793 { 794 #define MAX_SYSCTL_TRY 5 795 int mib[7]; 796 int ntry = 0; 797 size_t mibsize; 798 size_t len; 799 size_t needed; 800 size_t cnt; 801 int i; 802 char *buf; 803 struct in_addr *pina; 804 uint32_t *p; 805 uint32_t fmode; 806 const char *modestr; 807 808 mibsize = nitems(mib); 809 if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { 810 perror("sysctlnametomib"); 811 return; 812 } 813 814 needed = 0; 815 mib[5] = ifindex; 816 mib[6] = gina.s_addr; /* 32 bits wide */ 817 mibsize = nitems(mib); 818 do { 819 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { 820 perror("sysctl net.inet.ip.mcast.filters"); 821 return; 822 } 823 if ((buf = malloc(needed)) == NULL) { 824 perror("malloc"); 825 return; 826 } 827 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { 828 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 829 perror("sysctl"); 830 goto out_free; 831 } 832 free(buf); 833 buf = NULL; 834 } 835 } while (buf == NULL); 836 837 len = needed; 838 if (len < sizeof(uint32_t)) { 839 perror("sysctl"); 840 goto out_free; 841 } 842 843 p = (uint32_t *)buf; 844 fmode = *p++; 845 len -= sizeof(uint32_t); 846 847 modestr = inm_mode(fmode); 848 if (modestr) 849 printf(" mode %s", modestr); 850 else 851 printf(" mode (%u)", fmode); 852 853 if (vflag == 0) 854 goto out_free; 855 856 cnt = len / sizeof(struct in_addr); 857 pina = (struct in_addr *)p; 858 859 for (i = 0; i < cnt; i++) { 860 if (i == 0) 861 printf(" srcs "); 862 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), 863 inet_ntoa(*pina++)); 864 len -= sizeof(struct in_addr); 865 } 866 if (len > 0) { 867 fprintf(stderr, "warning: %u trailing bytes from %s\n", 868 (unsigned int)len, "net.inet.ip.mcast.filters"); 869 } 870 871 out_free: 872 free(buf); 873 #undef MAX_SYSCTL_TRY 874 } 875 876 #endif /* INET */ 877 878 #ifdef INET6 879 /* 880 * Retrieve MLD per-group source filter mode and lists via sysctl. 881 * 882 * Note: The 128-bit IPv6 group address needs to be segmented into 883 * 32-bit pieces for marshaling to sysctl. So the MIB name ends 884 * up looking like this: 885 * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3] 886 * Assumes that pgroup originated from the kernel, so its components 887 * are already in network-byte order. 888 */ 889 static void 890 in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup) 891 { 892 #define MAX_SYSCTL_TRY 5 893 char addrbuf[INET6_ADDRSTRLEN]; 894 int mib[10]; 895 int ntry = 0; 896 int *pi; 897 size_t mibsize; 898 size_t len; 899 size_t needed; 900 size_t cnt; 901 int i; 902 char *buf; 903 struct in6_addr *pina; 904 uint32_t *p; 905 uint32_t fmode; 906 const char *modestr; 907 908 mibsize = nitems(mib); 909 if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib, 910 &mibsize) == -1) { 911 perror("sysctlnametomib"); 912 return; 913 } 914 915 needed = 0; 916 mib[5] = ifindex; 917 pi = (int *)pgroup; 918 for (i = 0; i < 4; i++) 919 mib[6 + i] = *pi++; 920 921 mibsize = nitems(mib); 922 do { 923 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { 924 perror("sysctl net.inet6.ip6.mcast.filters"); 925 return; 926 } 927 if ((buf = malloc(needed)) == NULL) { 928 perror("malloc"); 929 return; 930 } 931 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { 932 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 933 perror("sysctl"); 934 goto out_free; 935 } 936 free(buf); 937 buf = NULL; 938 } 939 } while (buf == NULL); 940 941 len = needed; 942 if (len < sizeof(uint32_t)) { 943 perror("sysctl"); 944 goto out_free; 945 } 946 947 p = (uint32_t *)buf; 948 fmode = *p++; 949 len -= sizeof(uint32_t); 950 951 modestr = inm_mode(fmode); 952 if (modestr) 953 printf(" mode %s", modestr); 954 else 955 printf(" mode (%u)", fmode); 956 957 if (vflag == 0) 958 goto out_free; 959 960 cnt = len / sizeof(struct in6_addr); 961 pina = (struct in6_addr *)p; 962 963 for (i = 0; i < cnt; i++) { 964 if (i == 0) 965 printf(" srcs "); 966 inet_ntop(AF_INET6, (const char *)pina++, addrbuf, 967 INET6_ADDRSTRLEN); 968 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf); 969 len -= sizeof(struct in6_addr); 970 } 971 if (len > 0) { 972 fprintf(stderr, "warning: %u trailing bytes from %s\n", 973 (unsigned int)len, "net.inet6.ip6.mcast.filters"); 974 } 975 976 out_free: 977 free(buf); 978 #undef MAX_SYSCTL_TRY 979 } 980 #endif /* INET6 */ 981 982 static int 983 ifmcstat_getifmaddrs(void) 984 { 985 char thisifname[IFNAMSIZ]; 986 char addrbuf[NI_MAXHOST]; 987 struct ifaddrs *ifap, *ifa; 988 struct ifmaddrs *ifmap, *ifma; 989 sockunion_t lastifasa; 990 sockunion_t *psa, *pgsa, *pllsa, *pifasa; 991 char *pcolon; 992 char *pafname; 993 uint32_t lastifindex, thisifindex; 994 int error; 995 996 error = 0; 997 ifap = NULL; 998 ifmap = NULL; 999 lastifindex = 0; 1000 thisifindex = 0; 1001 lastifasa.ss.ss_family = AF_UNSPEC; 1002 1003 if (getifaddrs(&ifap) != 0) { 1004 warn("getifmaddrs"); 1005 return (-1); 1006 } 1007 1008 if (getifmaddrs(&ifmap) != 0) { 1009 warn("getifmaddrs"); 1010 error = -1; 1011 goto out; 1012 } 1013 1014 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { 1015 error = 0; 1016 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) 1017 continue; 1018 1019 psa = (sockunion_t *)ifma->ifma_name; 1020 if (psa->sa.sa_family != AF_LINK) { 1021 fprintf(stderr, 1022 "WARNING: Kernel returned invalid data.\n"); 1023 error = -1; 1024 break; 1025 } 1026 1027 /* Filter on interface name. */ 1028 thisifindex = psa->sdl.sdl_index; 1029 if (ifindex != 0 && thisifindex != ifindex) 1030 continue; 1031 1032 /* Filter on address family. */ 1033 pgsa = (sockunion_t *)ifma->ifma_addr; 1034 if (af != 0 && pgsa->sa.sa_family != af) 1035 continue; 1036 1037 strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ); 1038 pcolon = strchr(thisifname, ':'); 1039 if (pcolon) 1040 *pcolon = '\0'; 1041 1042 /* Only print the banner for the first ifmaddrs entry. */ 1043 if (lastifindex == 0 || lastifindex != thisifindex) { 1044 lastifindex = thisifindex; 1045 fprintf(stdout, "%s:\n", thisifname); 1046 } 1047 1048 /* 1049 * Currently, multicast joins only take place on the 1050 * primary IPv4 address, and only on the link-local IPv6 1051 * address, as per IGMPv2/3 and MLDv1/2 semantics. 1052 * Therefore, we only look up the primary address on 1053 * the first pass. 1054 */ 1055 pifasa = NULL; 1056 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 1057 if ((strcmp(ifa->ifa_name, thisifname) != 0) || 1058 (ifa->ifa_addr == NULL) || 1059 (ifa->ifa_addr->sa_family != pgsa->sa.sa_family)) 1060 continue; 1061 /* 1062 * For AF_INET6 only the link-local address should 1063 * be returned. If built without IPv6 support, 1064 * skip this address entirely. 1065 */ 1066 pifasa = (sockunion_t *)ifa->ifa_addr; 1067 if (pifasa->sa.sa_family == AF_INET6 1068 #ifdef INET6 1069 && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr) 1070 #endif 1071 ) { 1072 pifasa = NULL; 1073 continue; 1074 } 1075 break; 1076 } 1077 if (pifasa == NULL) 1078 continue; /* primary address not found */ 1079 1080 if (!vflag && pifasa->sa.sa_family == AF_LINK) 1081 continue; 1082 1083 /* Parse and print primary address, if not already printed. */ 1084 if (lastifasa.ss.ss_family == AF_UNSPEC || 1085 ((lastifasa.ss.ss_family == AF_LINK && 1086 !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) || 1087 !sa_equal(&lastifasa.sa, &pifasa->sa))) { 1088 1089 switch (pifasa->sa.sa_family) { 1090 case AF_INET: 1091 pafname = "inet"; 1092 break; 1093 case AF_INET6: 1094 pafname = "inet6"; 1095 break; 1096 case AF_LINK: 1097 pafname = "link"; 1098 break; 1099 default: 1100 pafname = "unknown"; 1101 break; 1102 } 1103 1104 switch (pifasa->sa.sa_family) { 1105 case AF_INET6: 1106 #ifdef INET6 1107 { 1108 const char *p = 1109 inet6_n2a(&pifasa->sin6.sin6_addr, 1110 pifasa->sin6.sin6_scope_id); 1111 strlcpy(addrbuf, p, sizeof(addrbuf)); 1112 break; 1113 } 1114 #else 1115 /* FALLTHROUGH */ 1116 #endif 1117 case AF_INET: 1118 case AF_LINK: 1119 error = getnameinfo(&pifasa->sa, 1120 pifasa->sa.sa_len, 1121 addrbuf, sizeof(addrbuf), NULL, 0, 1122 NI_NUMERICHOST); 1123 if (error) 1124 perror("getnameinfo"); 1125 break; 1126 default: 1127 addrbuf[0] = '\0'; 1128 break; 1129 } 1130 1131 fprintf(stdout, "\t%s %s", pafname, addrbuf); 1132 #ifdef INET6 1133 if (pifasa->sa.sa_family == AF_INET6 && 1134 pifasa->sin6.sin6_scope_id) 1135 fprintf(stdout, " scopeid 0x%x", 1136 pifasa->sin6.sin6_scope_id); 1137 #endif 1138 fprintf(stdout, "\n"); 1139 #ifdef INET 1140 /* 1141 * Print per-link IGMP information, if available. 1142 */ 1143 if (pifasa->sa.sa_family == AF_INET) { 1144 struct igmp_ifinfo igi; 1145 size_t mibsize, len; 1146 int mib[5]; 1147 1148 mibsize = nitems(mib); 1149 if (sysctlnametomib("net.inet.igmp.ifinfo", 1150 mib, &mibsize) == -1) { 1151 perror("sysctlnametomib"); 1152 goto next_ifnet; 1153 } 1154 mib[mibsize] = thisifindex; 1155 len = sizeof(struct igmp_ifinfo); 1156 if (sysctl(mib, mibsize + 1, &igi, &len, NULL, 1157 0) == -1) { 1158 perror("sysctl net.inet.igmp.ifinfo"); 1159 goto next_ifnet; 1160 } 1161 in_ifinfo(&igi); 1162 } 1163 #endif /* INET */ 1164 #ifdef INET6 1165 /* 1166 * Print per-link MLD information, if available. 1167 */ 1168 if (pifasa->sa.sa_family == AF_INET6) { 1169 struct mld_ifinfo mli; 1170 size_t mibsize, len; 1171 int mib[5]; 1172 1173 mibsize = nitems(mib); 1174 if (sysctlnametomib("net.inet6.mld.ifinfo", 1175 mib, &mibsize) == -1) { 1176 perror("sysctlnametomib"); 1177 goto next_ifnet; 1178 } 1179 mib[mibsize] = thisifindex; 1180 len = sizeof(struct mld_ifinfo); 1181 if (sysctl(mib, mibsize + 1, &mli, &len, NULL, 1182 0) == -1) { 1183 perror("sysctl net.inet6.mld.ifinfo"); 1184 goto next_ifnet; 1185 } 1186 in6_ifinfo(&mli); 1187 } 1188 #endif /* INET6 */ 1189 #if defined(INET) || defined(INET6) 1190 next_ifnet: 1191 #endif 1192 lastifasa = *pifasa; 1193 } 1194 1195 /* Print this group address. */ 1196 #ifdef INET6 1197 if (pgsa->sa.sa_family == AF_INET6) { 1198 const char *p = inet6_n2a(&pgsa->sin6.sin6_addr, 1199 pgsa->sin6.sin6_scope_id); 1200 strlcpy(addrbuf, p, sizeof(addrbuf)); 1201 } else 1202 #endif 1203 { 1204 error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, 1205 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 1206 if (error) 1207 perror("getnameinfo"); 1208 } 1209 1210 fprintf(stdout, "\t\tgroup %s", addrbuf); 1211 #ifdef INET6 1212 if (pgsa->sa.sa_family == AF_INET6 && 1213 pgsa->sin6.sin6_scope_id) 1214 fprintf(stdout, " scopeid 0x%x", 1215 pgsa->sin6.sin6_scope_id); 1216 #endif 1217 #ifdef INET 1218 if (pgsa->sa.sa_family == AF_INET) { 1219 inm_print_sources_sysctl(thisifindex, 1220 pgsa->sin.sin_addr); 1221 } 1222 #endif 1223 #ifdef INET6 1224 if (pgsa->sa.sa_family == AF_INET6) { 1225 in6m_print_sources_sysctl(thisifindex, 1226 &pgsa->sin6.sin6_addr); 1227 } 1228 #endif 1229 fprintf(stdout, "\n"); 1230 1231 /* Link-layer mapping, if present. */ 1232 pllsa = (sockunion_t *)ifma->ifma_lladdr; 1233 if (pllsa != NULL) { 1234 error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len, 1235 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 1236 fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf); 1237 } 1238 } 1239 out: 1240 if (ifmap != NULL) 1241 freeifmaddrs(ifmap); 1242 if (ifap != NULL) 1243 freeifaddrs(ifap); 1244 1245 return (error); 1246 } 1247