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