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