1 /* 2 * Copyright (c) 1983, 1988, 1993 3 * Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 char copyright[] = 36 "@(#) Copyright (c) 1983, 1988, 1993\n\ 37 Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 3/1/94"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/file.h> 46 #include <sys/protosw.h> 47 #include <sys/socket.h> 48 49 #include <netinet/in.h> 50 51 #include <ctype.h> 52 #include <errno.h> 53 #include <kvm.h> 54 #include <limits.h> 55 #include <netdb.h> 56 #include <nlist.h> 57 #include <paths.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 #include <err.h> 63 #include "netstat.h" 64 65 struct nlist nl[] = { 66 #define N_MBSTAT 0 67 { "_mbstat" }, 68 #define N_IPSTAT 1 69 { "_ipstat" }, 70 #define N_TCB 2 71 { "_tcb" }, 72 #define N_TCPSTAT 3 73 { "_tcpstat" }, 74 #define N_UDB 4 75 { "_udb" }, 76 #define N_UDPSTAT 5 77 { "_udpstat" }, 78 #define N_IFNET 6 79 { "_ifnet" }, 80 #define N_IMP 7 81 { "_imp_softc" }, 82 #define N_ICMPSTAT 8 83 { "_icmpstat" }, 84 #define N_RTSTAT 9 85 { "_rtstat" }, 86 #define N_UNIXSW 10 87 { "_localsw" }, 88 #define N_IDP 11 89 { "_nspcb"}, 90 #define N_IDPSTAT 12 91 { "_idpstat"}, 92 #define N_SPPSTAT 13 93 { "_spp_istat"}, 94 #define N_NSERR 14 95 { "_ns_errstat"}, 96 #define N_CLNPSTAT 15 97 { "_clnp_stat"}, 98 #define IN_NOTUSED 16 99 { "_tp_inpcb" }, 100 #define ISO_TP 17 101 { "_tp_refinfo" }, 102 #define N_TPSTAT 18 103 { "_tp_stat" }, 104 #define N_ESISSTAT 19 105 { "_esis_stat"}, 106 #define N_NIMP 20 107 { "_nimp"}, 108 #define N_RTREE 21 109 { "_rt_tables"}, 110 #define N_CLTP 22 111 { "_cltb"}, 112 #define N_CLTPSTAT 23 113 { "_cltpstat"}, 114 #define N_NFILE 24 115 { "_nfile" }, 116 #define N_FILE 25 117 { "_file" }, 118 #define N_IGMPSTAT 26 119 { "_igmpstat" }, 120 #define N_MRTPROTO 27 121 { "_ip_mrtproto" }, 122 #define N_MRTSTAT 28 123 { "_mrtstat" }, 124 #define N_MFCTABLE 29 125 { "_mfctable" }, 126 #define N_VIFTABLE 30 127 { "_viftable" }, 128 #define N_IPX 31 129 { "_ipxpcb"}, 130 #define N_IPXSTAT 32 131 { "_ipxstat"}, 132 #define N_SPXSTAT 33 133 { "_spx_istat"}, 134 #define N_IPXERR 34 135 { "_ipx_errstat"}, 136 #define N_DDPSTAT 35 137 { "_ddpstat"}, 138 #define N_DDPCB 36 139 { "_ddpcb"}, 140 #define N_DIVPCB 37 141 { "_divcb"}, 142 #define N_DIVSTAT 38 143 { "_divstat"}, 144 { "" }, 145 }; 146 147 struct protox { 148 u_char pr_index; /* index into nlist of cb head */ 149 u_char pr_sindex; /* index into nlist of stat block */ 150 u_char pr_wanted; /* 1 if wanted, 0 otherwise */ 151 void (*pr_cblocks)(); /* control blocks printing routine */ 152 void (*pr_stats)(); /* statistics printing routine */ 153 char *pr_name; /* well-known name */ 154 } protox[] = { 155 { N_TCB, N_TCPSTAT, 1, protopr, 156 tcp_stats, "tcp" }, 157 { N_UDB, N_UDPSTAT, 1, protopr, 158 udp_stats, "udp" }, 159 { N_DIVPCB, N_DIVSTAT, 1, protopr, 160 NULL, "divert" }, /* no stat structure yet */ 161 { -1, N_IPSTAT, 1, 0, 162 ip_stats, "ip" }, 163 { -1, N_ICMPSTAT, 1, 0, 164 icmp_stats, "icmp" }, 165 { -1, N_IGMPSTAT, 1, 0, 166 igmp_stats, "igmp" }, 167 { -1, -1, 0, 0, 168 0, 0 } 169 }; 170 171 struct protox atalkprotox[] = { 172 { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, 173 ddp_stats, "ddp" }, 174 { -1, -1, 0, 0, 175 0, 0 } 176 }; 177 178 struct protox ipxprotox[] = { 179 { N_IPX, N_IPXSTAT, 1, ipxprotopr, 180 ipx_stats, "ipx" }, 181 { N_IPX, N_SPXSTAT, 1, ipxprotopr, 182 spx_stats, "spx" }, 183 { -1, N_IPXERR, 1, 0, 184 ipxerr_stats, "ipx_err" }, 185 { -1, -1, 0, 0, 186 0, 0 } 187 }; 188 189 #ifdef NS 190 struct protox nsprotox[] = { 191 { N_IDP, N_IDPSTAT, 1, nsprotopr, 192 idp_stats, "idp" }, 193 { N_IDP, N_SPPSTAT, 1, nsprotopr, 194 spp_stats, "spp" }, 195 { -1, N_NSERR, 1, 0, 196 nserr_stats, "ns_err" }, 197 { -1, -1, 0, 0, 198 0, 0 } 199 }; 200 #endif 201 202 #ifdef ISO 203 struct protox isoprotox[] = { 204 { ISO_TP, N_TPSTAT, 1, iso_protopr, 205 tp_stats, "tp" }, 206 { N_CLTP, N_CLTPSTAT, 1, iso_protopr, 207 cltp_stats, "cltp" }, 208 { -1, N_CLNPSTAT, 1, 0, 209 clnp_stats, "clnp"}, 210 { -1, N_ESISSTAT, 1, 0, 211 esis_stats, "esis"}, 212 { -1, -1, 0, 0, 213 0, 0 } 214 }; 215 #endif 216 217 struct protox *protoprotox[] = { protox, ipxprotox, atalkprotox, 218 #ifdef NS 219 nsprotox, 220 #endif 221 #ifdef ISO 222 isoprotox, 223 #endif 224 NULL }; 225 226 static void printproto __P((struct protox *, char *)); 227 static void usage __P((void)); 228 static struct protox *name2protox __P((char *)); 229 static struct protox *knownname __P((char *)); 230 231 kvm_t *kvmd; 232 233 int 234 main(argc, argv) 235 int argc; 236 char *argv[]; 237 { 238 extern char *optarg; 239 extern int optind; 240 register struct protoent *p; 241 register struct protox *tp; /* for printing cblocks & stats */ 242 register char *cp; 243 int ch; 244 char *nlistf = NULL, *memf = NULL; 245 char buf[_POSIX2_LINE_MAX]; 246 247 if ((cp = rindex(argv[0], '/'))) 248 prog = cp + 1; 249 else 250 prog = argv[0]; 251 af = AF_UNSPEC; 252 253 while ((ch = getopt(argc, argv, "Aabdf:ghI:iM:mN:np:rstuw:")) != -1) 254 switch(ch) { 255 case 'A': 256 Aflag = 1; 257 break; 258 case 'a': 259 aflag = 1; 260 break; 261 case 'b': 262 bflag = 1; 263 break; 264 case 'd': 265 dflag = 1; 266 break; 267 case 'f': 268 #ifdef NS 269 if (strcmp(optarg, "ns") == 0) 270 af = AF_NS; 271 else 272 #endif 273 if (strcmp(optarg, "ipx") == 0) 274 af = AF_IPX; 275 else if (strcmp(optarg, "inet") == 0) 276 af = AF_INET; 277 else if (strcmp(optarg, "unix") == 0) 278 af = AF_UNIX; 279 else if (strcmp(optarg, "atalk") == 0) 280 af = AF_APPLETALK; 281 #ifdef ISO 282 else if (strcmp(optarg, "iso") == 0) 283 af = AF_ISO; 284 #endif 285 else { 286 errx(1, "%s: unknown address family", optarg); 287 } 288 break; 289 case 'g': 290 gflag = 1; 291 break; 292 case 'I': { 293 char *cp; 294 295 iflag = 1; 296 for (cp = interface = optarg; isalpha(*cp); cp++) 297 continue; 298 unit = atoi(cp); 299 break; 300 } 301 case 'i': 302 iflag = 1; 303 break; 304 case 'M': 305 memf = optarg; 306 break; 307 case 'm': 308 mflag = 1; 309 break; 310 case 'N': 311 nlistf = optarg; 312 break; 313 case 'n': 314 nflag = 1; 315 break; 316 case 'p': 317 if ((tp = name2protox(optarg)) == NULL) { 318 errx(1, 319 "%s: unknown or uninstrumented protocol", 320 optarg); 321 } 322 pflag = 1; 323 break; 324 case 'r': 325 rflag = 1; 326 break; 327 case 's': 328 ++sflag; 329 break; 330 case 't': 331 tflag = 1; 332 break; 333 case 'u': 334 af = AF_UNIX; 335 break; 336 case 'w': 337 interval = atoi(optarg); 338 iflag = 1; 339 break; 340 case '?': 341 default: 342 usage(); 343 } 344 argv += optind; 345 argc -= optind; 346 347 #define BACKWARD_COMPATIBILITY 348 #ifdef BACKWARD_COMPATIBILITY 349 if (*argv) { 350 if (isdigit(**argv)) { 351 interval = atoi(*argv); 352 if (interval <= 0) 353 usage(); 354 ++argv; 355 iflag = 1; 356 } 357 if (*argv) { 358 nlistf = *argv; 359 if (*++argv) 360 memf = *argv; 361 } 362 } 363 #endif 364 365 /* 366 * Discard setgid privileges if not the running kernel so that bad 367 * guys can't print interesting stuff from kernel memory. 368 */ 369 if (nlistf != NULL || memf != NULL) 370 setgid(getgid()); 371 372 kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); 373 if (kvmd == NULL) { 374 errx(1, "kvm_open: %s", buf); 375 } 376 if (kvm_nlist(kvmd, nl) < 0) { 377 if(nlistf) 378 errx(1, "%s: kvm_nlist: %s", nlistf, kvm_geterr(kvmd)); 379 else 380 errx(1, "kvm_nlist: %s", kvm_geterr(kvmd)); 381 } 382 383 if (nl[0].n_type == 0) { 384 if(nlistf) 385 errx(1, "%s: no namelist", nlistf); 386 else 387 errx(1, "no namelist"); 388 } 389 if (mflag) { 390 mbpr(nl[N_MBSTAT].n_value); 391 exit(0); 392 } 393 if (pflag) { 394 if (tp->pr_stats) 395 (*tp->pr_stats)(nl[tp->pr_sindex].n_value, 396 tp->pr_name); 397 else 398 printf("%s: no stats routine\n", tp->pr_name); 399 exit(0); 400 } 401 #if 0 402 /* 403 * Keep file descriptors open to avoid overhead 404 * of open/close on each call to get* routines. 405 */ 406 sethostent(1); 407 setnetent(1); 408 #else 409 /* 410 * This does not make sense any more with DNS being default over 411 * the files. Doing a setXXXXent(1) causes a tcp connection to be 412 * used for the queries, which is slower. 413 */ 414 #endif 415 if (iflag) { 416 intpr(interval, nl[N_IFNET].n_value); 417 exit(0); 418 } 419 if (rflag) { 420 if (sflag) 421 rt_stats(nl[N_RTSTAT].n_value); 422 else 423 routepr(nl[N_RTREE].n_value); 424 exit(0); 425 } 426 if (gflag) { 427 if (sflag) 428 mrt_stats(nl[N_MRTPROTO].n_value, 429 nl[N_MRTSTAT].n_value); 430 else 431 mroutepr(nl[N_MRTPROTO].n_value, 432 nl[N_MFCTABLE].n_value, 433 nl[N_VIFTABLE].n_value); 434 exit(0); 435 } 436 if (af == AF_INET || af == AF_UNSPEC) { 437 setprotoent(1); 438 setservent(1); 439 /* ugh, this is O(MN) ... why do we do this? */ 440 while ((p = getprotoent())) { 441 for (tp = protox; tp->pr_name; tp++) 442 if (strcmp(tp->pr_name, p->p_name) == 0) 443 break; 444 if (tp->pr_name == 0 || tp->pr_wanted == 0) 445 continue; 446 printproto(tp, p->p_name); 447 } 448 endprotoent(); 449 } 450 if (af == AF_IPX || af == AF_UNSPEC) 451 for (tp = ipxprotox; tp->pr_name; tp++) 452 printproto(tp, tp->pr_name); 453 if (af == AF_APPLETALK || af == AF_UNSPEC) 454 for (tp = atalkprotox; tp->pr_name; tp++) 455 printproto(tp, tp->pr_name); 456 #ifdef NS 457 if (af == AF_NS || af == AF_UNSPEC) 458 for (tp = nsprotox; tp->pr_name; tp++) 459 printproto(tp, tp->pr_name); 460 #endif 461 #ifdef ISO 462 if (af == AF_ISO || af == AF_UNSPEC) 463 for (tp = isoprotox; tp->pr_name; tp++) 464 printproto(tp, tp->pr_name); 465 #endif 466 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 467 unixpr(nl[N_UNIXSW].n_value); 468 exit(0); 469 } 470 471 /* 472 * Print out protocol statistics or control blocks (per sflag). 473 * If the interface was not specifically requested, and the symbol 474 * is not in the namelist, ignore this one. 475 */ 476 static void 477 printproto(tp, name) 478 register struct protox *tp; 479 char *name; 480 { 481 void (*pr)(); 482 u_long off; 483 484 if (sflag) { 485 pr = tp->pr_stats; 486 off = nl[tp->pr_sindex].n_value; 487 } else { 488 pr = tp->pr_cblocks; 489 off = nl[tp->pr_index].n_value; 490 } 491 if (pr != NULL && (off || af != AF_UNSPEC)) 492 (*pr)(off, name); 493 } 494 495 /* 496 * Read kernel memory, return 0 on success. 497 */ 498 int 499 kread(addr, buf, size) 500 u_long addr; 501 char *buf; 502 int size; 503 { 504 505 if (kvm_read(kvmd, addr, buf, size) != size) { 506 warnx("%s", kvm_geterr(kvmd)); 507 return (-1); 508 } 509 return (0); 510 } 511 512 char * 513 plural(n) 514 int n; 515 { 516 return (n != 1 ? "s" : ""); 517 } 518 519 char * 520 plurales(n) 521 int n; 522 { 523 return (n != 1 ? "es" : ""); 524 } 525 526 /* 527 * Find the protox for the given "well-known" name. 528 */ 529 static struct protox * 530 knownname(name) 531 char *name; 532 { 533 struct protox **tpp, *tp; 534 535 for (tpp = protoprotox; *tpp; tpp++) 536 for (tp = *tpp; tp->pr_name; tp++) 537 if (strcmp(tp->pr_name, name) == 0) 538 return (tp); 539 return (NULL); 540 } 541 542 /* 543 * Find the protox corresponding to name. 544 */ 545 static struct protox * 546 name2protox(name) 547 char *name; 548 { 549 struct protox *tp; 550 char **alias; /* alias from p->aliases */ 551 struct protoent *p; 552 553 /* 554 * Try to find the name in the list of "well-known" names. If that 555 * fails, check if name is an alias for an Internet protocol. 556 */ 557 if ((tp = knownname(name))) 558 return (tp); 559 560 setprotoent(1); /* make protocol lookup cheaper */ 561 while ((p = getprotoent())) { 562 /* assert: name not same as p->name */ 563 for (alias = p->p_aliases; *alias; alias++) 564 if (strcmp(name, *alias) == 0) { 565 endprotoent(); 566 return (knownname(p->p_name)); 567 } 568 } 569 endprotoent(); 570 return (NULL); 571 } 572 573 static void 574 usage() 575 { 576 (void)fprintf(stderr, 577 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", prog); 578 (void)fprintf(stderr, 579 " %s [-bdghimnrs] [-f address_family] [-M core] [-N system]\n", prog); 580 (void)fprintf(stderr, 581 " %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n", prog); 582 (void)fprintf(stderr, 583 " %s [-M core] [-N system] [-p protocol]\n", prog); 584 exit(1); 585 } 586 587 void 588 trimdomain(cp) 589 char *cp; 590 { 591 static char domain[MAXHOSTNAMELEN + 1]; 592 static int first = 1; 593 char *s; 594 595 if (first) { 596 first = 0; 597 if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 598 (s = strchr(domain, '.'))) 599 (void) strcpy(domain, s + 1); 600 else 601 domain[0] = 0; 602 } 603 604 if (domain[0]) { 605 while ((cp = strchr(cp, '.'))) { 606 if (!strcasecmp(cp + 1, domain)) { 607 *cp = 0; /* hit it */ 608 break; 609 } else { 610 cp++; 611 } 612 } 613 } 614 } 615 616