1 /* $OpenBSD: main.c,v 1.43 2003/12/15 07:11:31 mcbride Exp $ */ 2 /* $NetBSD: main.c,v 1.9 1996/05/07 02:55:02 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1988, 1993 6 * Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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 #ifndef lint 34 char copyright[] = 35 "@(#) Copyright (c) 1983, 1988, 1993\n\ 36 Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "from: @(#)main.c 8.4 (Berkeley) 3/1/94"; 42 #else 43 static char *rcsid = "$OpenBSD: main.c,v 1.43 2003/12/15 07:11:31 mcbride Exp $"; 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/file.h> 49 #include <sys/protosw.h> 50 #include <sys/socket.h> 51 52 #include <netinet/in.h> 53 54 #include <ctype.h> 55 #include <errno.h> 56 #include <kvm.h> 57 #include <limits.h> 58 #include <netdb.h> 59 #include <nlist.h> 60 #include <paths.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include "netstat.h" 66 67 struct nlist nl[] = { 68 #define N_MBSTAT 0 69 { "_mbstat" }, 70 #define N_IPSTAT 1 71 { "_ipstat" }, 72 #define N_TCBTABLE 2 73 { "_tcbtable" }, 74 #define N_TCPSTAT 3 75 { "_tcpstat" }, 76 #define N_UDBTABLE 4 77 { "_udbtable" }, 78 #define N_UDPSTAT 5 79 { "_udpstat" }, 80 #define N_IFNET 6 81 { "_ifnet" }, 82 #define N_IMP 7 83 { "_imp_softc" }, 84 #define N_ICMPSTAT 8 85 { "_icmpstat" }, 86 #define N_RTSTAT 9 87 { "_rtstat" }, 88 #define N_UNIXSW 10 89 { "_unixsw" }, 90 #define N_IDP 11 91 { "_nspcb"}, 92 #define N_IDPSTAT 12 93 { "_idpstat"}, 94 #define N_SPPSTAT 13 95 { "_spp_istat"}, 96 #define N_NSERR 14 97 { "_ns_errstat"}, 98 #define N_CLNPSTAT 15 99 { "_clnp_stat"}, 100 #define IN_NOTUSED 16 101 { "_tp_inpcb" }, 102 #define ISO_TP 17 103 { "_tp_refinfo" }, 104 #define N_TPSTAT 18 105 { "_tp_stat" }, 106 #define N_ESISSTAT 19 107 { "_esis_stat"}, 108 #define N_NIMP 20 109 { "_nimp"}, 110 #define N_RTREE 21 111 { "_rt_tables"}, 112 #define N_CLTP 22 113 { "_cltb"}, 114 #define N_CLTPSTAT 23 115 { "_cltpstat"}, 116 #define N_NFILE 24 117 { "_nfile" }, 118 #define N_FILE 25 119 { "_file" }, 120 #define N_IGMPSTAT 26 121 { "_igmpstat" }, 122 #define N_MRTPROTO 27 123 { "_ip_mrtproto" }, 124 #define N_MRTSTAT 28 125 { "_mrtstat" }, 126 #define N_MFCHASHTBL 29 127 { "_mfchashtbl" }, 128 #define N_MFCHASH 30 129 { "_mfchash" }, 130 #define N_VIFTABLE 31 131 { "_viftable" }, 132 #define N_IPX 32 133 { "_ipxcbtable"}, 134 #define N_IPXSTAT 33 135 { "_ipxstat"}, 136 #define N_SPXSTAT 34 137 { "_spx_istat"}, 138 #define N_IPXERR 35 139 { "_ipx_errstat"}, 140 #define N_AHSTAT 36 141 { "_ahstat"}, 142 #define N_ESPSTAT 37 143 { "_espstat"}, 144 #define N_IP4STAT 38 145 { "_ipipstat"}, 146 #define N_DDPSTAT 39 147 { "_ddpstat"}, 148 #define N_DDPCB 40 149 { "_ddpcb"}, 150 #define N_ETHERIPSTAT 41 151 { "_etheripstat"}, 152 #define N_IP6STAT 42 153 { "_ip6stat" }, 154 #define N_ICMP6STAT 43 155 { "_icmp6stat" }, 156 #define N_IPSECSTAT 44 157 { "_ipsecstat" }, 158 #define N_IPSEC6STAT 45 159 { "_ipsec6stat" }, 160 #define N_PIM6STAT 46 161 { "_pim6stat" }, 162 #define N_MRT6PROTO 47 163 { "_ip6_mrtproto" }, 164 #define N_MRT6STAT 48 165 { "_mrt6stat" }, 166 #define N_MF6CTABLE 49 167 { "_mf6ctable" }, 168 #define N_MIF6TABLE 50 169 { "_mif6table" }, 170 #define N_MBPOOL 51 171 { "_mbpool" }, 172 #define N_MCLPOOL 52 173 { "_mclpool" }, 174 #define N_IPCOMPSTAT 53 175 { "_ipcompstat" }, 176 #define N_RIP6STAT 54 177 { "_rip6stat" }, 178 #define N_CARPSTAT 55 179 { "_carpstats" }, 180 #define N_RAWIPTABLE 56 181 { "_rawcbtable" }, 182 #define N_RAWIP6TABLE 57 183 { "_rawin6pcbtable" }, 184 #define N_PFSYNCSTAT 58 185 { "_pfsyncstats" }, 186 { ""}, 187 }; 188 189 struct protox { 190 u_char pr_index; /* index into nlist of cb head */ 191 u_char pr_sindex; /* index into nlist of stat block */ 192 u_char pr_wanted; /* 1 if wanted, 0 otherwise */ 193 void (*pr_cblocks)(u_long, char *); /* control blocks printing routine */ 194 void (*pr_stats)(u_long, char *); /* statistics printing routine */ 195 char *pr_name; /* well-known name */ 196 } protox[] = { 197 { N_TCBTABLE, N_TCPSTAT, 1, protopr, 198 tcp_stats, "tcp" }, 199 { N_UDBTABLE, N_UDPSTAT, 1, protopr, 200 udp_stats, "udp" }, 201 { N_RAWIPTABLE, N_IPSTAT, 1, protopr, 202 ip_stats, "ip" }, 203 { -1, N_ICMPSTAT, 1, 0, 204 icmp_stats, "icmp" }, 205 { -1, N_IGMPSTAT, 1, 0, 206 igmp_stats, "igmp" }, 207 { -1, N_AHSTAT, 1, 0, 208 ah_stats, "ah" }, 209 { -1, N_ESPSTAT, 1, 0, 210 esp_stats, "esp" }, 211 { -1, N_IP4STAT, 1, 0, 212 ipip_stats, "ipencap" }, 213 { -1, N_ETHERIPSTAT, 1, 0, 214 etherip_stats,"etherip" }, 215 { -1, N_IPCOMPSTAT, 1, 0, 216 ipcomp_stats, "ipcomp" }, 217 { -1, N_CARPSTAT, 1, 0, 218 carp_stats, "carp" }, 219 { -1, N_PFSYNCSTAT, 1, 0, 220 pfsync_stats, "pfsync" }, 221 { -1, -1, 0, 0, 222 0, 0 } 223 }; 224 225 #ifdef INET6 226 struct protox ip6protox[] = { 227 { N_TCBTABLE, N_TCPSTAT, 1, ip6protopr, 228 0, "tcp" }, 229 { N_UDBTABLE, N_UDPSTAT, 1, ip6protopr, 230 0, "udp" }, 231 { N_RAWIP6TABLE,N_IP6STAT, 1, ip6protopr, 232 ip6_stats, "ip6" }, 233 { -1, N_ICMP6STAT, 1, 0, 234 icmp6_stats, "icmp6" }, 235 { -1, N_PIM6STAT, 1, 0, 236 pim6_stats, "pim6" }, 237 { -1, N_RIP6STAT, 1, 0, 238 rip6_stats, "rip6" }, 239 { -1, -1, 0, 0, 240 0, 0 } 241 }; 242 #endif 243 244 struct protox ipxprotox[] = { 245 { N_IPX, N_IPXSTAT, 1, ipxprotopr, 246 ipx_stats, "ipx" }, 247 { N_IPX, N_SPXSTAT, 1, ipxprotopr, 248 spx_stats, "spx" }, 249 { -1, -1, 0, 0, 250 0, 0 } 251 }; 252 253 struct protox nsprotox[] = { 254 { N_IDP, N_IDPSTAT, 1, nsprotopr, 255 idp_stats, "idp" }, 256 { N_IDP, N_SPPSTAT, 1, nsprotopr, 257 spp_stats, "spp" }, 258 { -1, N_NSERR, 1, 0, 259 nserr_stats, "ns_err" }, 260 { -1, -1, 0, 0, 261 0, 0 } 262 }; 263 264 struct protox isoprotox[] = { 265 { ISO_TP, N_TPSTAT, 1, iso_protopr, 266 tp_stats, "tp" }, 267 { N_CLTP, N_CLTPSTAT, 1, iso_protopr, 268 cltp_stats, "cltp" }, 269 { -1, N_CLNPSTAT, 1, 0, 270 clnp_stats, "clnp"}, 271 { -1, N_ESISSTAT, 1, 0, 272 esis_stats, "esis"}, 273 { -1, -1, 0, 0, 274 0, 0 } 275 }; 276 277 struct protox atalkprotox[] = { 278 { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, 279 ddp_stats, "ddp" }, 280 { -1, -1, 0, 0, 281 0, 0 } 282 }; 283 284 #ifndef INET6 285 struct protox *protoprotox[] = { 286 protox, ipxprotox, nsprotox, isoprotox, atalkprotox, NULL 287 }; 288 #else 289 struct protox *protoprotox[] = { 290 protox, ip6protox, ipxprotox, nsprotox, isoprotox, atalkprotox, NULL 291 }; 292 #endif 293 294 static void printproto(struct protox *, char *); 295 static void usage(void); 296 static struct protox *name2protox(char *); 297 static struct protox *knownname(char *); 298 299 kvm_t *kvmd; 300 301 int 302 main(int argc, char *argv[]) 303 { 304 extern char *optarg; 305 extern int optind; 306 struct protoent *p; 307 struct protox *tp = NULL; /* for printing cblocks & stats */ 308 int ch; 309 char *nlistf = NULL, *memf = NULL; 310 char buf[_POSIX2_LINE_MAX]; 311 312 af = AF_UNSPEC; 313 314 while ((ch = getopt(argc, argv, "Aabdf:gI:ilM:mN:np:qrstuvw:")) != -1) 315 switch (ch) { 316 case 'A': 317 Aflag = 1; 318 break; 319 case 'a': 320 aflag = 1; 321 break; 322 case 'b': 323 bflag = 1; 324 break; 325 case 'd': 326 dflag = 1; 327 break; 328 case 'f': 329 if (strcmp(optarg, "inet") == 0) 330 af = AF_INET; 331 else if (strcmp(optarg, "inet6") == 0) 332 af = AF_INET6; 333 else if (strcmp(optarg, "local") == 0) 334 af = AF_LOCAL; 335 else if (strcmp(optarg, "unix") == 0) 336 af = AF_UNIX; 337 else if (strcmp(optarg, "ipx") == 0) 338 af = AF_IPX; 339 else if (strcmp(optarg, "ns") == 0) 340 af = AF_NS; 341 else if (strcmp(optarg, "iso") == 0) 342 af = AF_ISO; 343 else if (strcmp(optarg, "encap") == 0) 344 af = PF_KEY; 345 else if (strcmp(optarg, "atalk") == 0) 346 af = AF_APPLETALK; 347 else { 348 (void)fprintf(stderr, 349 "%s: %s: unknown address family\n", 350 __progname, optarg); 351 exit(1); 352 } 353 break; 354 case 'g': 355 gflag = 1; 356 break; 357 case 'I': 358 iflag = 1; 359 interface = optarg; 360 break; 361 case 'i': 362 iflag = 1; 363 break; 364 case 'l': 365 lflag = 1; 366 break; 367 case 'M': 368 memf = optarg; 369 break; 370 case 'm': 371 mflag = 1; 372 break; 373 case 'N': 374 nlistf = optarg; 375 break; 376 case 'n': 377 nflag = 1; 378 break; 379 case 'p': 380 if ((tp = name2protox(optarg)) == NULL) { 381 (void)fprintf(stderr, 382 "%s: %s: unknown protocol\n", 383 __progname, optarg); 384 exit(1); 385 } 386 pflag = 1; 387 break; 388 case 'q': 389 qflag = 1; 390 break; 391 case 'r': 392 rflag = 1; 393 break; 394 case 's': 395 ++sflag; 396 break; 397 case 't': 398 tflag = 1; 399 break; 400 case 'u': 401 af = AF_UNIX; 402 break; 403 case 'v': 404 vflag = 1; 405 break; 406 case 'w': 407 interval = atoi(optarg); 408 iflag = 1; 409 break; 410 case '?': 411 default: 412 usage(); 413 } 414 argv += optind; 415 argc -= optind; 416 417 /* 418 * Discard setgid privileges if not the running kernel so that bad 419 * guys can't print interesting stuff from kernel memory. 420 */ 421 if (nlistf != NULL || memf != NULL) { 422 setegid(getgid()); 423 setgid(getgid()); 424 } 425 426 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 427 buf)) == NULL) { 428 fprintf(stderr, "%s: kvm_open: %s\n", __progname, buf); 429 exit(1); 430 } 431 setegid(getgid()); 432 setgid(getgid()); 433 434 #define BACKWARD_COMPATIBILITY 435 #ifdef BACKWARD_COMPATIBILITY 436 if (*argv) { 437 if (isdigit(**argv)) { 438 interval = atoi(*argv); 439 if (interval <= 0) 440 usage(); 441 ++argv; 442 iflag = 1; 443 } 444 if (*argv) { 445 nlistf = *argv; 446 if (*++argv) 447 memf = *argv; 448 } 449 } 450 #endif 451 452 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 453 if (nlistf) 454 fprintf(stderr, "%s: %s: no namelist\n", __progname, 455 nlistf); 456 else 457 fprintf(stderr, "%s: no namelist\n", __progname); 458 exit(1); 459 } 460 if (mflag) { 461 mbpr(nl[N_MBSTAT].n_value, nl[N_MBPOOL].n_value, 462 nl[N_MCLPOOL].n_value); 463 exit(0); 464 } 465 if (pflag) { 466 if (tp->pr_stats) 467 (*tp->pr_stats)(nl[tp->pr_sindex].n_value, 468 tp->pr_name); 469 else 470 printf("%s: no stats routine\n", tp->pr_name); 471 exit(0); 472 } 473 /* 474 * Keep file descriptors open to avoid overhead 475 * of open/close on each call to get* routines. 476 */ 477 sethostent(1); 478 setnetent(1); 479 if (iflag) { 480 intpr(interval, nl[N_IFNET].n_value); 481 exit(0); 482 } 483 if (rflag) { 484 if (sflag) 485 rt_stats(nl[N_RTSTAT].n_value); 486 else 487 routepr(nl[N_RTREE].n_value); 488 exit(0); 489 } 490 if (gflag) { 491 if (sflag) { 492 if (af == AF_INET || af == AF_UNSPEC) 493 mrt_stats(nl[N_MRTPROTO].n_value, 494 nl[N_MRTSTAT].n_value); 495 #ifdef INET6 496 if (af == AF_INET6 || af == AF_UNSPEC) 497 mrt6_stats(nl[N_MRT6PROTO].n_value, 498 nl[N_MRT6STAT].n_value); 499 #endif 500 } 501 else { 502 if (af == AF_INET || af == AF_UNSPEC) 503 mroutepr(nl[N_MRTPROTO].n_value, 504 nl[N_MFCHASHTBL].n_value, 505 nl[N_MFCHASH].n_value, 506 nl[N_VIFTABLE].n_value); 507 #ifdef INET6 508 if (af == AF_INET6 || af == AF_UNSPEC) 509 mroute6pr(nl[N_MRT6PROTO].n_value, 510 nl[N_MF6CTABLE].n_value, 511 nl[N_MIF6TABLE].n_value); 512 #endif 513 } 514 exit(0); 515 } 516 if (af == AF_INET || af == AF_UNSPEC) { 517 setprotoent(1); 518 setservent(1); 519 /* ugh, this is O(MN) ... why do we do this? */ 520 while ((p = getprotoent())) { 521 for (tp = protox; tp->pr_name; tp++) 522 if (strcmp(tp->pr_name, p->p_name) == 0) 523 break; 524 if (tp->pr_name == 0 || tp->pr_wanted == 0) 525 continue; 526 printproto(tp, p->p_name); 527 } 528 endprotoent(); 529 } 530 #ifdef INET6 531 if (af == AF_INET6 || af == AF_UNSPEC) 532 for (tp = ip6protox; tp->pr_name; tp++) 533 printproto(tp, tp->pr_name); 534 #endif 535 if (af == AF_IPX || af == AF_UNSPEC) 536 for (tp = ipxprotox; tp->pr_name; tp++) 537 printproto(tp, tp->pr_name); 538 if (af == AF_NS || af == AF_UNSPEC) 539 for (tp = nsprotox; tp->pr_name; tp++) 540 printproto(tp, tp->pr_name); 541 if (af == AF_ISO || af == AF_UNSPEC) 542 for (tp = isoprotox; tp->pr_name; tp++) 543 printproto(tp, tp->pr_name); 544 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 545 unixpr(nl[N_UNIXSW].n_value); 546 if (af == AF_APPLETALK || af == AF_UNSPEC) 547 for (tp = atalkprotox; tp->pr_name; tp++) 548 printproto(tp, tp->pr_name); 549 exit(0); 550 } 551 552 /* 553 * Print out protocol statistics or control blocks (per sflag). 554 * If the interface was not specifically requested, and the symbol 555 * is not in the namelist, ignore this one. 556 */ 557 static void 558 printproto(struct protox *tp, char *name) 559 { 560 void (*pr)(u_long, char *); 561 u_char i; 562 563 if (sflag) { 564 pr = tp->pr_stats; 565 i = tp->pr_sindex; 566 } else { 567 pr = tp->pr_cblocks; 568 i = tp->pr_index; 569 } 570 if (pr != NULL && i < sizeof(nl) / sizeof(nl[0]) && 571 (nl[i].n_value || af != AF_UNSPEC)) 572 (*pr)(nl[i].n_value, name); 573 } 574 575 /* 576 * Read kernel memory, return 0 on success. 577 */ 578 int 579 kread(u_long addr, char *buf, int size) 580 { 581 582 if (kvm_read(kvmd, addr, buf, size) != size) { 583 (void)fprintf(stderr, "%s: %s\n", __progname, 584 kvm_geterr(kvmd)); 585 return (-1); 586 } 587 return (0); 588 } 589 590 char * 591 plural(int n) 592 { 593 return (n != 1 ? "s" : ""); 594 } 595 596 char * 597 plurales(int n) 598 { 599 return (n != 1 ? "es" : ""); 600 } 601 602 /* 603 * Find the protox for the given "well-known" name. 604 */ 605 static struct protox * 606 knownname(char *name) 607 { 608 struct protox **tpp, *tp; 609 610 for (tpp = protoprotox; *tpp; tpp++) 611 for (tp = *tpp; tp->pr_name; tp++) 612 if (strcmp(tp->pr_name, name) == 0) 613 return (tp); 614 return (NULL); 615 } 616 617 /* 618 * Find the protox corresponding to name. 619 */ 620 static struct protox * 621 name2protox(char *name) 622 { 623 struct protox *tp; 624 char **alias; /* alias from p->aliases */ 625 struct protoent *p; 626 627 /* 628 * Try to find the name in the list of "well-known" names. If that 629 * fails, check if name is an alias for an Internet protocol. 630 */ 631 if ((tp = knownname(name))) 632 return (tp); 633 634 setprotoent(1); /* make protocol lookup cheaper */ 635 while ((p = getprotoent())) { 636 /* assert: name not same as p->name */ 637 for (alias = p->p_aliases; *alias; alias++) 638 if (strcmp(name, *alias) == 0) { 639 endprotoent(); 640 return (knownname(p->p_name)); 641 } 642 } 643 endprotoent(); 644 return (NULL); 645 } 646 647 static void 648 usage(void) 649 { 650 (void)fprintf(stderr, 651 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", __progname); 652 (void)fprintf(stderr, 653 " %s [-bdgilmnqrstu] [-f address_family] [-M core] [-N system]\n", __progname); 654 (void)fprintf(stderr, 655 " %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n", __progname); 656 (void)fprintf(stderr, 657 " %s [-M core] [-N system] [-p protocol]\n", __progname); 658 (void)fprintf(stderr, 659 " %s [-a] [-f address_family] [-i | -I interface]\n", __progname); 660 exit(1); 661 } 662