xref: /freebsd/usr.bin/netstat/main.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *	Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char const copyright[] =
36 "@(#) Copyright (c) 1983, 1988, 1993\n\
37 	Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)main.c	8.4 (Berkeley) 3/1/94";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/file.h>
50 #include <sys/protosw.h>
51 #include <sys/socket.h>
52 
53 #include <netinet/in.h>
54 
55 #include <netgraph/ng_socket.h>
56 
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <kvm.h>
61 #include <limits.h>
62 #include <netdb.h>
63 #include <nlist.h>
64 #include <paths.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include "netstat.h"
70 
71 static struct nlist nl[] = {
72 #define	N_IFNET		0
73 	{ "_ifnet" },
74 #define	N_IMP		1
75 	{ "_imp_softc" },
76 #define	N_RTSTAT	2
77 	{ "_rtstat" },
78 #define	N_UNIXSW	3
79 	{ "_localsw" },
80 #define N_IDP		4
81 	{ "_nspcb"},
82 #define N_IDPSTAT	5
83 	{ "_idpstat"},
84 #define N_SPPSTAT	6
85 	{ "_spp_istat"},
86 #define N_NSERR		7
87 	{ "_ns_errstat"},
88 #define	N_CLNPSTAT	8
89 	{ "_clnp_stat"},
90 #define	IN_NOTUSED	9
91 	{ "_tp_inpcb" },
92 #define	ISO_TP		10
93 	{ "_tp_refinfo" },
94 #define	N_TPSTAT	11
95 	{ "_tp_stat" },
96 #define	N_ESISSTAT	12
97 	{ "_esis_stat"},
98 #define N_NIMP		13
99 	{ "_nimp"},
100 #define N_RTREE		14
101 	{ "_rt_tables"},
102 #define N_CLTP		15
103 	{ "_cltb"},
104 #define N_CLTPSTAT	16
105 	{ "_cltpstat"},
106 #define	N_NFILE		17
107 	{ "_nfile" },
108 #define	N_FILE		18
109 	{ "_file" },
110 #define N_MRTSTAT	19
111 	{ "_mrtstat" },
112 #define N_MFCTABLE	20
113 	{ "_mfctable" },
114 #define N_VIFTABLE	21
115 	{ "_viftable" },
116 #define N_IPX		22
117 	{ "_ipxpcb"},
118 #define N_IPXSTAT	23
119 	{ "_ipxstat"},
120 #define N_SPXSTAT	24
121 	{ "_spx_istat"},
122 #define N_DDPSTAT	25
123 	{ "_ddpstat"},
124 #define N_DDPCB		26
125 	{ "_ddpcb"},
126 #define N_NGSOCKS	27
127 	{ "_ngsocklist"},
128 	{ "" },
129 };
130 
131 struct protox {
132 	u_char	pr_index;		/* index into nlist of cb head */
133 	u_char	pr_sindex;		/* index into nlist of stat block */
134 	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
135 	void	(*pr_cblocks)();	/* control blocks printing routine */
136 	void	(*pr_stats)();		/* statistics printing routine */
137 	char	*pr_name;		/* well-known name */
138 	int	pr_usesysctl;		/* true if we use sysctl, not kvm */
139 } protox[] = {
140 	{ -1,		-1,		1,	protopr,
141 	  tcp_stats,	"tcp",		IPPROTO_TCP },
142 	{ -1,		-1,		1,	protopr,
143 	  udp_stats,	"udp",		IPPROTO_UDP },
144 	{ -1,		-1,		1,	protopr,
145 	  NULL,		"divert",	IPPROTO_DIVERT },
146 	{ -1,		-1,		1,	protopr,
147 	  ip_stats,	"ip",		IPPROTO_RAW },
148 	{ -1,		-1,		1,	protopr,
149 	  icmp_stats,	"icmp",		IPPROTO_ICMP },
150 	{ -1,		-1,		1,	protopr,
151 	  igmp_stats,	"igmp",		IPPROTO_IGMP },
152 	{ -1,		-1,		1,	protopr,
153 	  bdg_stats,	"bdg",		1 /* bridging... */ },
154 	{ -1,		-1,		0,	0,
155 	  0,		0 }
156 };
157 
158 struct protox atalkprotox[] = {
159 	{ N_DDPCB,	N_DDPSTAT,	1,	atalkprotopr,
160 	  ddp_stats,	"ddp" },
161 	{ -1,		-1,		0,	0,
162 	  0,		0 }
163 };
164 
165 struct protox netgraphprotox[] = {
166 	{ N_NGSOCKS,	-1,		1,	netgraphprotopr,
167 	  NULL,		"ctrl" },
168 	{ N_NGSOCKS,	-1,		1,	netgraphprotopr,
169 	  NULL,		"data" },
170 	{ -1,		-1,		0,	0,
171 	  0,		0 }
172 };
173 
174 struct protox ipxprotox[] = {
175 	{ N_IPX,	N_IPXSTAT,	1,	ipxprotopr,
176 	  ipx_stats,	"ipx",		0 },
177 	{ N_IPX,	N_SPXSTAT,	1,	ipxprotopr,
178 	  spx_stats,	"spx",		0 },
179 	{ -1,		-1,		0,	0,
180 	  0,		0,		0 }
181 };
182 
183 #ifdef NS
184 struct protox nsprotox[] = {
185 	{ N_IDP,	N_IDPSTAT,	1,	nsprotopr,
186 	  idp_stats,	"idp" },
187 	{ N_IDP,	N_SPPSTAT,	1,	nsprotopr,
188 	  spp_stats,	"spp" },
189 	{ -1,		N_NSERR,	1,	0,
190 	  nserr_stats,	"ns_err" },
191 	{ -1,		-1,		0,	0,
192 	  0,		0 }
193 };
194 #endif
195 
196 #ifdef ISO
197 struct protox isoprotox[] = {
198 	{ ISO_TP,	N_TPSTAT,	1,	iso_protopr,
199 	  tp_stats,	"tp" },
200 	{ N_CLTP,	N_CLTPSTAT,	1,	iso_protopr,
201 	  cltp_stats,	"cltp" },
202 	{ -1,		N_CLNPSTAT,	1,	 0,
203 	  clnp_stats,	"clnp"},
204 	{ -1,		N_ESISSTAT,	1,	 0,
205 	  esis_stats,	"esis"},
206 	{ -1,		-1,		0,	0,
207 	  0,		0 }
208 };
209 #endif
210 
211 struct protox *protoprotox[] = { protox, ipxprotox, atalkprotox,
212 #ifdef NS
213 					 nsprotox,
214 #endif
215 #ifdef ISO
216 					 isoprotox,
217 #endif
218 					 NULL };
219 
220 static void printproto __P((struct protox *, char *));
221 static void usage __P((void));
222 static struct protox *name2protox __P((char *));
223 static struct protox *knownname __P((char *));
224 
225 static kvm_t *kvmd;
226 char *nlistf = NULL, *memf = NULL;
227 
228 int
229 main(argc, argv)
230 	int argc;
231 	char *argv[];
232 {
233 	register struct protoent *p;
234 	register struct protox *tp = NULL;  /* for printing cblocks & stats */
235 	int ch;
236 
237 	af = AF_UNSPEC;
238 
239 	while ((ch = getopt(argc, argv, "Aabdf:ghI:iM:mN:np:rstuw:")) != -1)
240 		switch(ch) {
241 		case 'A':
242 			Aflag = 1;
243 			break;
244 		case 'a':
245 			aflag = 1;
246 			break;
247 		case 'b':
248 			bflag = 1;
249 			break;
250 		case 'd':
251 			dflag = 1;
252 			break;
253 		case 'f':
254 #ifdef NS
255 			if (strcmp(optarg, "ns") == 0)
256 				af = AF_NS;
257 			else
258 #endif
259 			if (strcmp(optarg, "ipx") == 0)
260 				af = AF_IPX;
261 			else if (strcmp(optarg, "inet") == 0)
262 				af = AF_INET;
263 			else if (strcmp(optarg, "unix") == 0)
264 				af = AF_UNIX;
265 			else if (strcmp(optarg, "atalk") == 0)
266 				af = AF_APPLETALK;
267 			else if (strcmp(optarg, "ng") == 0
268 			    || strcmp(optarg, "netgraph") == 0)
269 				af = AF_NETGRAPH;
270 #ifdef ISO
271 			else if (strcmp(optarg, "iso") == 0)
272 				af = AF_ISO;
273 #endif
274 			else {
275 				errx(1, "%s: unknown address family", optarg);
276 			}
277 			break;
278 		case 'g':
279 			gflag = 1;
280 			break;
281 		case 'I': {
282 			char *cp;
283 
284 			iflag = 1;
285 			for (cp = interface = optarg; isalpha(*cp); cp++)
286 				continue;
287 			unit = atoi(cp);
288 			break;
289 		}
290 		case 'i':
291 			iflag = 1;
292 			break;
293 		case 'M':
294 			memf = optarg;
295 			break;
296 		case 'm':
297 			mflag = 1;
298 			break;
299 		case 'N':
300 			nlistf = optarg;
301 			break;
302 		case 'n':
303 			nflag = 1;
304 			break;
305 		case 'p':
306 			if ((tp = name2protox(optarg)) == NULL) {
307 				errx(1,
308 				     "%s: unknown or uninstrumented protocol",
309 				     optarg);
310 			}
311 			pflag = 1;
312 			break;
313 		case 'r':
314 			rflag = 1;
315 			break;
316 		case 's':
317 			++sflag;
318 			break;
319 		case 't':
320 			tflag = 1;
321 			break;
322 		case 'u':
323 			af = AF_UNIX;
324 			break;
325 		case 'w':
326 			interval = atoi(optarg);
327 			iflag = 1;
328 			break;
329 		case '?':
330 		default:
331 			usage();
332 		}
333 	argv += optind;
334 	argc -= optind;
335 
336 #define	BACKWARD_COMPATIBILITY
337 #ifdef	BACKWARD_COMPATIBILITY
338 	if (*argv) {
339 		if (isdigit(**argv)) {
340 			interval = atoi(*argv);
341 			if (interval <= 0)
342 				usage();
343 			++argv;
344 			iflag = 1;
345 		}
346 		if (*argv) {
347 			nlistf = *argv;
348 			if (*++argv)
349 				memf = *argv;
350 		}
351 	}
352 #endif
353 
354 	/*
355 	 * Discard setgid privileges if not the running kernel so that bad
356 	 * guys can't print interesting stuff from kernel memory.
357 	 */
358 	if (nlistf != NULL || memf != NULL)
359 		setgid(getgid());
360 
361 	if (mflag) {
362 		mbpr();
363 		exit(0);
364 	}
365 	if (pflag) {
366 		if (!tp->pr_stats) {
367 			printf("%s: no stats routine\n", tp->pr_name);
368 			exit(0);
369 		}
370 		if (tp->pr_usesysctl) {
371 			(*tp->pr_stats)(tp->pr_usesysctl, tp->pr_name);
372 		} else {
373 			kread(0, 0, 0);
374 			(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
375 					tp->pr_name);
376 		}
377 		exit(0);
378 	}
379 #if 0
380 	/*
381 	 * Keep file descriptors open to avoid overhead
382 	 * of open/close on each call to get* routines.
383 	 */
384 	sethostent(1);
385 	setnetent(1);
386 #else
387 	/*
388 	 * This does not make sense any more with DNS being default over
389 	 * the files.  Doing a setXXXXent(1) causes a tcp connection to be
390 	 * used for the queries, which is slower.
391 	 */
392 #endif
393 	if (iflag) {
394 		kread(0, 0, 0);
395 		intpr(interval, nl[N_IFNET].n_value);
396 		exit(0);
397 	}
398 	if (rflag) {
399 		kread(0, 0, 0);
400 		if (sflag)
401 			rt_stats(nl[N_RTSTAT].n_value);
402 		else
403 			routepr(nl[N_RTREE].n_value);
404 		exit(0);
405 	}
406 	if (gflag) {
407 		kread(0, 0, 0);
408 		if (sflag)
409 			mrt_stats(nl[N_MRTSTAT].n_value);
410 		else
411 			mroutepr(nl[N_MFCTABLE].n_value,
412 			    nl[N_VIFTABLE].n_value);
413 		exit(0);
414 	}
415 	if (af == AF_INET || af == AF_UNSPEC) {
416 		setprotoent(1);
417 		setservent(1);
418 		/* ugh, this is O(MN) ... why do we do this? */
419 		while ((p = getprotoent())) {
420 			for (tp = protox; tp->pr_name; tp++)
421 				if (strcmp(tp->pr_name, p->p_name) == 0)
422 					break;
423 			if (tp->pr_name == 0 || tp->pr_wanted == 0)
424 				continue;
425 			printproto(tp, p->p_name);
426 		}
427 		endprotoent();
428 	}
429 	if (af == AF_IPX || af == AF_UNSPEC) {
430 		kread(0, 0, 0);
431 		for (tp = ipxprotox; tp->pr_name; tp++)
432 			printproto(tp, tp->pr_name);
433 	}
434 	if (af == AF_APPLETALK || af == AF_UNSPEC)
435 		for (tp = atalkprotox; tp->pr_name; tp++)
436 			printproto(tp, tp->pr_name);
437 	if (af == AF_NETGRAPH || af == AF_UNSPEC)
438 		for (tp = netgraphprotox; tp->pr_name; tp++)
439 			printproto(tp, tp->pr_name);
440 #ifdef NS
441 	if (af == AF_NS || af == AF_UNSPEC)
442 		for (tp = nsprotox; tp->pr_name; tp++)
443 			printproto(tp, tp->pr_name);
444 #endif
445 #ifdef ISO
446 	if (af == AF_ISO || af == AF_UNSPEC)
447 		for (tp = isoprotox; tp->pr_name; tp++)
448 			printproto(tp, tp->pr_name);
449 #endif
450 	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
451 		unixpr();
452 	exit(0);
453 }
454 
455 /*
456  * Print out protocol statistics or control blocks (per sflag).
457  * If the interface was not specifically requested, and the symbol
458  * is not in the namelist, ignore this one.
459  */
460 static void
461 printproto(tp, name)
462 	register struct protox *tp;
463 	char *name;
464 {
465 	void (*pr)();
466 	u_long off;
467 
468 	if (sflag) {
469 		pr = tp->pr_stats;
470 		off = tp->pr_usesysctl ? tp->pr_usesysctl
471 			: nl[tp->pr_sindex].n_value;
472 	} else {
473 		pr = tp->pr_cblocks;
474 		off = tp->pr_usesysctl ? tp->pr_usesysctl
475 			: nl[tp->pr_index].n_value;
476 	}
477 	if (pr != NULL && (off || af != AF_UNSPEC))
478 		(*pr)(off, name);
479 }
480 
481 /*
482  * Read kernel memory, return 0 on success.
483  */
484 int
485 kread(addr, buf, size)
486 	u_long addr;
487 	char *buf;
488 	int size;
489 {
490 	if (kvmd == 0) {
491 		/*
492 		 * XXX.
493 		 */
494 		kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
495 		if (kvmd != NULL) {
496 			if (kvm_nlist(kvmd, nl) < 0) {
497 				if(nlistf)
498 					errx(1, "%s: kvm_nlist: %s", nlistf,
499 					     kvm_geterr(kvmd));
500 				else
501 					errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
502 			}
503 
504 			if (nl[0].n_type == 0) {
505 				if(nlistf)
506 					errx(1, "%s: no namelist", nlistf);
507 				else
508 					errx(1, "no namelist");
509 			}
510 		} else {
511 			warnx("kvm not available");
512 			return(-1);
513 		}
514 	}
515 	if (!buf)
516 		return (0);
517 	if (kvm_read(kvmd, addr, buf, size) != size) {
518 		warnx("%s", kvm_geterr(kvmd));
519 		return (-1);
520 	}
521 	return (0);
522 }
523 
524 char *
525 plural(n)
526 	int n;
527 {
528 	return (n != 1 ? "s" : "");
529 }
530 
531 char *
532 plurales(n)
533 	int n;
534 {
535 	return (n != 1 ? "es" : "");
536 }
537 
538 /*
539  * Find the protox for the given "well-known" name.
540  */
541 static struct protox *
542 knownname(name)
543 	char *name;
544 {
545 	struct protox **tpp, *tp;
546 
547 	for (tpp = protoprotox; *tpp; tpp++)
548 		for (tp = *tpp; tp->pr_name; tp++)
549 			if (strcmp(tp->pr_name, name) == 0)
550 				return (tp);
551 	return (NULL);
552 }
553 
554 /*
555  * Find the protox corresponding to name.
556  */
557 static struct protox *
558 name2protox(name)
559 	char *name;
560 {
561 	struct protox *tp;
562 	char **alias;			/* alias from p->aliases */
563 	struct protoent *p;
564 
565 	/*
566 	 * Try to find the name in the list of "well-known" names. If that
567 	 * fails, check if name is an alias for an Internet protocol.
568 	 */
569 	if ((tp = knownname(name)))
570 		return (tp);
571 
572 	setprotoent(1);			/* make protocol lookup cheaper */
573 	while ((p = getprotoent())) {
574 		/* assert: name not same as p->name */
575 		for (alias = p->p_aliases; *alias; alias++)
576 			if (strcmp(name, *alias) == 0) {
577 				endprotoent();
578 				return (knownname(p->p_name));
579 			}
580 	}
581 	endprotoent();
582 	return (NULL);
583 }
584 
585 static void
586 usage()
587 {
588 	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
589 "usage: netstat [-Aan] [-f address_family] [-M core] [-N system]",
590 "       netstat [-abdghimnrs] [-f address_family] [-M core] [-N system]",
591 "       netstat [-bdn] [-I interface] [-M core] [-N system] [-w wait]",
592 "       netstat [-M core] [-N system] [-p protocol]");
593 	exit(1);
594 }
595 
596 void
597 trimdomain(cp)
598 	char *cp;
599 {
600 	static char domain[MAXHOSTNAMELEN + 1];
601 	static int first = 1;
602 	char *s;
603 
604 	if (first) {
605 		first = 0;
606 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
607 		    (s = strchr(domain, '.')))
608 			(void) strcpy(domain, s + 1);
609 		else
610 			domain[0] = 0;
611 	}
612 
613 	if (domain[0]) {
614 		while ((cp = strchr(cp, '.'))) {
615 			if (!strcasecmp(cp + 1, domain)) {
616 				*cp = 0;	/* hit it */
617 				break;
618 			} else {
619 				cp++;
620 			}
621 		}
622 	}
623 }
624 
625