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