1 /* $OpenBSD: main.c,v 1.110 2016/07/20 19:57:54 bluhm 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/types.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_RTREE 0 58 { "_rt_tables"}, 59 #define N_RTMASK 1 60 { "_mask_rnhead" }, 61 #define N_AF2RTAFIDX 2 62 { "_af2rtafidx" }, 63 #define N_RTBLIDMAX 3 64 { "_rtbl_id_max" }, 65 66 { "" } 67 }; 68 69 struct protox { 70 void (*pr_stats)(char *); /* statistics printing routine */ 71 char *pr_name; /* well-known name */ 72 int pr_proto; /* protocol number */ 73 } protox[] = { 74 { ip_stats, "ip", IPPROTO_IPV4 }, 75 { icmp_stats, "icmp", 0 }, 76 { igmp_stats, "igmp", 0 }, 77 { ipip_stats, "ipencap", 0 }, 78 { tcp_stats, "tcp", IPPROTO_TCP }, 79 { udp_stats, "udp", IPPROTO_UDP }, 80 { esp_stats, "esp", 0 }, 81 { ah_stats, "ah", 0 }, 82 { etherip_stats,"etherip", 0 }, 83 { ipcomp_stats, "ipcomp", 0 }, 84 { carp_stats, "carp", 0 }, 85 { pfsync_stats, "pfsync", 0 }, 86 { div_stats, "divert", IPPROTO_DIVERT }, 87 { pim_stats, "pim", 0 }, 88 { pflow_stats, "pflow", 0 }, 89 { NULL, NULL, 0 } 90 }; 91 92 struct protox ip6protox[] = { 93 { ip6_stats, "ip6", IPPROTO_IPV6 }, 94 { div6_stats, "divert6", IPPROTO_DIVERT }, 95 { icmp6_stats, "icmp6", 0 }, 96 { pim6_stats, "pim6", 0 }, 97 { rip6_stats, "rip6", 0 }, 98 { NULL, NULL, 0 } 99 }; 100 101 struct protox *protoprotox[] = { 102 protox, ip6protox, NULL 103 }; 104 105 static void usage(void); 106 static struct protox *name2protox(char *); 107 static struct protox *knownname(char *); 108 u_int gettable(const char *); 109 110 kvm_t *kvmd; 111 112 int 113 main(int argc, char *argv[]) 114 { 115 extern char *optarg; 116 extern int optind; 117 const char *errstr; 118 struct protox *tp = NULL; /* for printing cblocks & stats */ 119 int ch; 120 char *nlistf = NULL, *memf = NULL, *ep; 121 char buf[_POSIX2_LINE_MAX]; 122 u_long pcbaddr = 0; 123 u_int tableid; 124 int Tflag = 0; 125 int repeatcount = 0; 126 int proto = 0; 127 int need_nlist, kvm_flags = O_RDONLY; 128 129 af = AF_UNSPEC; 130 tableid = getrtable(); 131 132 while ((ch = getopt(argc, argv, 133 "AaBbc:dFf:ghI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1) 134 switch (ch) { 135 case 'A': 136 Aflag = 1; 137 break; 138 case 'a': 139 aflag = 1; 140 break; 141 case 'B': 142 Bflag = 1; 143 break; 144 case 'b': 145 bflag = 1; 146 break; 147 case 'c': 148 repeatcount = strtonum(optarg, 1, INT_MAX, &errstr); 149 if (errstr) 150 errx(1, "count is %s", errstr); 151 break; 152 case 'd': 153 dflag = 1; 154 break; 155 case 'F': 156 Fflag = 1; 157 break; 158 case 'f': 159 if (strcmp(optarg, "inet") == 0) 160 af = AF_INET; 161 else if (strcmp(optarg, "inet6") == 0) 162 af = AF_INET6; 163 else if (strcmp(optarg, "local") == 0) 164 af = AF_LOCAL; 165 else if (strcmp(optarg, "unix") == 0) 166 af = AF_UNIX; 167 else if (strcmp(optarg, "mpls") == 0) 168 af = AF_MPLS; 169 else if (strcmp(optarg, "mask") == 0) 170 af = 0xff; 171 else { 172 (void)fprintf(stderr, 173 "%s: %s: unknown address family\n", 174 __progname, optarg); 175 exit(1); 176 } 177 break; 178 case 'g': 179 gflag = 1; 180 break; 181 case 'h': 182 hflag = 1; 183 break; 184 case 'I': 185 iflag = 1; 186 interface = optarg; 187 break; 188 case 'i': 189 iflag = 1; 190 break; 191 case 'l': 192 lflag = 1; 193 break; 194 case 'M': 195 memf = optarg; 196 break; 197 case 'm': 198 mflag = 1; 199 break; 200 case 'N': 201 nlistf = optarg; 202 break; 203 case 'n': 204 nflag = 1; 205 break; 206 case 'p': 207 if ((tp = name2protox(optarg)) == NULL) { 208 (void)fprintf(stderr, 209 "%s: %s: unknown protocol\n", 210 __progname, optarg); 211 exit(1); 212 } 213 pflag = 1; 214 break; 215 case 'P': 216 errno = 0; 217 pcbaddr = strtoul(optarg, &ep, 16); 218 if (optarg[0] == '\0' || *ep != '\0' || 219 errno == ERANGE) { 220 (void)fprintf(stderr, 221 "%s: %s: invalid PCB address\n", 222 __progname, optarg); 223 exit(1); 224 } 225 Pflag = 1; 226 break; 227 case 'q': 228 qflag = 1; 229 break; 230 case 'r': 231 rflag = 1; 232 break; 233 case 's': 234 ++sflag; 235 break; 236 case 'T': 237 Tflag = 1; 238 tableid = gettable(optarg); 239 break; 240 case 't': 241 tflag = 1; 242 break; 243 case 'u': 244 af = AF_UNIX; 245 break; 246 case 'v': 247 vflag = 1; 248 break; 249 case 'W': 250 Wflag = 1; 251 interface = optarg; 252 break; 253 case 'w': 254 interval = strtonum(optarg, 1, INT_MAX, &errstr); 255 if (errstr) 256 errx(1, "interval is %s", errstr); 257 iflag = 1; 258 break; 259 case '?': 260 default: 261 usage(); 262 } 263 argv += optind; 264 argc -= optind; 265 266 if (argc) { 267 interval = strtonum(*argv, 1, INT_MAX, &errstr); 268 if (errstr) 269 errx(1, "interval is %s", errstr); 270 ++argv; 271 --argc; 272 iflag = 1; 273 } 274 if (argc) 275 usage(); 276 277 /* 278 * Show per-interface statistics which don't need access to 279 * kernel memory (they're using IOCTLs) 280 */ 281 if (Wflag) { 282 if (interface == NULL) 283 usage(); 284 net80211_ifstats(interface); 285 exit(0); 286 } 287 288 if (mflag) { 289 mbpr(); 290 exit(0); 291 } 292 if (iflag) { 293 intpr(interval, repeatcount); 294 exit(0); 295 } 296 if (sflag) { 297 if (rflag) { 298 rt_stats(); 299 } else if (gflag) { 300 if (af == AF_INET || af == AF_UNSPEC) 301 mrt_stats(); 302 if (af == AF_INET6 || af == AF_UNSPEC) 303 mrt6_stats(); 304 } else if (pflag && tp->pr_name) { 305 (*tp->pr_stats)(tp->pr_name); 306 } else { 307 if (af == AF_INET || af == AF_UNSPEC) 308 for (tp = protox; tp->pr_name; tp++) 309 (*tp->pr_stats)(tp->pr_name); 310 if (af == AF_INET6 || af == AF_UNSPEC) 311 for (tp = ip6protox; tp->pr_name; tp++) 312 (*tp->pr_stats)(tp->pr_name); 313 } 314 exit(0); 315 } 316 if (gflag) { 317 if (af == AF_INET || af == AF_UNSPEC) 318 mroutepr(); 319 if (af == AF_INET6 || af == AF_UNSPEC) 320 mroute6pr(); 321 exit(0); 322 } 323 324 /* 325 * The remaining code may need kvm so lets try to open it. 326 * -r and -P are the only bits left that actually can use this. 327 */ 328 need_nlist = (nlistf != NULL) || (memf != NULL) || (Aflag && rflag); 329 if (!need_nlist && !Pflag) 330 kvm_flags |= KVM_NO_FILES; 331 332 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, kvm_flags, buf)) == NULL) 333 errx(1, "kvm_openfiles: %s", buf); 334 335 if (need_nlist && (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0)) { 336 if (nlistf) 337 errx(1, "%s: no namelist", nlistf); 338 else 339 errx(1, "no namelist"); 340 } 341 342 if (rflag) { 343 if (Aflag || nlistf != NULL || memf != NULL) 344 routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value, 345 nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value, 346 tableid); 347 else 348 p_rttables(af, tableid); 349 exit(0); 350 } 351 352 if (pflag) { 353 if (tp->pr_proto == 0) 354 errx(1, "no protocol handler for protocol %s", 355 tp->pr_name); 356 else 357 proto = tp->pr_proto; 358 } 359 360 protopr(kvmd, pcbaddr, tableid, proto); 361 exit(0); 362 } 363 364 /* 365 * Read kernel memory, return 0 on success. 366 */ 367 int 368 kread(u_long addr, void *buf, int size) 369 { 370 371 if (kvm_read(kvmd, addr, buf, size) != size) { 372 (void)fprintf(stderr, "%s: %s\n", __progname, 373 kvm_geterr(kvmd)); 374 return (-1); 375 } 376 return (0); 377 } 378 379 char * 380 plural(u_int64_t n) 381 { 382 return (n != 1 ? "s" : ""); 383 } 384 385 char * 386 plurales(u_int64_t n) 387 { 388 return (n != 1 ? "es" : ""); 389 } 390 391 char * 392 pluralys(u_int64_t n) 393 { 394 return (n != 1 ? "ies" : "y"); 395 } 396 397 /* 398 * Find the protox for the given "well-known" name. 399 */ 400 static struct protox * 401 knownname(char *name) 402 { 403 struct protox **tpp, *tp; 404 405 for (tpp = protoprotox; *tpp; tpp++) 406 for (tp = *tpp; tp->pr_name; tp++) 407 if (strcmp(tp->pr_name, name) == 0) 408 return (tp); 409 return (NULL); 410 } 411 412 /* 413 * Find the protox corresponding to name. 414 */ 415 static struct protox * 416 name2protox(char *name) 417 { 418 struct protox *tp; 419 char **alias; /* alias from p->aliases */ 420 struct protoent *p; 421 422 /* 423 * Try to find the name in the list of "well-known" names. If that 424 * fails, check if name is an alias for an Internet protocol. 425 */ 426 if ((tp = knownname(name))) 427 return (tp); 428 429 setprotoent(1); /* make protocol lookup cheaper */ 430 while ((p = getprotoent())) { 431 /* assert: name not same as p->name */ 432 for (alias = p->p_aliases; *alias; alias++) 433 if (strcmp(name, *alias) == 0) { 434 endprotoent(); 435 return (knownname(p->p_name)); 436 } 437 } 438 endprotoent(); 439 return (NULL); 440 } 441 442 static void 443 usage(void) 444 { 445 (void)fprintf(stderr, 446 "usage: %s [-AaBn] [-f address_family] [-M core] [-N system]\n" 447 " %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n" 448 " [-T tableid]\n" 449 " %s [-bdhn] [-c count] [-I interface] [-M core] [-N system] [-w wait]\n" 450 " %s [-v] [-M core] [-N system] -P pcbaddr\n" 451 " %s [-s] [-M core] [-N system] [-p protocol]\n" 452 " %s [-a] [-f address_family] [-i | -I interface]\n" 453 " %s [-W interface]\n", 454 __progname, __progname, __progname, __progname, 455 __progname, __progname, __progname); 456 exit(1); 457 } 458 459 u_int 460 gettable(const char *s) 461 { 462 const char *errstr; 463 struct rt_tableinfo info; 464 int mib[6]; 465 size_t len; 466 u_int tableid; 467 468 tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); 469 if (errstr) 470 errx(1, "invalid table id: %s", errstr); 471 472 mib[0] = CTL_NET; 473 mib[1] = PF_ROUTE; 474 mib[2] = 0; 475 mib[3] = 0; 476 mib[4] = NET_RT_TABLE; 477 mib[5] = tableid; 478 479 len = sizeof(info); 480 if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) 481 err(1, "routing table %d", tableid); 482 483 return (tableid); 484 } 485