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