1 /* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* TODO: use sysctl. */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/types.h> 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #include <sys/queue.h> 41 42 #include <net/if.h> 43 #include <net/if_var.h> 44 #include <net/if_types.h> 45 #include <net/if_dl.h> 46 #include <net/route.h> 47 48 #include <netinet/in.h> 49 #include <netinet/in_var.h> 50 #include <netinet/in_systm.h> 51 #include <netinet/ip.h> 52 #include <netinet/igmp.h> 53 #ifdef HAVE_IGMPV3 54 # include <netinet/in_msf.h> 55 #endif 56 #define KERNEL 57 # include <netinet/if_ether.h> 58 #undef KERNEL 59 #define _KERNEL 60 # include <sys/sysctl.h> 61 # include <netinet/igmp_var.h> 62 #undef _KERNEL 63 64 #ifdef INET6 65 # ifdef HAVE_MLDV2 66 # include <netinet6/in6_msf.h> 67 # endif 68 #include <netinet/icmp6.h> 69 #define _KERNEL 70 # include <netinet6/mld6_var.h> 71 #undef _KERNEL 72 #endif /* INET6 */ 73 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <fcntl.h> 77 #include <kvm.h> 78 #include <nlist.h> 79 #include <string.h> 80 #include <limits.h> 81 #include <unistd.h> 82 #include <arpa/inet.h> 83 #include <netdb.h> 84 85 kvm_t *kvmd; 86 int ifindex = 0; 87 int af = AF_UNSPEC; 88 89 struct nlist nl[] = { 90 #define N_IFNET 0 91 { "_ifnet" }, 92 { "" }, 93 }; 94 95 const char *inet6_n2a __P((struct in6_addr *)); 96 int main __P((int, char **)); 97 char *ifname __P((struct ifnet *)); 98 void kread __P((u_long, void *, int)); 99 #ifdef INET6 100 void if6_addrlist __P((struct ifaddr *)); 101 void in6_multilist __P((struct in6_multi *)); 102 struct in6_multi * in6_multientry __P((struct in6_multi *)); 103 #endif 104 void if_addrlist(struct ifaddr *); 105 void in_multilist(struct in_multi *); 106 struct in_multi * in_multientry(struct in_multi *); 107 #ifdef HAVE_IGMPV3 108 void in_addr_slistentry(struct in_addr_slist *ias, char *heading); 109 #endif 110 #ifdef HAVE_MLDV2 111 void in6_addr_slistentry(struct in6_addr_slist *ias, char *heading); 112 #endif 113 114 #define KREAD(addr, buf, type) \ 115 kread((u_long)addr, (void *)buf, sizeof(type)) 116 117 const char *inet6_n2a(p) 118 struct in6_addr *p; 119 { 120 static char buf[NI_MAXHOST]; 121 struct sockaddr_in6 sin6; 122 u_int32_t scopeid; 123 const int niflags = NI_NUMERICHOST; 124 125 memset(&sin6, 0, sizeof(sin6)); 126 sin6.sin6_family = AF_INET6; 127 sin6.sin6_len = sizeof(struct sockaddr_in6); 128 sin6.sin6_addr = *p; 129 if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) || 130 IN6_IS_ADDR_MC_NODELOCAL(p)) { 131 scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 132 if (scopeid) { 133 sin6.sin6_scope_id = scopeid; 134 sin6.sin6_addr.s6_addr[2] = 0; 135 sin6.sin6_addr.s6_addr[3] = 0; 136 } 137 } 138 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 139 buf, sizeof(buf), NULL, 0, niflags) == 0) 140 return buf; 141 else 142 return "(invalid)"; 143 } 144 145 int main(argc, argv) 146 int argc; 147 char **argv; 148 { 149 char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ]; 150 int c; 151 struct ifnet *ifp, *nifp, ifnet; 152 const char *kernel = NULL; 153 const char *core = NULL; 154 155 /* "ifmcstat [kernel]" format is supported for backward compatiblity */ 156 if (argc == 2) 157 kernel = argv[1]; 158 159 while ((c = getopt(argc, argv, "i:f:M:N:")) != -1) { 160 switch (c) { 161 case 'i': 162 if ((ifindex = if_nametoindex(optarg)) == 0) { 163 fprintf(stderr, "%s: unknown interface\n", optarg); 164 exit(1); 165 } 166 break; 167 case 'f': 168 if (strcmp(optarg, "inet") == 0) { 169 af = AF_INET; 170 break; 171 } 172 if (strcmp(optarg, "inet6") == 0) { 173 af = AF_INET6; 174 break; 175 } 176 fprintf(stderr, "%s: unknown address family\n", optarg); 177 exit(1); 178 /*NOTREACHED*/ 179 case 'M': 180 core = strdup(optarg); 181 break; 182 case 'N': 183 kernel = strdup(optarg); 184 break; 185 default: 186 fprintf(stderr, 187 "usage: ifmcstat [-i interface] [-f address family] [-M core] [-N system]\n"); 188 exit(1); 189 /*NOTREACHED*/ 190 } 191 } 192 193 if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) == 194 NULL) { 195 perror("kvm_openfiles"); 196 exit(1); 197 } 198 if (kvm_nlist(kvmd, nl) < 0) { 199 perror("kvm_nlist"); 200 exit(1); 201 } 202 if (nl[N_IFNET].n_value == 0) { 203 printf("symbol %s not found\n", nl[N_IFNET].n_name); 204 exit(1); 205 } 206 KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); 207 while (ifp) { 208 KREAD(ifp, &ifnet, struct ifnet); 209 nifp = ifnet.if_link.tqe_next; 210 if (ifindex && ifindex != ifnet.if_index) 211 goto next; 212 213 printf("%s:\n", if_indextoname(ifnet.if_index, ifname)); 214 if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); 215 #ifdef INET6 216 if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); 217 #endif 218 next: 219 ifp = nifp; 220 } 221 222 exit(0); 223 /*NOTREACHED*/ 224 } 225 226 char *ifname(ifp) 227 struct ifnet *ifp; 228 { 229 static char buf[BUFSIZ]; 230 struct ifnet ifnet; 231 232 KREAD(ifp, &ifnet, struct ifnet); 233 strlcpy(buf, ifnet.if_xname, sizeof(buf)); 234 return buf; 235 } 236 237 void kread(addr, buf, len) 238 u_long addr; 239 void *buf; 240 int len; 241 { 242 if (kvm_read(kvmd, addr, buf, len) != len) { 243 perror("kvm_read"); 244 exit(1); 245 } 246 } 247 248 #ifdef INET6 249 250 void 251 if6_addrlist(ifap) 252 struct ifaddr *ifap; 253 { 254 struct ifaddr ifa; 255 struct sockaddr sa; 256 struct in6_ifaddr if6a; 257 struct ifaddr *ifap0; 258 259 if (af && af != AF_INET6) 260 return; 261 ifap0 = ifap; 262 while (ifap) { 263 KREAD(ifap, &ifa, struct ifaddr); 264 if (ifa.ifa_addr == NULL) 265 goto nextifap; 266 KREAD(ifa.ifa_addr, &sa, struct sockaddr); 267 if (sa.sa_family != PF_INET6) 268 goto nextifap; 269 KREAD(ifap, &if6a, struct in6_ifaddr); 270 printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr)); 271 nextifap: 272 ifap = ifa.ifa_link.tqe_next; 273 } 274 if (ifap0) { 275 struct ifnet ifnet; 276 struct ifmultiaddr ifm, *ifmp = 0; 277 struct sockaddr_dl sdl; 278 279 KREAD(ifap0, &ifa, struct ifaddr); 280 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 281 if (TAILQ_FIRST(&ifnet.if_multiaddrs)) 282 ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); 283 while (ifmp) { 284 KREAD(ifmp, &ifm, struct ifmultiaddr); 285 if (ifm.ifma_addr == NULL) 286 goto nextmulti; 287 KREAD(ifm.ifma_addr, &sa, struct sockaddr); 288 if (sa.sa_family != AF_INET6) 289 goto nextmulti; 290 (void)in6_multientry((struct in6_multi *) 291 ifm.ifma_protospec); 292 if (ifm.ifma_lladdr == 0) 293 goto nextmulti; 294 KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); 295 printf("\t\t\tmcast-macaddr %s multicnt %d\n", 296 ether_ntoa((struct ether_addr *)LLADDR(&sdl)), 297 ifm.ifma_refcount); 298 nextmulti: 299 ifmp = TAILQ_NEXT(&ifm, ifma_link); 300 } 301 } 302 } 303 304 struct in6_multi * 305 in6_multientry(mc) 306 struct in6_multi *mc; 307 { 308 struct in6_multi multi; 309 #ifdef HAVE_MLDV2 310 struct in6_multi_source src; 311 struct router6_info rt6i; 312 #endif 313 314 KREAD(mc, &multi, struct in6_multi); 315 printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr)); 316 printf(" refcnt %u\n", multi.in6m_refcount); 317 318 #ifdef HAVE_MLDV2 319 if (multi.in6m_rti != NULL) { 320 KREAD(multi.in6m_rti, &rt6i, struct router_info); 321 printf("\t\t\t"); 322 switch (rt6i.rt6i_type) { 323 case MLD_V1_ROUTER: 324 printf("mldv1"); 325 break; 326 case MLD_V2_ROUTER: 327 printf("mldv2"); 328 break; 329 default: 330 printf("mldv?(%d)", rt6i.rt6i_type); 331 break; 332 } 333 334 if (multi.in6m_source == NULL) { 335 printf("\n"); 336 return(multi.in6m_entry.le_next); 337 } 338 339 KREAD(multi.in6m_source, &src, struct in6_multi_source); 340 printf(" mode=%s grpjoin=%d\n", 341 src.i6ms_mode == MCAST_INCLUDE ? "include" : 342 src.i6ms_mode == MCAST_EXCLUDE ? "exclude" : 343 "???", 344 src.i6ms_grpjoin); 345 in6_addr_slistentry(src.i6ms_cur, "current"); 346 in6_addr_slistentry(src.i6ms_rec, "recorded"); 347 in6_addr_slistentry(src.i6ms_in, "included"); 348 in6_addr_slistentry(src.i6ms_ex, "excluded"); 349 in6_addr_slistentry(src.i6ms_alw, "allowed"); 350 in6_addr_slistentry(src.i6ms_blk, "blocked"); 351 in6_addr_slistentry(src.i6ms_toin, "to-include"); 352 in6_addr_slistentry(src.i6ms_ex, "to-exclude"); 353 } 354 #endif 355 return(multi.in6m_entry.le_next); 356 } 357 358 #ifdef HAVE_MLDV2 359 void 360 in6_addr_slistentry(struct in6_addr_slist *ias, char *heading) 361 { 362 struct in6_addr_slist slist; 363 struct i6as_head head; 364 struct in6_addr_source src; 365 366 if (ias == NULL) { 367 printf("\t\t\t\t%s (none)\n", heading); 368 return; 369 } 370 memset(&slist, 0, sizeof(slist)); 371 KREAD(ias, &slist, struct in6_addr_source); 372 printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc); 373 if (slist.numsrc == 0) { 374 return; 375 } 376 KREAD(slist.head, &head, struct i6as_head); 377 378 KREAD(head.lh_first, &src, struct in6_addr_source); 379 while (1) { 380 printf("\t\t\t\t\tsource %s (ref=%d)\n", 381 inet6_n2a(&src.i6as_addr.sin6_addr), 382 src.i6as_refcount); 383 if (src.i6as_list.le_next == NULL) 384 break; 385 KREAD(src.i6as_list.le_next, &src, struct in6_addr_source); 386 } 387 return; 388 } 389 #endif 390 391 void 392 in6_multilist(mc) 393 struct in6_multi *mc; 394 { 395 while (mc) 396 mc = in6_multientry(mc); 397 } 398 399 #endif /* INET6 */ 400 401 void 402 if_addrlist(ifap) 403 struct ifaddr *ifap; 404 { 405 struct ifaddr ifa; 406 struct sockaddr sa; 407 struct in_ifaddr ia; 408 struct ifaddr *ifap0; 409 410 if (af && af != AF_INET) 411 return; 412 ifap0 = ifap; 413 while (ifap) { 414 KREAD(ifap, &ifa, struct ifaddr); 415 if (ifa.ifa_addr == NULL) 416 goto nextifap; 417 KREAD(ifa.ifa_addr, &sa, struct sockaddr); 418 if (sa.sa_family != PF_INET) 419 goto nextifap; 420 KREAD(ifap, &ia, struct in_ifaddr); 421 printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr)); 422 nextifap: 423 ifap = ifa.ifa_link.tqe_next; 424 } 425 if (ifap0) { 426 struct ifnet ifnet; 427 struct ifmultiaddr ifm, *ifmp = 0; 428 struct sockaddr_dl sdl; 429 430 KREAD(ifap0, &ifa, struct ifaddr); 431 KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); 432 if (TAILQ_FIRST(&ifnet.if_multiaddrs)) 433 ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); 434 while (ifmp) { 435 KREAD(ifmp, &ifm, struct ifmultiaddr); 436 if (ifm.ifma_addr == NULL) 437 goto nextmulti; 438 KREAD(ifm.ifma_addr, &sa, struct sockaddr); 439 if (sa.sa_family != AF_INET) 440 goto nextmulti; 441 (void)in_multientry((struct in_multi *) 442 ifm.ifma_protospec); 443 if (ifm.ifma_lladdr == 0) 444 goto nextmulti; 445 KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); 446 printf("\t\t\tmcast-macaddr %s multicnt %d\n", 447 ether_ntoa((struct ether_addr *)LLADDR(&sdl)), 448 ifm.ifma_refcount); 449 nextmulti: 450 ifmp = TAILQ_NEXT(&ifm, ifma_link); 451 } 452 } 453 } 454 455 void 456 in_multilist(mc) 457 struct in_multi *mc; 458 { 459 while (mc) 460 mc = in_multientry(mc); 461 } 462 463 struct in_multi * 464 in_multientry(mc) 465 struct in_multi *mc; 466 { 467 struct in_multi multi; 468 struct router_info rti; 469 #ifdef HAVE_IGMPV3 470 struct in_multi_source src; 471 #endif 472 473 KREAD(mc, &multi, struct in_multi); 474 printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr)); 475 476 if (multi.inm_rti != NULL) { 477 KREAD(multi.inm_rti, &rti, struct router_info); 478 printf("\t\t\t"); 479 switch (rti.rti_type) { 480 case IGMP_V1_ROUTER: 481 printf("igmpv1"); 482 break; 483 case IGMP_V2_ROUTER: 484 printf("igmpv2"); 485 break; 486 #ifdef HAVE_IGMPV3 487 case IGMP_V3_ROUTER: 488 printf("igmpv3"); 489 break; 490 #endif 491 default: 492 printf("igmpv?(%d)", rti.rti_type); 493 break; 494 } 495 496 #ifdef HAVE_IGMPV3 497 if (multi.inm_source == NULL) { 498 printf("\n"); 499 return (multi.inm_list.le_next); 500 } 501 502 KREAD(multi.inm_source, &src, struct in_multi_source); 503 printf(" mode=%s grpjoin=%d\n", 504 src.ims_mode == MCAST_INCLUDE ? "include" : 505 src.ims_mode == MCAST_EXCLUDE ? "exclude" : 506 "???", 507 src.ims_grpjoin); 508 in_addr_slistentry(src.ims_cur, "current"); 509 in_addr_slistentry(src.ims_rec, "recorded"); 510 in_addr_slistentry(src.ims_in, "included"); 511 in_addr_slistentry(src.ims_ex, "excluded"); 512 in_addr_slistentry(src.ims_alw, "allowed"); 513 in_addr_slistentry(src.ims_blk, "blocked"); 514 in_addr_slistentry(src.ims_toin, "to-include"); 515 in_addr_slistentry(src.ims_ex, "to-exclude"); 516 #else 517 printf("\n"); 518 #endif 519 } 520 521 return (NULL); 522 } 523 524 #ifdef HAVE_IGMPV3 525 void 526 in_addr_slistentry(struct in_addr_slist *ias, char *heading) 527 { 528 struct in_addr_slist slist; 529 struct ias_head head; 530 struct in_addr_source src; 531 532 if (ias == NULL) { 533 printf("\t\t\t\t%s (none)\n", heading); 534 return; 535 } 536 memset(&slist, 0, sizeof(slist)); 537 KREAD(ias, &slist, struct in_addr_source); 538 printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc); 539 if (slist.numsrc == 0) { 540 return; 541 } 542 KREAD(slist.head, &head, struct ias_head); 543 544 KREAD(head.lh_first, &src, struct in_addr_source); 545 while (1) { 546 printf("\t\t\t\t\tsource %s (ref=%d)\n", 547 inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount); 548 if (src.ias_list.le_next == NULL) 549 break; 550 KREAD(src.ias_list.le_next, &src, struct in_addr_source); 551 } 552 return; 553 } 554 #endif 555