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