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