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