1/* 2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org> 30 * All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 */ 54 55#include <sys/cdefs.h> 56 57/* 58 * Print the running system's current multicast group memberships. 59 * As this relies on getifmaddrs(), it may not be used with a core file. 60 */ 61 62#include <sys/types.h> 63#include <sys/param.h> 64#include <sys/sysctl.h> 65#include <sys/ioctl.h> 66#include <sys/socket.h> 67#include <sys/errno.h> 68 69#include <net/if.h> 70#include <net/if_var.h> 71#include <net/if_mib.h> 72#include <net/if_types.h> 73#include <net/if_dl.h> 74#include <net/route.h> 75#include <netinet/in.h> 76#include <netinet/if_ether.h> 77#include <netinet/igmp_var.h> 78#include <netinet6/mld6_var.h> 79#include <arpa/inet.h> 80#include <netdb.h> 81 82#include <ctype.h> 83#include <err.h> 84#include <ifaddrs.h> 85#include <sysexits.h> 86 87#include <stddef.h> 88#include <stdarg.h> 89#include <stdlib.h> 90#include <stdint.h> 91#include <stdio.h> 92#include <string.h> 93#include <ifaddrs.h> 94 95 96#include "netstat.h" 97 98union sockunion { 99 struct sockaddr_storage ss; 100 struct sockaddr sa; 101 struct sockaddr_dl sdl; 102 struct sockaddr_in sin; 103 struct sockaddr_in6 sin6; 104}; 105typedef union sockunion sockunion_t; 106 107/* 108 * This may have been defined in <net/if.h>. Note that if <net/if.h> is 109 * to be included it must be included before this header file. 110 */ 111#ifndef ifa_broadaddr 112#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ 113#endif 114 115//struct ifmaddrs { 116// struct ifmaddrs *ifma_next; 117// struct sockaddr *ifma_name; 118// struct sockaddr *ifma_addr; 119// struct sockaddr *ifma_lladdr; 120//}; 121 122void ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af); 123static int ifmalist_dump_mcstat(struct ifmaddrs *); 124static void in_ifinfo(struct igmp_ifinfo *); 125static const char *inm_mode(u_int); 126static void inm_print_sources_sysctl(uint32_t, struct in_addr); 127#ifdef INET6 128static void in6_ifinfo(struct mld_ifinfo *); 129static void in6m_print_sources_sysctl(uint32_t, struct in6_addr *); 130static const char *inet6_n2a(struct in6_addr *); 131#endif 132static void printb(const char *, unsigned int, const char *); 133static const char *sdl_addr_to_hex(const struct sockaddr_dl *, char *, int); 134 135extern char *routename6(struct sockaddr_in6 *); 136 137#define sa_equal(a1, a2) \ 138 (bcmp((a1), (a2), ((a1))->sa_len) == 0) 139 140#define sa_dl_equal(a1, a2) \ 141 ((((struct sockaddr_dl *)(a1))->sdl_len == \ 142 ((struct sockaddr_dl *)(a2))->sdl_len) && \ 143 (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \ 144 LLADDR((struct sockaddr_dl *)(a2)), \ 145 ((struct sockaddr_dl *)(a1))->sdl_alen) == 0)) 146 147#define SALIGN (sizeof(uint32_t) - 1) 148#define SA_RLEN(sa) (sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ 149 (SALIGN + 1)) : 0) 150#define MAX_SYSCTL_TRY 5 151#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) 152 153void 154ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af) 155{ 156 const struct ifmaddrs *ifma; 157 sockunion_t *psa; 158 char myifname[IFNAMSIZ]; 159 char *pcolon; 160 char *pafname, *pifname, *plladdr = NULL, *pgroup = NULL; 161 162 switch (af) { 163 case AF_INET: 164 pafname = "IPv4"; 165 break; 166#ifdef INET6 167 case AF_INET6: 168 pafname = "IPv6"; 169 break; 170#endif 171 case AF_LINK: 172 pafname = "Link-layer"; 173 break; 174 default: 175 return; /* XXX */ 176 } 177 178 fprintf(stdout, "%s Multicast Group Memberships\n", pafname); 179 fprintf(stdout, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address", 180 "Netif"); 181 182 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { 183 184 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) 185 continue; 186 187 /* Group address */ 188 psa = (sockunion_t *)ifma->ifma_addr; 189 if (psa->sa.sa_family != af) 190 continue; 191 192 switch (psa->sa.sa_family) { 193 case AF_INET: 194 pgroup = inet_ntoa(psa->sin.sin_addr); 195 break; 196#ifdef INET6 197 case AF_INET6: 198 pgroup = routename6(&(psa->sin6)); 199 break; 200#endif 201 case AF_LINK: 202 if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || 203 (psa->sdl.sdl_type == IFT_ETHER)) { 204 pgroup = 205ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); 206#ifdef notyet 207 } else { 208 pgroup = addr2ascii(AF_LINK, 209 &psa->sdl, 210 sizeof(struct sockaddr_dl), 211 addrbuf); 212#endif 213 } 214 break; 215 default: 216 continue; /* XXX */ 217 } 218 219 /* Link-layer mapping, if any */ 220 psa = (sockunion_t *)ifma->ifma_lladdr; 221 if (psa != NULL) { 222 if (psa->sa.sa_family == AF_LINK) { 223 if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || 224 (psa->sdl.sdl_type == IFT_ETHER)) { 225 /* IEEE 802 */ 226 plladdr = 227ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); 228#ifdef notyet 229 } else { 230 /* something more exotic */ 231 plladdr = addr2ascii(AF_LINK, 232 &psa->sdl, 233 sizeof(struct sockaddr_dl), 234 addrbuf); 235#endif 236 } 237 } else { 238 int i; 239 240 /* not a link-layer address */ 241 plladdr = "<invalid>"; 242 243 for (i = 0; psa->sa.sa_len > 2 && i < psa->sa.sa_len - 2; i++) 244 printf("0x%x ", psa->sa.sa_data[i]); 245 printf("\n"); 246 } 247 } else { 248 plladdr = "<none>"; 249 } 250 251 /* Interface upon which the membership exists */ 252 psa = (sockunion_t *)ifma->ifma_name; 253 if (psa != NULL && psa->sa.sa_family == AF_LINK) { 254 strlcpy(myifname, link_ntoa(&psa->sdl), IFNAMSIZ); 255 pcolon = strchr(myifname, ':'); 256 if (pcolon) 257 *pcolon = '\0'; 258 pifname = myifname; 259 } else { 260 pifname = ""; 261 } 262 263 fprintf(stdout, "%-20s\t%-16s\t%s\n", pgroup, plladdr, pifname); 264 } 265} 266 267void 268ifmalist_dump(void) 269{ 270 struct ifmaddrs *ifmap; 271 272 if (getifmaddrs(&ifmap)) 273 err(EX_OSERR, "getifmaddrs"); 274 275 ifmalist_dump_af(ifmap, AF_LINK); 276 fputs("\n", stdout); 277 ifmalist_dump_af(ifmap, AF_INET); 278#ifdef INET6 279 fputs("\n", stdout); 280 ifmalist_dump_af(ifmap, AF_INET6); 281#endif 282 if (sflag) { 283 fputs("\n", stdout); 284 ifmalist_dump_mcstat(ifmap); 285 } 286 287 freeifmaddrs(ifmap); 288} 289 290static int 291ifmalist_dump_mcstat(struct ifmaddrs *ifmap) 292{ 293 char thisifname[IFNAMSIZ]; 294 char addrbuf[NI_MAXHOST]; 295 struct ifaddrs *ifap, *ifa; 296 struct ifmaddrs *ifma; 297 sockunion_t lastifasa; 298 sockunion_t *psa, *pgsa, *pllsa, *pifasa; 299 char *pcolon; 300 char *pafname; 301 uint32_t lastifindex, thisifindex; 302 int error; 303 uint32_t ifindex = 0; 304 305 if (interface != NULL) 306 ifindex = if_nametoindex(interface); 307 308 error = 0; 309 ifap = NULL; 310 lastifindex = 0; 311 thisifindex = 0; 312 lastifasa.ss.ss_family = AF_UNSPEC; 313 314 if (getifaddrs(&ifap) != 0) { 315 warn("getifmaddrs"); 316 return (-1); 317 } 318 319 for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { 320 error = 0; 321 if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) 322 continue; 323 324 psa = (sockunion_t *)ifma->ifma_name; 325 if (psa->sa.sa_family != AF_LINK) { 326 fprintf(stderr, 327 "WARNING: Kernel returned invalid data.\n"); 328 error = -1; 329 break; 330 } 331 332 /* Filter on interface name. */ 333 thisifindex = psa->sdl.sdl_index; 334 if (ifindex != 0 && thisifindex != ifindex) 335 continue; 336 337 /* Filter on address family. */ 338 pgsa = (sockunion_t *)ifma->ifma_addr; 339 if (af != 0 && pgsa->sa.sa_family != af) 340 continue; 341 342 strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ); 343 pcolon = strchr(thisifname, ':'); 344 if (pcolon) 345 *pcolon = '\0'; 346 347 /* Only print the banner for the first ifmaddrs entry. */ 348 if (lastifindex == 0 || lastifindex != thisifindex) { 349 lastifindex = thisifindex; 350 fprintf(stdout, "%s:\n", thisifname); 351 } 352 353 /* 354 * Currently, multicast joins only take place on the 355 * primary IPv4 address, and only on the link-local IPv6 356 * address, as per IGMPv2/3 and MLDv1/2 semantics. 357 * Therefore, we only look up the primary address on 358 * the first pass. 359 */ 360 pifasa = NULL; 361 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 362 if ((strcmp(ifa->ifa_name, thisifname) != 0) || 363 (ifa->ifa_addr == NULL) || 364 (ifa->ifa_addr->sa_family != pgsa->sa.sa_family)) 365 continue; 366 /* 367 * For AF_INET6 only the link-local address should 368 * be returned. If built without IPv6 support, 369 * skip this address entirely. 370 */ 371 pifasa = (sockunion_t *)ifa->ifa_addr; 372 if (pifasa->sa.sa_family == AF_INET6 373#ifdef INET6 374 && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr) 375#endif 376 ) { 377 pifasa = NULL; 378 continue; 379 } 380 break; 381 } 382 if (pifasa == NULL) 383 continue; /* primary address not found */ 384 385 if (!vflag && pifasa->sa.sa_family == AF_LINK) 386 continue; 387 388 /* Parse and print primary address, if not already printed. */ 389 if (lastifasa.ss.ss_family == AF_UNSPEC || 390 ((lastifasa.ss.ss_family == AF_LINK && 391 !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) || 392 !sa_equal(&lastifasa.sa, &pifasa->sa))) { 393 394 switch (pifasa->sa.sa_family) { 395 case AF_INET: 396 pafname = "inet"; 397 break; 398 case AF_INET6: 399 pafname = "inet6"; 400 break; 401 case AF_LINK: 402 pafname = "link"; 403 break; 404 default: 405 pafname = "unknown"; 406 break; 407 } 408 409 switch (pifasa->sa.sa_family) { 410 case AF_INET6: 411#ifdef INET6 412 { 413 const char *p = 414 inet6_n2a(&pifasa->sin6.sin6_addr); 415 strlcpy(addrbuf, p, sizeof(addrbuf)); 416 break; 417 } 418#else 419 /* FALLTHROUGH */ 420#endif 421 case AF_INET: 422 error = getnameinfo(&pifasa->sa, 423 pifasa->sa.sa_len, 424 addrbuf, sizeof(addrbuf), NULL, 0, 425 NI_NUMERICHOST); 426 if (error) 427 printf("getnameinfo: %s\n", 428 gai_strerror(error)); 429 break; 430 case AF_LINK: { 431 (void) sdl_addr_to_hex(&pifasa->sdl, addrbuf, 432 sizeof (addrbuf)); 433 break; 434 } 435 default: 436 addrbuf[0] = '\0'; 437 break; 438 } 439 440 fprintf(stdout, "\t%s %s\n", pafname, addrbuf); 441 /* 442 * Print per-link IGMP information, if available. 443 */ 444 if (pifasa->sa.sa_family == AF_INET) { 445 struct igmp_ifinfo igi; 446 size_t mibsize, len; 447 int mib[5]; 448 449 mibsize = sizeof(mib) / sizeof(mib[0]); 450 if (sysctlnametomib("net.inet.igmp.ifinfo", 451 mib, &mibsize) == -1) { 452 perror("sysctlnametomib"); 453 goto next_ifnet; 454 } 455 mib[mibsize] = thisifindex; 456 len = sizeof(struct igmp_ifinfo); 457 if (sysctl(mib, mibsize + 1, &igi, &len, NULL, 458 0) == -1) { 459 perror("sysctl net.inet.igmp.ifinfo"); 460 goto next_ifnet; 461 } 462 in_ifinfo(&igi); 463 } 464#ifdef INET6 465 /* 466 * Print per-link MLD information, if available. 467 */ 468 if (pifasa->sa.sa_family == AF_INET6) { 469 struct mld_ifinfo mli; 470 size_t mibsize, len; 471 int mib[5]; 472 473 mibsize = sizeof(mib) / sizeof(mib[0]); 474 if (sysctlnametomib("net.inet6.mld.ifinfo", 475 mib, &mibsize) == -1) { 476 perror("sysctlnametomib"); 477 goto next_ifnet; 478 } 479 mib[mibsize] = thisifindex; 480 len = sizeof(struct mld_ifinfo); 481 if (sysctl(mib, mibsize + 1, &mli, &len, NULL, 482 0) == -1) { 483 perror("sysctl net.inet6.mld.ifinfo"); 484 goto next_ifnet; 485 } 486 in6_ifinfo(&mli); 487 } 488#endif /* INET6 */ 489#if defined(INET6) 490next_ifnet: 491#endif 492 lastifasa = *pifasa; 493 } 494 495 /* Print this group address. */ 496#ifdef INET6 497 if (pgsa->sa.sa_family == AF_INET6) { 498 const char *p = inet6_n2a(&pgsa->sin6.sin6_addr); 499 strlcpy(addrbuf, p, sizeof(addrbuf)); 500 } else 501#endif 502 if (pgsa->sa.sa_family == AF_INET) { 503 error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, 504 addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 505 if (error) 506 printf("getnameinfo: %s\n", 507 gai_strerror(error)); 508 } else { 509 (void) sdl_addr_to_hex(&pgsa->sdl, addrbuf, 510 sizeof (addrbuf)); 511 } 512 513 fprintf(stdout, "\t\tgroup %s", addrbuf); 514 if (pgsa->sa.sa_family == AF_INET) { 515 inm_print_sources_sysctl(thisifindex, 516 pgsa->sin.sin_addr); 517 } 518#ifdef INET6 519 if (pgsa->sa.sa_family == AF_INET6) { 520 in6m_print_sources_sysctl(thisifindex, 521 &pgsa->sin6.sin6_addr); 522 } 523#endif 524 fprintf(stdout, "\n"); 525 526 /* Link-layer mapping, if present. */ 527 pllsa = (sockunion_t *)ifma->ifma_lladdr; 528 if (pllsa != NULL) { 529 (void) sdl_addr_to_hex(&pllsa->sdl, addrbuf, 530 sizeof (addrbuf)); 531 fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf); 532 } 533 } 534 535 if (ifap != NULL) 536 freeifaddrs(ifap); 537 538 return (error); 539} 540 541static void 542in_ifinfo(struct igmp_ifinfo *igi) 543{ 544 545 printf("\t"); 546 switch (igi->igi_version) { 547 case IGMP_VERSION_1: 548 case IGMP_VERSION_2: 549 case IGMP_VERSION_3: 550 printf("igmpv%d", igi->igi_version); 551 break; 552 default: 553 printf("igmpv?(%d)", igi->igi_version); 554 break; 555 } 556 printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); 557 if (igi->igi_version == IGMP_VERSION_3) { 558 printf(" rv %u qi %u qri %u uri %u", 559 igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); 560 } 561 if (vflag >= 2) { 562 printf(" v1timer %u v2timer %u v3timer %u", 563 igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); 564 } 565 printf("\n"); 566} 567 568static const char *inm_modes[] = { 569 "undefined", 570 "include", 571 "exclude", 572}; 573 574static const char * 575inm_mode(u_int mode) 576{ 577 578 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 579 return (inm_modes[mode]); 580 return (NULL); 581} 582 583/* 584 * Retrieve per-group source filter mode and lists via sysctl. 585 */ 586static void 587inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) 588{ 589#define MAX_SYSCTL_TRY 5 590 int mib[7]; 591 int ntry = 0; 592 size_t mibsize; 593 size_t len; 594 size_t needed; 595 size_t cnt; 596 int i; 597 char *buf; 598 struct in_addr *pina; 599 uint32_t *p; 600 uint32_t fmode; 601 const char *modestr; 602 603 mibsize = sizeof(mib) / sizeof(mib[0]); 604 if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { 605 perror("sysctlnametomib"); 606 return; 607 } 608 609 needed = 0; 610 mib[5] = ifindex; 611 mib[6] = gina.s_addr; /* 32 bits wide */ 612 mibsize = sizeof(mib) / sizeof(mib[0]); 613 do { 614 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { 615 perror("sysctl net.inet.ip.mcast.filters"); 616 return; 617 } 618 if ((buf = malloc(needed)) == NULL) { 619 perror("malloc"); 620 return; 621 } 622 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { 623 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 624 perror("sysctl"); 625 goto out_free; 626 } 627 free(buf); 628 buf = NULL; 629 } 630 } while (buf == NULL); 631 632 len = needed; 633 if (len < sizeof(uint32_t)) { 634 perror("sysctl"); 635 goto out_free; 636 } 637 638 p = (uint32_t *)buf; 639 fmode = *p++; 640 len -= sizeof(uint32_t); 641 642 modestr = inm_mode(fmode); 643 if (modestr) 644 printf(" mode %s", modestr); 645 else 646 printf(" mode (%u)", fmode); 647 648 if (vflag == 0) 649 goto out_free; 650 651 cnt = len / sizeof(struct in_addr); 652 pina = (struct in_addr *)p; 653 654 for (i = 0; i < cnt; i++) { 655 if (i == 0) 656 printf(" srcs "); 657 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), 658 inet_ntoa(*pina++)); 659 len -= sizeof(struct in_addr); 660 } 661 if (len > 0) { 662 fprintf(stderr, "warning: %u trailing bytes from %s\n", 663 (unsigned int)len, "net.inet.ip.mcast.filters"); 664 } 665 666out_free: 667 free(buf); 668#undef MAX_SYSCTL_TRY 669} 670 671#ifdef INET6 672 673static void 674in6_ifinfo(struct mld_ifinfo *mli) 675{ 676 677 printf("\t"); 678 switch (mli->mli_version) { 679 case MLD_VERSION_1: 680 case MLD_VERSION_2: 681 printf("mldv%d", mli->mli_version); 682 break; 683 default: 684 printf("mldv?(%d)", mli->mli_version); 685 break; 686 } 687 printb(" flags", mli->mli_flags, "\020\1SILENT"); 688 if (mli->mli_version == MLD_VERSION_2) { 689 printf(" rv %u qi %u qri %u uri %u", 690 mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri); 691 } 692 if (vflag >= 2) { 693 printf(" v1timer %u v2timer %u", mli->mli_v1_timer, 694 mli->mli_v2_timer); 695 } 696 printf("\n"); 697} 698 699/* 700 * Retrieve MLD per-group source filter mode and lists via sysctl. 701 * 702 * Note: The 128-bit IPv6 group addres needs to be segmented into 703 * 32-bit pieces for marshaling to sysctl. So the MIB name ends 704 * up looking like this: 705 * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3] 706 * Assumes that pgroup originated from the kernel, so its components 707 * are already in network-byte order. 708 */ 709static void 710in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup) 711{ 712#define MAX_SYSCTL_TRY 5 713 char addrbuf[INET6_ADDRSTRLEN]; 714 int mib[10]; 715 int ntry = 0; 716 int *pi; 717 size_t mibsize; 718 size_t len; 719 size_t needed; 720 size_t cnt; 721 int i; 722 char *buf; 723 struct in6_addr *pina; 724 uint32_t *p; 725 uint32_t fmode; 726 const char *modestr; 727 728 mibsize = sizeof(mib) / sizeof(mib[0]); 729 if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib, 730 &mibsize) == -1) { 731 perror("sysctlnametomib"); 732 return; 733 } 734 735 needed = 0; 736 mib[5] = ifindex; 737 pi = (int *)pgroup; 738 for (i = 0; i < 4; i++) 739 mib[6 + i] = *pi++; 740 741 mibsize = sizeof(mib) / sizeof(mib[0]); 742 do { 743 if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { 744 perror("sysctl net.inet6.ip6.mcast.filters"); 745 return; 746 } 747 if ((buf = malloc(needed)) == NULL) { 748 perror("malloc"); 749 return; 750 } 751 if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { 752 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 753 perror("sysctl"); 754 goto out_free; 755 } 756 free(buf); 757 buf = NULL; 758 } 759 } while (buf == NULL); 760 761 len = needed; 762 if (len < sizeof(uint32_t)) { 763 perror("sysctl"); 764 goto out_free; 765 } 766 767 p = (uint32_t *)buf; 768 fmode = *p++; 769 len -= sizeof(uint32_t); 770 771 modestr = inm_mode(fmode); 772 if (modestr) 773 printf(" mode %s", modestr); 774 else 775 printf(" mode (%u)", fmode); 776 777 if (vflag == 0) 778 goto out_free; 779 780 cnt = len / sizeof(struct in6_addr); 781 pina = (struct in6_addr *)p; 782 783 for (i = 0; i < cnt; i++) { 784 if (i == 0) 785 printf(" srcs "); 786 inet_ntop(AF_INET6, (const char *)pina++, addrbuf, 787 INET6_ADDRSTRLEN); 788 fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf); 789 len -= sizeof(struct in6_addr); 790 } 791 if (len > 0) { 792 fprintf(stderr, "warning: %u trailing bytes from %s\n", 793 (unsigned int)len, "net.inet6.ip6.mcast.filters"); 794 } 795 796out_free: 797 free(buf); 798#undef MAX_SYSCTL_TRY 799} 800 801static const char * 802inet6_n2a(struct in6_addr *p) 803{ 804 static char buf[NI_MAXHOST]; 805 struct sockaddr_in6 sin6; 806 u_int32_t scopeid; 807 const int niflags = NI_NUMERICHOST; 808 809 memset(&sin6, 0, sizeof(sin6)); 810 sin6.sin6_family = AF_INET6; 811 sin6.sin6_len = sizeof(struct sockaddr_in6); 812 sin6.sin6_addr = *p; 813 if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) || 814 IN6_IS_ADDR_MC_NODELOCAL(p)) { 815 scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 816 if (scopeid) { 817 sin6.sin6_scope_id = scopeid; 818 sin6.sin6_addr.s6_addr[2] = 0; 819 sin6.sin6_addr.s6_addr[3] = 0; 820 } 821 } 822 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 823 buf, sizeof(buf), NULL, 0, niflags) == 0) { 824 return (buf); 825 } else { 826 return ("(invalid)"); 827 } 828} 829#endif /* INET6 */ 830 831/* 832 * Print a value a la the %b format of the kernel's printf 833 */ 834void 835printb(const char *s, unsigned int v, const char *bits) 836{ 837 int i, any = 0; 838 char c; 839 840 if (bits && *bits == 8) 841 printf("%s=%o", s, v); 842 else 843 printf("%s=%x", s, v); 844 bits++; 845 if (bits) { 846 putchar('<'); 847 while ((i = *bits++) != '\0') { 848 if (v & (1 << (i-1))) { 849 if (any) 850 putchar(','); 851 any = 1; 852 for (; (c = *bits) > 32; bits++) 853 putchar(c); 854 } else 855 for (; *bits > 32; bits++) 856 ; 857 } 858 putchar('>'); 859 } 860} 861 862/* 863 * convert hardware address to hex string for logging errors. 864 */ 865static const char * 866sdl_addr_to_hex(const struct sockaddr_dl *sdl, char *orig_buf, int buflen) 867{ 868 char *buf = orig_buf; 869 int i; 870 const u_char *lladdr; 871 int maxbytes = buflen / 3; 872 873 lladdr = (u_char *)(size_t)sdl->sdl_data + sdl->sdl_nlen; 874 875 if (maxbytes > sdl->sdl_alen) { 876 maxbytes = sdl->sdl_alen; 877 } 878 *buf = '\0'; 879 for (i = 0; i < maxbytes; i++) { 880 snprintf(buf, 3, "%02x", lladdr[i]); 881 buf += 2; 882 *buf = (i == maxbytes - 1) ? '\0' : ':'; 883 buf++; 884 } 885 return (orig_buf); 886} 887 888