1 /* $OpenBSD: main.c,v 1.102 2014/10/23 16:45:57 schwarze 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/protosw.h> 35 #include <sys/socket.h> 36 #include <sys/sysctl.h> 37 38 #include <net/route.h> 39 #include <netinet/in.h> 40 41 #include <ctype.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.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 62 #define N_MFCHASHTBL 2 63 { "_mfchashtbl" }, 64 #define N_MFCHASH 3 65 { "_mfchash" }, 66 #define N_VIFTABLE 4 67 { "_viftable" }, 68 69 #define N_MF6CTABLE 5 70 { "_mf6ctable" }, 71 #define N_MIF6TABLE 6 72 { "_mif6table" }, 73 74 #define N_RTREE 7 75 { "_rt_tables"}, 76 #define N_RTMASK 8 77 { "_mask_rnhead" }, 78 #define N_AF2RTAFIDX 9 79 { "_af2rtafidx" }, 80 #define N_RTBLIDMAX 10 81 { "_rtbl_id_max" }, 82 83 #define N_RAWIPTABLE 11 84 { "_rawcbtable" }, 85 #define N_RAWIP6TABLE 12 86 { "_rawin6pcbtable" }, 87 #define N_DIVBTABLE 13 88 { "_divbtable" }, 89 #define N_DIVB6TABLE 14 90 { "_divb6table" }, 91 92 { "" } 93 }; 94 95 struct protox { 96 u_char pr_index; /* index into nlist of cb head */ 97 void (*pr_cblocks)(u_long, char *, int, u_int, u_long); 98 /* control blocks printing routine */ 99 void (*pr_stats)(char *); /* statistics printing routine */ 100 char *pr_name; /* well-known name */ 101 } protox[] = { 102 { N_TCBTABLE, protopr, tcp_stats, "tcp" }, 103 { N_UDBTABLE, protopr, udp_stats, "udp" }, 104 { N_RAWIPTABLE, protopr, ip_stats, "ip" }, 105 { N_DIVBTABLE, protopr, div_stats, "divert" }, 106 { -1, NULL, icmp_stats, "icmp" }, 107 { -1, NULL, igmp_stats, "igmp" }, 108 { -1, NULL, ah_stats, "ah" }, 109 { -1, NULL, esp_stats, "esp" }, 110 { -1, NULL, ipip_stats, "ipencap" }, 111 { -1, NULL, etherip_stats, "etherip" }, 112 { -1, NULL, ipcomp_stats, "ipcomp" }, 113 { -1, NULL, carp_stats, "carp" }, 114 { -1, NULL, pfsync_stats, "pfsync" }, 115 { -1, NULL, pim_stats, "pim" }, 116 { -1, NULL, pflow_stats, "pflow" }, 117 { -1, NULL, NULL, NULL } 118 }; 119 120 struct protox ip6protox[] = { 121 { N_TCBTABLE, protopr, NULL, "tcp" }, 122 { N_UDBTABLE, protopr, NULL, "udp" }, 123 { N_RAWIP6TABLE,protopr, ip6_stats, "ip6" }, 124 { N_DIVB6TABLE, protopr, div6_stats, "divert6" }, 125 { -1, NULL, icmp6_stats, "icmp6" }, 126 { -1, NULL, pim6_stats, "pim6" }, 127 { -1, NULL, rip6_stats, "rip6" }, 128 { -1, NULL, NULL, NULL } 129 }; 130 131 struct protox *protoprotox[] = { 132 protox, ip6protox, NULL 133 }; 134 135 static void printproto(struct protox *, char *, int, u_int, u_long); 136 static void usage(void); 137 static struct protox *name2protox(char *); 138 static struct protox *knownname(char *); 139 u_int gettable(const char *); 140 141 int hideroot; 142 143 kvm_t *kvmd; 144 145 int 146 main(int argc, char *argv[]) 147 { 148 extern char *optarg; 149 extern int optind; 150 const char *errstr; 151 struct protoent *p; 152 struct protox *tp = NULL; /* for printing cblocks & stats */ 153 int ch; 154 char *nlistf = NULL, *memf = NULL, *ep; 155 char buf[_POSIX2_LINE_MAX]; 156 gid_t gid; 157 u_long pcbaddr = 0; 158 u_int tableid; 159 int Tflag = 0; 160 int repeatcount = 0; 161 int need_nlist; 162 163 hideroot = getuid(); 164 165 af = AF_UNSPEC; 166 tableid = getrtable(); 167 168 while ((ch = getopt(argc, argv, 169 "AaBbc:dFf:ghI: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 if (errstr) 186 errx(1, "count is %s", errstr); 187 break; 188 case 'd': 189 dflag = 1; 190 break; 191 case 'F': 192 Fflag = 1; 193 break; 194 case 'f': 195 if (strcmp(optarg, "inet") == 0) 196 af = AF_INET; 197 else if (strcmp(optarg, "inet6") == 0) 198 af = AF_INET6; 199 else if (strcmp(optarg, "local") == 0) 200 af = AF_LOCAL; 201 else if (strcmp(optarg, "unix") == 0) 202 af = AF_UNIX; 203 else if (strcmp(optarg, "encap") == 0) 204 af = PF_KEY; 205 else if (strcmp(optarg, "mpls") == 0) 206 af = AF_MPLS; 207 else if (strcmp(optarg, "pflow") == 0) 208 af = PF_PFLOW; 209 else if (strcmp(optarg, "mask") == 0) 210 af = 0xff; 211 else { 212 (void)fprintf(stderr, 213 "%s: %s: unknown address family\n", 214 __progname, optarg); 215 exit(1); 216 } 217 break; 218 case 'g': 219 gflag = 1; 220 break; 221 case 'h': 222 hflag = 1; 223 break; 224 case 'I': 225 iflag = 1; 226 interface = optarg; 227 break; 228 case 'i': 229 iflag = 1; 230 break; 231 case 'l': 232 lflag = 1; 233 break; 234 case 'M': 235 memf = optarg; 236 break; 237 case 'm': 238 mflag = 1; 239 break; 240 case 'N': 241 nlistf = optarg; 242 break; 243 case 'n': 244 nflag = 1; 245 break; 246 case 'p': 247 if ((tp = name2protox(optarg)) == NULL) { 248 (void)fprintf(stderr, 249 "%s: %s: unknown protocol\n", 250 __progname, optarg); 251 exit(1); 252 } 253 pflag = 1; 254 break; 255 case 'P': 256 errno = 0; 257 pcbaddr = strtoul(optarg, &ep, 16); 258 if (optarg[0] == '\0' || *ep != '\0' || 259 errno == ERANGE) { 260 (void)fprintf(stderr, 261 "%s: %s: invalid PCB address\n", 262 __progname, optarg); 263 exit(1); 264 } 265 Pflag = 1; 266 break; 267 case 'q': 268 qflag = 1; 269 break; 270 case 'r': 271 rflag = 1; 272 break; 273 case 's': 274 ++sflag; 275 break; 276 case 'T': 277 Tflag = 1; 278 tableid = gettable(optarg); 279 break; 280 case 't': 281 tflag = 1; 282 break; 283 case 'u': 284 af = AF_UNIX; 285 break; 286 case 'v': 287 vflag = 1; 288 break; 289 case 'W': 290 Wflag = 1; 291 interface = optarg; 292 break; 293 case 'w': 294 interval = strtonum(optarg, 1, INT_MAX, &errstr); 295 if (errstr) 296 errx(1, "interval is %s", errstr); 297 iflag = 1; 298 break; 299 case '?': 300 default: 301 usage(); 302 } 303 argv += optind; 304 argc -= optind; 305 306 /* 307 * Show per-interface statistics which don't need access to 308 * kernel memory (they're using IOCTLs) 309 */ 310 if (Wflag) { 311 if (interface == NULL) 312 usage(); 313 net80211_ifstats(interface); 314 exit(0); 315 } 316 317 #define BACKWARD_COMPATIBILITY 318 #ifdef BACKWARD_COMPATIBILITY 319 if (*argv) { 320 if (isdigit((unsigned char)**argv)) { 321 interval = strtonum(*argv, 1, INT_MAX, &errstr); 322 if (errstr) 323 errx(1, "interval is %s", errstr); 324 ++argv; 325 iflag = 1; 326 } 327 if (*argv) { 328 nlistf = *argv; 329 if (*++argv) 330 memf = *argv; 331 } 332 } 333 #endif 334 335 need_nlist = !mflag && (pflag || nlistf != NULL || memf != NULL || 336 (!iflag && !sflag && (rflag ? Aflag : 337 (gflag || af != AF_UNIX || Pflag)))); 338 339 /* 340 * Discard setgid privileges if not the running kernel so that bad 341 * guys can't print interesting stuff from kernel memory. 342 * Dumping PCB info is also restricted. 343 */ 344 gid = getgid(); 345 if (nlistf != NULL || memf != NULL || Pflag) 346 if (setresgid(gid, gid, gid) == -1) 347 err(1, "setresgid"); 348 349 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY | 350 (need_nlist ? 0 : KVM_NO_FILES), buf)) == NULL) { 351 fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf); 352 exit(1); 353 } 354 355 if (nlistf == NULL && memf == NULL && !Pflag) 356 if (setresgid(gid, gid, gid) == -1) 357 err(1, "setresgid"); 358 359 if (need_nlist && (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0)) { 360 if (nlistf) 361 fprintf(stderr, "%s: %s: no namelist\n", __progname, 362 nlistf); 363 else 364 fprintf(stderr, "%s: no namelist\n", __progname); 365 exit(1); 366 } 367 if (mflag) { 368 mbpr(); 369 exit(0); 370 } 371 if (pflag) { 372 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 373 exit(0); 374 } 375 if (iflag) { 376 intpr(interval, repeatcount); 377 exit(0); 378 } 379 if (rflag) { 380 if (sflag) 381 rt_stats(); 382 else if (Aflag || nlistf != NULL || memf != NULL) 383 routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value, 384 nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value, 385 tableid); 386 else 387 p_rttables(af, tableid); 388 exit(0); 389 } 390 if (gflag) { 391 if (sflag) { 392 if (af == AF_INET || af == AF_UNSPEC) 393 mrt_stats(); 394 if (af == AF_INET6 || af == AF_UNSPEC) 395 mrt6_stats(); 396 } else { 397 if (af == AF_INET || af == AF_UNSPEC) 398 mroutepr(nl[N_MFCHASHTBL].n_value, 399 nl[N_MFCHASH].n_value, 400 nl[N_VIFTABLE].n_value); 401 if (af == AF_INET6 || af == AF_UNSPEC) 402 mroute6pr(nl[N_MF6CTABLE].n_value, 403 nl[N_MIF6TABLE].n_value); 404 } 405 exit(0); 406 } 407 if (af == AF_INET || af == AF_UNSPEC) { 408 setprotoent(1); 409 setservent(1); 410 /* ugh, this is O(MN) ... why do we do this? */ 411 while ((p = getprotoent())) { 412 for (tp = protox; tp->pr_name; tp++) 413 if (strcmp(tp->pr_name, p->p_name) == 0) 414 break; 415 if (tp->pr_name == 0) 416 continue; 417 printproto(tp, p->p_name, AF_INET, tableid, pcbaddr); 418 } 419 endprotoent(); 420 } 421 if (af == PF_PFLOW || af == AF_UNSPEC) { 422 tp = name2protox("pflow"); 423 printproto(tp, tp->pr_name, af, tableid, pcbaddr); 424 } 425 if (af == AF_INET6 || af == AF_UNSPEC) 426 for (tp = ip6protox; tp->pr_name; tp++) 427 printproto(tp, tp->pr_name, AF_INET6, tableid, 428 pcbaddr); 429 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 430 unixpr(kvmd, pcbaddr); 431 exit(0); 432 } 433 434 /* 435 * Print out protocol statistics or control blocks (per sflag). 436 * If the interface was not specifically requested, and the symbol 437 * is not in the namelist, ignore this one. 438 */ 439 static void 440 printproto(struct protox *tp, char *name, int af, u_int tableid, 441 u_long pcbaddr) 442 { 443 if (sflag) { 444 if (tp->pr_stats != NULL) 445 (*tp->pr_stats)(name); 446 } else { 447 u_char i = tp->pr_index; 448 if (tp->pr_cblocks != NULL && 449 i < sizeof(nl) / sizeof(nl[0]) && 450 (nl[i].n_value || af != AF_UNSPEC)) 451 (*tp->pr_cblocks)(nl[i].n_value, name, af, tableid, 452 pcbaddr); 453 } 454 } 455 456 /* 457 * Read kernel memory, return 0 on success. 458 */ 459 int 460 kread(u_long addr, void *buf, int size) 461 { 462 463 if (kvm_read(kvmd, addr, buf, size) != size) { 464 (void)fprintf(stderr, "%s: %s\n", __progname, 465 kvm_geterr(kvmd)); 466 return (-1); 467 } 468 return (0); 469 } 470 471 char * 472 plural(u_int64_t n) 473 { 474 return (n != 1 ? "s" : ""); 475 } 476 477 char * 478 plurales(u_int64_t n) 479 { 480 return (n != 1 ? "es" : ""); 481 } 482 483 /* 484 * Find the protox for the given "well-known" name. 485 */ 486 static struct protox * 487 knownname(char *name) 488 { 489 struct protox **tpp, *tp; 490 491 for (tpp = protoprotox; *tpp; tpp++) 492 for (tp = *tpp; tp->pr_name; tp++) 493 if (strcmp(tp->pr_name, name) == 0) 494 return (tp); 495 return (NULL); 496 } 497 498 /* 499 * Find the protox corresponding to name. 500 */ 501 static struct protox * 502 name2protox(char *name) 503 { 504 struct protox *tp; 505 char **alias; /* alias from p->aliases */ 506 struct protoent *p; 507 508 /* 509 * Try to find the name in the list of "well-known" names. If that 510 * fails, check if name is an alias for an Internet protocol. 511 */ 512 if ((tp = knownname(name))) 513 return (tp); 514 515 setprotoent(1); /* make protocol lookup cheaper */ 516 while ((p = getprotoent())) { 517 /* assert: name not same as p->name */ 518 for (alias = p->p_aliases; *alias; alias++) 519 if (strcmp(name, *alias) == 0) { 520 endprotoent(); 521 return (knownname(p->p_name)); 522 } 523 } 524 endprotoent(); 525 return (NULL); 526 } 527 528 static void 529 usage(void) 530 { 531 (void)fprintf(stderr, 532 "usage: %s [-AaBn] [-f address_family] [-M core] [-N system]\n" 533 " %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n" 534 " [-T tableid]\n" 535 " %s [-bdhn] [-c count] [-I interface] [-M core] [-N system] [-w wait]\n" 536 " %s [-v] [-M core] [-N system] -P pcbaddr\n" 537 " %s [-s] [-M core] [-N system] [-p protocol]\n" 538 " %s [-a] [-f address_family] [-i | -I interface]\n" 539 " %s [-W interface]\n", 540 __progname, __progname, __progname, __progname, 541 __progname, __progname, __progname); 542 exit(1); 543 } 544 545 u_int 546 gettable(const char *s) 547 { 548 const char *errstr; 549 struct rt_tableinfo info; 550 int mib[6]; 551 size_t len; 552 u_int tableid; 553 554 tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); 555 if (errstr) 556 errx(1, "invalid table id: %s", errstr); 557 558 mib[0] = CTL_NET; 559 mib[1] = PF_ROUTE; 560 mib[2] = 0; 561 mib[3] = 0; 562 mib[4] = NET_RT_TABLE; 563 mib[5] = tableid; 564 565 len = sizeof(info); 566 if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) 567 err(1, "routing table %d", tableid); 568 569 return (tableid); 570 } 571