xref: /freebsd/usr.bin/netstat/main.c (revision 182e8ae2)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1988, 1993
5  *	Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static char const copyright[] =
34 "@(#) Copyright (c) 1983, 1988, 1993\n\
35 	Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37 
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)main.c	8.4 (Berkeley) 3/1/94";
41 #endif /* not lint */
42 #endif
43 
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46 
47 #include <sys/param.h>
48 #include <sys/file.h>
49 #ifdef JAIL
50 #include <sys/jail.h>
51 #endif
52 #include <sys/protosw.h>
53 #include <sys/socket.h>
54 #include <sys/socketvar.h>
55 #include <sys/sysctl.h>
56 
57 #include <netinet/in.h>
58 
59 #ifdef NETGRAPH
60 #include <netgraph/ng_socket.h>
61 #endif
62 
63 #include <ctype.h>
64 #include <err.h>
65 #include <errno.h>
66 #ifdef JAIL
67 #include <jail.h>
68 #endif
69 #include <kvm.h>
70 #include <limits.h>
71 #include <netdb.h>
72 #include <nlist.h>
73 #include <paths.h>
74 #include <stdint.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <stdbool.h>
78 #include <string.h>
79 #include <unistd.h>
80 #include "netstat.h"
81 #include "nl_defs.h"
82 #include <libxo/xo.h>
83 
84 static struct protox {
85 	int	pr_index;		/* index into nlist of cb head */
86 	int	pr_sindex;		/* index into nlist of stat block */
87 	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
88 	void	(*pr_cblocks)(u_long, const char *, int, int);
89 					/* control blocks printing routine */
90 	void	(*pr_stats)(u_long, const char *, int, int);
91 					/* statistics printing routine */
92 	void	(*pr_istats)(char *);	/* per/if statistics printing routine */
93 	const char	*pr_name;		/* well-known name */
94 	int	pr_usesysctl;		/* non-zero if we use sysctl, not kvm */
95 	int	pr_protocol;
96 } protox[] = {
97 	{ -1	,	N_TCPSTAT,	1,	protopr,
98 	  tcp_stats,	NULL,		"tcp",	1,	IPPROTO_TCP },
99 	{ -1	,	N_UDPSTAT,	1,	protopr,
100 	  udp_stats,	NULL,		"udp",	1,	IPPROTO_UDP },
101 #ifdef SCTP
102 	{ -1,		N_SCTPSTAT,	1,	sctp_protopr,
103 	  sctp_stats,	NULL,		"sctp",	1,	IPPROTO_SCTP },
104 #endif
105 #ifdef SDP
106 	{ -1,		-1,		1,	protopr,
107 	 NULL,		NULL,		"sdp",	1,	IPPROTO_TCP },
108 #endif
109 	{ -1	,	-1,		1,	protopr,
110 	  divert_stats,	NULL,		"divert", 1,	0 },
111 	{ -1	,	N_IPSTAT,	1,	protopr,
112 	  ip_stats,	NULL,		"ip",	1,	IPPROTO_RAW },
113 	{ -1	,	N_ICMPSTAT,	1,	protopr,
114 	  icmp_stats,	NULL,		"icmp",	1,	IPPROTO_ICMP },
115 	{ -1	,	N_IGMPSTAT,	1,	protopr,
116 	  igmp_stats,	NULL,		"igmp",	1,	IPPROTO_IGMP },
117 #ifdef IPSEC
118 	{ -1,		N_IPSEC4STAT,	1,	NULL,	/* keep as compat */
119 	  ipsec_stats,	NULL,		"ipsec", 1,	0},
120 	{ -1,		N_AHSTAT,	1,	NULL,
121 	  ah_stats,	NULL,		"ah",	1,	0},
122 	{ -1,		N_ESPSTAT,	1,	NULL,
123 	  esp_stats,	NULL,		"esp",	1,	0},
124 	{ -1,		N_IPCOMPSTAT,	1,	NULL,
125 	  ipcomp_stats,	NULL,		"ipcomp", 1,	0},
126 #endif
127 	{ -1	,	N_PIMSTAT,	1,	protopr,
128 	  pim_stats,	NULL,		"pim",	1,	IPPROTO_PIM },
129 	{ -1,		N_CARPSTATS,	1,	NULL,
130 	  carp_stats,	NULL,		"carp",	1,	0 },
131 #ifdef PF
132 	{ -1,		N_PFSYNCSTATS,	1,	NULL,
133 	  pfsync_stats,	NULL,		"pfsync", 1,	0 },
134 #endif
135 	{ -1,		N_ARPSTAT,	1,	NULL,
136 	  arp_stats,	NULL,		"arp", 1,	0 },
137 	{ -1,		-1,		0,	NULL,
138 	  NULL,		NULL,		NULL,	0,	0 }
139 };
140 
141 #ifdef INET6
142 static struct protox ip6protox[] = {
143 	{ -1	,	N_TCPSTAT,	1,	protopr,
144 	  tcp_stats,	NULL,		"tcp",	1,	IPPROTO_TCP },
145 	{ -1	,	N_UDPSTAT,	1,	protopr,
146 	  udp_stats,	NULL,		"udp",	1,	IPPROTO_UDP },
147 	{ -1	,	N_IP6STAT,	1,	protopr,
148 	  ip6_stats,	ip6_ifstats,	"ip6",	1,	IPPROTO_RAW },
149 	{ -1	,	N_ICMP6STAT,	1,	protopr,
150 	  icmp6_stats,	icmp6_ifstats,	"icmp6", 1,	IPPROTO_ICMPV6 },
151 #ifdef SDP
152 	{ -1,		-1,		1,	protopr,
153 	 NULL,		NULL,		"sdp",	1,	IPPROTO_TCP },
154 #endif
155 #ifdef IPSEC
156 	{ -1,		N_IPSEC6STAT,	1,	NULL,
157 	  ipsec_stats,	NULL,		"ipsec6", 1,	0 },
158 #endif
159 #ifdef notyet
160 	{ -1,		N_PIM6STAT,	1,	NULL,
161 	  pim6_stats,	NULL,		"pim6",	1,	0 },
162 #endif
163 	{ -1,		N_RIP6STAT,	1,	NULL,
164 	  rip6_stats,	NULL,		"rip6",	1,	0 },
165 	{ -1,		-1,		0,	NULL,
166 	  NULL,		NULL,		NULL,	0,	0 }
167 };
168 #endif /*INET6*/
169 
170 #ifdef IPSEC
171 static struct protox pfkeyprotox[] = {
172 	{ -1,		N_PFKEYSTAT,	1,	NULL,
173 	  pfkey_stats,	NULL,		"pfkey", 0,	0 },
174 	{ -1,		-1,		0,	NULL,
175 	  NULL,		NULL,		NULL,	0,	0 }
176 };
177 #endif
178 
179 #ifdef NETGRAPH
180 static struct protox netgraphprotox[] = {
181 	{ N_NGSOCKLIST,	-1,		1,	netgraphprotopr,
182 	  NULL,		NULL,		"ctrl",	0,	0 },
183 	{ N_NGSOCKLIST,	-1,		1,	netgraphprotopr,
184 	  NULL,		NULL,		"data",	0,	0 },
185 	{ -1,		-1,		0,	NULL,
186 	  NULL,		NULL,		NULL,	0,	0 }
187 };
188 #endif
189 
190 static struct protox *protoprotox[] = {
191 					 protox,
192 #ifdef INET6
193 					 ip6protox,
194 #endif
195 #ifdef IPSEC
196 					 pfkeyprotox,
197 #endif
198 					 NULL };
199 
200 static void printproto(struct protox *, const char *, bool *);
201 static void usage(void) __dead2;
202 static struct protox *name2protox(const char *);
203 static struct protox *knownname(const char *);
204 
205 static int kresolve_list(struct nlist *_nl);
206 
207 static kvm_t *kvmd;
208 static char *nlistf = NULL, *memf = NULL;
209 
210 int	Aflag;		/* show addresses of protocol control block */
211 int	aflag;		/* show all sockets (including servers) */
212 static int	Bflag;		/* show information about bpf consumers */
213 int	bflag;		/* show i/f total bytes in/out */
214 int	cflag;		/* show TCP congestion control stack */
215 int	Cflag;		/* show congestion control algo and vars */
216 int	dflag;		/* show i/f dropped packets */
217 int	gflag;		/* show group (multicast) routing or stats */
218 int	hflag;		/* show counters in human readable format */
219 int	iflag;		/* show interfaces */
220 int	Lflag;		/* show size of listen queues */
221 int	mflag;		/* show memory stats */
222 int	noutputs = 0;	/* how much outputs before we exit */
223 int	numeric_addr;	/* show addresses numerically */
224 int	numeric_port;	/* show ports numerically */
225 int	Oflag;		/* show nhgrp objects*/
226 int	oflag;		/* show nexthop objects*/
227 int	Pflag;		/* show TCP log ID */
228 static int pflag;	/* show given protocol */
229 static int	Qflag;		/* show netisr information */
230 int	rflag;		/* show routing tables (or routing stats) */
231 int	Rflag;		/* show flow / RSS statistics */
232 int	sflag;		/* show protocol statistics */
233 int	Wflag;		/* wide display */
234 int	Tflag;		/* TCP Information */
235 int	xflag;		/* extra information, includes all socket buffer info */
236 int	zflag;		/* zero stats */
237 
238 int	interval;	/* repeat interval for i/f stats */
239 
240 char	*interface;	/* desired i/f for stats, or NULL for all i/fs */
241 int	unit;		/* unit number for above */
242 #ifdef JAIL
243 char	*jail_name;	/* desired jail to operate in */
244 #endif
245 
246 static int	af;		/* address family */
247 int	live;		/* true if we are examining a live system */
248 
249 int
250 main(int argc, char *argv[])
251 {
252 	struct protox *tp = NULL;  /* for printing cblocks & stats */
253 	int ch;
254 	int fib = -1;
255 	char *endptr;
256 	bool first = true;
257 #ifdef JAIL
258 	int jid;
259 #endif
260 
261 	af = AF_UNSPEC;
262 
263 	argc = xo_parse_args(argc, argv);
264 	if (argc < 0)
265 		exit(EXIT_FAILURE);
266 
267 	while ((ch = getopt(argc, argv, "46AaBbCcdF:f:ghI:ij:LlM:mN:nOoPp:Qq:RrSTsuWw:xz"))
268 	    != -1)
269 		switch(ch) {
270 		case '4':
271 #ifdef INET
272 			af = AF_INET;
273 #else
274 			errx(1, "IPv4 support is not compiled in");
275 #endif
276 			break;
277 		case '6':
278 #ifdef INET6
279 			af = AF_INET6;
280 #else
281 			errx(1, "IPv6 support is not compiled in");
282 #endif
283 			break;
284 		case 'A':
285 			Aflag = 1;
286 			break;
287 		case 'a':
288 			aflag = 1;
289 			break;
290 		case 'B':
291 			Bflag = 1;
292 			break;
293 		case 'b':
294 			bflag = 1;
295 			break;
296 		case 'c':
297 			cflag = 1;
298 			break;
299 		case 'C':
300 			Cflag = 1;
301 			break;
302 		case 'd':
303 			dflag = 1;
304 			break;
305 		case 'F':
306 			fib = strtol(optarg, &endptr, 0);
307 			if (*endptr != '\0' ||
308 			    (fib == 0 && (errno == EINVAL || errno == ERANGE)))
309 				xo_errx(1, "%s: invalid fib", optarg);
310 			break;
311 		case 'f':
312 			if (strcmp(optarg, "inet") == 0)
313 				af = AF_INET;
314 #ifdef INET6
315 			else if (strcmp(optarg, "inet6") == 0)
316 				af = AF_INET6;
317 #endif
318 #ifdef IPSEC
319 			else if (strcmp(optarg, "pfkey") == 0)
320 				af = PF_KEY;
321 #endif
322 			else if (strcmp(optarg, "unix") == 0 ||
323 				 strcmp(optarg, "local") == 0)
324 				af = AF_UNIX;
325 #ifdef NETGRAPH
326 			else if (strcmp(optarg, "ng") == 0
327 			    || strcmp(optarg, "netgraph") == 0)
328 				af = AF_NETGRAPH;
329 #endif
330 			else if (strcmp(optarg, "link") == 0)
331 				af = AF_LINK;
332 			else {
333 				xo_errx(1, "%s: unknown address family",
334 				    optarg);
335 			}
336 			break;
337 		case 'g':
338 			gflag = 1;
339 			break;
340 		case 'h':
341 			hflag = 1;
342 			break;
343 		case 'I': {
344 			char *cp;
345 
346 			iflag = 1;
347 			for (cp = interface = optarg; isalpha(*cp); cp++)
348 				continue;
349 			unit = atoi(cp);
350 			break;
351 		}
352 		case 'i':
353 			iflag = 1;
354 			break;
355 		case 'j':
356 #ifdef JAIL
357 			if (optarg == NULL)
358 				usage();
359 			jail_name = optarg;
360 #else
361 			errx(1, "Jail support is not compiled in");
362 #endif
363 			break;
364 		case 'L':
365 			Lflag = 1;
366 			break;
367 		case 'M':
368 			memf = optarg;
369 			break;
370 		case 'm':
371 			mflag = 1;
372 			break;
373 		case 'N':
374 			nlistf = optarg;
375 			break;
376 		case 'n':
377 			numeric_addr = numeric_port = 1;
378 			break;
379 		case 'o':
380 			oflag = 1;
381 			break;
382 		case 'O':
383 			Oflag = 1;
384 			break;
385 		case 'P':
386 			Pflag = 1;
387 			break;
388 		case 'p':
389 			if ((tp = name2protox(optarg)) == NULL) {
390 				xo_errx(1, "%s: unknown or uninstrumented "
391 				    "protocol", optarg);
392 			}
393 			pflag = 1;
394 			break;
395 		case 'Q':
396 			Qflag = 1;
397 			break;
398 		case 'q':
399 			noutputs = atoi(optarg);
400 			if (noutputs != 0)
401 				noutputs++;
402 			break;
403 		case 'r':
404 			rflag = 1;
405 			break;
406 		case 'R':
407 			Rflag = 1;
408 			break;
409 		case 's':
410 			++sflag;
411 			break;
412 		case 'S':
413 			numeric_addr = 1;
414 			break;
415 		case 'u':
416 			af = AF_UNIX;
417 			break;
418 		case 'W':
419 		case 'l':
420 			Wflag = 1;
421 			break;
422 		case 'w':
423 			interval = atoi(optarg);
424 			iflag = 1;
425 			break;
426 		case 'T':
427 			Tflag = 1;
428 			break;
429 		case 'x':
430 			xflag = 1;
431 			break;
432 		case 'z':
433 			zflag = 1;
434 			break;
435 		case '?':
436 		default:
437 			usage();
438 		}
439 	argv += optind;
440 	argc -= optind;
441 
442 #define	BACKWARD_COMPATIBILITY
443 #ifdef	BACKWARD_COMPATIBILITY
444 	if (*argv) {
445 		if (isdigit(**argv)) {
446 			interval = atoi(*argv);
447 			if (interval <= 0)
448 				usage();
449 			++argv;
450 			iflag = 1;
451 		}
452 		if (*argv) {
453 			nlistf = *argv;
454 			if (*++argv)
455 				memf = *argv;
456 		}
457 	}
458 #endif
459 
460 #ifdef JAIL
461 	if (jail_name != NULL) {
462 		jid = jail_getid(jail_name);
463 		if (jid == -1)
464 			errx(1, "Jail not found");
465 		if (jail_attach(jid) != 0)
466 			errx(1, "Cannot attach to jail");
467 	}
468 #endif
469 
470 	/*
471 	 * Discard setgid privileges if not the running kernel so that bad
472 	 * guys can't print interesting stuff from kernel memory.
473 	 */
474 	live = (nlistf == NULL && memf == NULL);
475 	if (!live) {
476 		if (setgid(getgid()) != 0)
477 			xo_err(-1, "setgid");
478 		/* Load all necessary kvm symbols */
479 		kresolve_list(nl);
480 	}
481 
482 	if (xflag && Tflag)
483 		xo_errx(1, "-x and -T are incompatible, pick one.");
484 
485 	if (Bflag) {
486 		if (!live)
487 			usage();
488 		bpf_stats(interface);
489 		xo_finish();
490 		exit(0);
491 	}
492 	if (mflag) {
493 		if (!live) {
494 			if (kread(0, NULL, 0) == 0)
495 				mbpr(kvmd, nl[N_SFSTAT].n_value);
496 		} else
497 			mbpr(NULL, 0);
498 		xo_finish();
499 		exit(0);
500 	}
501 	if (Qflag) {
502 		if (!live) {
503 			if (kread(0, NULL, 0) == 0)
504 				netisr_stats();
505 		} else
506 			netisr_stats();
507 		xo_finish();
508 		exit(0);
509 	}
510 #if 0
511 	/*
512 	 * Keep file descriptors open to avoid overhead
513 	 * of open/close on each call to get* routines.
514 	 */
515 	sethostent(1);
516 	setnetent(1);
517 #else
518 	/*
519 	 * This does not make sense any more with DNS being default over
520 	 * the files.  Doing a setXXXXent(1) causes a tcp connection to be
521 	 * used for the queries, which is slower.
522 	 */
523 #endif
524 	if (iflag && !sflag) {
525 		xo_open_container("statistics");
526 		intpr(NULL, af);
527 		xo_close_container("statistics");
528 		xo_finish();
529 		exit(0);
530 	}
531 	if (rflag) {
532 		xo_open_container("statistics");
533 		if (sflag) {
534 			if (live) {
535 				kresolve_list(nl);
536 			}
537 			rt_stats();
538 		} else
539 			routepr(fib, af);
540 		xo_close_container("statistics");
541 		xo_finish();
542 		exit(0);
543 	}
544 	if (oflag) {
545 		xo_open_container("statistics");
546 		nhops_print(fib, af);
547 		xo_close_container("statistics");
548 		xo_finish();
549 		exit(0);
550 	}
551 	if (Oflag) {
552 		xo_open_container("statistics");
553 		nhgrp_print(fib, af);
554 		xo_close_container("statistics");
555 		xo_finish();
556 		exit(0);
557 	}
558 
559 
560 
561 	if (gflag) {
562 		xo_open_container("statistics");
563 		if (sflag) {
564 			if (af == AF_INET || af == AF_UNSPEC)
565 				mrt_stats();
566 #ifdef INET6
567 			if (af == AF_INET6 || af == AF_UNSPEC)
568 				mrt6_stats();
569 #endif
570 		} else {
571 			if (af == AF_INET || af == AF_UNSPEC)
572 				mroutepr();
573 #ifdef INET6
574 			if (af == AF_INET6 || af == AF_UNSPEC)
575 				mroute6pr();
576 #endif
577 		}
578 		xo_close_container("statistics");
579 		xo_finish();
580 		exit(0);
581 	}
582 
583 	if (tp) {
584 		xo_open_container("statistics");
585 		printproto(tp, tp->pr_name, &first);
586 		if (!first)
587 			xo_close_list("socket");
588 		xo_close_container("statistics");
589 		xo_finish();
590 		exit(0);
591 	}
592 
593 	xo_open_container("statistics");
594 	if (af == AF_INET || af == AF_UNSPEC)
595 		for (tp = protox; tp->pr_name; tp++)
596 			printproto(tp, tp->pr_name, &first);
597 #ifdef INET6
598 	if (af == AF_INET6 || af == AF_UNSPEC)
599 		for (tp = ip6protox; tp->pr_name; tp++)
600 			printproto(tp, tp->pr_name, &first);
601 #endif /*INET6*/
602 #ifdef IPSEC
603 	if (af == PF_KEY || af == AF_UNSPEC)
604 		for (tp = pfkeyprotox; tp->pr_name; tp++)
605 			printproto(tp, tp->pr_name, &first);
606 #endif /*IPSEC*/
607 #ifdef NETGRAPH
608 	if (af == AF_NETGRAPH || af == AF_UNSPEC)
609 		for (tp = netgraphprotox; tp->pr_name; tp++)
610 			printproto(tp, tp->pr_name, &first);
611 #endif /* NETGRAPH */
612 	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
613 		unixpr(nl[N_UNP_COUNT].n_value, nl[N_UNP_GENCNT].n_value,
614 		    nl[N_UNP_DHEAD].n_value, nl[N_UNP_SHEAD].n_value,
615 		    nl[N_UNP_SPHEAD].n_value, &first);
616 
617 	if (!first)
618 		xo_close_list("socket");
619 	xo_close_container("statistics");
620 	xo_finish();
621 	exit(0);
622 }
623 
624 static int
625 fetch_stats_internal(const char *sysctlname, u_long off, void *stats,
626     size_t len, kreadfn_t kreadfn, int zero)
627 {
628 	int error;
629 
630 	if (live) {
631 		memset(stats, 0, len);
632 		if (zero)
633 			error = sysctlbyname(sysctlname, NULL, NULL, stats,
634 			    len);
635 		else
636 			error = sysctlbyname(sysctlname, stats, &len, NULL, 0);
637 		if (error == -1 && errno != ENOENT)
638 			xo_warn("sysctl %s", sysctlname);
639 	} else {
640 		if (off == 0)
641 			return (1);
642 		error = kreadfn(off, stats, len);
643 	}
644 	return (error);
645 }
646 
647 int
648 fetch_stats(const char *sysctlname, u_long off, void *stats,
649     size_t len, kreadfn_t kreadfn)
650 {
651 
652 	return (fetch_stats_internal(sysctlname, off, stats, len, kreadfn,
653     zflag));
654 }
655 
656 int
657 fetch_stats_ro(const char *sysctlname, u_long off, void *stats,
658     size_t len, kreadfn_t kreadfn)
659 {
660 
661 	return (fetch_stats_internal(sysctlname, off, stats, len, kreadfn, 0));
662 }
663 
664 /*
665  * Print out protocol statistics or control blocks (per sflag).
666  * If the interface was not specifically requested, and the symbol
667  * is not in the namelist, ignore this one.
668  */
669 static void
670 printproto(struct protox *tp, const char *name, bool *first)
671 {
672 	void (*pr)(u_long, const char *, int, int);
673 	u_long off;
674 	bool doingdblocks = false;
675 
676 	if (sflag) {
677 		if (iflag) {
678 			if (tp->pr_istats)
679 				intpr(tp->pr_istats, af);
680 			else if (pflag)
681 				xo_message("%s: no per-interface stats routine",
682 				    tp->pr_name);
683 			return;
684 		} else {
685 			pr = tp->pr_stats;
686 			if (!pr) {
687 				if (pflag)
688 					xo_message("%s: no stats routine",
689 					    tp->pr_name);
690 				return;
691 			}
692 			if (tp->pr_usesysctl && live)
693 				off = 0;
694 			else if (tp->pr_sindex < 0) {
695 				if (pflag)
696 					xo_message("%s: stats routine doesn't "
697 					    "work on cores", tp->pr_name);
698 				return;
699 			} else
700 				off = nl[tp->pr_sindex].n_value;
701 		}
702 	} else {
703 		doingdblocks = true;
704 		pr = tp->pr_cblocks;
705 		if (!pr) {
706 			if (pflag)
707 				xo_message("%s: no PCB routine", tp->pr_name);
708 			return;
709 		}
710 		if (tp->pr_usesysctl && live)
711 			off = 0;
712 		else if (tp->pr_index < 0) {
713 			if (pflag)
714 				xo_message("%s: PCB routine doesn't work on "
715 				    "cores", tp->pr_name);
716 			return;
717 		} else
718 			off = nl[tp->pr_index].n_value;
719 	}
720 	if (pr != NULL && (off || (live && tp->pr_usesysctl) ||
721 	    af != AF_UNSPEC)) {
722 		if (doingdblocks && *first) {
723 			xo_open_list("socket");
724 			*first = false;
725 		}
726 
727 		(*pr)(off, name, af, tp->pr_protocol);
728 	}
729 }
730 
731 static int
732 kvmd_init(void)
733 {
734 	char errbuf[_POSIX2_LINE_MAX];
735 
736 	if (kvmd != NULL)
737 		return (0);
738 
739 	kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
740 	if (setgid(getgid()) != 0)
741 		xo_err(-1, "setgid");
742 
743 	if (kvmd == NULL) {
744 		xo_warnx("kvm not available: %s", errbuf);
745 		return (-1);
746 	}
747 
748 	return (0);
749 }
750 
751 /*
752  * Resolve symbol list, return 0 on success.
753  */
754 static int
755 kresolve_list(struct nlist *_nl)
756 {
757 
758 	if ((kvmd == NULL) && (kvmd_init() != 0))
759 		return (-1);
760 
761 	if (_nl[0].n_type != 0)
762 		return (0);
763 
764 	if (kvm_nlist(kvmd, _nl) < 0) {
765 		if (nlistf)
766 			xo_errx(1, "%s: kvm_nlist: %s", nlistf,
767 			    kvm_geterr(kvmd));
768 		else
769 			xo_errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
770 	}
771 
772 	return (0);
773 }
774 
775 /*
776  * Wrapper of kvm_dpcpu_setcpu().
777  */
778 void
779 kset_dpcpu(u_int cpuid)
780 {
781 
782 	if ((kvmd == NULL) && (kvmd_init() != 0))
783 		xo_errx(-1, "%s: kvm is not available", __func__);
784 
785 	if (kvm_dpcpu_setcpu(kvmd, cpuid) < 0)
786 		xo_errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__,
787 		    cpuid, kvm_geterr(kvmd));
788 	return;
789 }
790 
791 /*
792  * Read kernel memory, return 0 on success.
793  */
794 int
795 kread(u_long addr, void *buf, size_t size)
796 {
797 
798 	if (kvmd_init() < 0)
799 		return (-1);
800 
801 	if (!buf)
802 		return (0);
803 	if (kvm_read(kvmd, addr, buf, size) != (ssize_t)size) {
804 		xo_warnx("%s", kvm_geterr(kvmd));
805 		return (-1);
806 	}
807 	return (0);
808 }
809 
810 /*
811  * Read single counter(9).
812  */
813 uint64_t
814 kread_counter(u_long addr)
815 {
816 
817 	if (kvmd_init() < 0)
818 		return (-1);
819 
820 	return (kvm_counter_u64_fetch(kvmd, addr));
821 }
822 
823 /*
824  * Read an array of N counters in kernel memory into array of N uint64_t's.
825  */
826 int
827 kread_counters(u_long addr, void *buf, size_t size)
828 {
829 	uint64_t *c;
830 	u_long *counters;
831 	size_t i, n;
832 
833 	if (kvmd_init() < 0)
834 		return (-1);
835 
836 	if (size % sizeof(uint64_t) != 0) {
837 		xo_warnx("kread_counters: invalid counter set size");
838 		return (-1);
839 	}
840 
841 	n = size / sizeof(uint64_t);
842 	if ((counters = malloc(n * sizeof(u_long))) == NULL)
843 		xo_err(-1, "malloc");
844 	if (kread(addr, counters, n * sizeof(u_long)) < 0) {
845 		free(counters);
846 		return (-1);
847 	}
848 
849 	c = buf;
850 	for (i = 0; i < n; i++)
851 		c[i] = kvm_counter_u64_fetch(kvmd, counters[i]);
852 
853 	free(counters);
854 	return (0);
855 }
856 
857 const char *
858 plural(uintmax_t n)
859 {
860 	return (n != 1 ? "s" : "");
861 }
862 
863 const char *
864 plurales(uintmax_t n)
865 {
866 	return (n != 1 ? "es" : "");
867 }
868 
869 const char *
870 pluralies(uintmax_t n)
871 {
872 	return (n != 1 ? "ies" : "y");
873 }
874 
875 /*
876  * Find the protox for the given "well-known" name.
877  */
878 static struct protox *
879 knownname(const char *name)
880 {
881 	struct protox **tpp, *tp;
882 
883 	for (tpp = protoprotox; *tpp; tpp++)
884 		for (tp = *tpp; tp->pr_name; tp++)
885 			if (strcmp(tp->pr_name, name) == 0)
886 				return (tp);
887 	return (NULL);
888 }
889 
890 /*
891  * Find the protox corresponding to name.
892  */
893 static struct protox *
894 name2protox(const char *name)
895 {
896 	struct protox *tp;
897 	char **alias;			/* alias from p->aliases */
898 	struct protoent *p;
899 
900 	/*
901 	 * Try to find the name in the list of "well-known" names. If that
902 	 * fails, check if name is an alias for an Internet protocol.
903 	 */
904 	if ((tp = knownname(name)) != NULL)
905 		return (tp);
906 
907 	setprotoent(1);			/* make protocol lookup cheaper */
908 	while ((p = getprotoent()) != NULL) {
909 		/* assert: name not same as p->name */
910 		for (alias = p->p_aliases; *alias; alias++)
911 			if (strcmp(name, *alias) == 0) {
912 				endprotoent();
913 				return (knownname(p->p_name));
914 			}
915 	}
916 	endprotoent();
917 	return (NULL);
918 }
919 
920 static void
921 usage(void)
922 {
923 	(void)xo_error("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
924 "usage: netstat [-j jail] [-46AaCcLnRSTWx] [-f protocol_family | -p protocol]\n"
925 "               [-M core] [-N system]",
926 "       netstat [-j jail] -i | -I interface [-46abdhnW] [-f address_family]\n"
927 "               [-M core] [-N system]",
928 "       netstat [-j jail] -w wait [-I interface] [-46d] [-M core] [-N system]\n"
929 "               [-q howmany]",
930 "       netstat [-j jail] -s [-46sz] [-f protocol_family | -p protocol]\n"
931 "               [-M core] [-N system]",
932 "       netstat [-j jail] -i | -I interface -s [-46s]\n"
933 "               [-f protocol_family | -p protocol] [-M core] [-N system]",
934 "       netstat [-j jail] -m [-M core] [-N system]",
935 "       netstat [-j jail] -B [-z] [-I interface]",
936 "       netstat [-j jail] -r [-46AnW] [-F fibnum] [-f address_family]\n"
937 "               [-M core] [-N system]",
938 "       netstat [-j jail] -rs [-s] [-M core] [-N system]",
939 "       netstat [-j jail] -g [-46W] [-f address_family] [-M core] [-N system]",
940 "       netstat [-j jail] -gs [-46s] [-f address_family] [-M core] [-N system]",
941 "       netstat [-j jail] -Q");
942 	xo_finish();
943 	exit(1);
944 }
945