1 /* $OpenBSD: main.c,v 1.78 2009/02/21 20:07:49 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 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 91 { ""} 92 }; 93 94 struct protox { 95 u_char pr_index; /* index into nlist of cb head */ 96 void (*pr_cblocks)(u_long, char *); /* control blocks printing routine */ 97 void (*pr_stats)(char *); /* statistics printing routine */ 98 void (*pr_dump)(u_long); /* pcb printing routine */ 99 char *pr_name; /* well-known name */ 100 } protox[] = { 101 { N_TCBTABLE, protopr, tcp_stats, tcp_dump, "tcp" }, 102 { N_UDBTABLE, protopr, udp_stats, NULL, "udp" }, 103 { N_RAWIPTABLE, protopr, ip_stats, NULL, "ip" }, 104 { -1, NULL, icmp_stats, NULL, "icmp" }, 105 { -1, NULL, igmp_stats, NULL, "igmp" }, 106 { -1, NULL, ah_stats, NULL, "ah" }, 107 { -1, NULL, esp_stats, NULL, "esp" }, 108 { -1, NULL, ipip_stats, NULL, "ipencap" }, 109 { -1, NULL, etherip_stats, NULL, "etherip" }, 110 { -1, NULL, ipcomp_stats, NULL, "ipcomp" }, 111 { -1, NULL, carp_stats, NULL, "carp" }, 112 { -1, NULL, pfsync_stats, NULL, "pfsync" }, 113 { -1, NULL, pim_stats, NULL, "pim" }, 114 { -1, NULL, pflow_stats, NULL, "pflow" }, 115 { -1, NULL, NULL, NULL, NULL } 116 }; 117 118 struct protox ip6protox[] = { 119 { N_TCBTABLE, ip6protopr, NULL, tcp_dump, "tcp" }, 120 { N_UDBTABLE, ip6protopr, NULL, NULL, "udp" }, 121 { N_RAWIP6TABLE,ip6protopr, ip6_stats, NULL, "ip6" }, 122 { -1, NULL, icmp6_stats, NULL, "icmp6" }, 123 { -1, NULL, pim6_stats, NULL, "pim6" }, 124 { -1, NULL, rip6_stats, NULL, "rip6" }, 125 { -1, NULL, NULL, NULL, NULL } 126 }; 127 128 struct protox atalkprotox[] = { 129 { N_DDPCB, atalkprotopr, ddp_stats, NULL, "ddp" }, 130 { -1, NULL, NULL, NULL, NULL } 131 }; 132 133 struct protox *protoprotox[] = { 134 protox, ip6protox, atalkprotox, NULL 135 }; 136 137 static void printproto(struct protox *, char *); 138 static void usage(void); 139 static struct protox *name2protox(char *); 140 static struct protox *knownname(char *); 141 142 kvm_t *kvmd; 143 144 int 145 main(int argc, char *argv[]) 146 { 147 extern char *optarg; 148 extern int optind; 149 const char *errstr; 150 struct protoent *p; 151 struct protox *tp = NULL; /* for printing cblocks & stats */ 152 int ch; 153 char *nlistf = NULL, *memf = NULL, *ep; 154 char buf[_POSIX2_LINE_MAX]; 155 gid_t gid; 156 u_long pcbaddr = 0; 157 u_int tableid = 0; 158 159 af = AF_UNSPEC; 160 161 while ((ch = getopt(argc, argv, "AabdFf:gI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1) 162 switch (ch) { 163 case 'A': 164 Aflag = 1; 165 break; 166 case 'a': 167 aflag = 1; 168 break; 169 case 'b': 170 bflag = 1; 171 break; 172 case 'd': 173 dflag = 1; 174 break; 175 case 'F': 176 Fflag = 1; 177 break; 178 case 'f': 179 if (strcmp(optarg, "inet") == 0) 180 af = AF_INET; 181 else if (strcmp(optarg, "inet6") == 0) 182 af = AF_INET6; 183 else if (strcmp(optarg, "local") == 0) 184 af = AF_LOCAL; 185 else if (strcmp(optarg, "unix") == 0) 186 af = AF_UNIX; 187 else if (strcmp(optarg, "encap") == 0) 188 af = PF_KEY; 189 else if (strcmp(optarg, "atalk") == 0) 190 af = AF_APPLETALK; 191 else if (strcmp(optarg, "mpls") == 0) 192 af = AF_MPLS; 193 else if (strcmp(optarg, "pflow") == 0) 194 af = PF_PFLOW; 195 else if (strcmp(optarg, "mask") == 0) 196 af = 0xff; 197 else { 198 (void)fprintf(stderr, 199 "%s: %s: unknown address family\n", 200 __progname, optarg); 201 exit(1); 202 } 203 break; 204 case 'g': 205 gflag = 1; 206 break; 207 case 'I': 208 iflag = 1; 209 interface = optarg; 210 break; 211 case 'i': 212 iflag = 1; 213 break; 214 case 'l': 215 lflag = 1; 216 break; 217 case 'M': 218 memf = optarg; 219 break; 220 case 'm': 221 mflag = 1; 222 break; 223 case 'N': 224 nlistf = optarg; 225 break; 226 case 'n': 227 nflag = 1; 228 break; 229 case 'p': 230 if ((tp = name2protox(optarg)) == NULL) { 231 (void)fprintf(stderr, 232 "%s: %s: unknown protocol\n", 233 __progname, optarg); 234 exit(1); 235 } 236 pflag = 1; 237 break; 238 case 'P': 239 errno = 0; 240 pcbaddr = strtoul(optarg, &ep, 16); 241 if (optarg[0] == '\0' || *ep != '\0' || 242 errno == ERANGE) { 243 (void)fprintf(stderr, 244 "%s: %s: invalid PCB address\n", 245 __progname, optarg); 246 exit(1); 247 } 248 Pflag = 1; 249 break; 250 case 'q': 251 qflag = 1; 252 break; 253 case 'r': 254 rflag = 1; 255 break; 256 case 's': 257 ++sflag; 258 break; 259 case 'T': 260 tableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); 261 if (errstr) 262 errx(1, "invalid table id: %s", errstr); 263 break; 264 case 't': 265 tflag = 1; 266 break; 267 case 'u': 268 af = AF_UNIX; 269 break; 270 case 'v': 271 vflag = 1; 272 break; 273 case 'W': 274 Wflag = 1; 275 interface = optarg; 276 break; 277 case 'w': 278 interval = atoi(optarg); 279 iflag = 1; 280 break; 281 case '?': 282 default: 283 usage(); 284 } 285 argv += optind; 286 argc -= optind; 287 288 /* 289 * Show per-interface statistics which don't need access to 290 * kernel memory (they're using IOCTLs) 291 */ 292 if (Wflag) { 293 if (interface == NULL) 294 usage(); 295 net80211_ifstats(interface); 296 exit(0); 297 } 298 299 /* 300 * Discard setgid privileges if not the running kernel so that bad 301 * guys can't print interesting stuff from kernel memory. 302 * Dumping PCB info is also restricted. 303 */ 304 gid = getgid(); 305 if (nlistf != NULL || memf != NULL || Pflag) 306 if (setresgid(gid, gid, gid) == -1) 307 err(1, "setresgid"); 308 309 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 310 buf)) == NULL) { 311 fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf); 312 exit(1); 313 } 314 315 if (nlistf == NULL && memf == NULL && !Pflag) 316 if (setresgid(gid, gid, gid) == -1) 317 err(1, "setresgid"); 318 319 #define BACKWARD_COMPATIBILITY 320 #ifdef BACKWARD_COMPATIBILITY 321 if (*argv) { 322 if (isdigit(**argv)) { 323 interval = atoi(*argv); 324 if (interval <= 0) 325 usage(); 326 ++argv; 327 iflag = 1; 328 } 329 if (*argv) { 330 nlistf = *argv; 331 if (*++argv) 332 memf = *argv; 333 } 334 } 335 #endif 336 337 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { 338 if (nlistf) 339 fprintf(stderr, "%s: %s: no namelist\n", __progname, 340 nlistf); 341 else 342 fprintf(stderr, "%s: no namelist\n", __progname); 343 exit(1); 344 } 345 if (mflag) { 346 mbpr(); 347 exit(0); 348 } 349 if (pflag) { 350 printproto(tp, tp->pr_name); 351 exit(0); 352 } 353 if (Pflag) { 354 if (tp == NULL && (tp = name2protox("tcp")) == NULL) { 355 (void)fprintf(stderr, 356 "%s: %s: unknown protocol\n", 357 __progname, "tcp"); 358 exit(1); 359 } 360 if (tp->pr_dump) 361 (tp->pr_dump)(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); 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 else 382 p_rttables(af, tableid); 383 exit(0); 384 } 385 if (gflag) { 386 if (sflag) { 387 if (af == AF_INET || af == AF_UNSPEC) 388 mrt_stats(); 389 if (af == AF_INET6 || af == AF_UNSPEC) 390 mrt6_stats(); 391 } else { 392 if (af == AF_INET || af == AF_UNSPEC) 393 mroutepr(nl[N_MFCHASHTBL].n_value, 394 nl[N_MFCHASH].n_value, 395 nl[N_VIFTABLE].n_value); 396 if (af == AF_INET6 || af == AF_UNSPEC) 397 mroute6pr(nl[N_MF6CTABLE].n_value, 398 nl[N_MIF6TABLE].n_value); 399 } 400 exit(0); 401 } 402 if (af == AF_INET || af == AF_UNSPEC) { 403 setprotoent(1); 404 setservent(1); 405 /* ugh, this is O(MN) ... why do we do this? */ 406 while ((p = getprotoent())) { 407 for (tp = protox; tp->pr_name; tp++) 408 if (strcmp(tp->pr_name, p->p_name) == 0) 409 break; 410 if (tp->pr_name == 0) 411 continue; 412 printproto(tp, p->p_name); 413 } 414 endprotoent(); 415 } 416 if (af == PF_PFLOW || af == AF_UNSPEC) { 417 tp = name2protox("pflow"); 418 printproto(tp, tp->pr_name); 419 } 420 if (af == AF_INET6 || af == AF_UNSPEC) 421 for (tp = ip6protox; tp->pr_name; tp++) 422 printproto(tp, tp->pr_name); 423 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) 424 unixpr(nl[N_UNIXSW].n_value); 425 if (af == AF_APPLETALK || af == AF_UNSPEC) 426 for (tp = atalkprotox; tp->pr_name; tp++) 427 printproto(tp, tp->pr_name); 428 exit(0); 429 } 430 431 /* 432 * Print out protocol statistics or control blocks (per sflag). 433 * If the interface was not specifically requested, and the symbol 434 * is not in the namelist, ignore this one. 435 */ 436 static void 437 printproto(struct protox *tp, char *name) 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); 448 } 449 } 450 451 /* 452 * Read kernel memory, return 0 on success. 453 */ 454 int 455 kread(u_long addr, void *buf, int size) 456 { 457 458 if (kvm_read(kvmd, addr, buf, size) != size) { 459 (void)fprintf(stderr, "%s: %s\n", __progname, 460 kvm_geterr(kvmd)); 461 return (-1); 462 } 463 return (0); 464 } 465 466 char * 467 plural(u_int64_t n) 468 { 469 return (n != 1 ? "s" : ""); 470 } 471 472 char * 473 plurales(u_int64_t n) 474 { 475 return (n != 1 ? "es" : ""); 476 } 477 478 /* 479 * Find the protox for the given "well-known" name. 480 */ 481 static struct protox * 482 knownname(char *name) 483 { 484 struct protox **tpp, *tp; 485 486 for (tpp = protoprotox; *tpp; tpp++) 487 for (tp = *tpp; tp->pr_name; tp++) 488 if (strcmp(tp->pr_name, name) == 0) 489 return (tp); 490 return (NULL); 491 } 492 493 /* 494 * Find the protox corresponding to name. 495 */ 496 static struct protox * 497 name2protox(char *name) 498 { 499 struct protox *tp; 500 char **alias; /* alias from p->aliases */ 501 struct protoent *p; 502 503 /* 504 * Try to find the name in the list of "well-known" names. If that 505 * fails, check if name is an alias for an Internet protocol. 506 */ 507 if ((tp = knownname(name))) 508 return (tp); 509 510 setprotoent(1); /* make protocol lookup cheaper */ 511 while ((p = getprotoent())) { 512 /* assert: name not same as p->name */ 513 for (alias = p->p_aliases; *alias; alias++) 514 if (strcmp(name, *alias) == 0) { 515 endprotoent(); 516 return (knownname(p->p_name)); 517 } 518 } 519 endprotoent(); 520 return (NULL); 521 } 522 523 static void 524 usage(void) 525 { 526 (void)fprintf(stderr, 527 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n" 528 " %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n" 529 " [-T tableid]\n" 530 " %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n" 531 " %s [-M core] [-N system] -P pcbaddr\n" 532 " %s [-s] [-M core] [-N system] [-p protocol]\n" 533 " %s [-a] [-f address_family] [-i | -I interface]\n" 534 " %s [-W interface]\n", 535 __progname, __progname, __progname, __progname, 536 __progname, __progname, __progname); 537 exit(1); 538 } 539