1 /* $OpenBSD: main.c,v 1.92 2011/11/01 17:30:04 mikeb Exp $ */ 2 /* $NetBSD: main.c,v 1.9 1996/05/07 02:55:02 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1988, 1993 6 * Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/file.h> 35 #include <sys/protosw.h> 36 #include <sys/socket.h> 37 #include <sys/sysctl.h> 38 39 #include <net/route.h> 40 #include <netinet/in.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <kvm.h> 46 #include <limits.h> 47 #include <netdb.h> 48 #include <nlist.h> 49 #include <paths.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include "netstat.h" 55 56 struct nlist nl[] = { 57 #define N_TCBTABLE 0 58 { "_tcbtable" }, 59 #define N_UDBTABLE 1 60 { "_udbtable" }, 61 #define N_DDPCB 2 62 { "_ddpcb"}, 63 #define N_UNIXSW 3 64 { "_unixsw" }, 65 66 #define N_MFCHASHTBL 4 67 { "_mfchashtbl" }, 68 #define N_MFCHASH 5 69 { "_mfchash" }, 70 #define N_VIFTABLE 6 71 { "_viftable" }, 72 73 #define N_MF6CTABLE 7 74 { "_mf6ctable" }, 75 #define N_MIF6TABLE 8 76 { "_mif6table" }, 77 78 #define N_RTREE 9 79 { "_rt_tables"}, 80 #define N_RTMASK 10 81 { "_mask_rnhead" }, 82 #define N_AF2RTAFIDX 11 83 { "_af2rtafidx" }, 84 #define N_RTBLIDMAX 12 85 { "_rtbl_id_max" }, 86 87 #define N_RAWIPTABLE 13 88 { "_rawcbtable" }, 89 #define N_RAWIP6TABLE 14 90 { "_rawin6pcbtable" }, 91 #define N_DIVBTABLE 15 92 { "_divbtable" }, 93 #define N_DIVB6TABLE 16 94 { "_divb6table" }, 95 96 { "" } 97 }; 98 99 struct protox { 100 u_char pr_index; /* index into nlist of cb head */ 101 void (*pr_cblocks)(u_long, char *, int, u_int, u_long); 102 /* control blocks printing routine */ 103 void (*pr_stats)(char *); /* statistics printing routine */ 104 char *pr_name; /* well-known name */ 105 } protox[] = { 106 { N_TCBTABLE, protopr, tcp_stats, "tcp" }, 107 { N_UDBTABLE, protopr, udp_stats, "udp" }, 108 { N_RAWIPTABLE, protopr, ip_stats, "ip" }, 109 { N_DIVBTABLE, protopr, div_stats, "divert" }, 110 { -1, NULL, icmp_stats, "icmp" }, 111 { -1, NULL, igmp_stats, "igmp" }, 112 { -1, NULL, ah_stats, "ah" }, 113 { -1, NULL, esp_stats, "esp" }, 114 { -1, NULL, ipip_stats, "ipencap" }, 115 { -1, NULL, etherip_stats, "etherip" }, 116 { -1, NULL, ipcomp_stats, "ipcomp" }, 117 { -1, NULL, carp_stats, "carp" }, 118 { -1, NULL, pfsync_stats, "pfsync" }, 119 { -1, NULL, pim_stats, "pim" }, 120 { -1, NULL, pflow_stats, "pflow" }, 121 { -1, NULL, NULL, NULL } 122 }; 123 124 struct protox ip6protox[] = { 125 { N_TCBTABLE, protopr, NULL, "tcp" }, 126 { N_UDBTABLE, protopr, NULL, "udp" }, 127 { N_RAWIP6TABLE,protopr, ip6_stats, "ip6" }, 128 { N_DIVB6TABLE, protopr, div6_stats, "divert6" }, 129 { -1, NULL, icmp6_stats, "icmp6" }, 130 { -1, NULL, pim6_stats, "pim6" }, 131 { -1, NULL, rip6_stats, "rip6" }, 132 { -1, NULL, NULL, NULL } 133 }; 134 135 struct protox *protoprotox[] = { 136 protox, ip6protox, NULL 137 }; 138 139 static void printproto(struct protox *, char *, int, u_int, u_long); 140 static void usage(void); 141 static struct protox *name2protox(char *); 142 static struct protox *knownname(char *); 143 u_int gettable(const char *); 144 145 146 kvm_t *kvmd; 147 148 int 149 main(int argc, char *argv[]) 150 { 151 extern char *optarg; 152 extern int optind; 153 const char *errstr; 154 struct protoent *p; 155 struct protox *tp = NULL; /* for printing cblocks & stats */ 156 int ch; 157 char *nlistf = NULL, *memf = NULL, *ep; 158 char buf[_POSIX2_LINE_MAX]; 159 gid_t gid; 160 u_long pcbaddr = 0; 161 u_int tableid; 162 int Tflag = 0; 163 int repeatcount = 0; 164 165 af = AF_UNSPEC; 166 tableid = getrtable(); 167 168 while ((ch = getopt(argc, argv, 169 "AaBbc:dFf:gI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1) 170 switch (ch) { 171 case 'A': 172 Aflag = 1; 173 break; 174 case 'a': 175 aflag = 1; 176 break; 177 case 'B': 178 Bflag = 1; 179 break; 180 case 'b': 181 bflag = 1; 182 break; 183 case 'c': 184 repeatcount = strtonum(optarg, 1, INT_MAX, &errstr); 185 break; 186 case 'd': 187 dflag = 1; 188 break; 189 case 'F': 190 Fflag = 1; 191 break; 192 case 'f': 193 if (strcmp(optarg, "inet") == 0) 194 af = AF_INET; 195 else if (strcmp(optarg, "inet6") == 0) 196 af = AF_INET6; 197 else if (strcmp(optarg, "local") == 0) 198 af = AF_LOCAL; 199 else if (strcmp(optarg, "unix") == 0) 200 af = AF_UNIX; 201 else if (strcmp(optarg, "encap") == 0) 202 af = PF_KEY; 203 else if (strcmp(optarg, "mpls") == 0) 204 af = AF_MPLS; 205 else if (strcmp(optarg, "pflow") == 0) 206 af = PF_PFLOW; 207 else if (strcmp(optarg, "mask") == 0) 208 af = 0xff; 209 else { 210 (void)fprintf(stderr, 211 "%s: %s: unknown address family\n", 212 __progname, optarg); 213 exit(1); 214 } 215 break; 216 case 'g': 217 gflag = 1; 218 break; 219 case 'I': 220 iflag = 1; 221 interface = optarg; 222 break; 223 case 'i': 224 iflag = 1; 225 break; 226 case 'l': 227 lflag = 1; 228 break; 229 case 'M': 230 memf = optarg; 231 break; 232 case 'm': 233 mflag = 1; 234 break; 235 case 'N': 236 nlistf = optarg; 237 break; 238 case 'n': 239 nflag = 1; 240 break; 241 case 'p': 242 if ((tp = name2protox(optarg)) == NULL) { 243 (void)fprintf(stderr, 244 "%s: %s: unknown protocol\n", 245 __progname, optarg); 246 exit(1); 247 } 248 pflag = 1; 249 break; 250 case 'P': 251 errno = 0; 252 pcbaddr = strtoul(optarg, &ep, 16); 253 if (optarg[0] == '\0' || *ep != '\0' || 254 errno == ERANGE) { 255 (void)fprintf(stderr, 256 "%s: %s: invalid PCB address\n", 257 __progname, optarg); 258 exit(1); 259 } 260 Pflag = 1; 261 break; 262 case 'q': 263 qflag = 1; 264 break; 265 case 'r': 266 rflag = 1; 267 break; 268 case 's': 269 ++sflag; 270 break; 271 case 'T': 272 Tflag = 1; 273 tableid = gettable(optarg); 274 break; 275 case 't': 276 tflag = 1; 277 break; 278 case 'u': 279 af = AF_UNIX; 280 break; 281 case 'v': 282 vflag = 1; 283 break; 284 case 'W': 285 Wflag = 1; 286 interface = optarg; 287 break; 288 case 'w': 289 interval = atoi(optarg); 290 iflag = 1; 291 break; 292 case '?': 293 default: 294 usage(); 295 } 296 argv += optind; 297 argc -= optind; 298 299 /* 300 * Show per-interface statistics which don't need access to 301 * kernel memory (they're using IOCTLs) 302 */ 303 if (Wflag) { 304 if (interface == NULL) 305 usage(); 306 net80211_ifstats(interface); 307 exit(0); 308 } 309 310 #define BACKWARD_COMPATIBILITY 311 #ifdef BACKWARD_COMPATIBILITY 312 if (*argv) { 313 if (isdigit(**argv)) { 314 interval = atoi(*argv); 315 if (interval <= 0) 316 usage(); 317 ++argv; 318 iflag = 1; 319 } 320 if (*argv) { 321 nlistf = *argv; 322 if (*++argv) 323 memf = *argv; 324 } 325 } 326 #endif 327 328 /* 329 * Discard setgid privileges if not the running kernel so that bad 330 * guys can't print interesting stuff from kernel memory. 331 * Dumping PCB info is also restricted. 332 */ 333 gid = getgid(); 334 if (nlistf != NULL || memf != NULL || Pflag) 335 if (setresgid(gid, gid, gid) == -1) 336 err(1, "setresgid"); 337 338 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 339 buf)) == NULL) { 340 fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf); 341 exit(1); 342 } 343 344 if (nlistf == NULL && memf == NULL && !Pflag) 345 if (setresgid(gid, gid, gid) == -1) 346 err(1, "setresgid"); 347 348 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 349 if (nlistf) 350 fprintf(stderr, "%s: %s: no namelist\n", __progname, 351 nlistf); 352 else 353 fprintf(stderr, "%s: no namelist\n", __progname); 354 exit(1); 355 } 356 if (mflag) { 357 mbpr(); 358 exit(0); 359 } 360 if (pflag) { 361 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 362 exit(0); 363 } 364 /* 365 * Keep file descriptors open to avoid overhead 366 * of open/close on each call to get* routines. 367 */ 368 sethostent(1); 369 setnetent(1); 370 371 if (iflag) { 372 intpr(interval, repeatcount); 373 exit(0); 374 } 375 if (rflag) { 376 if (sflag) 377 rt_stats(); 378 else if (Aflag || nlistf != NULL || memf != NULL) 379 routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value, 380 nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value, 381 tableid); 382 else 383 p_rttables(af, tableid); 384 exit(0); 385 } 386 if (gflag) { 387 if (sflag) { 388 if (af == AF_INET || af == AF_UNSPEC) 389 mrt_stats(); 390 if (af == AF_INET6 || af == AF_UNSPEC) 391 mrt6_stats(); 392 } else { 393 if (af == AF_INET || af == AF_UNSPEC) 394 mroutepr(nl[N_MFCHASHTBL].n_value, 395 nl[N_MFCHASH].n_value, 396 nl[N_VIFTABLE].n_value); 397 if (af == AF_INET6 || af == AF_UNSPEC) 398 mroute6pr(nl[N_MF6CTABLE].n_value, 399 nl[N_MIF6TABLE].n_value); 400 } 401 exit(0); 402 } 403 if (af == AF_INET || af == AF_UNSPEC) { 404 setprotoent(1); 405 setservent(1); 406 /* ugh, this is O(MN) ... why do we do this? */ 407 while ((p = getprotoent())) { 408 for (tp = protox; tp->pr_name; tp++) 409 if (strcmp(tp->pr_name, p->p_name) == 0) 410 break; 411 if (tp->pr_name == 0) 412 continue; 413 printproto(tp, p->p_name, AF_INET, tableid, pcbaddr); 414 } 415 endprotoent(); 416 } 417 if (af == PF_PFLOW || af == AF_UNSPEC) { 418 tp = name2protox("pflow"); 419 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 420 } 421 if (af == AF_INET6 || af == AF_UNSPEC) 422 for (tp = ip6protox; tp->pr_name; tp++) 423 printproto(tp, tp->pr_name, AF_INET6, tableid, 424 pcbaddr); 425 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 426 unixpr(nl[N_UNIXSW].n_value, pcbaddr); 427 exit(0); 428 } 429 430 /* 431 * Print out protocol statistics or control blocks (per sflag). 432 * If the interface was not specifically requested, and the symbol 433 * is not in the namelist, ignore this one. 434 */ 435 static void 436 printproto(struct protox *tp, char *name, int af, u_int tableid, 437 u_long pcbaddr) 438 { 439 if (sflag) { 440 if (tp->pr_stats != NULL) 441 (*tp->pr_stats)(name); 442 } else { 443 u_char i = tp->pr_index; 444 if (tp->pr_cblocks != NULL && 445 i < sizeof(nl) / sizeof(nl[0]) && 446 (nl[i].n_value || af != AF_UNSPEC)) 447 (*tp->pr_cblocks)(nl[i].n_value, name, af, tableid, 448 pcbaddr); 449 } 450 } 451 452 /* 453 * Read kernel memory, return 0 on success. 454 */ 455 int 456 kread(u_long addr, void *buf, int size) 457 { 458 459 if (kvm_read(kvmd, addr, buf, size) != size) { 460 (void)fprintf(stderr, "%s: %s\n", __progname, 461 kvm_geterr(kvmd)); 462 return (-1); 463 } 464 return (0); 465 } 466 467 char * 468 plural(u_int64_t n) 469 { 470 return (n != 1 ? "s" : ""); 471 } 472 473 char * 474 plurales(u_int64_t n) 475 { 476 return (n != 1 ? "es" : ""); 477 } 478 479 /* 480 * Find the protox for the given "well-known" name. 481 */ 482 static struct protox * 483 knownname(char *name) 484 { 485 struct protox **tpp, *tp; 486 487 for (tpp = protoprotox; *tpp; tpp++) 488 for (tp = *tpp; tp->pr_name; tp++) 489 if (strcmp(tp->pr_name, name) == 0) 490 return (tp); 491 return (NULL); 492 } 493 494 /* 495 * Find the protox corresponding to name. 496 */ 497 static struct protox * 498 name2protox(char *name) 499 { 500 struct protox *tp; 501 char **alias; /* alias from p->aliases */ 502 struct protoent *p; 503 504 /* 505 * Try to find the name in the list of "well-known" names. If that 506 * fails, check if name is an alias for an Internet protocol. 507 */ 508 if ((tp = knownname(name))) 509 return (tp); 510 511 setprotoent(1); /* make protocol lookup cheaper */ 512 while ((p = getprotoent())) { 513 /* assert: name not same as p->name */ 514 for (alias = p->p_aliases; *alias; alias++) 515 if (strcmp(name, *alias) == 0) { 516 endprotoent(); 517 return (knownname(p->p_name)); 518 } 519 } 520 endprotoent(); 521 return (NULL); 522 } 523 524 static void 525 usage(void) 526 { 527 (void)fprintf(stderr, 528 "usage: %s [-AaBn] [-f address_family] [-M core] [-N system]\n" 529 " %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n" 530 " [-T tableid]\n" 531 " %s [-bdn] [-c count] [-I interface] [-M core] [-N system] [-w wait]\n" 532 " %s [-v] [-M core] [-N system] -P pcbaddr\n" 533 " %s [-s] [-M core] [-N system] [-p protocol]\n" 534 " %s [-a] [-f address_family] [-i | -I interface]\n" 535 " %s [-W interface]\n", 536 __progname, __progname, __progname, __progname, 537 __progname, __progname, __progname); 538 exit(1); 539 } 540 541 u_int 542 gettable(const char *s) 543 { 544 const char *errstr; 545 struct rt_tableinfo info; 546 int mib[6]; 547 size_t len; 548 u_int tableid; 549 550 tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); 551 if (errstr) 552 errx(1, "invalid table id: %s", errstr); 553 554 mib[0] = CTL_NET; 555 mib[1] = AF_ROUTE; 556 mib[2] = 0; 557 mib[3] = 0; 558 mib[4] = NET_RT_TABLE; 559 mib[5] = tableid; 560 561 len = sizeof(info); 562 if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) 563 err(1, "routing table %i", tableid); 564 565 return (tableid); 566 } 567