jcs's openbsd hax
openbsd
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}