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