xref: /openbsd-src/usr.bin/netstat/main.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: main.c,v 1.81 2009/11/05 20:50:14 michele 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 #include <sys/param.h>
34 #include <sys/file.h>
35 #include <sys/protosw.h>
36 #include <sys/socket.h>
37 
38 #include <net/route.h>
39 #include <netinet/in.h>
40 
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <kvm.h>
45 #include <limits.h>
46 #include <netdb.h>
47 #include <nlist.h>
48 #include <paths.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include "netstat.h"
54 
55 struct nlist nl[] = {
56 #define N_TCBTABLE	0
57 	{ "_tcbtable" },
58 #define N_UDBTABLE	1
59 	{ "_udbtable" },
60 #define N_DDPCB		2
61 	{ "_ddpcb"},
62 #define N_UNIXSW	3
63 	{ "_unixsw" },
64 
65 #define N_MFCHASHTBL	4
66 	{ "_mfchashtbl" },
67 #define N_MFCHASH	5
68 	{ "_mfchash" },
69 #define N_VIFTABLE	6
70 	{ "_viftable" },
71 
72 #define N_MF6CTABLE	7
73 	{ "_mf6ctable" },
74 #define N_MIF6TABLE	8
75 	{ "_mif6table" },
76 
77 #define N_RTREE		9
78 	{ "_rt_tables"},
79 #define N_RTMASK	10
80 	{ "_mask_rnhead" },
81 #define N_AF2RTAFIDX	11
82 	{ "_af2rtafidx" },
83 #define N_RTBLIDMAX	12
84 	{ "_rtbl_id_max" },
85 
86 #define N_RAWIPTABLE	13
87 	{ "_rawcbtable" },
88 #define N_RAWIP6TABLE	14
89 	{ "_rawin6pcbtable" },
90 #define N_DIVBTABLE	15
91 	{ "_divbtable" },
92 #define N_DIVB6TABLE	16
93 	{ "_divb6table" },
94 
95 	{ ""}
96 };
97 
98 struct protox {
99 	u_char	pr_index;			/* index into nlist of cb head */
100 	void	(*pr_cblocks)(u_long, char *);	/* control blocks printing routine */
101 	void	(*pr_stats)(char *);		/* statistics printing routine */
102 	void	(*pr_dump)(u_long);		/* pcb printing routine */
103 	char	*pr_name;			/* well-known name */
104 } protox[] = {
105 	{ N_TCBTABLE,	protopr,	tcp_stats,	tcp_dump,	"tcp" },
106 	{ N_UDBTABLE,	protopr,	udp_stats,	NULL,		"udp" },
107 	{ N_RAWIPTABLE,	protopr,	ip_stats,	NULL,		"ip" },
108 	{ N_DIVBTABLE,	protopr,	div_stats,	NULL,		"divert" },
109 	{ -1,		NULL,		icmp_stats,	NULL,		"icmp" },
110 	{ -1,		NULL,		igmp_stats,	NULL,		"igmp" },
111 	{ -1,		NULL,		ah_stats,	NULL,		"ah" },
112 	{ -1,		NULL,		esp_stats,	NULL,		"esp" },
113 	{ -1,		NULL,		ipip_stats,	NULL,		"ipencap" },
114 	{ -1,		NULL,		etherip_stats,	NULL,		"etherip" },
115 	{ -1,		NULL,		ipcomp_stats,	NULL,		"ipcomp" },
116 	{ -1,		NULL,		carp_stats,	NULL,		"carp" },
117 	{ -1,		NULL,		pfsync_stats,	NULL,		"pfsync" },
118 	{ -1,		NULL,		pim_stats,	NULL,		"pim" },
119 	{ -1,		NULL,		pflow_stats,	NULL,		"pflow" },
120 	{ -1,		NULL,		NULL,		NULL,		NULL }
121 };
122 
123 struct protox ip6protox[] = {
124 	{ N_TCBTABLE,	ip6protopr,	NULL,		tcp_dump,	"tcp" },
125 	{ N_UDBTABLE,	ip6protopr,	NULL,		NULL,		"udp" },
126 	{ N_RAWIP6TABLE,ip6protopr,	ip6_stats,	NULL,		"ip6" },
127 	{ N_DIVB6TABLE,	ip6protopr,	div6_stats,	NULL,		"divert6" },
128 	{ -1,		NULL,		icmp6_stats,	NULL,		"icmp6" },
129 	{ -1,		NULL,		pim6_stats,	NULL,		"pim6" },
130 	{ -1,		NULL,		rip6_stats,	NULL,		"rip6" },
131 	{ -1,		NULL,		NULL,		NULL,		NULL }
132 };
133 
134 struct protox atalkprotox[] = {
135 	{ N_DDPCB,	atalkprotopr,	ddp_stats,	NULL,		"ddp" },
136 	{ -1,		NULL,		NULL,		NULL,		NULL }
137 };
138 
139 struct protox *protoprotox[] = {
140 	protox, ip6protox, atalkprotox, NULL
141 };
142 
143 static void printproto(struct protox *, char *);
144 static void usage(void);
145 static struct protox *name2protox(char *);
146 static struct protox *knownname(char *);
147 
148 kvm_t *kvmd;
149 
150 int
151 main(int argc, char *argv[])
152 {
153 	extern char *optarg;
154 	extern int optind;
155 	const char *errstr;
156 	struct protoent *p;
157 	struct protox *tp = NULL; /* for printing cblocks & stats */
158 	int ch;
159 	char *nlistf = NULL, *memf = NULL, *ep;
160 	char buf[_POSIX2_LINE_MAX];
161 	gid_t gid;
162 	u_long pcbaddr = 0;
163 	u_int tableid = 0;
164 
165 	af = AF_UNSPEC;
166 
167 	while ((ch = getopt(argc, argv, "AabdFf:gI:ilM:mN:np:P:qrsT:tuvW:w:")) != -1)
168 		switch (ch) {
169 		case 'A':
170 			Aflag = 1;
171 			break;
172 		case 'a':
173 			aflag = 1;
174 			break;
175 		case 'b':
176 			bflag = 1;
177 			break;
178 		case 'd':
179 			dflag = 1;
180 			break;
181 		case 'F':
182 			Fflag = 1;
183 			break;
184 		case 'f':
185 			if (strcmp(optarg, "inet") == 0)
186 				af = AF_INET;
187 			else if (strcmp(optarg, "inet6") == 0)
188 				af = AF_INET6;
189 			else if (strcmp(optarg, "local") == 0)
190 				af = AF_LOCAL;
191 			else if (strcmp(optarg, "unix") == 0)
192 				af = AF_UNIX;
193 			else if (strcmp(optarg, "encap") == 0)
194 				af = PF_KEY;
195 			else if (strcmp(optarg, "atalk") == 0)
196 				af = AF_APPLETALK;
197 			else if (strcmp(optarg, "mpls") == 0)
198 				af = AF_MPLS;
199 			else if (strcmp(optarg, "pflow") == 0)
200 				af = PF_PFLOW;
201 			else if (strcmp(optarg, "mask") == 0)
202 				af = 0xff;
203 			else {
204 				(void)fprintf(stderr,
205 				    "%s: %s: unknown address family\n",
206 				    __progname, optarg);
207 				exit(1);
208 			}
209 			break;
210 		case 'g':
211 			gflag = 1;
212 			break;
213 		case 'I':
214 			iflag = 1;
215 			interface = optarg;
216 			break;
217 		case 'i':
218 			iflag = 1;
219 			break;
220 		case 'l':
221 			lflag = 1;
222 			break;
223 		case 'M':
224 			memf = optarg;
225 			break;
226 		case 'm':
227 			mflag = 1;
228 			break;
229 		case 'N':
230 			nlistf = optarg;
231 			break;
232 		case 'n':
233 			nflag = 1;
234 			break;
235 		case 'p':
236 			if ((tp = name2protox(optarg)) == NULL) {
237 				(void)fprintf(stderr,
238 				    "%s: %s: unknown protocol\n",
239 				    __progname, optarg);
240 				exit(1);
241 			}
242 			pflag = 1;
243 			break;
244 		case 'P':
245 			errno = 0;
246 			pcbaddr = strtoul(optarg, &ep, 16);
247 			if (optarg[0] == '\0' || *ep != '\0' ||
248 			    errno == ERANGE) {
249 				(void)fprintf(stderr,
250 				    "%s: %s: invalid PCB address\n",
251 				    __progname, optarg);
252 				exit(1);
253 			}
254 			Pflag = 1;
255 			break;
256 		case 'q':
257 			qflag = 1;
258 			break;
259 		case 'r':
260 			rflag = 1;
261 			break;
262 		case 's':
263 			++sflag;
264 			break;
265 		case 'T':
266 			tableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
267 			if (errstr)
268 				errx(1, "invalid table id: %s", errstr);
269 			break;
270 		case 't':
271 			tflag = 1;
272 			break;
273 		case 'u':
274 			af = AF_UNIX;
275 			break;
276 		case 'v':
277 			vflag = 1;
278 			break;
279 		case 'W':
280 			Wflag = 1;
281 			interface = optarg;
282 			break;
283 		case 'w':
284 			interval = atoi(optarg);
285 			iflag = 1;
286 			break;
287 		case '?':
288 		default:
289 			usage();
290 		}
291 	argv += optind;
292 	argc -= optind;
293 
294 	/*
295 	 * Show per-interface statistics which don't need access to
296 	 * kernel memory (they're using IOCTLs)
297 	 */
298 	if (Wflag) {
299 		if (interface == NULL)
300 			usage();
301 		net80211_ifstats(interface);
302 		exit(0);
303 	}
304 
305 	/*
306 	 * Discard setgid privileges if not the running kernel so that bad
307 	 * guys can't print interesting stuff from kernel memory.
308 	 * Dumping PCB info is also restricted.
309 	 */
310 	gid = getgid();
311 	if (nlistf != NULL || memf != NULL || Pflag)
312 		if (setresgid(gid, gid, gid) == -1)
313 			err(1, "setresgid");
314 
315 	if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
316 	    buf)) == NULL) {
317 		fprintf(stderr, "%s: kvm_openfiles: %s\n", __progname, buf);
318 		exit(1);
319 	}
320 
321 	if (nlistf == NULL && memf == NULL && !Pflag)
322 		if (setresgid(gid, gid, gid) == -1)
323 			err(1, "setresgid");
324 
325 #define	BACKWARD_COMPATIBILITY
326 #ifdef	BACKWARD_COMPATIBILITY
327 	if (*argv) {
328 		if (isdigit(**argv)) {
329 			interval = atoi(*argv);
330 			if (interval <= 0)
331 				usage();
332 			++argv;
333 			iflag = 1;
334 		}
335 		if (*argv) {
336 			nlistf = *argv;
337 			if (*++argv)
338 				memf = *argv;
339 		}
340 	}
341 #endif
342 
343 	if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) {
344 		if (nlistf)
345 			fprintf(stderr, "%s: %s: no namelist\n", __progname,
346 			    nlistf);
347 		else
348 			fprintf(stderr, "%s: no namelist\n", __progname);
349 		exit(1);
350 	}
351 	if (mflag) {
352 		mbpr();
353 		exit(0);
354 	}
355 	if (pflag) {
356 		printproto(tp, tp->pr_name);
357 		exit(0);
358 	}
359 	if (Pflag) {
360 		if (tp == NULL && (tp = name2protox("tcp")) == NULL) {
361 			(void)fprintf(stderr,
362 			    "%s: %s: unknown protocol\n",
363 			    __progname, "tcp");
364 			exit(1);
365 		}
366 		if (tp->pr_dump)
367 			(tp->pr_dump)(pcbaddr);
368 		exit(0);
369 	}
370 	/*
371 	 * Keep file descriptors open to avoid overhead
372 	 * of open/close on each call to get* routines.
373 	 */
374 	sethostent(1);
375 	setnetent(1);
376 
377 	if (iflag) {
378 		intpr(interval);
379 		exit(0);
380 	}
381 	if (rflag) {
382 		if (sflag)
383 			rt_stats();
384 		else if (Aflag || nlistf != NULL || memf != NULL)
385 			routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value,
386 			    nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value,
387 			    tableid);
388 		else
389 			p_rttables(af, tableid);
390 		exit(0);
391 	}
392 	if (gflag) {
393 		if (sflag) {
394 			if (af == AF_INET || af == AF_UNSPEC)
395 				mrt_stats();
396 			if (af == AF_INET6 || af == AF_UNSPEC)
397 				mrt6_stats();
398 		} else {
399 			if (af == AF_INET || af == AF_UNSPEC)
400 				mroutepr(nl[N_MFCHASHTBL].n_value,
401 				    nl[N_MFCHASH].n_value,
402 				    nl[N_VIFTABLE].n_value);
403 			if (af == AF_INET6 || af == AF_UNSPEC)
404 				mroute6pr(nl[N_MF6CTABLE].n_value,
405 				    nl[N_MIF6TABLE].n_value);
406 		}
407 		exit(0);
408 	}
409 	if (af == AF_INET || af == AF_UNSPEC) {
410 		setprotoent(1);
411 		setservent(1);
412 		/* ugh, this is O(MN) ... why do we do this? */
413 		while ((p = getprotoent())) {
414 			for (tp = protox; tp->pr_name; tp++)
415 				if (strcmp(tp->pr_name, p->p_name) == 0)
416 					break;
417 			if (tp->pr_name == 0)
418 				continue;
419 			printproto(tp, p->p_name);
420 		}
421 		endprotoent();
422 	}
423 	if (af == PF_PFLOW || af == AF_UNSPEC) {
424 		tp = name2protox("pflow");
425 		printproto(tp, tp->pr_name);
426 	}
427 	if (af == AF_INET6 || af == AF_UNSPEC)
428 		for (tp = ip6protox; tp->pr_name; tp++)
429 			printproto(tp, tp->pr_name);
430 	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
431 		unixpr(nl[N_UNIXSW].n_value);
432 	if (af == AF_APPLETALK || af == AF_UNSPEC)
433 		for (tp = atalkprotox; tp->pr_name; tp++)
434 			printproto(tp, tp->pr_name);
435 	exit(0);
436 }
437 
438 /*
439  * Print out protocol statistics or control blocks (per sflag).
440  * If the interface was not specifically requested, and the symbol
441  * is not in the namelist, ignore this one.
442  */
443 static void
444 printproto(struct protox *tp, char *name)
445 {
446 	if (sflag) {
447 		if (tp->pr_stats != NULL)
448 			(*tp->pr_stats)(name);
449 	} else {
450 		u_char i = tp->pr_index;
451 		if (tp->pr_cblocks != NULL &&
452 		    i < sizeof(nl) / sizeof(nl[0]) &&
453 		    (nl[i].n_value || af != AF_UNSPEC))
454 			(*tp->pr_cblocks)(nl[i].n_value, name);
455 	}
456 }
457 
458 /*
459  * Read kernel memory, return 0 on success.
460  */
461 int
462 kread(u_long addr, void *buf, int size)
463 {
464 
465 	if (kvm_read(kvmd, addr, buf, size) != size) {
466 		(void)fprintf(stderr, "%s: %s\n", __progname,
467 		    kvm_geterr(kvmd));
468 		return (-1);
469 	}
470 	return (0);
471 }
472 
473 char *
474 plural(u_int64_t n)
475 {
476 	return (n != 1 ? "s" : "");
477 }
478 
479 char *
480 plurales(u_int64_t n)
481 {
482 	return (n != 1 ? "es" : "");
483 }
484 
485 /*
486  * Find the protox for the given "well-known" name.
487  */
488 static struct protox *
489 knownname(char *name)
490 {
491 	struct protox **tpp, *tp;
492 
493 	for (tpp = protoprotox; *tpp; tpp++)
494 		for (tp = *tpp; tp->pr_name; tp++)
495 			if (strcmp(tp->pr_name, name) == 0)
496 				return (tp);
497 	return (NULL);
498 }
499 
500 /*
501  * Find the protox corresponding to name.
502  */
503 static struct protox *
504 name2protox(char *name)
505 {
506 	struct protox *tp;
507 	char **alias;			/* alias from p->aliases */
508 	struct protoent *p;
509 
510 	/*
511 	 * Try to find the name in the list of "well-known" names. If that
512 	 * fails, check if name is an alias for an Internet protocol.
513 	 */
514 	if ((tp = knownname(name)))
515 		return (tp);
516 
517 	setprotoent(1);			/* make protocol lookup cheaper */
518 	while ((p = getprotoent())) {
519 		/* assert: name not same as p->name */
520 		for (alias = p->p_aliases; *alias; alias++)
521 			if (strcmp(name, *alias) == 0) {
522 				endprotoent();
523 				return (knownname(p->p_name));
524 			}
525 	}
526 	endprotoent();
527 	return (NULL);
528 }
529 
530 static void
531 usage(void)
532 {
533 	(void)fprintf(stderr,
534 	    "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n"
535 	    "       %s [-bdFgilmnqrstu] [-f address_family] [-M core] [-N system]\n"
536 	    "               [-T tableid]\n"
537 	    "       %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n"
538 	    "       %s [-M core] [-N system] -P pcbaddr\n"
539 	    "       %s [-s] [-M core] [-N system] [-p protocol]\n"
540 	    "       %s [-a] [-f address_family] [-i | -I interface]\n"
541 	    "       %s [-W interface]\n",
542 	    __progname, __progname, __progname, __progname,
543 	    __progname, __progname, __progname);
544 	exit(1);
545 }
546