ifmcstat.c revision 167731
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: head/usr.sbin/ifmcstat/ifmcstat.c 167731 2007-03-20 02:08:28Z bms $"); 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 85kvm_t *kvmd; 86int ifindex = 0; 87int af = AF_UNSPEC; 88 89struct nlist nl[] = { 90#define N_IFNET 0 91 { "_ifnet" }, 92 { "" }, 93}; 94 95const char *inet6_n2a __P((struct in6_addr *)); 96int main __P((int, char **)); 97char *ifname __P((struct ifnet *)); 98void kread __P((u_long, void *, int)); 99#ifdef INET6 100void if6_addrlist __P((struct ifaddr *)); 101void in6_multilist __P((struct in6_multi *)); 102struct in6_multi * in6_multientry __P((struct in6_multi *)); 103#endif 104void if_addrlist(struct ifaddr *); 105void in_multilist(struct in_multi *); 106struct in_multi * in_multientry(struct in_multi *); 107#ifdef HAVE_IGMPV3 108void in_addr_slistentry(struct in_addr_slist *ias, char *heading); 109#endif 110#ifdef HAVE_MLDV2 111void 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 117const 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 145int 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 218next: 219 ifp = nifp; 220 } 221 222 exit(0); 223 /*NOTREACHED*/ 224} 225 226char *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 237void 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 250void 251if6_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 304struct in6_multi * 305in6_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 359void 360in6_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 391void 392in6_multilist(mc) 393 struct in6_multi *mc; 394{ 395 while (mc) 396 mc = in6_multientry(mc); 397} 398 399#endif /* INET6 */ 400 401void 402if_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 455void 456in_multilist(mc) 457 struct in_multi *mc; 458{ 459 while (mc) 460 mc = in_multientry(mc); 461} 462 463struct in_multi * 464in_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 525void 526in_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