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 const 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 #if 0 42 static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 3/1/94"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/file.h> 50 #include <sys/protosw.h> 51 #include <sys/socket.h> 52 53 #include <netinet/in.h> 54 55 #include <netgraph/ng_socket.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 static struct nlist nl[] = { 72 #define N_IFNET 0 73 { "_ifnet" }, 74 #define N_IMP 1 75 { "_imp_softc" }, 76 #define N_RTSTAT 2 77 { "_rtstat" }, 78 #define N_UNIXSW 3 79 { "_localsw" }, 80 #define N_IDP 4 81 { "_nspcb"}, 82 #define N_IDPSTAT 5 83 { "_idpstat"}, 84 #define N_SPPSTAT 6 85 { "_spp_istat"}, 86 #define N_NSERR 7 87 { "_ns_errstat"}, 88 #define N_CLNPSTAT 8 89 { "_clnp_stat"}, 90 #define IN_NOTUSED 9 91 { "_tp_inpcb" }, 92 #define ISO_TP 10 93 { "_tp_refinfo" }, 94 #define N_TPSTAT 11 95 { "_tp_stat" }, 96 #define N_ESISSTAT 12 97 { "_esis_stat"}, 98 #define N_NIMP 13 99 { "_nimp"}, 100 #define N_RTREE 14 101 { "_rt_tables"}, 102 #define N_CLTP 15 103 { "_cltb"}, 104 #define N_CLTPSTAT 16 105 { "_cltpstat"}, 106 #define N_NFILE 17 107 { "_nfile" }, 108 #define N_FILE 18 109 { "_file" }, 110 #define N_MRTSTAT 19 111 { "_mrtstat" }, 112 #define N_MFCTABLE 20 113 { "_mfctable" }, 114 #define N_VIFTABLE 21 115 { "_viftable" }, 116 #define N_IPX 22 117 { "_ipxpcb"}, 118 #define N_IPXSTAT 23 119 { "_ipxstat"}, 120 #define N_SPXSTAT 24 121 { "_spx_istat"}, 122 #define N_DDPSTAT 25 123 { "_ddpstat"}, 124 #define N_DDPCB 26 125 { "_ddpcb"}, 126 #define N_NGSOCKS 27 127 { "_ngsocklist"}, 128 #define N_IP6STAT 28 129 { "_ip6stat" }, 130 #define N_ICMP6STAT 29 131 { "_icmp6stat" }, 132 #define N_IPSECSTAT 30 133 { "_ipsecstat" }, 134 #define N_IPSEC6STAT 31 135 { "_ipsec6stat" }, 136 #define N_PIM6STAT 32 137 { "_pim6stat" }, 138 #define N_MRT6PROTO 33 139 { "_ip6_mrtproto" }, 140 #define N_MRT6STAT 34 141 { "_mrt6stat" }, 142 #define N_MF6CTABLE 35 143 { "_mf6ctable" }, 144 #define N_MIF6TABLE 36 145 { "_mif6table" }, 146 { "" }, 147 }; 148 149 struct protox { 150 u_char pr_index; /* index into nlist of cb head */ 151 u_char pr_sindex; /* index into nlist of stat block */ 152 u_char pr_wanted; /* 1 if wanted, 0 otherwise */ 153 void (*pr_cblocks)(); /* control blocks printing routine */ 154 void (*pr_stats)(); /* statistics printing routine */ 155 void (*pr_istats)(); /* per/if statistics printing routine */ 156 char *pr_name; /* well-known name */ 157 int pr_usesysctl; /* true if we use sysctl, not kvm */ 158 } protox[] = { 159 { -1, -1, 1, protopr, 160 tcp_stats, NULL, "tcp", IPPROTO_TCP }, 161 { -1, -1, 1, protopr, 162 udp_stats, NULL, "udp", IPPROTO_UDP }, 163 { -1, -1, 1, protopr, 164 NULL, NULL, "divert",IPPROTO_DIVERT }, 165 { -1, -1, 1, protopr, 166 ip_stats, NULL, "ip", IPPROTO_RAW }, 167 { -1, -1, 1, protopr, 168 icmp_stats, NULL, "icmp", IPPROTO_ICMP }, 169 { -1, -1, 1, protopr, 170 igmp_stats, NULL, "igmp", IPPROTO_IGMP }, 171 #ifdef IPSEC 172 { -1, N_IPSECSTAT, 1, 0, 173 ipsec_stats, NULL, "ipsec", 0}, 174 #endif 175 { -1, -1, 1, protopr, 176 bdg_stats, NULL, "bdg", 1 /* bridging... */ }, 177 { -1, -1, 0, 0, 178 0, NULL, 0 } 179 }; 180 181 #ifdef INET6 182 struct protox ip6protox[] = { 183 { -1, -1, 1, protopr, 184 tcp_stats, NULL, "tcp", IPPROTO_TCP }, 185 { -1, -1, 1, protopr, 186 udp_stats, NULL, "udp", IPPROTO_UDP }, 187 { -1, N_IP6STAT, 1, 0, 188 ip6_stats, ip6_ifstats, "ip6", 0 }, 189 { -1, N_ICMP6STAT, 1, 0, 190 icmp6_stats, icmp6_ifstats, "icmp6",0 }, 191 #ifdef IPSEC 192 { -1, N_IPSEC6STAT, 1, 0, 193 ipsec_stats, NULL, "ipsec6",0 }, 194 #endif 195 #ifdef notyet 196 { -1, N_PIM6STAT, 1, 0, 197 pim6_stats, NULL, "pim6", 0 }, 198 #endif 199 { -1, -1, 1, protopr, 200 bdg_stats, NULL, "bdg", 1 /* bridging... */ }, 201 { -1, -1, 0, 0, 202 0, NULL, 0, 0 } 203 }; 204 #endif /*INET6*/ 205 206 struct protox atalkprotox[] = { 207 { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, 208 ddp_stats, NULL, "ddp" }, 209 { -1, -1, 0, 0, 210 0, NULL, 0 } 211 }; 212 213 struct protox netgraphprotox[] = { 214 { N_NGSOCKS, -1, 1, netgraphprotopr, 215 NULL, NULL, "ctrl" }, 216 { N_NGSOCKS, -1, 1, netgraphprotopr, 217 NULL, NULL, "data" }, 218 { -1, NULL, 0, 0, 219 0, NULL, 0 } 220 }; 221 222 struct protox ipxprotox[] = { 223 { N_IPX, N_IPXSTAT, 1, ipxprotopr, 224 ipx_stats, NULL, "ipx", 0 }, 225 { N_IPX, N_SPXSTAT, 1, ipxprotopr, 226 spx_stats, NULL, "spx", 0 }, 227 { -1, -1, 0, 0, 228 0, NULL, 0, 0 } 229 }; 230 231 #ifdef NS 232 struct protox nsprotox[] = { 233 { N_IDP, N_IDPSTAT, 1, nsprotopr, 234 idp_stats, NULL, "idp" }, 235 { N_IDP, N_SPPSTAT, 1, nsprotopr, 236 spp_stats, NULL, "spp" }, 237 { -1, N_NSERR, 1, 0, 238 nserr_stats, NULL, "ns_err" }, 239 { -1, -1, 0, 0, 240 0, NULL, 0 } 241 }; 242 #endif 243 244 #ifdef ISO 245 struct protox isoprotox[] = { 246 { ISO_TP, N_TPSTAT, 1, iso_protopr, 247 tp_stats, NULL, "tp" }, 248 { N_CLTP, N_CLTPSTAT, 1, iso_protopr, 249 cltp_stats, NULL, "cltp" }, 250 { -1, N_CLNPSTAT, 1, 0, 251 clnp_stats, NULL, "clnp"}, 252 { -1, N_ESISSTAT, 1, 0, 253 esis_stats, NULL, "esis"}, 254 { -1, -1, 0, 0, 255 0, NULL, 0 } 256 }; 257 #endif 258 259 struct protox *protoprotox[] = { 260 protox, 261 #ifdef INET6 262 ip6protox, 263 #endif 264 ipxprotox, atalkprotox, 265 #ifdef NS 266 nsprotox, 267 #endif 268 #ifdef ISO 269 isoprotox, 270 #endif 271 NULL }; 272 273 static void printproto __P((struct protox *, char *)); 274 static void usage __P((void)); 275 static struct protox *name2protox __P((char *)); 276 static struct protox *knownname __P((char *)); 277 278 static kvm_t *kvmd; 279 char *nlistf = NULL, *memf = NULL; 280 281 int 282 main(argc, argv) 283 int argc; 284 char *argv[]; 285 { 286 register struct protox *tp = NULL; /* for printing cblocks & stats */ 287 int ch; 288 289 af = AF_UNSPEC; 290 291 while ((ch = getopt(argc, argv, "Aabdf:ghI:lLiM:mN:np:rstuw:")) != -1) 292 switch(ch) { 293 case 'A': 294 Aflag = 1; 295 break; 296 case 'a': 297 aflag = 1; 298 break; 299 case 'b': 300 bflag = 1; 301 break; 302 case 'd': 303 dflag = 1; 304 break; 305 case 'f': 306 #ifdef NS 307 if (strcmp(optarg, "ns") == 0) 308 af = AF_NS; 309 else 310 #endif 311 if (strcmp(optarg, "ipx") == 0) 312 af = AF_IPX; 313 else if (strcmp(optarg, "inet") == 0) 314 af = AF_INET; 315 #ifdef INET6 316 else if (strcmp(optarg, "inet6") == 0) 317 af = AF_INET6; 318 #endif /*INET6*/ 319 else if (strcmp(optarg, "unix") == 0) 320 af = AF_UNIX; 321 else if (strcmp(optarg, "atalk") == 0) 322 af = AF_APPLETALK; 323 else if (strcmp(optarg, "ng") == 0 324 || strcmp(optarg, "netgraph") == 0) 325 af = AF_NETGRAPH; 326 #ifdef ISO 327 else if (strcmp(optarg, "iso") == 0) 328 af = AF_ISO; 329 #endif 330 else { 331 errx(1, "%s: unknown address family", optarg); 332 } 333 break; 334 case 'g': 335 gflag = 1; 336 break; 337 case 'I': { 338 char *cp; 339 340 iflag = 1; 341 for (cp = interface = optarg; isalpha(*cp); cp++) 342 continue; 343 unit = atoi(cp); 344 break; 345 } 346 case 'i': 347 iflag = 1; 348 break; 349 case 'l': 350 lflag = 1; 351 break; 352 case 'L': 353 Lflag = 1; 354 break; 355 case 'M': 356 memf = optarg; 357 break; 358 case 'm': 359 mflag = 1; 360 break; 361 case 'N': 362 nlistf = optarg; 363 break; 364 case 'n': 365 nflag = 1; 366 break; 367 case 'p': 368 if ((tp = name2protox(optarg)) == NULL) { 369 errx(1, 370 "%s: unknown or uninstrumented protocol", 371 optarg); 372 } 373 pflag = 1; 374 break; 375 case 'r': 376 rflag = 1; 377 break; 378 case 's': 379 ++sflag; 380 break; 381 case 't': 382 tflag = 1; 383 break; 384 case 'u': 385 af = AF_UNIX; 386 break; 387 case 'w': 388 interval = atoi(optarg); 389 iflag = 1; 390 break; 391 case '?': 392 default: 393 usage(); 394 } 395 argv += optind; 396 argc -= optind; 397 398 #define BACKWARD_COMPATIBILITY 399 #ifdef BACKWARD_COMPATIBILITY 400 if (*argv) { 401 if (isdigit(**argv)) { 402 interval = atoi(*argv); 403 if (interval <= 0) 404 usage(); 405 ++argv; 406 iflag = 1; 407 } 408 if (*argv) { 409 nlistf = *argv; 410 if (*++argv) 411 memf = *argv; 412 } 413 } 414 #endif 415 416 /* 417 * Discard setgid privileges if not the running kernel so that bad 418 * guys can't print interesting stuff from kernel memory. 419 */ 420 if (nlistf != NULL || memf != NULL) 421 setgid(getgid()); 422 423 if (mflag) { 424 mbpr(); 425 exit(0); 426 } 427 if (pflag) { 428 if (iflag && tp->pr_istats) { 429 kread(0, 0, 0); 430 intpr(interval, nl[N_IFNET].n_value, tp->pr_istats); 431 exit(0); 432 } 433 if (!tp->pr_stats) { 434 printf("%s: no stats routine\n", tp->pr_name); 435 exit(0); 436 } 437 if (tp->pr_usesysctl) { 438 (*tp->pr_stats)(tp->pr_usesysctl, tp->pr_name); 439 } else { 440 kread(0, 0, 0); 441 (*tp->pr_stats)(nl[tp->pr_sindex].n_value, 442 tp->pr_name); 443 } 444 exit(0); 445 } 446 #if 0 447 /* 448 * Keep file descriptors open to avoid overhead 449 * of open/close on each call to get* routines. 450 */ 451 sethostent(1); 452 setnetent(1); 453 #else 454 /* 455 * This does not make sense any more with DNS being default over 456 * the files. Doing a setXXXXent(1) causes a tcp connection to be 457 * used for the queries, which is slower. 458 */ 459 #endif 460 if (iflag) { 461 if (sflag && af != AF_UNSPEC) 462 goto protostat; 463 464 kread(0, 0, 0); 465 intpr(interval, nl[N_IFNET].n_value, NULL); 466 exit(0); 467 } 468 if (rflag) { 469 kread(0, 0, 0); 470 if (sflag) 471 rt_stats(nl[N_RTSTAT].n_value); 472 else 473 routepr(nl[N_RTREE].n_value); 474 exit(0); 475 } 476 if (gflag) { 477 kread(0, 0, 0); 478 if (sflag) { 479 if (af == AF_INET || af == AF_UNSPEC) 480 mrt_stats(nl[N_MRTSTAT].n_value); 481 #ifdef INET6 482 if (af == AF_INET6 || af == AF_UNSPEC) 483 mrt6_stats(nl[N_MRT6STAT].n_value); 484 #endif 485 } else { 486 if (af == AF_INET || af == AF_UNSPEC) 487 mroutepr(nl[N_MFCTABLE].n_value, 488 nl[N_VIFTABLE].n_value); 489 #ifdef INET6 490 if (af == AF_INET6 || af == AF_UNSPEC) 491 mroute6pr(nl[N_MF6CTABLE].n_value, 492 nl[N_MIF6TABLE].n_value); 493 #endif 494 } 495 exit(0); 496 } 497 498 protostat: 499 kread(0, 0, 0); 500 if (af == AF_INET || af == AF_UNSPEC) 501 for (tp = protox; tp->pr_name; tp++) 502 printproto(tp, tp->pr_name); 503 #ifdef INET6 504 if (af == AF_INET6 || af == AF_UNSPEC) 505 for (tp = ip6protox; tp->pr_name; tp++) 506 printproto(tp, tp->pr_name); 507 #endif /*INET6*/ 508 if (af == AF_IPX || af == AF_UNSPEC) { 509 kread(0, 0, 0); 510 for (tp = ipxprotox; tp->pr_name; tp++) 511 printproto(tp, tp->pr_name); 512 } 513 if (af == AF_APPLETALK || af == AF_UNSPEC) 514 for (tp = atalkprotox; tp->pr_name; tp++) 515 printproto(tp, tp->pr_name); 516 if (af == AF_NETGRAPH || af == AF_UNSPEC) 517 for (tp = netgraphprotox; tp->pr_name; tp++) 518 printproto(tp, tp->pr_name); 519 #ifdef NS 520 if (af == AF_NS || af == AF_UNSPEC) 521 for (tp = nsprotox; tp->pr_name; tp++) 522 printproto(tp, tp->pr_name); 523 #endif 524 #ifdef ISO 525 if (af == AF_ISO || af == AF_UNSPEC) 526 for (tp = isoprotox; tp->pr_name; tp++) 527 printproto(tp, tp->pr_name); 528 #endif 529 if ((af == AF_UNIX || af == AF_UNSPEC) && !Lflag && !sflag) 530 unixpr(); 531 exit(0); 532 } 533 534 /* 535 * Print out protocol statistics or control blocks (per sflag). 536 * If the interface was not specifically requested, and the symbol 537 * is not in the namelist, ignore this one. 538 */ 539 static void 540 printproto(tp, name) 541 register struct protox *tp; 542 char *name; 543 { 544 void (*pr)(); 545 u_long off; 546 547 if (sflag) { 548 if (iflag) { 549 if (tp->pr_istats) 550 intpr(interval, nl[N_IFNET].n_value, 551 tp->pr_istats); 552 return; 553 } 554 else { 555 pr = tp->pr_stats; 556 off = tp->pr_usesysctl ? tp->pr_usesysctl 557 : nl[tp->pr_sindex].n_value; 558 } 559 } else { 560 pr = tp->pr_cblocks; 561 off = tp->pr_usesysctl ? tp->pr_usesysctl 562 : nl[tp->pr_index].n_value; 563 } 564 if (pr != NULL && (off || af != AF_UNSPEC)) 565 (*pr)(off, name, af); 566 } 567 568 /* 569 * Read kernel memory, return 0 on success. 570 */ 571 int 572 kread(addr, buf, size) 573 u_long addr; 574 char *buf; 575 int size; 576 { 577 if (kvmd == 0) { 578 /* 579 * XXX. 580 */ 581 kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); 582 if (kvmd != NULL) { 583 if (kvm_nlist(kvmd, nl) < 0) { 584 if(nlistf) 585 errx(1, "%s: kvm_nlist: %s", nlistf, 586 kvm_geterr(kvmd)); 587 else 588 errx(1, "kvm_nlist: %s", kvm_geterr(kvmd)); 589 } 590 591 if (nl[0].n_type == 0) { 592 if(nlistf) 593 errx(1, "%s: no namelist", nlistf); 594 else 595 errx(1, "no namelist"); 596 } 597 } else { 598 warnx("kvm not available"); 599 return(-1); 600 } 601 } 602 if (!buf) 603 return (0); 604 if (kvm_read(kvmd, addr, buf, size) != size) { 605 warnx("%s", kvm_geterr(kvmd)); 606 return (-1); 607 } 608 return (0); 609 } 610 611 char * 612 plural(n) 613 int n; 614 { 615 return (n != 1 ? "s" : ""); 616 } 617 618 char * 619 plurales(n) 620 int n; 621 { 622 return (n != 1 ? "es" : ""); 623 } 624 625 /* 626 * Find the protox for the given "well-known" name. 627 */ 628 static struct protox * 629 knownname(name) 630 char *name; 631 { 632 struct protox **tpp, *tp; 633 634 for (tpp = protoprotox; *tpp; tpp++) 635 for (tp = *tpp; tp->pr_name; tp++) 636 if (strcmp(tp->pr_name, name) == 0) 637 return (tp); 638 return (NULL); 639 } 640 641 /* 642 * Find the protox corresponding to name. 643 */ 644 static struct protox * 645 name2protox(name) 646 char *name; 647 { 648 struct protox *tp; 649 char **alias; /* alias from p->aliases */ 650 struct protoent *p; 651 652 /* 653 * Try to find the name in the list of "well-known" names. If that 654 * fails, check if name is an alias for an Internet protocol. 655 */ 656 if ((tp = knownname(name)) != NULL) 657 return (tp); 658 659 setprotoent(1); /* make protocol lookup cheaper */ 660 while ((p = getprotoent()) != NULL) { 661 /* assert: name not same as p->name */ 662 for (alias = p->p_aliases; *alias; alias++) 663 if (strcmp(name, *alias) == 0) { 664 endprotoent(); 665 return (knownname(p->p_name)); 666 } 667 } 668 endprotoent(); 669 return (NULL); 670 } 671 672 static void 673 usage() 674 { 675 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 676 "usage: netstat [-Aan] [-f address_family] [-M core] [-N system]", 677 " netstat [-abdghimnrs] [-f address_family] [-M core] [-N system]", 678 " netstat [-bdn] [-I interface] [-M core] [-N system] [-w wait]", 679 " netstat [-M core] [-N system] [-p protocol]"); 680 exit(1); 681 } 682 683 void 684 trimdomain(cp) 685 char *cp; 686 { 687 static char domain[MAXHOSTNAMELEN + 1]; 688 static int first = 1; 689 char *s; 690 691 if (first) { 692 first = 0; 693 if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 694 (s = strchr(domain, '.'))) 695 (void) strcpy(domain, s + 1); 696 else 697 domain[0] = 0; 698 } 699 700 if (domain[0]) { 701 while ((cp = strchr(cp, '.'))) { 702 if (!strcasecmp(cp + 1, domain)) { 703 *cp = 0; /* hit it */ 704 break; 705 } else { 706 cp++; 707 } 708 } 709 } 710 } 711 712