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