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