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. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1983, 1988, 1993 Regents of the University of California. All rights reserved. 30 * @(#)main.c 8.4 (Berkeley) 3/1/94 31 * $FreeBSD: src/usr.bin/netstat/main.c,v 1.34.2.12 2001/09/17 15:17:46 ru Exp $ 32 */ 33 34 #include <sys/param.h> 35 #include <sys/file.h> 36 #include <sys/protosw.h> 37 #include <sys/socket.h> 38 #include <sys/sysctl.h> 39 40 #include <netinet/in.h> 41 42 #include <netgraph/socket/ng_socket.h> 43 44 #include <ctype.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <kvm.h> 48 #include <limits.h> 49 #include <netdb.h> 50 #include <nlist.h> 51 #include <paths.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include "netstat.h" 57 58 static struct nlist nl[] = { 59 #define N_IFNET 0 60 { .n_name = "_ifnet" }, 61 #define N_IMP 1 62 { .n_name = "_imp_softc" }, 63 #define N_RTSTAT 2 64 { .n_name = "_rtstat" }, 65 #define N_UNIXSW 3 66 { .n_name = "_localsw" }, 67 #define N_IDP 4 68 { .n_name = "_nspcb"}, 69 #define N_IDPSTAT 5 70 { .n_name = "_idpstat"}, 71 #define N_SPPSTAT 6 72 { .n_name = "_spp_istat"}, 73 #define N_NSERR 7 74 { .n_name = "_ns_errstat"}, 75 #define N_CLNPSTAT 8 76 { .n_name = "_clnp_stat"}, 77 #define IN_NOTUSED 9 78 { .n_name = "_tp_inpcb" }, 79 #define ISO_TP 10 80 { .n_name = "_tp_refinfo" }, 81 #define N_TPSTAT 11 82 { .n_name = "_tp_stat" }, 83 #define N_ESISSTAT 12 84 { .n_name = "_esis_stat"}, 85 #define N_NIMP 13 86 { .n_name = "_nimp"}, 87 #define N_RTREE 14 88 { .n_name = "_rt_tables"}, 89 #define N_CLTP 15 90 { .n_name = "_cltb"}, 91 #define N_CLTPSTAT 16 92 { .n_name = "_cltpstat"}, 93 #define N_NFILE 17 94 { .n_name = "_nfile" }, 95 #define N_FILE 18 96 { .n_name = "_file" }, 97 #define N_MRTSTAT 19 98 { .n_name = "_mrtstat" }, 99 #define N_MFCTABLE 20 100 { .n_name = "_mfctable" }, 101 #define N_VIFTABLE 21 102 { .n_name = "_viftable" }, 103 #define N_NGSOCKS 22 104 { .n_name = "_ngsocklist"}, 105 #define N_IP6STAT 23 106 { .n_name = "_ip6stat" }, 107 #define N_ICMP6STAT 24 108 { .n_name = "_icmp6stat" }, 109 #define N_IPSECSTAT 25 110 { .n_name = "_ipsecstat" }, 111 #define N_IPSEC6STAT 26 112 { .n_name = "_ipsec6stat" }, 113 #define N_PIM6STAT 27 114 { .n_name = "_pim6stat" }, 115 #define N_MRT6PROTO 28 116 { .n_name = "_ip6_mrtproto" }, 117 #define N_MRT6STAT 29 118 { .n_name = "_mrt6stat" }, 119 #define N_MF6CTABLE 30 120 { .n_name = "_mf6ctable" }, 121 #define N_MIF6TABLE 31 122 { .n_name = "_mif6table" }, 123 #define N_PFKEYSTAT 32 124 { .n_name = "_pfkeystat" }, 125 #define N_MBSTAT 33 126 { .n_name = "_mbstat" }, 127 #define N_MBTYPES 34 128 { .n_name = "_mbtypes" }, 129 #define N_NMBCLUSTERS 35 130 { .n_name = "_nmbclusters" }, 131 #define N_NMBUFS 36 132 { .n_name = "_nmbufs" }, 133 #define N_RTTRASH 37 134 { .n_name = "_rttrash" }, 135 #define N_NCPUS 38 136 { .n_name = "_ncpus" }, 137 #define N_CARPSTAT 39 138 { .n_name = "_carpstats" }, 139 #define N_NMBJCLUSTERS 40 140 { .n_name = "_nmbjclusters" }, 141 { .n_name = NULL }, 142 }; 143 144 struct protox { 145 u_char pr_index; /* index into nlist of cb head */ 146 u_char pr_sindex; /* index into nlist of stat block */ 147 u_char pr_wanted; /* 1 if wanted, 0 otherwise */ 148 void (*pr_cblocks)(u_long, const char *, int); 149 /* control blocks printing routine */ 150 void (*pr_stats)(u_long, const char *, int); 151 /* statistics printing routine */ 152 void (*pr_istats)(char *); /* per/if statistics printing routine */ 153 const char *pr_name; /* well-known name */ 154 u_int pr_usesysctl; /* true if we use sysctl, not kvm */ 155 } protox[] = { 156 { -1, -1, 1, protopr, 157 tcp_stats, NULL, "tcp", IPPROTO_TCP }, 158 { -1, -1, 1, protopr, 159 udp_stats, NULL, "udp", IPPROTO_UDP }, 160 { -1, -1, 1, protopr, 161 NULL, NULL, "divert",IPPROTO_DIVERT }, 162 { -1, -1, 1, protopr, 163 ip_stats, NULL, "ip", IPPROTO_RAW }, 164 { -1, -1, 1, protopr, 165 icmp_stats, NULL, "icmp", IPPROTO_ICMP }, 166 { -1, -1, 1, protopr, 167 igmp_stats, NULL, "igmp", IPPROTO_IGMP }, 168 #ifdef IPSEC 169 { -1, N_IPSECSTAT, 1, 0, 170 ipsec_stats, NULL, "ipsec", 0}, 171 #endif 172 { -1, N_CARPSTAT, 1, 0, 173 carp_stats, NULL, "carp", 0}, 174 { -1, -1, 0, 0, 175 0, NULL, NULL, 0} 176 }; 177 178 #ifdef INET6 179 struct protox ip6protox[] = { 180 { -1, -1, 1, protopr, 181 tcp_stats, NULL, "tcp", IPPROTO_TCP }, 182 { -1, -1, 1, protopr, 183 udp_stats, NULL, "udp", IPPROTO_UDP }, 184 { -1, N_IP6STAT, 1, protopr, 185 ip6_stats, ip6_ifstats, "ip6", IPPROTO_RAW }, 186 { -1, N_ICMP6STAT, 1, protopr, 187 icmp6_stats, icmp6_ifstats, "icmp6",IPPROTO_ICMPV6 }, 188 #ifdef IPSEC 189 { -1, N_IPSEC6STAT, 1, 0, 190 ipsec_stats, NULL, "ipsec6",0 }, 191 #endif 192 #ifdef notyet 193 { -1, N_PIM6STAT, 1, 0, 194 pim6_stats, NULL, "pim6", 0 }, 195 #endif 196 { -1, -1, 1, 0, 197 rip6_stats, NULL, "rip6", 0 }, 198 { -1, -1, 1, protopr, 199 pim_stats, NULL, "pim", IPPROTO_PIM }, 200 { -1, -1, 0, 0, 201 0, NULL, 0, 0 } 202 }; 203 #endif /*INET6*/ 204 205 #ifdef IPSEC 206 struct protox pfkeyprotox[] = { 207 { -1, N_PFKEYSTAT, 1, 0, 208 pfkey_stats, NULL, "pfkey", 0 }, 209 { -1, -1, 0, 0, 210 0, NULL, 0, 0 } 211 }; 212 #endif 213 214 struct protox netgraphprotox[] = { 215 { N_NGSOCKS, -1, 1, netgraphprotopr, 216 NULL, NULL, "ctrl", 0 }, 217 { N_NGSOCKS, -1, 1, netgraphprotopr, 218 NULL, NULL, "data", 0 }, 219 { -1, -1, 0, 0, 220 0, NULL, NULL, 0 } 221 }; 222 223 struct protox *protoprotox[] = { 224 protox, 225 #ifdef INET6 226 ip6protox, 227 #endif 228 #ifdef IPSEC 229 pfkeyprotox, 230 #endif 231 NULL }; 232 233 static void printproto (struct protox *, const char *, u_long); 234 static void usage (void); 235 static struct protox *name2protox (char *); 236 static struct protox *knownname (char *); 237 238 static kvm_t *kvmd; 239 static char *nlistf = NULL, *memf = NULL; 240 241 int Aflag; /* show addresses of protocol control block */ 242 int aflag; /* show all sockets (including servers) */ 243 int bflag; /* show i/f total bytes in/out */ 244 int cpuflag = -1; /* dump route table from specific cpu */ 245 int dflag; /* show i/f dropped packets */ 246 int gflag; /* show group (multicast) routing or stats */ 247 int hflag; /* show counters in human readable format */ 248 int iflag; /* show interfaces */ 249 int Lflag; /* show size of listen queues */ 250 int mflag; /* show memory stats */ 251 int Pflag; /* show more protocol info (go past 80 columns) */ 252 int numeric_addr; /* show addresses numerically */ 253 int numeric_port; /* show ports numerically */ 254 static int pflag; /* show given protocol */ 255 int rflag; /* show routing tables (or routing stats) */ 256 int sflag; /* show protocol statistics */ 257 int tflag; /* show i/f watchdog timers */ 258 int Bflag; /* show buffer limit instead of buffer use */ 259 int Wflag; /* wide display */ 260 int zflag; /* zero stats */ 261 262 int interval; /* repeat interval for i/f stats */ 263 264 char *interface; /* desired i/f for stats, or NULL for all i/fs */ 265 int unit; /* unit number for above */ 266 267 int af; /* address family */ 268 269 int 270 main(int argc, char **argv) 271 { 272 struct protox *tp = NULL; /* for printing cblocks & stats */ 273 int ch; 274 int n; 275 size_t nsz; 276 277 af = AF_UNSPEC; 278 279 while ((ch = getopt(argc, argv, "Aabc:df:ghI:iLlM:mN:nPp:rSsBtuWw:z")) != -1) 280 switch(ch) { 281 case 'A': 282 Aflag = 1; 283 break; 284 case 'a': 285 aflag = 1; 286 break; 287 case 'b': 288 bflag = 1; 289 break; 290 case 'c': 291 nsz = sizeof(n); 292 sysctlbyname("net.netisr.ncpus", &n, &nsz, NULL, 0); 293 cpuflag = strtol(optarg, NULL, 0); 294 if (cpuflag < 0 || cpuflag >= n) { 295 errx(1, "cpu%d does not have network data", 296 cpuflag); 297 } 298 break; 299 case 'd': 300 dflag = 1; 301 break; 302 case 'f': 303 if (strcmp(optarg, "inet") == 0) 304 af = AF_INET; 305 #ifdef INET6 306 else if (strcmp(optarg, "inet6") == 0) 307 af = AF_INET6; 308 #endif /*INET6*/ 309 #ifdef INET6 310 else if (strcmp(optarg, "pfkey") == 0) 311 af = PF_KEY; 312 #endif /*INET6*/ 313 else if (strcmp(optarg, "unix") == 0) 314 af = AF_UNIX; 315 else if (strcmp(optarg, "ng") == 0 316 || strcmp(optarg, "netgraph") == 0) 317 af = AF_NETGRAPH; 318 else if (strcmp(optarg, "link") == 0) 319 af = AF_LINK; 320 else if (strcmp(optarg, "mpls") == 0) 321 af = AF_MPLS; 322 else { 323 errx(1, "%s: unknown address family", optarg); 324 } 325 break; 326 case 'g': 327 gflag = 1; 328 break; 329 case 'h': 330 hflag = 1; 331 break; 332 case 'I': { 333 char *cp; 334 335 iflag = 1; 336 for (cp = interface = optarg; isalpha(*cp); cp++) 337 continue; 338 unit = atoi(cp); 339 break; 340 } 341 case 'i': 342 iflag = 1; 343 break; 344 case 'L': 345 Lflag = 1; 346 break; 347 case 'M': 348 memf = optarg; 349 break; 350 case 'm': 351 mflag = 1; 352 break; 353 case 'N': 354 nlistf = optarg; 355 break; 356 case 'n': 357 numeric_addr = numeric_port = 1; 358 break; 359 case 'P': 360 Pflag = 1; 361 break; 362 case 'p': 363 if ((tp = name2protox(optarg)) == NULL) { 364 errx(1, 365 "%s: unknown or uninstrumented protocol", 366 optarg); 367 } 368 pflag = 1; 369 break; 370 case 'r': 371 rflag = 1; 372 break; 373 case 's': 374 ++sflag; 375 break; 376 case 'S': 377 numeric_addr = 1; 378 break; 379 case 'B': 380 Bflag = 1; 381 break; 382 case 't': 383 tflag = 1; 384 break; 385 case 'u': 386 af = AF_UNIX; 387 break; 388 case 'W': 389 case 'l': 390 Wflag = 1; 391 break; 392 case 'w': 393 interval = atoi(optarg); 394 iflag = 1; 395 break; 396 case 'z': 397 zflag = 1; 398 break; 399 case '?': 400 default: 401 usage(); 402 } 403 argv += optind; 404 argc -= optind; 405 406 #define BACKWARD_COMPATIBILITY 407 #ifdef BACKWARD_COMPATIBILITY 408 if (*argv) { 409 if (isdigit(**argv)) { 410 interval = atoi(*argv); 411 if (interval <= 0) 412 usage(); 413 ++argv; 414 iflag = 1; 415 } 416 if (*argv) { 417 nlistf = *argv; 418 if (*++argv) 419 memf = *argv; 420 } 421 } 422 #endif 423 424 /* 425 * Discard setgid privileges if not the running kernel so that bad 426 * guys can't print interesting stuff from kernel memory. 427 */ 428 if (nlistf != NULL || memf != NULL) 429 setgid(getgid()); 430 431 if (mflag) { 432 if (memf != NULL) { 433 if (kread(0, 0, 0) == 0) 434 mbpr(nl[N_MBSTAT].n_value, 435 nl[N_MBTYPES].n_value, 436 nl[N_NMBCLUSTERS].n_value, 437 nl[N_NMBJCLUSTERS].n_value, 438 nl[N_NMBUFS].n_value, 439 nl[N_NCPUS].n_value); 440 } else { 441 mbpr(0, 0, 0, 0, 0, 0); 442 } 443 exit(0); 444 } 445 #if 0 446 /* 447 * Keep file descriptors open to avoid overhead 448 * of open/close on each call to get* routines. 449 */ 450 sethostent(1); 451 setnetent(1); 452 #else 453 /* 454 * This does not make sense any more with DNS being default over 455 * the files. Doing a setXXXXent(1) causes a tcp connection to be 456 * used for the queries, which is slower. 457 */ 458 #endif 459 if (iflag && !sflag) { 460 kread(0, 0, 0); 461 intpr(interval, nl[N_IFNET].n_value, NULL, nl[N_NCPUS].n_value); 462 exit(0); 463 } 464 if (rflag) { 465 kread(0, 0, 0); 466 if (sflag) 467 rt_stats(); 468 else 469 routepr(nl[N_RTREE].n_value); 470 exit(0); 471 } 472 if (gflag) { 473 kread(0, 0, 0); 474 if (sflag) { 475 if (af == AF_INET || af == AF_UNSPEC) 476 mrt_stats(nl[N_MRTSTAT].n_value); 477 #ifdef INET6 478 if (af == AF_INET6 || af == AF_UNSPEC) 479 mrt6_stats(nl[N_MRT6STAT].n_value); 480 #endif 481 } else { 482 if (af == AF_INET || af == AF_UNSPEC) 483 mroutepr(nl[N_MFCTABLE].n_value, 484 nl[N_VIFTABLE].n_value); 485 #ifdef INET6 486 if (af == AF_INET6 || af == AF_UNSPEC) 487 mroute6pr(nl[N_MF6CTABLE].n_value, 488 nl[N_MIF6TABLE].n_value); 489 #endif 490 } 491 exit(0); 492 } 493 494 kread(0, 0, 0); 495 if (tp) { 496 printproto(tp, tp->pr_name, nl[N_NCPUS].n_value); 497 exit(0); 498 } 499 if (af == AF_INET || af == AF_UNSPEC) 500 for (tp = protox; tp->pr_name; tp++) 501 printproto(tp, tp->pr_name, nl[N_NCPUS].n_value); 502 #ifdef INET6 503 if (af == AF_INET6 || af == AF_UNSPEC) 504 for (tp = ip6protox; tp->pr_name; tp++) 505 printproto(tp, tp->pr_name, nl[N_NCPUS].n_value); 506 #endif /*INET6*/ 507 #ifdef IPSEC 508 if (af == PF_KEY || af == AF_UNSPEC) 509 for (tp = pfkeyprotox; tp->pr_name; tp++) 510 printproto(tp, tp->pr_name, nl[N_NCPUS].n_value); 511 #endif /*IPSEC*/ 512 if (af == AF_NETGRAPH || af == AF_UNSPEC) 513 for (tp = netgraphprotox; tp->pr_name; tp++) 514 printproto(tp, tp->pr_name, nl[N_NCPUS].n_value); 515 if ((af == AF_UNIX || af == AF_UNSPEC) && !Lflag && !sflag) 516 unixpr(); 517 exit(0); 518 } 519 520 /* 521 * Print out protocol statistics or control blocks (per sflag). 522 * If the interface was not specifically requested, and the symbol 523 * is not in the namelist, ignore this one. 524 */ 525 static void 526 printproto(struct protox *tp, const char *name, u_long ncpusaddr) 527 { 528 void (*pr)(u_long, const char *, int); 529 u_long off; 530 531 if (sflag) { 532 if (iflag) { 533 if (tp->pr_istats) 534 intpr(interval, nl[N_IFNET].n_value, 535 tp->pr_istats, ncpusaddr); 536 else if (pflag) 537 printf("%s: no per-interface stats routine\n", 538 tp->pr_name); 539 return; 540 } 541 else { 542 pr = tp->pr_stats; 543 if (!pr) { 544 if (pflag) 545 printf("%s: no stats routine\n", 546 tp->pr_name); 547 return; 548 } 549 off = tp->pr_usesysctl ? tp->pr_usesysctl 550 : nl[tp->pr_sindex].n_value; 551 } 552 } else { 553 pr = tp->pr_cblocks; 554 if (!pr) { 555 if (pflag) 556 printf("%s: no PCB routine\n", tp->pr_name); 557 return; 558 } 559 off = tp->pr_usesysctl ? tp->pr_usesysctl 560 : nl[tp->pr_index].n_value; 561 } 562 if (pr != NULL && (off || af != AF_UNSPEC)) 563 (*pr)(off, name, af); 564 } 565 566 /* 567 * Read kernel memory, return 0 on success. 568 */ 569 int 570 kread(u_long addr, char *buf, int size) 571 { 572 if (kvmd == NULL) { 573 /* 574 * XXX. 575 */ 576 kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); 577 if (kvmd != NULL) { 578 if (kvm_nlist(kvmd, nl) < 0) { 579 if(nlistf) 580 errx(1, "%s: kvm_nlist: %s", nlistf, 581 kvm_geterr(kvmd)); 582 else 583 errx(1, "kvm_nlist: %s", kvm_geterr(kvmd)); 584 } 585 586 if (nl[0].n_type == 0) { 587 if(nlistf) 588 errx(1, "%s: no namelist", nlistf); 589 else 590 errx(1, "no namelist"); 591 } 592 } else { 593 warnx("kvm not available"); 594 return(-1); 595 } 596 } 597 if (!buf) 598 return (0); 599 if (kvm_read(kvmd, addr, buf, size) != size) { 600 warnx("%s", kvm_geterr(kvmd)); 601 return (-1); 602 } 603 return (0); 604 } 605 606 const char * 607 plural(int n) 608 { 609 return (n != 1 ? "s" : ""); 610 } 611 612 const char * 613 plurales(int n) 614 { 615 return (n != 1 ? "es" : ""); 616 } 617 618 /* 619 * Find the protox for the given "well-known" name. 620 */ 621 static struct protox * 622 knownname(char *name) 623 { 624 struct protox **tpp, *tp; 625 626 for (tpp = protoprotox; *tpp; tpp++) 627 for (tp = *tpp; tp->pr_name; tp++) 628 if (strcmp(tp->pr_name, name) == 0) 629 return (tp); 630 return (NULL); 631 } 632 633 /* 634 * Find the protox corresponding to name. 635 */ 636 static struct protox * 637 name2protox(char *name) 638 { 639 struct protox *tp; 640 char **alias; /* alias from p->aliases */ 641 struct protoent *p; 642 643 /* 644 * Try to find the name in the list of "well-known" names. If that 645 * fails, check if name is an alias for an Internet protocol. 646 */ 647 if ((tp = knownname(name)) != NULL) 648 return (tp); 649 650 setprotoent(1); /* make protocol lookup cheaper */ 651 while ((p = getprotoent()) != NULL) { 652 /* assert: name not same as p->name */ 653 for (alias = p->p_aliases; *alias; alias++) 654 if (strcmp(name, *alias) == 0) { 655 endprotoent(); 656 return (knownname(p->p_name)); 657 } 658 } 659 endprotoent(); 660 return (NULL); 661 } 662 663 static void 664 usage(void) 665 { 666 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 667 "usage: netstat [-AaLnPSW] [-c cpu] [-f protocol_family | -p protocol]\n" 668 " [-M core] [-N system]", 669 " netstat -i | -I interface [-aBbdhnt] [-f address_family]\n" 670 " [-M core] [-N system]", 671 " netstat -w wait [-I interface] [-dh] [-M core] [-N system]", 672 " netstat -s [-s] [-z] [-f protocol_family | -p protocol] [-M core]", 673 " netstat -i | -I interface -s [-f protocol_family | -p protocol]\n" 674 " [-M core] [-N system]", 675 " netstat -m [-M core] [-N system]", 676 " netstat -r [-AanW] [-f address_family] [-M core] [-N system]", 677 " netstat -rs [-s] [-M core] [-N system]", 678 " netstat -g [-W] [-f address_family] [-M core] [-N system]", 679 " netstat -gs [-s] [-f address_family] [-M core] [-N system]"); 680 exit(1); 681 } 682