1 /* $OpenBSD: main.c,v 1.95 2013/03/20 15:23:37 deraadt 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 int hideroot; 146 147 kvm_t *kvmd; 148 149 int 150 main(int argc, char *argv[]) 151 { 152 extern char *optarg; 153 extern int optind; 154 const char *errstr; 155 struct protoent *p; 156 struct protox *tp = NULL; /* for printing cblocks & stats */ 157 int ch; 158 char *nlistf = NULL, *memf = NULL, *ep; 159 char buf[_POSIX2_LINE_MAX]; 160 gid_t gid; 161 u_long pcbaddr = 0; 162 u_int tableid; 163 int Tflag = 0; 164 int repeatcount = 0; 165 166 hideroot = getuid(); 167 168 af = AF_UNSPEC; 169 tableid = getrtable(); 170 171 while ((ch = getopt(argc, argv, 172 "AaBbc:dFf:ghI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1) 173 switch (ch) { 174 case 'A': 175 Aflag = 1; 176 break; 177 case 'a': 178 aflag = 1; 179 break; 180 case 'B': 181 Bflag = 1; 182 break; 183 case 'b': 184 bflag = 1; 185 break; 186 case 'c': 187 repeatcount = strtonum(optarg, 1, INT_MAX, &errstr); 188 break; 189 case 'd': 190 dflag = 1; 191 break; 192 case 'F': 193 Fflag = 1; 194 break; 195 case 'f': 196 if (strcmp(optarg, "inet") == 0) 197 af = AF_INET; 198 else if (strcmp(optarg, "inet6") == 0) 199 af = AF_INET6; 200 else if (strcmp(optarg, "local") == 0) 201 af = AF_LOCAL; 202 else if (strcmp(optarg, "unix") == 0) 203 af = AF_UNIX; 204 else if (strcmp(optarg, "encap") == 0) 205 af = PF_KEY; 206 else if (strcmp(optarg, "mpls") == 0) 207 af = AF_MPLS; 208 else if (strcmp(optarg, "pflow") == 0) 209 af = PF_PFLOW; 210 else if (strcmp(optarg, "mask") == 0) 211 af = 0xff; 212 else { 213 (void)fprintf(stderr, 214 "%s: %s: unknown address family\n", 215 __progname, optarg); 216 exit(1); 217 } 218 break; 219 case 'g': 220 gflag = 1; 221 break; 222 case 'h': 223 hflag = 1; 224 break; 225 case 'I': 226 iflag = 1; 227 interface = optarg; 228 break; 229 case 'i': 230 iflag = 1; 231 break; 232 case 'l': 233 lflag = 1; 234 break; 235 case 'M': 236 memf = optarg; 237 break; 238 case 'm': 239 mflag = 1; 240 break; 241 case 'N': 242 nlistf = optarg; 243 break; 244 case 'n': 245 nflag = 1; 246 break; 247 case 'p': 248 if ((tp = name2protox(optarg)) == NULL) { 249 (void)fprintf(stderr, 250 "%s: %s: unknown protocol\n", 251 __progname, optarg); 252 exit(1); 253 } 254 pflag = 1; 255 break; 256 case 'P': 257 errno = 0; 258 pcbaddr = strtoul(optarg, &ep, 16); 259 if (optarg[0] == '\0' || *ep != '\0' || 260 errno == ERANGE) { 261 (void)fprintf(stderr, 262 "%s: %s: invalid PCB address\n", 263 __progname, optarg); 264 exit(1); 265 } 266 Pflag = 1; 267 break; 268 case 'q': 269 qflag = 1; 270 break; 271 case 'r': 272 rflag = 1; 273 break; 274 case 's': 275 ++sflag; 276 break; 277 case 'T': 278 Tflag = 1; 279 tableid = gettable(optarg); 280 break; 281 case 't': 282 tflag = 1; 283 break; 284 case 'u': 285 af = AF_UNIX; 286 break; 287 case 'v': 288 vflag = 1; 289 break; 290 case 'W': 291 Wflag = 1; 292 interface = optarg; 293 break; 294 case 'w': 295 interval = atoi(optarg); 296 iflag = 1; 297 break; 298 case '?': 299 default: 300 usage(); 301 } 302 argv += optind; 303 argc -= optind; 304 305 /* 306 * Show per-interface statistics which don't need access to 307 * kernel memory (they're using IOCTLs) 308 */ 309 if (Wflag) { 310 if (interface == NULL) 311 usage(); 312 net80211_ifstats(interface); 313 exit(0); 314 } 315 316 #define BACKWARD_COMPATIBILITY 317 #ifdef BACKWARD_COMPATIBILITY 318 if (*argv) { 319 if (isdigit(**argv)) { 320 interval = atoi(*argv); 321 if (interval <= 0) 322 usage(); 323 ++argv; 324 iflag = 1; 325 } 326 if (*argv) { 327 nlistf = *argv; 328 if (*++argv) 329 memf = *argv; 330 } 331 } 332 #endif 333 334 /* 335 * Discard setgid privileges if not the running kernel so that bad 336 * guys can't print interesting stuff from kernel memory. 337 * Dumping PCB info is also restricted. 338 */ 339 gid = getgid(); 340 if (nlistf != NULL || memf != NULL || Pflag) 341 if (setresgid(gid, gid, gid) == -1) 342 err(1, "setresgid"); 343 344 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 345 buf)) == NULL) { 346 fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf); 347 exit(1); 348 } 349 350 if (nlistf == NULL && memf == NULL && !Pflag) 351 if (setresgid(gid, gid, gid) == -1) 352 err(1, "setresgid"); 353 354 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 355 if (nlistf) 356 fprintf(stderr, "%s: %s: no namelist\n", __progname, 357 nlistf); 358 else 359 fprintf(stderr, "%s: no namelist\n", __progname); 360 exit(1); 361 } 362 if (mflag) { 363 mbpr(); 364 exit(0); 365 } 366 if (pflag) { 367 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 368 exit(0); 369 } 370 /* 371 * Keep file descriptors open to avoid overhead 372 * of open/close on each call to get* routines. 373 */ 374 sethostent(1); 375 setnetent(1); 376 377 if (iflag) { 378 intpr(interval, repeatcount); 379 exit(0); 380 } 381 if (rflag) { 382 if (sflag) 383 rt_stats(); 384 else if (Aflag || nlistf != NULL || memf != NULL) 385 routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value, 386 nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value, 387 tableid); 388 else 389 p_rttables(af, tableid); 390 exit(0); 391 } 392 if (gflag) { 393 if (sflag) { 394 if (af == AF_INET || af == AF_UNSPEC) 395 mrt_stats(); 396 if (af == AF_INET6 || af == AF_UNSPEC) 397 mrt6_stats(); 398 } else { 399 if (af == AF_INET || af == AF_UNSPEC) 400 mroutepr(nl[N_MFCHASHTBL].n_value, 401 nl[N_MFCHASH].n_value, 402 nl[N_VIFTABLE].n_value); 403 if (af == AF_INET6 || af == AF_UNSPEC) 404 mroute6pr(nl[N_MF6CTABLE].n_value, 405 nl[N_MIF6TABLE].n_value); 406 } 407 exit(0); 408 } 409 if (af == AF_INET || af == AF_UNSPEC) { 410 setprotoent(1); 411 setservent(1); 412 /* ugh, this is O(MN) ... why do we do this? */ 413 while ((p = getprotoent())) { 414 for (tp = protox; tp->pr_name; tp++) 415 if (strcmp(tp->pr_name, p->p_name) == 0) 416 break; 417 if (tp->pr_name == 0) 418 continue; 419 printproto(tp, p->p_name, AF_INET, tableid, pcbaddr); 420 } 421 endprotoent(); 422 } 423 if (af == PF_PFLOW || af == AF_UNSPEC) { 424 tp = name2protox("pflow"); 425 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 426 } 427 if (af == AF_INET6 || af == AF_UNSPEC) 428 for (tp = ip6protox; tp->pr_name; tp++) 429 printproto(tp, tp->pr_name, AF_INET6, tableid, 430 pcbaddr); 431 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 432 unixpr(nl[N_UNIXSW].n_value, pcbaddr); 433 exit(0); 434 } 435 436 /* 437 * Print out protocol statistics or control blocks (per sflag). 438 * If the interface was not specifically requested, and the symbol 439 * is not in the namelist, ignore this one. 440 */ 441 static void 442 printproto(struct protox *tp, char *name, int af, u_int tableid, 443 u_long pcbaddr) 444 { 445 if (sflag) { 446 if (tp->pr_stats != NULL) 447 (*tp->pr_stats)(name); 448 } else { 449 u_char i = tp->pr_index; 450 if (tp->pr_cblocks != NULL && 451 i < sizeof(nl) / sizeof(nl[0]) && 452 (nl[i].n_value || af != AF_UNSPEC)) 453 (*tp->pr_cblocks)(nl[i].n_value, name, af, tableid, 454 pcbaddr); 455 } 456 } 457 458 /* 459 * Read kernel memory, return 0 on success. 460 */ 461 int 462 kread(u_long addr, void *buf, int size) 463 { 464 465 if (kvm_read(kvmd, addr, buf, size) != size) { 466 (void)fprintf(stderr, "%s: %s\n", __progname, 467 kvm_geterr(kvmd)); 468 return (-1); 469 } 470 return (0); 471 } 472 473 char * 474 plural(u_int64_t n) 475 { 476 return (n != 1 ? "s" : ""); 477 } 478 479 char * 480 plurales(u_int64_t n) 481 { 482 return (n != 1 ? "es" : ""); 483 } 484 485 /* 486 * Find the protox for the given "well-known" name. 487 */ 488 static struct protox * 489 knownname(char *name) 490 { 491 struct protox **tpp, *tp; 492 493 for (tpp = protoprotox; *tpp; tpp++) 494 for (tp = *tpp; tp->pr_name; tp++) 495 if (strcmp(tp->pr_name, name) == 0) 496 return (tp); 497 return (NULL); 498 } 499 500 /* 501 * Find the protox corresponding to name. 502 */ 503 static struct protox * 504 name2protox(char *name) 505 { 506 struct protox *tp; 507 char **alias; /* alias from p->aliases */ 508 struct protoent *p; 509 510 /* 511 * Try to find the name in the list of "well-known" names. If that 512 * fails, check if name is an alias for an Internet protocol. 513 */ 514 if ((tp = knownname(name))) 515 return (tp); 516 517 setprotoent(1); /* make protocol lookup cheaper */ 518 while ((p = getprotoent())) { 519 /* assert: name not same as p->name */ 520 for (alias = p->p_aliases; *alias; alias++) 521 if (strcmp(name, *alias) == 0) { 522 endprotoent(); 523 return (knownname(p->p_name)); 524 } 525 } 526 endprotoent(); 527 return (NULL); 528 } 529 530 static void 531 usage(void) 532 { 533 (void)fprintf(stderr, 534 "usage: %s [-AaBn] [-f address_family] [-M core] [-N system]\n" 535 " %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n" 536 " [-T tableid]\n" 537 " %s [-bdhn] [-c count] [-I interface] [-M core] [-N system] [-w wait]\n" 538 " %s [-v] [-M core] [-N system] -P pcbaddr\n" 539 " %s [-s] [-M core] [-N system] [-p protocol]\n" 540 " %s [-a] [-f address_family] [-i | -I interface]\n" 541 " %s [-W interface]\n", 542 __progname, __progname, __progname, __progname, 543 __progname, __progname, __progname); 544 exit(1); 545 } 546 547 u_int 548 gettable(const char *s) 549 { 550 const char *errstr; 551 struct rt_tableinfo info; 552 int mib[6]; 553 size_t len; 554 u_int tableid; 555 556 tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); 557 if (errstr) 558 errx(1, "invalid table id: %s", errstr); 559 560 mib[0] = CTL_NET; 561 mib[1] = AF_ROUTE; 562 mib[2] = 0; 563 mib[3] = 0; 564 mib[4] = NET_RT_TABLE; 565 mib[5] = tableid; 566 567 len = sizeof(info); 568 if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) 569 err(1, "routing table %i", tableid); 570 571 return (tableid); 572 } 573