1 /* $OpenBSD: main.c,v 1.81 2009/11/05 20:50:14 michele 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 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 <kvm.h> 45 #include <limits.h> 46 #include <netdb.h> 47 #include <nlist.h> 48 #include <paths.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include "netstat.h" 54 55 struct nlist nl[] = { 56 #define N_TCBTABLE 0 57 { "_tcbtable" }, 58 #define N_UDBTABLE 1 59 { "_udbtable" }, 60 #define N_DDPCB 2 61 { "_ddpcb"}, 62 #define N_UNIXSW 3 63 { "_unixsw" }, 64 65 #define N_MFCHASHTBL 4 66 { "_mfchashtbl" }, 67 #define N_MFCHASH 5 68 { "_mfchash" }, 69 #define N_VIFTABLE 6 70 { "_viftable" }, 71 72 #define N_MF6CTABLE 7 73 { "_mf6ctable" }, 74 #define N_MIF6TABLE 8 75 { "_mif6table" }, 76 77 #define N_RTREE 9 78 { "_rt_tables"}, 79 #define N_RTMASK 10 80 { "_mask_rnhead" }, 81 #define N_AF2RTAFIDX 11 82 { "_af2rtafidx" }, 83 #define N_RTBLIDMAX 12 84 { "_rtbl_id_max" }, 85 86 #define N_RAWIPTABLE 13 87 { "_rawcbtable" }, 88 #define N_RAWIP6TABLE 14 89 { "_rawin6pcbtable" }, 90 #define N_DIVBTABLE 15 91 { "_divbtable" }, 92 #define N_DIVB6TABLE 16 93 { "_divb6table" }, 94 95 { ""} 96 }; 97 98 struct protox { 99 u_char pr_index; /* index into nlist of cb head */ 100 void (*pr_cblocks)(u_long, char *); /* control blocks printing routine */ 101 void (*pr_stats)(char *); /* statistics printing routine */ 102 void (*pr_dump)(u_long); /* pcb printing routine */ 103 char *pr_name; /* well-known name */ 104 } protox[] = { 105 { N_TCBTABLE, protopr, tcp_stats, tcp_dump, "tcp" }, 106 { N_UDBTABLE, protopr, udp_stats, NULL, "udp" }, 107 { N_RAWIPTABLE, protopr, ip_stats, NULL, "ip" }, 108 { N_DIVBTABLE, protopr, div_stats, NULL, "divert" }, 109 { -1, NULL, icmp_stats, NULL, "icmp" }, 110 { -1, NULL, igmp_stats, NULL, "igmp" }, 111 { -1, NULL, ah_stats, NULL, "ah" }, 112 { -1, NULL, esp_stats, NULL, "esp" }, 113 { -1, NULL, ipip_stats, NULL, "ipencap" }, 114 { -1, NULL, etherip_stats, NULL, "etherip" }, 115 { -1, NULL, ipcomp_stats, NULL, "ipcomp" }, 116 { -1, NULL, carp_stats, NULL, "carp" }, 117 { -1, NULL, pfsync_stats, NULL, "pfsync" }, 118 { -1, NULL, pim_stats, NULL, "pim" }, 119 { -1, NULL, pflow_stats, NULL, "pflow" }, 120 { -1, NULL, NULL, NULL, NULL } 121 }; 122 123 struct protox ip6protox[] = { 124 { N_TCBTABLE, ip6protopr, NULL, tcp_dump, "tcp" }, 125 { N_UDBTABLE, ip6protopr, NULL, NULL, "udp" }, 126 { N_RAWIP6TABLE,ip6protopr, ip6_stats, NULL, "ip6" }, 127 { N_DIVB6TABLE, ip6protopr, div6_stats, NULL, "divert6" }, 128 { -1, NULL, icmp6_stats, NULL, "icmp6" }, 129 { -1, NULL, pim6_stats, NULL, "pim6" }, 130 { -1, NULL, rip6_stats, NULL, "rip6" }, 131 { -1, NULL, NULL, NULL, NULL } 132 }; 133 134 struct protox atalkprotox[] = { 135 { N_DDPCB, atalkprotopr, ddp_stats, NULL, "ddp" }, 136 { -1, NULL, NULL, NULL, NULL } 137 }; 138 139 struct protox *protoprotox[] = { 140 protox, ip6protox, atalkprotox, NULL 141 }; 142 143 static void printproto(struct protox *, char *); 144 static void usage(void); 145 static struct protox *name2protox(char *); 146 static struct protox *knownname(char *); 147 148 kvm_t *kvmd; 149 150 int 151 main(int argc, char *argv[]) 152 { 153 extern char *optarg; 154 extern int optind; 155 const char *errstr; 156 struct protoent *p; 157 struct protox *tp = NULL; /* for printing cblocks & stats */ 158 int ch; 159 char *nlistf = NULL, *memf = NULL, *ep; 160 char buf[_POSIX2_LINE_MAX]; 161 gid_t gid; 162 u_long pcbaddr = 0; 163 u_int tableid = 0; 164 165 af = AF_UNSPEC; 166 167 while ((ch = getopt(argc, argv, "AabdFf:gI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1) 168 switch (ch) { 169 case 'A': 170 Aflag = 1; 171 break; 172 case 'a': 173 aflag = 1; 174 break; 175 case 'b': 176 bflag = 1; 177 break; 178 case 'd': 179 dflag = 1; 180 break; 181 case 'F': 182 Fflag = 1; 183 break; 184 case 'f': 185 if (strcmp(optarg, "inet") == 0) 186 af = AF_INET; 187 else if (strcmp(optarg, "inet6") == 0) 188 af = AF_INET6; 189 else if (strcmp(optarg, "local") == 0) 190 af = AF_LOCAL; 191 else if (strcmp(optarg, "unix") == 0) 192 af = AF_UNIX; 193 else if (strcmp(optarg, "encap") == 0) 194 af = PF_KEY; 195 else if (strcmp(optarg, "atalk") == 0) 196 af = AF_APPLETALK; 197 else if (strcmp(optarg, "mpls") == 0) 198 af = AF_MPLS; 199 else if (strcmp(optarg, "pflow") == 0) 200 af = PF_PFLOW; 201 else if (strcmp(optarg, "mask") == 0) 202 af = 0xff; 203 else { 204 (void)fprintf(stderr, 205 "%s: %s: unknown address family\n", 206 __progname, optarg); 207 exit(1); 208 } 209 break; 210 case 'g': 211 gflag = 1; 212 break; 213 case 'I': 214 iflag = 1; 215 interface = optarg; 216 break; 217 case 'i': 218 iflag = 1; 219 break; 220 case 'l': 221 lflag = 1; 222 break; 223 case 'M': 224 memf = optarg; 225 break; 226 case 'm': 227 mflag = 1; 228 break; 229 case 'N': 230 nlistf = optarg; 231 break; 232 case 'n': 233 nflag = 1; 234 break; 235 case 'p': 236 if ((tp = name2protox(optarg)) == NULL) { 237 (void)fprintf(stderr, 238 "%s: %s: unknown protocol\n", 239 __progname, optarg); 240 exit(1); 241 } 242 pflag = 1; 243 break; 244 case 'P': 245 errno = 0; 246 pcbaddr = strtoul(optarg, &ep, 16); 247 if (optarg[0] == '\0' || *ep != '\0' || 248 errno == ERANGE) { 249 (void)fprintf(stderr, 250 "%s: %s: invalid PCB address\n", 251 __progname, optarg); 252 exit(1); 253 } 254 Pflag = 1; 255 break; 256 case 'q': 257 qflag = 1; 258 break; 259 case 'r': 260 rflag = 1; 261 break; 262 case 's': 263 ++sflag; 264 break; 265 case 'T': 266 tableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); 267 if (errstr) 268 errx(1, "invalid table id: %s", errstr); 269 break; 270 case 't': 271 tflag = 1; 272 break; 273 case 'u': 274 af = AF_UNIX; 275 break; 276 case 'v': 277 vflag = 1; 278 break; 279 case 'W': 280 Wflag = 1; 281 interface = optarg; 282 break; 283 case 'w': 284 interval = atoi(optarg); 285 iflag = 1; 286 break; 287 case '?': 288 default: 289 usage(); 290 } 291 argv += optind; 292 argc -= optind; 293 294 /* 295 * Show per-interface statistics which don't need access to 296 * kernel memory (they're using IOCTLs) 297 */ 298 if (Wflag) { 299 if (interface == NULL) 300 usage(); 301 net80211_ifstats(interface); 302 exit(0); 303 } 304 305 /* 306 * Discard setgid privileges if not the running kernel so that bad 307 * guys can't print interesting stuff from kernel memory. 308 * Dumping PCB info is also restricted. 309 */ 310 gid = getgid(); 311 if (nlistf != NULL || memf != NULL || Pflag) 312 if (setresgid(gid, gid, gid) == -1) 313 err(1, "setresgid"); 314 315 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 316 buf)) == NULL) { 317 fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf); 318 exit(1); 319 } 320 321 if (nlistf == NULL && memf == NULL && !Pflag) 322 if (setresgid(gid, gid, gid) == -1) 323 err(1, "setresgid"); 324 325 #define BACKWARD_COMPATIBILITY 326 #ifdef BACKWARD_COMPATIBILITY 327 if (*argv) { 328 if (isdigit(**argv)) { 329 interval = atoi(*argv); 330 if (interval <= 0) 331 usage(); 332 ++argv; 333 iflag = 1; 334 } 335 if (*argv) { 336 nlistf = *argv; 337 if (*++argv) 338 memf = *argv; 339 } 340 } 341 #endif 342 343 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 344 if (nlistf) 345 fprintf(stderr, "%s: %s: no namelist\n", __progname, 346 nlistf); 347 else 348 fprintf(stderr, "%s: no namelist\n", __progname); 349 exit(1); 350 } 351 if (mflag) { 352 mbpr(); 353 exit(0); 354 } 355 if (pflag) { 356 printproto(tp, tp->pr_name); 357 exit(0); 358 } 359 if (Pflag) { 360 if (tp == NULL && (tp = name2protox("tcp")) == NULL) { 361 (void)fprintf(stderr, 362 "%s: %s: unknown protocol\n", 363 __progname, "tcp"); 364 exit(1); 365 } 366 if (tp->pr_dump) 367 (tp->pr_dump)(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); 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); 420 } 421 endprotoent(); 422 } 423 if (af == PF_PFLOW || af == AF_UNSPEC) { 424 tp = name2protox("pflow"); 425 printproto(tp, tp->pr_name); 426 } 427 if (af == AF_INET6 || af == AF_UNSPEC) 428 for (tp = ip6protox; tp->pr_name; tp++) 429 printproto(tp, tp->pr_name); 430 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 431 unixpr(nl[N_UNIXSW].n_value); 432 if (af == AF_APPLETALK || af == AF_UNSPEC) 433 for (tp = atalkprotox; tp->pr_name; tp++) 434 printproto(tp, tp->pr_name); 435 exit(0); 436 } 437 438 /* 439 * Print out protocol statistics or control blocks (per sflag). 440 * If the interface was not specifically requested, and the symbol 441 * is not in the namelist, ignore this one. 442 */ 443 static void 444 printproto(struct protox *tp, char *name) 445 { 446 if (sflag) { 447 if (tp->pr_stats != NULL) 448 (*tp->pr_stats)(name); 449 } else { 450 u_char i = tp->pr_index; 451 if (tp->pr_cblocks != NULL && 452 i < sizeof(nl) / sizeof(nl[0]) && 453 (nl[i].n_value || af != AF_UNSPEC)) 454 (*tp->pr_cblocks)(nl[i].n_value, name); 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 [-Aan] [-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 [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n" 538 " %s [-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