xref: /netbsd/usr.bin/netstat/main.c (revision a399bebb)
1 /*	$NetBSD: main.c,v 1.104 2022/10/24 08:11:25 msaitoh Exp $	*/
2 
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/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\
35  Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "from: @(#)main.c	8.4 (Berkeley) 3/1/94";
41 #else
42 __RCSID("$NetBSD: main.c,v 1.104 2022/10/24 08:11:25 msaitoh Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <sys/param.h>
47 #include <sys/file.h>
48 #include <sys/protosw.h>
49 #include <sys/socket.h>
50 
51 #include <net/if.h>
52 #include <netinet/in.h>
53 
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <kvm.h>
58 #include <limits.h>
59 #include <netdb.h>
60 #include <nlist.h>
61 #include <paths.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include "netstat.h"
67 #include "rtutil.h"
68 #include "prog_ops.h"
69 
70 int	Aflag;
71 int	aflag;
72 int	Bflag;
73 int	bflag;
74 int	dflag;
75 #ifndef SMALL
76 int	gflag;
77 #endif
78 int	hflag;
79 int	iflag;
80 int	Lflag;
81 int	lflag;
82 int	mflag;
83 int	numeric_addr;
84 int	numeric_port;
85 int	nflag;
86 int	Pflag;
87 int	pflag;
88 int	qflag;
89 int	rflag;
90 int	sflag;
91 int	tagflag;
92 int	tflag;
93 int	Vflag;
94 int	vflag;
95 
96 char	*interface;
97 
98 int	af;
99 int	use_sysctl;
100 int	force_sysctl;
101 
102 struct nlist nl[] = {
103 #define	N_MBSTAT	0
104 	{ "_mbstat", 0, 0, 0, 0 },
105 #define	N_IPSTAT	1
106 	{ "_ipstat", 0, 0, 0, 0 },	/* not available via kvm */
107 #define	N_TCBTABLE	2
108 	{ "_tcbtable", 0, 0, 0, 0 },
109 #define	N_TCPSTAT	3
110 	{ "_tcpstat", 0, 0, 0, 0 },	/* not available via kvm */
111 #define	N_UDBTABLE	4
112 	{ "_udbtable", 0, 0, 0, 0 },
113 #define	N_UDPSTAT	5
114 	{ "_udpstat", 0, 0, 0, 0 },	/* not available via kvm */
115 #define	N_IFNET_LIST		6
116 	{ "_ifnet_list", 0, 0, 0, 0 },
117 #define	N_ICMPSTAT	7
118 	{ "_icmpstat", 0, 0, 0, 0 },	/* not available via kvm */
119 #define	N_RTSTAT	8
120 	{ "_rtstat", 0, 0, 0, 0 },
121 #define	N_UNIXSW	9
122 	{ "_unixsw", 0, 0, 0, 0 },
123 #define N_RTREE		10
124 	{ "_rt_tables", 0, 0, 0, 0 },
125 #define	N_NFILE		11
126 	{ "_nfile", 0, 0, 0, 0 },
127 #define N_IGMPSTAT	12
128 	{ "_igmpstat", 0, 0, 0, 0 },	/* not available via kvm */
129 #define N_MRTPROTO	13
130 	{ "_ip_mrtproto", 0, 0, 0, 0 },
131 #define N_MRTSTAT	14
132 	{ "_mrtstat", 0, 0, 0, 0 },
133 #define N_MFCHASHTBL	15
134 	{ "_mfchashtbl", 0, 0, 0, 0 },
135 #define	N_MFCHASH	16
136 	{ "_mfchash", 0, 0, 0, 0 },
137 #define N_VIFTABLE	17
138 	{ "_viftable", 0, 0, 0, 0 },
139 #define N_MSIZE		18
140 	{ "_msize", 0, 0, 0, 0 },
141 #define N_MCLBYTES	19
142 	{ "_mclbytes", 0, 0, 0, 0 },
143 #define N_DDPSTAT	20
144 	{ "_ddpstat", 0, 0, 0, 0 },	/* not available via kvm */
145 #define N_DDPCB		21
146 	{ "_ddpcb", 0, 0, 0, 0 },
147 #define N_MBPOOL	22
148 	{ "_mbpool", 0, 0, 0, 0 },
149 #define N_MCLPOOL	23
150 	{ "_mclpool", 0, 0, 0, 0 },
151 #define N_IP6STAT	24
152 	{ "_ip6stat", 0, 0, 0, 0 },	/* not available via kvm */
153 #define N_TCP6STAT	25
154 	{ "_tcp6stat", 0, 0, 0, 0 },	/* not available via kvm */
155 #define N_UDP6STAT	26
156 	{ "_udp6stat", 0, 0, 0, 0 },	/* not available via kvm */
157 #define N_ICMP6STAT	27
158 	{ "_icmp6stat", 0, 0, 0, 0 },	/* not available via kvm */
159 #define N_IPSECSTAT	28
160 	{ "_ipsecstat", 0, 0, 0, 0 },	/* not available via kvm */
161 #define N_IPSEC6STAT	29
162 	{ "_ipsec6stat", 0, 0, 0, 0 },	/* not available via kvm */
163 #define N_PIM6STAT	30
164 	{ "_pim6stat", 0, 0, 0, 0 },	/* not available via kvm */
165 #define N_MRT6PROTO	31
166 	{ "_ip6_mrtproto", 0, 0, 0, 0 },
167 #define N_MRT6STAT	32
168 	{ "_mrt6stat", 0, 0, 0, 0 },
169 #define N_MF6CTABLE	33
170 	{ "_mf6ctable", 0, 0, 0, 0 },
171 #define N_MIF6TABLE	34
172 	{ "_mif6table", 0, 0, 0, 0 },
173 #define N_PFKEYSTAT	35
174 	{ "_pfkeystat", 0, 0, 0, 0 },	/* not available via kvm */
175 #define N_ARPSTAT	36
176 	{ "_arpstat", 0, 0, 0, 0 },	/* not available via kvm */
177 #define N_RIP6STAT	37
178 	{ "_rip6stat", 0, 0, 0, 0 },	/* not available via kvm */
179 #define	N_ARPINTRQ	38
180 	{ "_arpintrq", 0, 0, 0, 0 },
181 #define	N_ATINTRQ1	39
182 	{ "_atintrq1", 0, 0, 0, 0 },
183 #define	N_ATINTRQ2	40
184 	{ "_atintrq2", 0, 0, 0, 0 },
185 #define	N_PPPOEDISCINQ	41
186 	{ "_ppoediscinq", 0, 0, 0, 0 },
187 #define	N_PPPOEINQ	42
188 	{ "_ppoeinq", 0, 0, 0, 0 },
189 #define	N_HARDCLOCK_TICKS 43
190 	{ "_hardclock_ticks", 0, 0, 0, 0 },
191 #define N_PIMSTAT	44
192 	{ "_pimstat", 0, 0, 0, 0 },
193 #define N_CARPSTAT	45
194 	{ "_carpstats", 0, 0, 0, 0 },	/* not available via kvm */
195 #define N_PFSYNCSTAT	46
196 	{ "_pfsyncstats", 0, 0, 0, 0},  /* not available via kvm */
197 	{ "", 0, 0, 0, 0 },
198 };
199 
200 struct protox {
201 	u_char	pr_index;		/* index into nlist of cb head */
202 	u_char	pr_sindex;		/* index into nlist of stat block */
203 	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
204 	void	(*pr_cblocks)		/* control blocks printing routine */
205 			(u_long, const char *);
206 	void	(*pr_stats)		/* statistics printing routine */
207 			(u_long, const char *);
208 	void	(*pr_istats)	       /* per/if statistics printing routine */
209 			(const char *);
210 	void	(*pr_dump)		/* PCB state dump routine */
211 			(u_long, const char *, u_long);
212 	const char *pr_name;		/* well-known name */
213 } protox[] = {
214 	{ N_TCBTABLE,	N_TCPSTAT,	1,	protopr,
215 	  tcp_stats,	NULL,		tcp_dump,	"tcp" },
216 	{ N_UDBTABLE,	N_UDPSTAT,	1,	protopr,
217 	  udp_stats,	NULL,		0,	"udp" },
218 	{ -1,		N_IPSTAT,	1,	0,
219 	  ip_stats,	NULL,		0,	"ip" },
220 	{ -1,		N_ICMPSTAT,	1,	0,
221 	  icmp_stats,	NULL,		0,	"icmp" },
222 	{ -1,		N_IGMPSTAT,	1,	0,
223 	  igmp_stats,	NULL,		0,	"igmp" },
224 	{ -1,		N_CARPSTAT,	1,	0,
225 	  carp_stats,	NULL,		0,	"carp" },
226 #ifdef IPSEC
227 	{ -1,		N_IPSECSTAT,	1,	0,
228 	  fast_ipsec_stats, NULL,	0,	"ipsec" },
229 #endif
230 	{ -1,		N_PIMSTAT,	1,	0,
231 	  pim_stats,	NULL,		0,	"pim" },
232 	{ -1,		N_PFSYNCSTAT,  1,  0,
233 	  pfsync_stats,  NULL,		0,  "pfsync" },
234 	{ -1,		-1,		0,	0,
235 	  0,		NULL,		0,	0 }
236 };
237 
238 #ifdef INET6
239 struct protox ip6protox[] = {
240 	{ -1,		N_IP6STAT,	1,	0,
241 	  ip6_stats,	ip6_ifstats,	0,	"ip6" },
242 	{ -1,		N_ICMP6STAT,	1,	0,
243 	  icmp6_stats,	icmp6_ifstats,	0,	"icmp6" },
244 #ifdef TCP6
245 	{ N_TCBTABLE,	N_TCP6STAT,	1,	ip6protopr,
246 	  tcp6_stats,	NULL,		tcp6_dump,	"tcp6" },
247 #else
248 	{ N_TCBTABLE,	N_TCP6STAT,	1,	ip6protopr,
249 	  tcp_stats,	NULL,		tcp6_dump,	"tcp6" },
250 #endif
251 	{ N_UDBTABLE,	N_UDP6STAT,	1,	ip6protopr,
252 	  udp6_stats,	NULL,		0,	"udp6" },
253 #ifdef IPSEC
254 	{ -1,		N_IPSEC6STAT,	1,	0,
255 	  fast_ipsec_stats, NULL,	0,	"ipsec6" },
256 #endif
257 	{ -1,		N_PIM6STAT,	1,	0,
258 	  pim6_stats,	NULL,		0,	"pim6" },
259 	{ -1,		N_RIP6STAT,	1,	0,
260 	  rip6_stats,	NULL,		0,	"rip6" },
261 	{ -1,		-1,		0,	0,
262 	  0,		NULL,		0,	0 }
263 };
264 #endif
265 
266 struct protox arpprotox[] = {
267 	{ -1,		N_ARPSTAT,	1,	0,
268 	  arp_stats,	NULL,		0,	"arp" },
269 	{ -1,		-1,		0,	0,
270 	  0,		NULL,		0,	0 }
271 };
272 
273 #ifdef IPSEC
274 struct protox pfkeyprotox[] = {
275 	{ -1,		N_PFKEYSTAT,	1,	0,
276 	  pfkey_stats,	NULL,		0,	"pfkey" },
277 	{ -1,		-1,		0,	0,
278 	  0,		NULL,		0,	0 }
279 };
280 #endif
281 
282 #ifndef SMALL
283 struct protox atalkprotox[] = {
284 	{ N_DDPCB,	N_DDPSTAT,	1,	atalkprotopr,
285 	  ddp_stats,	NULL,		0,	"ddp" },
286 	{ -1,		-1,		0,	0,
287 	  0,		NULL,		0,	NULL }
288 };
289 #endif
290 
291 struct protox *protoprotox[] = { protox,
292 #ifdef INET6
293 				 ip6protox,
294 #endif
295 				 arpprotox,
296 #ifdef IPSEC
297 				 pfkeyprotox,
298 #endif
299 #ifndef SMALL
300 				 atalkprotox,
301 #endif
302 				 NULL };
303 
304 const struct softintrq {
305 	const char *siq_name;
306 	int siq_index;
307 } softintrq[] = {
308 	{ "arpintrq", N_ARPINTRQ },
309 	{ "atintrq1", N_ATINTRQ1 },
310 	{ "atintrq2", N_ATINTRQ2 },
311 	{ "ppoediscinq", N_PPPOEDISCINQ },
312 	{ "ppoeinq", N_PPPOEINQ },
313 	{ NULL, -1 },
314 };
315 
316 static void printproto(struct protox *, const char *);
317 static void print_softintrq(void);
318 __dead static void usage(void);
319 static struct protox *name2protox(const char *);
320 static struct protox *knownname(const char *);
321 static void prepare(const char *, const char *, struct protox *tp);
322 static kvm_t *prepare_kvmd(const char *, const char *, char *);
323 
324 static kvm_t *kvmd = NULL;
325 gid_t egid;
326 int interval;	/* repeat interval for i/f stats */
327 static const char *nlistf = NULL, *memf = NULL;
328 
329 kvm_t *
get_kvmd(void)330 get_kvmd(void)
331 {
332 	char buf[_POSIX2_LINE_MAX];
333 
334 	if (kvmd != NULL)
335 		return kvmd;
336 	if ((kvmd = prepare_kvmd(nlistf, memf, buf)) == NULL)
337 		errx(1, "kvm error: %s", buf);
338 	return kvmd;
339 }
340 
341 static kvm_t *
prepare_kvmd(const char * nf,const char * mf,char * errbuf)342 prepare_kvmd(const char *nf, const char *mf, char *errbuf)
343 {
344 	kvm_t *k;
345 
346 	(void)setegid(egid);
347 	k = kvm_openfiles(nf, mf, NULL, O_RDONLY, errbuf);
348 	(void)setgid(getgid());
349 	return k;
350 }
351 
352 void
prepare(const char * nf,const char * mf,struct protox * tp)353 prepare(const char *nf, const char *mf, struct protox *tp)
354 {
355 	char buf[_POSIX2_LINE_MAX];
356 
357 	/*
358 	 * Try to figure out if we can use sysctl or not.
359 	 */
360 	if (nf != NULL || mf != NULL) {
361 		/* Of course, we can't use sysctl with dumps. */
362 		if (force_sysctl)
363 			errx(EXIT_FAILURE, "can't use sysctl with dumps");
364 
365 		/*
366 		 * If we have -M or -N, we're not dealing with live memory
367 		 * or want to use kvm interface explicitly.  It is sometimes
368 		 * useful to dig inside of kernel without extending
369 		 * sysctl interface (i.e., without rebuilding kernel).
370 		 */
371 		use_sysctl = 0;
372 	} else if (qflag ||
373 #ifndef SMALL
374 		   gflag ||
375 #endif
376 		   (pflag && tp->pr_sindex == N_PIMSTAT) ||
377 		   Pflag) {
378 		/* These flags are not yet supported via sysctl(3). */
379 		use_sysctl = 0;
380 	} else {
381 		/* We can use sysctl(3). */
382 		use_sysctl = 1;
383 	}
384 
385 	if (force_sysctl && !use_sysctl) {
386 		/* Let the user know what's about to happen. */
387 		warnx("forcing sysctl usage even though it might not be "
388 		    "supported");
389 		use_sysctl = 1;
390 	}
391 
392 	kvmd = prepare_kvmd(nf, mf, buf);
393 
394 	if (!use_sysctl) {
395 
396 		if (kvmd == NULL)
397 			errx(1, "kvm error: %s", buf);
398 		if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) {
399 			if (nf)
400 				errx(1, "%s: no namelist", nf);
401 			else
402 				errx(1, "no namelist");
403 		}
404 	} else
405 		(void)setgid(getgid());
406 }
407 
408 int
main(int argc,char * argv[])409 main(int argc, char *argv[])
410 {
411 	struct protoent *p;
412 	struct protox *tp;	/* for printing cblocks & stats */
413 	int ch;
414 	char *cp;
415 	char *afname, *afnames;
416 	u_long pcbaddr;
417 
418 	if (prog_init) {
419 		if (prog_init() == -1)
420 			err(1, "init failed");
421 		force_sysctl = 1; /* cheap trick */
422 	}
423 
424 	egid = getegid();
425 	(void)setegid(getgid());
426 	tp = NULL;
427 	af = AF_UNSPEC;
428 	afnames = NULL;
429 	pcbaddr = 0;
430 
431 	while ((ch = getopt(argc, argv,
432 	    "AabBdf:ghI:LliM:mN:nP:p:qrsStTuVvw:X")) != -1)
433 		switch (ch) {
434 		case 'A':
435 			Aflag = RT_AFLAG;
436 			break;
437 		case 'a':
438 			aflag = 1;
439 			break;
440 		case 'b':
441 			bflag = 1;
442 			break;
443 		case 'B':
444 			Bflag = 1;
445 			break;
446 		case 'd':
447 			dflag = 1;
448 			break;
449 		case 'f':
450 			afnames = optarg;
451 			break;
452 #ifndef SMALL
453 		case 'g':
454 			gflag = 1;
455 			break;
456 #endif
457 		case 'h':
458 			hflag = 1;
459 			break;
460 		case 'I':
461 			iflag = 1;
462 			interface = optarg;
463 			break;
464 		case 'i':
465 			iflag = 1;
466 			break;
467 		case 'L':
468 			Lflag = RT_LFLAG;
469 			break;
470 		case 'l':
471 			lflag = 1;
472 			break;
473 		case 'M':
474 			memf = optarg;
475 			break;
476 		case 'm':
477 			mflag = 1;
478 			break;
479 		case 'N':
480 			nlistf = optarg;
481 			break;
482 		case 'n':
483 			numeric_addr = numeric_port = nflag = RT_NFLAG;
484 			break;
485 		case 'P':
486 			errno = 0;
487 			pcbaddr = strtoul(optarg, &cp, 16);
488 			if (*cp != '\0' || errno == ERANGE)
489 				errx(1, "invalid PCB address %s", optarg);
490 			Pflag = 1;
491 			break;
492 		case 'p':
493 			if ((tp = name2protox(optarg)) == NULL)
494 				errx(1,
495 				    "%s: unknown or uninstrumented protocol",
496 				    optarg);
497 			pflag = 1;
498 			break;
499 		case 'q':
500 			qflag = 1;
501 			break;
502 		case 'r':
503 			rflag = 1;
504 			break;
505 		case 's':
506 			++sflag;
507 			break;
508 		case 'S':
509 			numeric_addr = 1;
510 			break;
511 		case 't':
512 			tflag = 1;
513 			break;
514 		case 'T':
515 			tagflag = RT_TFLAG;
516 			break;
517 		case 'u':
518 			af = AF_LOCAL;
519 			break;
520 		case 'V':
521 			Vflag++;
522 			break;
523 		case 'v':
524 			vflag = RT_VFLAG;
525 			break;
526 		case 'w':
527 			interval = atoi(optarg);
528 			iflag = 1;
529 			break;
530 		case 'X':
531 			force_sysctl = 1;
532 			break;
533 		case '?':
534 		default:
535 			usage();
536 		}
537 	argv += optind;
538 	argc -= optind;
539 
540 #define	BACKWARD_COMPATIBILITY
541 #ifdef	BACKWARD_COMPATIBILITY
542 	if (*argv) {
543 		if (isdigit((unsigned char)**argv)) {
544 			interval = atoi(*argv);
545 			if (interval <= 0)
546 				usage();
547 			++argv;
548 			iflag = 1;
549 		}
550 		if (*argv) {
551 			nlistf = *argv;
552 			if (*++argv)
553 				memf = *argv;
554 		}
555 	}
556 #endif
557 
558 	prepare(nlistf, memf, tp);
559 
560 #ifndef SMALL
561 	if (Bflag) {
562 		if (sflag)
563 			bpf_stats();
564 		else
565 			bpf_dump(interface);
566 		exit(0);
567 	}
568 #endif
569 
570 	if (mflag) {
571 		mbpr(nl[N_MBSTAT].n_value,  nl[N_MSIZE].n_value,
572 		    nl[N_MCLBYTES].n_value, nl[N_MBPOOL].n_value,
573 		    nl[N_MCLPOOL].n_value);
574 		exit(0);
575 	}
576 	if (Pflag) {
577 		if (tp == NULL) {
578 			/* Default to TCP. */
579 			tp = name2protox("tcp");
580 		}
581 		if (tp->pr_dump)
582 			(*tp->pr_dump)(nl[tp->pr_index].n_value, tp->pr_name,
583 			    pcbaddr);
584 		else
585 			printf("%s: no PCB dump routine\n", tp->pr_name);
586 		exit(0);
587 	}
588 	if (pflag) {
589 		if (iflag && tp->pr_istats)
590 			intpr(interval, nl[N_IFNET_LIST].n_value,
591 			    tp->pr_istats);
592 		else if (tp->pr_stats)
593 			(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
594 				tp->pr_name);
595 		else
596 			printf("%s: no stats routine\n", tp->pr_name);
597 		exit(0);
598 	}
599 	if (qflag) {
600 		print_softintrq();
601 		exit(0);
602 	}
603 	/*
604 	 * Keep file descriptors open to avoid overhead
605 	 * of open/close on each call to get* routines.
606 	 */
607 	sethostent(1);
608 	setnetent(1);
609 	/*
610 	 * If -f was used afnames != NULL, loop over the address families.
611 	 * Otherwise do this at least once (with af == AF_UNSPEC).
612 	 */
613 	afname = NULL;
614 	do {
615 		if (afnames != NULL) {
616 			afname = strsep(&afnames, ",");
617 			if (afname == NULL)
618 				break;		/* Exit early */
619 			if (strcmp(afname, "inet") == 0)
620 				af = AF_INET;
621 			else if (strcmp(afname, "inet6") == 0)
622 				af = AF_INET6;
623 			else if (strcmp(afname, "arp") == 0)
624 				af = AF_ARP;
625 			else if (strcmp(afname, "pfkey") == 0)
626 				af = PF_KEY;
627 			else if (strcmp(afname, "unix") == 0
628 			    || strcmp(afname, "local") == 0)
629 				af = AF_LOCAL;
630 			else if (strcmp(afname, "atalk") == 0)
631 				af = AF_APPLETALK;
632 			else if (strcmp(afname, "mpls") == 0)
633 				af = AF_MPLS;
634 			else {
635 				warnx("%s: unknown address family",
636 				    afname);
637 				continue;
638 			}
639 		}
640 
641 		if (iflag) {
642 			if (af != AF_UNSPEC)
643 				goto protostat;
644 
645 			intpr(interval, nl[N_IFNET_LIST].n_value, NULL);
646 			break;
647 		}
648 		if (rflag) {
649 			if (sflag)
650 				rt_stats(use_sysctl ? 0 :
651 				    nl[N_RTSTAT].n_value);
652 			else {
653 				if (use_sysctl)
654 					p_rttables(af,
655 					    nflag|tagflag|vflag|Lflag, 0, ~0);
656 				else
657 					routepr(nl[N_RTREE].n_value);
658 			}
659 			break;
660 		}
661 #ifndef SMALL
662 		if (gflag) {
663 			if (sflag) {
664 				if (af == AF_INET || af == AF_UNSPEC)
665 					mrt_stats(nl[N_MRTPROTO].n_value,
666 						  nl[N_MRTSTAT].n_value);
667 #ifdef INET6
668 				if (af == AF_INET6 || af == AF_UNSPEC)
669 					mrt6_stats(nl[N_MRT6PROTO].n_value,
670 						   nl[N_MRT6STAT].n_value);
671 #endif
672 			}
673 			else {
674 				if (af == AF_INET || af == AF_UNSPEC)
675 					mroutepr(nl[N_MRTPROTO].n_value,
676 						 nl[N_MFCHASHTBL].n_value,
677 						 nl[N_MFCHASH].n_value,
678 						 nl[N_VIFTABLE].n_value);
679 #ifdef INET6
680 				if (af == AF_INET6 || af == AF_UNSPEC)
681 					mroute6pr(nl[N_MRT6PROTO].n_value,
682 						  nl[N_MF6CTABLE].n_value,
683 						  nl[N_MIF6TABLE].n_value);
684 #endif
685 			}
686 			break;
687 		}
688 #endif
689 	  protostat:
690 		if (af == AF_INET || af == AF_UNSPEC) {
691 			setprotoent(1);
692 			setservent(1);
693 			/* ugh, this is O(MN) ... why do we do this? */
694 			while ((p = getprotoent()) != NULL) {
695 				for (tp = protox; tp->pr_name; tp++)
696 					if (strcmp(tp->pr_name, p->p_name) == 0)
697 						break;
698 				if (tp->pr_name == 0 || tp->pr_wanted == 0)
699 					continue;
700 				printproto(tp, p->p_name);
701 				tp->pr_wanted = 0;
702 			}
703 			endprotoent();
704 			for (tp = protox; tp->pr_name; tp++)
705 				if (tp->pr_wanted)
706 					printproto(tp, tp->pr_name);
707 		}
708 #ifdef INET6
709 		if (af == AF_INET6 || af == AF_UNSPEC)
710 			for (tp = ip6protox; tp->pr_name; tp++)
711 				printproto(tp, tp->pr_name);
712 #endif
713 		if (af == AF_ARP || af == AF_UNSPEC)
714 			for (tp = arpprotox; tp->pr_name; tp++)
715 				printproto(tp, tp->pr_name);
716 #ifdef IPSEC
717 		if (af == PF_KEY || af == AF_UNSPEC)
718 			for (tp = pfkeyprotox; tp->pr_name; tp++)
719 				printproto(tp, tp->pr_name);
720 #endif
721 #ifndef SMALL
722 		if (af == AF_APPLETALK || af == AF_UNSPEC)
723 			for (tp = atalkprotox; tp->pr_name; tp++)
724 				printproto(tp, tp->pr_name);
725 		if ((af == AF_LOCAL || af == AF_UNSPEC) && !sflag)
726 			unixpr(nl[N_UNIXSW].n_value);
727 #endif
728 	} while (afnames != NULL && afname != NULL);
729 	exit(0);
730 }
731 
732 /*
733  * Print out protocol statistics or control blocks (per sflag).
734  * If the interface was not specifically requested, and the symbol
735  * is not in the namelist, ignore this one.
736  */
737 static void
printproto(struct protox * tp,const char * name)738 printproto(struct protox *tp, const char *name)
739 {
740 	void (*pr)(u_long, const char *);
741 	u_long off;
742 
743 	if (sflag) {
744 		if (iflag) {
745 			if (tp->pr_istats)
746 				intpr(interval, nl[N_IFNET_LIST].n_value,
747 				    tp->pr_istats);
748 			return;
749 		}
750 		else {
751 			pr = tp->pr_stats;
752 			off = nl[tp->pr_sindex].n_value;
753 		}
754 	} else {
755 		pr = tp->pr_cblocks;
756 		off = nl[tp->pr_index].n_value;
757 	}
758 	if (pr != NULL && ((off || af != AF_UNSPEC) || use_sysctl))
759 		(*pr)(off, name);
760 }
761 
762 /*
763  * Print softintrq status.
764  */
765 void
print_softintrq(void)766 print_softintrq(void)
767 {
768 	struct ifqueue intrq, *ifq = &intrq;
769 	const struct softintrq *siq;
770 	u_long off;
771 
772 	for (siq = softintrq; siq->siq_name != NULL; siq++) {
773 		off = nl[siq->siq_index].n_value;
774 		if (off == 0)
775 			continue;
776 
777 		kread(off, (char *)ifq, sizeof(*ifq));
778 		printf("%s:\n", siq->siq_name);
779 		printf("\tqueue length: %d\n", ifq->ifq_len);
780 		printf("\tmaximum queue length: %d\n", ifq->ifq_maxlen);
781 		printf("\tpackets dropped: %" PRIu64 "\n", ifq->ifq_drops);
782 	}
783 }
784 
785 /*
786  * Read kernel memory, return 0 on success.
787  */
788 int
kread(u_long addr,char * buf,int size)789 kread(u_long addr, char *buf, int size)
790 {
791 
792 	if (kvm_read(kvmd, addr, buf, size) != size) {
793 		warnx("%s", kvm_geterr(kvmd));
794 		return -1;
795 	}
796 	return 0;
797 }
798 
799 const char *
plural(int n)800 plural(int n)
801 {
802 
803 	return (n != 1 ? "s" : "");
804 }
805 
806 const char *
plurales(int n)807 plurales(int n)
808 {
809 
810 	return (n != 1 ? "es" : "");
811 }
812 
813 int
get_hardticks(void)814 get_hardticks(void)
815 {
816 	int hardticks;
817 
818 	kread(nl[N_HARDCLOCK_TICKS].n_value, (char *)&hardticks,
819 	    sizeof(hardticks));
820 	return hardticks;
821 }
822 
823 /*
824  * Find the protox for the given "well-known" name.
825  */
826 static struct protox *
knownname(const char * name)827 knownname(const char *name)
828 {
829 	struct protox **tpp, *tp;
830 
831 	for (tpp = protoprotox; *tpp; tpp++)
832 		for (tp = *tpp; tp->pr_name; tp++)
833 			if (strcmp(tp->pr_name, name) == 0)
834 				return tp;
835 	return NULL;
836 }
837 
838 /*
839  * Find the protox corresponding to name.
840  */
841 static struct protox *
name2protox(const char * name)842 name2protox(const char *name)
843 {
844 	struct protox *tp;
845 	char **alias;			/* alias from p->aliases */
846 	struct protoent *p;
847 
848 	/*
849 	 * Try to find the name in the list of "well-known" names. If that
850 	 * fails, check if name is an alias for an Internet protocol.
851 	 */
852 	if ((tp = knownname(name)) != NULL)
853 		return tp;
854 
855 	setprotoent(1);			/* make protocol lookup cheaper */
856 	while ((p = getprotoent()) != NULL) {
857 		/* assert: name not same as p->name */
858 		for (alias = p->p_aliases; *alias; alias++)
859 			if (strcmp(name, *alias) == 0) {
860 				endprotoent();
861 				return knownname(p->p_name);
862 			}
863 	}
864 	endprotoent();
865 	return NULL;
866 }
867 
868 static void
usage(void)869 usage(void)
870 {
871 	const char *progname = getprogname();
872 
873 	(void)fprintf(stderr,
874 "usage: %s [-Aan] [-f address_family[,family ...]] [-M core] [-N system]\n", progname);
875 	(void)fprintf(stderr,
876 "       %s [-bdgiLmnqrsSv] [-f address_family[,family ...]] [-M core] [-N system]\n",
877 	progname);
878 	(void)fprintf(stderr,
879 "       %s [-dn] [-I interface] [-M core] [-N system] [-w wait]\n", progname);
880 	(void)fprintf(stderr,
881 "       %s [-p protocol] [-M core] [-N system]\n", progname);
882 	(void)fprintf(stderr,
883 "       %s [-p protocol] [-M core] [-N system] -P pcbaddr\n", progname);
884 	(void)fprintf(stderr,
885 "       %s [-p protocol] [-i] [-I Interface] \n", progname);
886 	(void)fprintf(stderr,
887 "       %s [-s] [-f address_family[,family ...]] [-i] [-I Interface]\n", progname);
888 	(void)fprintf(stderr,
889 "       %s [-s] [-B] [-I interface]\n", progname);
890 	exit(1);
891 }
892