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