xref: /openbsd-src/usr.bin/netstat/main.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: main.c,v 1.63 2006/08/29 21:51:13 claudio Exp $	*/
2 /*	$NetBSD: main.c,v 1.9 1996/05/07 02:55:02 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1988, 1993
6  *	Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 char copyright[] =
35 "@(#) Copyright (c) 1983, 1988, 1993\n\
36 	Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "from: @(#)main.c	8.4 (Berkeley) 3/1/94";
42 #else
43 static char *rcsid = "$OpenBSD: main.c,v 1.63 2006/08/29 21:51:13 claudio Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/file.h>
49 #include <sys/protosw.h>
50 #include <sys/socket.h>
51 
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 
68 struct nlist nl[] = {
69 #define N_MBSTAT	0
70 	{ "_mbstat" },
71 #define N_IPSTAT	1
72 	{ "_ipstat" },
73 #define N_TCBTABLE	2
74 	{ "_tcbtable" },
75 #define N_TCPSTAT	3
76 	{ "_tcpstat" },
77 #define N_UDBTABLE	4
78 	{ "_udbtable" },
79 #define N_UDPSTAT	5
80 	{ "_udpstat" },
81 #define N_IFNET		6
82 	{ "_ifnet" },
83 #define N_ICMPSTAT	7
84 	{ "_icmpstat" },
85 #define N_RTSTAT	8
86 	{ "_rtstat" },
87 #define N_UNIXSW	9
88 	{ "_unixsw" },
89 #define N_RTREE		10
90 	{ "_rt_tables"},
91 #define N_FILE		11
92 	{ "_file" },
93 #define N_IGMPSTAT	12
94 	{ "_igmpstat" },
95 #define N_MRTPROTO	13
96 	{ "_ip_mrtproto" },
97 #define N_MRTSTAT	14
98 	{ "_mrtstat" },
99 #define N_MFCHASHTBL	15
100 	{ "_mfchashtbl" },
101 #define N_MFCHASH	16
102 	{ "_mfchash" },
103 #define N_VIFTABLE	17
104 	{ "_viftable" },
105 #define N_IPX		18
106 	{ "_ipxcbtable"},
107 #define N_IPXSTAT	19
108 	{ "_ipxstat"},
109 #define N_SPXSTAT	20
110 	{ "_spx_istat"},
111 #define N_AHSTAT	21
112 	{ "_ahstat"},
113 #define N_ESPSTAT	22
114 	{ "_espstat"},
115 #define N_IP4STAT	23
116 	{ "_ipipstat"},
117 #define N_DDPSTAT	24
118 	{ "_ddpstat"},
119 #define N_DDPCB		25
120 	{ "_ddpcb"},
121 #define N_ETHERIPSTAT	26
122 	{ "_etheripstat"},
123 #define N_IP6STAT	27
124 	{ "_ip6stat" },
125 #define N_ICMP6STAT	28
126 	{ "_icmp6stat" },
127 #define N_PIM6STAT	29
128 	{ "_pim6stat" },
129 #define N_MRT6PROTO	30
130 	{ "_ip6_mrtproto" },
131 #define N_MRT6STAT	31
132 	{ "_mrt6stat" },
133 #define N_MF6CTABLE	32
134 	{ "_mf6ctable" },
135 #define N_MIF6TABLE	33
136 	{ "_mif6table" },
137 #define N_MBPOOL	34
138 	{ "_mbpool" },
139 #define N_MCLPOOL	35
140 	{ "_mclpool" },
141 #define N_IPCOMPSTAT	36
142 	{ "_ipcompstat" },
143 #define N_RIP6STAT	37
144 	{ "_rip6stat" },
145 #define N_CARPSTAT	38
146 	{ "_carpstats" },
147 #define N_RAWIPTABLE	39
148 	{ "_rawcbtable" },
149 #define N_RAWIP6TABLE	40
150 	{ "_rawin6pcbtable" },
151 #define N_PFSYNCSTAT	41
152 	{ "_pfsyncstats" },
153 #define N_PIMSTAT	42
154 	{ "_pimstat" },
155 #define N_AF2RTAFIDX	43
156 	{ "_af2rtafidx" },
157 #define N_RTBLIDMAX	44
158 	{ "_rtbl_id_max" },
159 #define N_RTMASK	45
160 	{ "_mask_rnhead" },
161 	{ ""}
162 };
163 
164 struct protox {
165 	u_char	pr_index;			/* index into nlist of cb head */
166 	u_char	pr_sindex;			/* index into nlist of stat block */
167 	u_char	pr_wanted;			/* 1 if wanted, 0 otherwise */
168 	void	(*pr_cblocks)(u_long, char *);	/* control blocks printing routine */
169 	void	(*pr_stats)(u_long, char *);	/* statistics printing routine */
170 	void	(*pr_dump)(u_long);		/* pcb printing routine */
171 	char	*pr_name;			/* well-known name */
172 } protox[] = {
173 	{ N_TCBTABLE,	N_TCPSTAT,	1,	protopr,
174 	  tcp_stats,	tcp_dump,	"tcp" },
175 	{ N_UDBTABLE,	N_UDPSTAT,	1,	protopr,
176 	  udp_stats,	0,		"udp" },
177 	{ N_RAWIPTABLE,	N_IPSTAT,	1,	protopr,
178 	  ip_stats,	0,		"ip" },
179 	{ -1,		N_ICMPSTAT,	1,	0,
180 	  icmp_stats,	0,		"icmp" },
181 	{ -1,		N_IGMPSTAT,	1,	0,
182 	  igmp_stats,	0,		"igmp" },
183 	{ -1,		N_AHSTAT,	1,	0,
184 	  ah_stats,	0,		"ah" },
185 	{ -1,		N_ESPSTAT,	1,	0,
186 	  esp_stats,	0,		"esp" },
187 	{ -1,		N_IP4STAT,	1,	0,
188 	  ipip_stats,	0,		"ipencap" },
189 	{ -1,		N_ETHERIPSTAT,	1,	0,
190 	  etherip_stats,0,		"etherip" },
191 	{ -1,		N_IPCOMPSTAT,	1,	0,
192 	  ipcomp_stats,	0,		"ipcomp" },
193 	{ -1,		N_CARPSTAT,	1,	0,
194 	  carp_stats,	0,		"carp" },
195 	{ -1,		N_PFSYNCSTAT,	1,	0,
196 	  pfsync_stats,	0,		"pfsync" },
197 	{ -1,		N_PIMSTAT,	1,	0,
198 	  pim_stats,	0,		"pim" },
199 	{ -1,		-1,		0,	0,
200 	  0,		0,		0 }
201 };
202 
203 #ifdef INET6
204 struct protox ip6protox[] = {
205 	{ N_TCBTABLE,	N_TCPSTAT,	1,	ip6protopr,
206 	  0,		tcp_dump,	"tcp" },
207 	{ N_UDBTABLE,	N_UDPSTAT,	1,	ip6protopr,
208 	  0,		0,		"udp" },
209 	{ N_RAWIP6TABLE,N_IP6STAT,	1,	ip6protopr,
210 	  ip6_stats,	0,		"ip6" },
211 	{ -1,		N_ICMP6STAT,	1,	0,
212 	  icmp6_stats,	0,		"icmp6" },
213 	{ -1,		N_PIM6STAT,	1,	0,
214 	  pim6_stats,	0,		"pim6" },
215 	{ -1,		N_RIP6STAT,	1,	0,
216 	  rip6_stats,	0,		"rip6" },
217 	{ -1,		-1,		0,	0,
218 	  0,		0,		0 }
219 };
220 #endif
221 
222 struct protox ipxprotox[] = {
223 	{ N_IPX,	N_IPXSTAT,	1,	ipxprotopr,
224 	  ipx_stats,	0,		"ipx" },
225 	{ N_IPX,	N_SPXSTAT,	1,	ipxprotopr,
226 	  spx_stats,	0,		"spx" },
227 	{ -1,		-1,		0,	0,
228 	  0,		0,		0 }
229 };
230 
231 struct protox atalkprotox[] = {
232 	{ N_DDPCB,	N_DDPSTAT,	1,	atalkprotopr,
233 	  ddp_stats,	0,		"ddp" },
234 	{ -1,		-1,		0,	0,
235 	  0,		0,		0 }
236 };
237 
238 #ifndef INET6
239 struct protox *protoprotox[] = {
240 	protox, ipxprotox, atalkprotox, NULL
241 };
242 #else
243 struct protox *protoprotox[] = {
244 	protox, ip6protox, ipxprotox, atalkprotox, NULL
245 };
246 #endif
247 
248 static void printproto(struct protox *, char *);
249 static void usage(void);
250 static struct protox *name2protox(char *);
251 static struct protox *knownname(char *);
252 
253 kvm_t *kvmd;
254 
255 int
256 main(int argc, char *argv[])
257 {
258 	extern char *optarg;
259 	extern int optind;
260 	struct protoent *p;
261 	struct protox *tp = NULL; /* for printing cblocks & stats */
262 	int ch;
263 	char *nlistf = NULL, *memf = NULL, *ep;
264 	char buf[_POSIX2_LINE_MAX];
265 	gid_t gid;
266 	u_long pcbaddr = 0;
267 
268 	af = AF_UNSPEC;
269 
270 	while ((ch = getopt(argc, argv, "Aabdf:gI:ilM:mN:np:P:qrstuvW:w:")) != -1)
271 		switch (ch) {
272 		case 'A':
273 			Aflag = 1;
274 			break;
275 		case 'a':
276 			aflag = 1;
277 			break;
278 		case 'b':
279 			bflag = 1;
280 			break;
281 		case 'd':
282 			dflag = 1;
283 			break;
284 		case 'f':
285 			if (strcmp(optarg, "inet") == 0)
286 				af = AF_INET;
287 			else if (strcmp(optarg, "inet6") == 0)
288 				af = AF_INET6;
289 			else if (strcmp(optarg, "local") == 0)
290 				af = AF_LOCAL;
291 			else if (strcmp(optarg, "unix") == 0)
292 				af = AF_UNIX;
293 			else if (strcmp(optarg, "ipx") == 0)
294 				af = AF_IPX;
295 			else if (strcmp(optarg, "encap") == 0)
296 				af = PF_KEY;
297 			else if (strcmp(optarg, "atalk") == 0)
298 				af = AF_APPLETALK;
299 			else if (strcmp(optarg, "mask") == 0)
300 				af = 0xff;
301 			else {
302 				(void)fprintf(stderr,
303 				    "%s: %s: unknown address family\n",
304 				    __progname, optarg);
305 				exit(1);
306 			}
307 			break;
308 		case 'g':
309 			gflag = 1;
310 			break;
311 		case 'I':
312 			iflag = 1;
313 			interface = optarg;
314 			break;
315 		case 'i':
316 			iflag = 1;
317 			break;
318 		case 'l':
319 			lflag = 1;
320 			break;
321 		case 'M':
322 			memf = optarg;
323 			break;
324 		case 'm':
325 			mflag = 1;
326 			break;
327 		case 'N':
328 			nlistf = optarg;
329 			break;
330 		case 'n':
331 			nflag = 1;
332 			break;
333 		case 'p':
334 			if ((tp = name2protox(optarg)) == NULL) {
335 				(void)fprintf(stderr,
336 				    "%s: %s: unknown protocol\n",
337 				    __progname, optarg);
338 				exit(1);
339 			}
340 			pflag = 1;
341 			break;
342 		case 'P':
343 			errno = 0;
344 			pcbaddr = strtoul(optarg, &ep, 16);
345 			if (optarg[0] == '\0' || *ep != '\0' ||
346 			    errno == ERANGE) {
347 				(void)fprintf(stderr,
348 				    "%s: %s: invalid PCB address\n",
349 				    __progname, optarg);
350 				exit(1);
351 			}
352 			Pflag = 1;
353 			break;
354 		case 'q':
355 			qflag = 1;
356 			break;
357 		case 'r':
358 			rflag = 1;
359 			break;
360 		case 's':
361 			++sflag;
362 			break;
363 		case 't':
364 			tflag = 1;
365 			break;
366 		case 'u':
367 			af = AF_UNIX;
368 			break;
369 		case 'v':
370 			vflag = 1;
371 			break;
372 		case 'W':
373 			Wflag = 1;
374 			interface = optarg;
375 			break;
376 		case 'w':
377 			interval = atoi(optarg);
378 			iflag = 1;
379 			break;
380 		case '?':
381 		default:
382 			usage();
383 		}
384 	argv += optind;
385 	argc -= optind;
386 
387 	/*
388 	 * Show per-interface statistics which don't need access to
389 	 * kernel memory (they're using IOCTLs)
390 	 */
391 	if (Wflag) {
392 		if (interface == NULL)
393 			usage();
394 		net80211_ifstats(interface);
395 		exit(0);
396 	}
397 
398 	/*
399 	 * Discard setgid privileges if not the running kernel so that bad
400 	 * guys can't print interesting stuff from kernel memory.
401 	 * Dumping PCB info is also restricted.
402 	 */
403 	gid = getgid();
404 	if (nlistf != NULL || memf != NULL || Pflag)
405 		if (setresgid(gid, gid, gid) == -1)
406 			err(1, "setresgid");
407 	if (nlistf == NULL && memf == NULL && rflag && !Aflag) {
408 		/* printing the routing table no longer needs kvm */
409 		if (setresgid(gid, gid, gid) == -1)
410 			err(1, "setresgid");
411 		if (sflag)
412 			rt_stats(1, 0);
413 		else
414 			p_rttables(af);
415 		exit(0);
416 	}
417 	if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
418 	    buf)) == NULL) {
419 		fprintf(stderr, "%s: kvm_open: %s\n", __progname, buf);
420 		exit(1);
421 	}
422 
423 	if (nlistf == NULL && memf == NULL && !Pflag)
424 		if (setresgid(gid, gid, gid) == -1)
425 			err(1, "setresgid");
426 
427 #define	BACKWARD_COMPATIBILITY
428 #ifdef	BACKWARD_COMPATIBILITY
429 	if (*argv) {
430 		if (isdigit(**argv)) {
431 			interval = atoi(*argv);
432 			if (interval <= 0)
433 				usage();
434 			++argv;
435 			iflag = 1;
436 		}
437 		if (*argv) {
438 			nlistf = *argv;
439 			if (*++argv)
440 				memf = *argv;
441 		}
442 	}
443 #endif
444 
445 	if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) {
446 		if (nlistf)
447 			fprintf(stderr, "%s: %s: no namelist\n", __progname,
448 			    nlistf);
449 		else
450 			fprintf(stderr, "%s: no namelist\n", __progname);
451 		exit(1);
452 	}
453 	if (mflag) {
454 		mbpr(nl[N_MBSTAT].n_value, nl[N_MBPOOL].n_value,
455 		    nl[N_MCLPOOL].n_value);
456 		exit(0);
457 	}
458 	if (pflag) {
459 		printproto(tp, tp->pr_name);
460 		exit(0);
461 	}
462 	if (Pflag) {
463 		if (tp == NULL && (tp = name2protox("tcp")) == NULL) {
464 			(void)fprintf(stderr,
465 			    "%s: %s: unknown protocol\n",
466 			    __progname, "tcp");
467 			exit(1);
468 		}
469 		if (tp->pr_dump)
470 			(tp->pr_dump)(pcbaddr);
471 		exit(0);
472 	}
473 	/*
474 	 * Keep file descriptors open to avoid overhead
475 	 * of open/close on each call to get* routines.
476 	 */
477 	sethostent(1);
478 	setnetent(1);
479 
480 	if (iflag) {
481 		intpr(interval, nl[N_IFNET].n_value);
482 		exit(0);
483 	}
484 	if (rflag) {
485 		if (sflag)
486 			rt_stats(0, nl[N_RTSTAT].n_value);
487 		else
488 			routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value,
489 			    nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value);
490 		exit(0);
491 	}
492 	if (gflag) {
493 		if (sflag) {
494 			if (af == AF_INET || af == AF_UNSPEC)
495 				mrt_stats(nl[N_MRTPROTO].n_value,
496 				    nl[N_MRTSTAT].n_value);
497 #ifdef INET6
498 			if (af == AF_INET6 || af == AF_UNSPEC)
499 				mrt6_stats(nl[N_MRT6PROTO].n_value,
500 				    nl[N_MRT6STAT].n_value);
501 #endif
502 		}
503 		else {
504 			if (af == AF_INET || af == AF_UNSPEC)
505 				mroutepr(nl[N_MRTPROTO].n_value,
506 				    nl[N_MFCHASHTBL].n_value,
507 				    nl[N_MFCHASH].n_value,
508 				    nl[N_VIFTABLE].n_value);
509 #ifdef INET6
510 			if (af == AF_INET6 || af == AF_UNSPEC)
511 				mroute6pr(nl[N_MRT6PROTO].n_value,
512 				    nl[N_MF6CTABLE].n_value,
513 				    nl[N_MIF6TABLE].n_value);
514 #endif
515 		}
516 		exit(0);
517 	}
518 	if (af == AF_INET || af == AF_UNSPEC) {
519 		setprotoent(1);
520 		setservent(1);
521 		/* ugh, this is O(MN) ... why do we do this? */
522 		while ((p = getprotoent())) {
523 			for (tp = protox; tp->pr_name; tp++)
524 				if (strcmp(tp->pr_name, p->p_name) == 0)
525 					break;
526 			if (tp->pr_name == 0 || tp->pr_wanted == 0)
527 				continue;
528 			printproto(tp, p->p_name);
529 		}
530 		endprotoent();
531 	}
532 #ifdef INET6
533 	if (af == AF_INET6 || af == AF_UNSPEC)
534 		for (tp = ip6protox; tp->pr_name; tp++)
535 			printproto(tp, tp->pr_name);
536 #endif
537 	if (af == AF_IPX || af == AF_UNSPEC)
538 		for (tp = ipxprotox; tp->pr_name; tp++)
539 			printproto(tp, tp->pr_name);
540 	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
541 		unixpr(nl[N_UNIXSW].n_value);
542 	if (af == AF_APPLETALK || af == AF_UNSPEC)
543 		for (tp = atalkprotox; tp->pr_name; tp++)
544 			printproto(tp, tp->pr_name);
545 	exit(0);
546 }
547 
548 /*
549  * Print out protocol statistics or control blocks (per sflag).
550  * If the interface was not specifically requested, and the symbol
551  * is not in the namelist, ignore this one.
552  */
553 static void
554 printproto(struct protox *tp, char *name)
555 {
556 	void (*pr)(u_long, char *);
557 	u_char i;
558 
559 	if (sflag) {
560 		pr = tp->pr_stats;
561 		i = tp->pr_sindex;
562 	} else {
563 		pr = tp->pr_cblocks;
564 		i = tp->pr_index;
565 	}
566 	if (pr != NULL && i < sizeof(nl) / sizeof(nl[0]) &&
567 	    (nl[i].n_value || af != AF_UNSPEC))
568 		(*pr)(nl[i].n_value, name);
569 }
570 
571 /*
572  * Read kernel memory, return 0 on success.
573  */
574 int
575 kread(u_long addr, void *buf, int size)
576 {
577 
578 	if (kvm_read(kvmd, addr, buf, size) != size) {
579 		(void)fprintf(stderr, "%s: %s\n", __progname,
580 		    kvm_geterr(kvmd));
581 		return (-1);
582 	}
583 	return (0);
584 }
585 
586 char *
587 plural(int n)
588 {
589 	return (n != 1 ? "s" : "");
590 }
591 
592 char *
593 plurales(int n)
594 {
595 	return (n != 1 ? "es" : "");
596 }
597 
598 /*
599  * Find the protox for the given "well-known" name.
600  */
601 static struct protox *
602 knownname(char *name)
603 {
604 	struct protox **tpp, *tp;
605 
606 	for (tpp = protoprotox; *tpp; tpp++)
607 		for (tp = *tpp; tp->pr_name; tp++)
608 			if (strcmp(tp->pr_name, name) == 0)
609 				return (tp);
610 	return (NULL);
611 }
612 
613 /*
614  * Find the protox corresponding to name.
615  */
616 static struct protox *
617 name2protox(char *name)
618 {
619 	struct protox *tp;
620 	char **alias;			/* alias from p->aliases */
621 	struct protoent *p;
622 
623 	/*
624 	 * Try to find the name in the list of "well-known" names. If that
625 	 * fails, check if name is an alias for an Internet protocol.
626 	 */
627 	if ((tp = knownname(name)))
628 		return (tp);
629 
630 	setprotoent(1);			/* make protocol lookup cheaper */
631 	while ((p = getprotoent())) {
632 		/* assert: name not same as p->name */
633 		for (alias = p->p_aliases; *alias; alias++)
634 			if (strcmp(name, *alias) == 0) {
635 				endprotoent();
636 				return (knownname(p->p_name));
637 			}
638 	}
639 	endprotoent();
640 	return (NULL);
641 }
642 
643 static void
644 usage(void)
645 {
646 	(void)fprintf(stderr,
647 	    "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n"
648 	    "       %s [-bdgilmnqrstu] [-f address_family] [-M core] [-N system]\n"
649 	    "       %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n"
650 	    "       %s [-M core] [-N system] -P pcbaddr\n"
651 	    "       %s [-s] [-M core] [-N system] [-p protocol]\n"
652 	    "       %s [-a] [-f address_family] [-i | -I interface]\n"
653 	    "       %s [-W interface]\n",
654 	    __progname, __progname, __progname, __progname,
655 	    __progname, __progname, __progname);
656 	exit(1);
657 }
658