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