1 /* $NetBSD: main.c,v 1.17 1998/07/18 05:04:10 lukem 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.17 1998/07/18 05:04:10 lukem 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 struct protox atalkprotox[] = { 174 { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, 175 ddp_stats, 0, "ddp" }, 176 { -1, -1, 0, 0, 177 0, 0 } 178 }; 179 180 struct protox nsprotox[] = { 181 { N_IDP, N_IDPSTAT, 1, nsprotopr, 182 idp_stats, 0, "idp" }, 183 { N_IDP, N_SPPSTAT, 1, nsprotopr, 184 spp_stats, 0, "spp" }, 185 { -1, N_NSERR, 1, 0, 186 nserr_stats, 0, "ns_err" }, 187 { -1, -1, 0, 0, 188 0, 0 } 189 }; 190 191 struct protox isoprotox[] = { 192 { ISO_TP, N_TPSTAT, 1, iso_protopr, 193 tp_stats, 0, "tp" }, 194 { N_CLTP, N_CLTPSTAT, 1, iso_protopr, 195 cltp_stats, 0, "cltp" }, 196 { -1, N_CLNPSTAT, 1, 0, 197 clnp_stats, 0, "clnp"}, 198 { -1, N_ESISSTAT, 1, 0, 199 esis_stats, 0, "esis"}, 200 { -1, -1, 0, 0, 201 0, 0, 0 } 202 }; 203 204 struct protox *protoprotox[] = { protox, atalkprotox, 205 nsprotox, isoprotox, NULL }; 206 207 int main __P((int, char *[])); 208 static void printproto __P((struct protox *, char *)); 209 static void usage __P((void)); 210 static struct protox *name2protox __P((char *)); 211 static struct protox *knownname __P((char *)); 212 213 kvm_t *kvmd; 214 215 int 216 main(argc, argv) 217 int argc; 218 char *argv[]; 219 { 220 extern char *optarg; 221 extern int optind; 222 struct protoent *p; 223 struct protox *tp; /* for printing cblocks & stats */ 224 int ch; 225 char *nlistf = NULL, *memf = NULL; 226 char buf[_POSIX2_LINE_MAX], *cp; 227 u_long pcbaddr; 228 gid_t egid = getegid(); 229 230 (void)setegid(getgid()); 231 tp = NULL; 232 af = AF_UNSPEC; 233 pcbaddr = 0; 234 235 while ((ch = getopt(argc, argv, "Aabdf:ghI:iM:mN:nP:p:rstuw:")) != -1) 236 switch(ch) { 237 case 'A': 238 Aflag = 1; 239 break; 240 case 'a': 241 aflag = 1; 242 break; 243 case 'b': 244 bflag = 1; 245 break; 246 case 'd': 247 dflag = 1; 248 break; 249 case 'f': 250 if (strcmp(optarg, "ns") == 0) 251 af = AF_NS; 252 else if (strcmp(optarg, "inet") == 0) 253 af = AF_INET; 254 else if (strcmp(optarg, "unix") == 0 255 || strcmp(optarg, "local") == 0) 256 af = AF_LOCAL; 257 else if (strcmp(optarg, "iso") == 0) 258 af = AF_ISO; 259 else if (strcmp(optarg, "atalk") == 0) 260 af = AF_APPLETALK; 261 else 262 errx(1, "%s: unknown address family", 263 optarg); 264 break; 265 case 'g': 266 gflag = 1; 267 break; 268 case 'I': 269 iflag = 1; 270 interface = optarg; 271 break; 272 case 'i': 273 iflag = 1; 274 break; 275 case 'M': 276 memf = optarg; 277 break; 278 case 'm': 279 mflag = 1; 280 break; 281 case 'N': 282 nlistf = optarg; 283 break; 284 case 'n': 285 nflag = 1; 286 break; 287 case 'P': 288 pcbaddr = strtoul(optarg, &cp, 16); 289 if (*cp != '\0' || errno == ERANGE) 290 errx(1, "invalid PCB address %s", 291 optarg); 292 Pflag = 1; 293 break; 294 case 'p': 295 if ((tp = name2protox(optarg)) == NULL) 296 errx(1, "%s: unknown or uninstrumented protocol", 297 optarg); 298 pflag = 1; 299 break; 300 case 'r': 301 rflag = 1; 302 break; 303 case 's': 304 ++sflag; 305 break; 306 case 't': 307 tflag = 1; 308 break; 309 case 'u': 310 af = AF_LOCAL; 311 break; 312 case 'w': 313 interval = atoi(optarg); 314 iflag = 1; 315 break; 316 case '?': 317 default: 318 usage(); 319 } 320 argv += optind; 321 argc -= optind; 322 323 #define BACKWARD_COMPATIBILITY 324 #ifdef BACKWARD_COMPATIBILITY 325 if (*argv) { 326 if (isdigit(**argv)) { 327 interval = atoi(*argv); 328 if (interval <= 0) 329 usage(); 330 ++argv; 331 iflag = 1; 332 } 333 if (*argv) { 334 nlistf = *argv; 335 if (*++argv) 336 memf = *argv; 337 } 338 } 339 #endif 340 341 /* 342 * Discard setgid privileges. If not the running kernel, we toss 343 * them away totally so that bad guys can't print interesting stuff 344 * from kernel memory, otherwise switch back to kmem for the 345 * duration of the kvm_openfiles() call. 346 */ 347 if (nlistf != NULL || memf != NULL) 348 (void)setgid(getgid()); 349 else 350 (void)setegid(egid); 351 352 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 353 buf)) == NULL) 354 errx(1, "%s", buf); 355 356 /* do this now anyway */ 357 if (nlistf == NULL && memf == NULL) 358 (void)setgid(getgid()); 359 360 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 361 if (nlistf) 362 errx(1, "%s: no namelist", nlistf); 363 else 364 errx(1, "no namelist"); 365 } 366 if (mflag) { 367 mbpr(nl[N_MBSTAT].n_value, nl[N_MSIZE].n_value, 368 nl[N_MCLBYTES].n_value); 369 exit(0); 370 } 371 if (Pflag) { 372 if (tp == NULL) { 373 /* Default to TCP. */ 374 tp = name2protox("tcp"); 375 } 376 if (tp->pr_dump) 377 (*tp->pr_dump)(pcbaddr); 378 else 379 printf("%s: no PCB dump routine\n", tp->pr_name); 380 exit(0); 381 } 382 if (pflag) { 383 if (tp->pr_stats) 384 (*tp->pr_stats)(nl[tp->pr_sindex].n_value, 385 tp->pr_name); 386 else 387 printf("%s: no stats routine\n", tp->pr_name); 388 exit(0); 389 } 390 /* 391 * Keep file descriptors open to avoid overhead 392 * of open/close on each call to get* routines. 393 */ 394 sethostent(1); 395 setnetent(1); 396 if (iflag) { 397 intpr(interval, nl[N_IFNET].n_value); 398 exit(0); 399 } 400 if (rflag) { 401 if (sflag) 402 rt_stats(nl[N_RTSTAT].n_value); 403 else 404 routepr(nl[N_RTREE].n_value); 405 exit(0); 406 } 407 if (gflag) { 408 if (sflag) 409 mrt_stats(nl[N_MRTPROTO].n_value, 410 nl[N_MRTSTAT].n_value); 411 else 412 mroutepr(nl[N_MRTPROTO].n_value, 413 nl[N_MFCHASHTBL].n_value, 414 nl[N_MFCHASH].n_value, 415 nl[N_VIFTABLE].n_value); 416 exit(0); 417 } 418 if (af == AF_INET || af == AF_UNSPEC) { 419 setprotoent(1); 420 setservent(1); 421 /* ugh, this is O(MN) ... why do we do this? */ 422 while ((p = getprotoent()) != NULL) { 423 for (tp = protox; tp->pr_name; tp++) 424 if (strcmp(tp->pr_name, p->p_name) == 0) 425 break; 426 if (tp->pr_name == 0 || tp->pr_wanted == 0) 427 continue; 428 printproto(tp, p->p_name); 429 } 430 endprotoent(); 431 } 432 if (af == AF_APPLETALK || af == AF_UNSPEC) 433 for (tp = atalkprotox; tp->pr_name; tp++) 434 printproto(tp, tp->pr_name); 435 if (af == AF_NS || af == AF_UNSPEC) 436 for (tp = nsprotox; tp->pr_name; tp++) 437 printproto(tp, tp->pr_name); 438 if (af == AF_ISO || af == AF_UNSPEC) 439 for (tp = isoprotox; tp->pr_name; tp++) 440 printproto(tp, tp->pr_name); 441 if ((af == AF_LOCAL || af == AF_UNSPEC) && !sflag) 442 unixpr(nl[N_UNIXSW].n_value); 443 exit(0); 444 } 445 446 /* 447 * Print out protocol statistics or control blocks (per sflag). 448 * If the interface was not specifically requested, and the symbol 449 * is not in the namelist, ignore this one. 450 */ 451 static void 452 printproto(tp, name) 453 struct protox *tp; 454 char *name; 455 { 456 void (*pr) __P((u_long, char *)); 457 u_long off; 458 459 if (sflag) { 460 pr = tp->pr_stats; 461 off = nl[tp->pr_sindex].n_value; 462 } else { 463 pr = tp->pr_cblocks; 464 off = nl[tp->pr_index].n_value; 465 } 466 if (pr != NULL && (off || af != AF_UNSPEC)) 467 (*pr)(off, name); 468 } 469 470 /* 471 * Read kernel memory, return 0 on success. 472 */ 473 int 474 kread(addr, buf, size) 475 u_long addr; 476 char *buf; 477 int size; 478 { 479 480 if (kvm_read(kvmd, addr, buf, size) != size) { 481 warnx("%s\n", kvm_geterr(kvmd)); 482 return (-1); 483 } 484 return (0); 485 } 486 487 char * 488 plural(n) 489 int n; 490 { 491 492 return (n != 1 ? "s" : ""); 493 } 494 495 char * 496 plurales(n) 497 int n; 498 { 499 500 return (n != 1 ? "es" : ""); 501 } 502 503 /* 504 * Find the protox for the given "well-known" name. 505 */ 506 static struct protox * 507 knownname(name) 508 char *name; 509 { 510 struct protox **tpp, *tp; 511 512 for (tpp = protoprotox; *tpp; tpp++) 513 for (tp = *tpp; tp->pr_name; tp++) 514 if (strcmp(tp->pr_name, name) == 0) 515 return (tp); 516 return (NULL); 517 } 518 519 /* 520 * Find the protox corresponding to name. 521 */ 522 static struct protox * 523 name2protox(name) 524 char *name; 525 { 526 struct protox *tp; 527 char **alias; /* alias from p->aliases */ 528 struct protoent *p; 529 530 /* 531 * Try to find the name in the list of "well-known" names. If that 532 * fails, check if name is an alias for an Internet protocol. 533 */ 534 if ((tp = knownname(name)) != NULL) 535 return (tp); 536 537 setprotoent(1); /* make protocol lookup cheaper */ 538 while ((p = getprotoent()) != NULL) { 539 /* assert: name not same as p->name */ 540 for (alias = p->p_aliases; *alias; alias++) 541 if (strcmp(name, *alias) == 0) { 542 endprotoent(); 543 return (knownname(p->p_name)); 544 } 545 } 546 endprotoent(); 547 return (NULL); 548 } 549 550 static void 551 usage() 552 { 553 (void)fprintf(stderr, 554 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", __progname); 555 (void)fprintf(stderr, 556 " %s [-ghimnrs] [-f address_family] [-M core] [-N system]\n", __progname); 557 (void)fprintf(stderr, 558 " %s [-n] [-I interface] [-M core] [-N system] [-w wait]\n", __progname); 559 (void)fprintf(stderr, 560 " %s [-M core] [-N system] [-p protocol]\n", __progname); 561 (void)fprintf(stderr, 562 " %s [-M core] [-N system] [-p protocol] -P pcbaddr\n", __progname); 563 exit(1); 564 } 565