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