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