main.c revision 54263
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
35char 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
42static char sccsid[] = "@(#)main.c	8.4 (Berkeley) 3/1/94";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/usr.bin/netstat/main.c 54263 1999-12-07 17:39:16Z shin $";
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
71static 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#define N_IP6STAT	28
129	{ "_ip6stat" },
130#define N_ICMP6STAT	29
131	{ "_icmp6stat" },
132#ifdef notyet
133#define N_IPSECSTAT	30
134	{ "_ipsecstat" },
135#define N_IPSEC6STAT	31
136	{ "_ipsec6stat" },
137#define N_PIM6STAT	32
138	{ "_pim6stat" },
139#define N_MRT6PROTO	33
140	{ "_ip6_mrtproto" },
141#define N_MRT6STAT	34
142	{ "_mrt6stat" },
143#define N_MF6CTABLE	35
144	{ "_mf6ctable" },
145#define N_MIF6TABLE	36
146	{ "_mif6table" },
147#endif
148	{ "" },
149};
150
151struct protox {
152	u_char	pr_index;		/* index into nlist of cb head */
153	u_char	pr_sindex;		/* index into nlist of stat block */
154	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
155	void	(*pr_cblocks)();	/* control blocks printing routine */
156	void	(*pr_stats)();		/* statistics printing routine */
157	void	(*pr_istats)();		/* per/if statistics printing routine */
158	char	*pr_name;		/* well-known name */
159	int	pr_usesysctl;		/* true if we use sysctl, not kvm */
160} protox[] = {
161	{ -1,		-1,		1,	protopr,
162	  tcp_stats,	NULL,		"tcp",	IPPROTO_TCP },
163	{ -1,		-1,		1,	protopr,
164	  udp_stats,	NULL,		"udp",	IPPROTO_UDP },
165	{ -1,		-1,		1,	protopr,
166	  NULL,		NULL,		"divert",IPPROTO_DIVERT },
167	{ -1,		-1,		1,	protopr,
168	  ip_stats,	NULL,		"ip",	IPPROTO_RAW },
169	{ -1,		-1,		1,	protopr,
170	  icmp_stats,	NULL,		"icmp",	IPPROTO_ICMP },
171	{ -1,		-1,		1,	protopr,
172	  igmp_stats,	NULL,		"igmp",	IPPROTO_IGMP },
173#ifdef IPSEC
174	{ -1,		N_IPSECSTAT,	1,	0,
175	  ipsec_stats,	NULL,		"ipsec",	0},
176#endif
177	{ -1,		-1,		1,	protopr,
178	  bdg_stats,	NULL,		"bdg",	1 /* bridging... */ },
179	{ -1,		-1,		0,	0,
180	  0,		NULL,		0 }
181};
182
183#ifdef INET6
184struct protox ip6protox[] = {
185	{ -1,		-1,		1,	protopr,
186	  tcp_stats,	NULL,		"tcp",	IPPROTO_TCP },
187	{ -1,		-1,		1,	protopr,
188	  udp_stats,	NULL,		"udp",	IPPROTO_UDP },
189	{ -1,		N_IP6STAT,	1,	0,
190	  ip6_stats,	ip6_ifstats,	"ip6",	0 },
191	{ -1,		N_ICMP6STAT,	1,	0,
192	  icmp6_stats,	icmp6_ifstats,	"icmp6",0 },
193#ifdef IPSEC
194	{ -1,		N_IPSEC6STAT,	1,	0,
195	  ipsec_stats,	NULL,		"ipsec6",0 },
196#endif
197#ifdef notyet
198	{ -1,		N_PIM6STAT,	1,	0,
199	  pim6_stats,	NULL,		"pim6",	0 },
200#endif
201	{ -1,		-1,		1,	protopr,
202	  bdg_stats,	NULL,		"bdg",	1 /* bridging... */ },
203	{ -1,		-1,		0,	0,
204	  0,		NULL,		0,	0 }
205};
206#endif /*INET6*/
207
208struct protox atalkprotox[] = {
209	{ N_DDPCB,	N_DDPSTAT,	1,	atalkprotopr,
210	  ddp_stats,	NULL,		"ddp" },
211	{ -1,		-1,		0,	0,
212	  0,		NULL,		0 }
213};
214
215struct protox netgraphprotox[] = {
216	{ N_NGSOCKS,	-1,		1,	netgraphprotopr,
217	  NULL,		NULL,		"ctrl" },
218	{ N_NGSOCKS,	-1,		1,	netgraphprotopr,
219	  NULL,		NULL,		"data" },
220	{ -1,		NULL,		0,	0,
221	  0,		NULL,		0 }
222};
223
224struct protox ipxprotox[] = {
225	{ N_IPX,	N_IPXSTAT,	1,	ipxprotopr,
226	  ipx_stats,	NULL,		"ipx",	0 },
227	{ N_IPX,	N_SPXSTAT,	1,	ipxprotopr,
228	  spx_stats,	NULL,		"spx",	0 },
229	{ -1,		-1,		0,	0,
230	  0,		NULL,		0,	0 }
231};
232
233#ifdef NS
234struct protox nsprotox[] = {
235	{ N_IDP,	N_IDPSTAT,	1,	nsprotopr,
236	  idp_stats,	NULL,		"idp" },
237	{ N_IDP,	N_SPPSTAT,	1,	nsprotopr,
238	  spp_stats,	NULL,		"spp" },
239	{ -1,		N_NSERR,	1,	0,
240	  nserr_stats,	NULL,		"ns_err" },
241	{ -1,		-1,		0,	0,
242	  0,		NULL,		0 }
243};
244#endif
245
246#ifdef ISO
247struct protox isoprotox[] = {
248	{ ISO_TP,	N_TPSTAT,	1,	iso_protopr,
249	  tp_stats,	NULL,		"tp" },
250	{ N_CLTP,	N_CLTPSTAT,	1,	iso_protopr,
251	  cltp_stats,	NULL,		"cltp" },
252	{ -1,		N_CLNPSTAT,	1,	 0,
253	  clnp_stats,	NULL,		"clnp"},
254	{ -1,		N_ESISSTAT,	1,	 0,
255	  esis_stats,	NULL,		"esis"},
256	{ -1,		-1,		0,	0,
257	  0,		NULL,		0 }
258};
259#endif
260
261struct protox *protoprotox[] = {
262					 protox,
263#ifdef INET6
264					 ip6protox,
265#endif
266					 ipxprotox, atalkprotox,
267#ifdef NS
268					 nsprotox,
269#endif
270#ifdef ISO
271					 isoprotox,
272#endif
273					 NULL };
274
275static void printproto __P((struct protox *, char *));
276static void usage __P((void));
277static struct protox *name2protox __P((char *));
278static struct protox *knownname __P((char *));
279
280static kvm_t *kvmd;
281char *nlistf = NULL, *memf = NULL;
282
283int
284main(argc, argv)
285	int argc;
286	char *argv[];
287{
288	register struct protox *tp = NULL;  /* for printing cblocks & stats */
289	int ch;
290
291	af = AF_UNSPEC;
292
293	while ((ch = getopt(argc, argv, "Aabdf:ghI:liM:mN:np:rstuw:")) != -1)
294		switch(ch) {
295		case 'A':
296			Aflag = 1;
297			break;
298		case 'a':
299			aflag = 1;
300			break;
301		case 'b':
302			bflag = 1;
303			break;
304		case 'd':
305			dflag = 1;
306			break;
307		case 'f':
308#ifdef NS
309			if (strcmp(optarg, "ns") == 0)
310				af = AF_NS;
311			else
312#endif
313			if (strcmp(optarg, "ipx") == 0)
314				af = AF_IPX;
315			else if (strcmp(optarg, "inet") == 0)
316				af = AF_INET;
317#ifdef INET6
318			else if (strcmp(optarg, "inet6") == 0)
319				af = AF_INET6;
320#endif /*INET6*/
321			else if (strcmp(optarg, "unix") == 0)
322				af = AF_UNIX;
323			else if (strcmp(optarg, "atalk") == 0)
324				af = AF_APPLETALK;
325			else if (strcmp(optarg, "ng") == 0
326			    || strcmp(optarg, "netgraph") == 0)
327				af = AF_NETGRAPH;
328#ifdef ISO
329			else if (strcmp(optarg, "iso") == 0)
330				af = AF_ISO;
331#endif
332			else {
333				errx(1, "%s: unknown address family", optarg);
334			}
335			break;
336		case 'g':
337			gflag = 1;
338			break;
339		case 'I': {
340			char *cp;
341
342			iflag = 1;
343			for (cp = interface = optarg; isalpha(*cp); cp++)
344				continue;
345			unit = atoi(cp);
346			break;
347		}
348		case 'i':
349			iflag = 1;
350			break;
351		case 'l':
352			lflag = 1;
353			break;
354		case 'M':
355			memf = optarg;
356			break;
357		case 'm':
358			mflag = 1;
359			break;
360		case 'N':
361			nlistf = optarg;
362			break;
363		case 'n':
364			nflag = 1;
365			break;
366		case 'p':
367			if ((tp = name2protox(optarg)) == NULL) {
368				errx(1,
369				     "%s: unknown or uninstrumented protocol",
370				     optarg);
371			}
372			pflag = 1;
373			break;
374		case 'r':
375			rflag = 1;
376			break;
377		case 's':
378			++sflag;
379			break;
380		case 't':
381			tflag = 1;
382			break;
383		case 'u':
384			af = AF_UNIX;
385			break;
386		case 'w':
387			interval = atoi(optarg);
388			iflag = 1;
389			break;
390		case '?':
391		default:
392			usage();
393		}
394	argv += optind;
395	argc -= optind;
396
397#define	BACKWARD_COMPATIBILITY
398#ifdef	BACKWARD_COMPATIBILITY
399	if (*argv) {
400		if (isdigit(**argv)) {
401			interval = atoi(*argv);
402			if (interval <= 0)
403				usage();
404			++argv;
405			iflag = 1;
406		}
407		if (*argv) {
408			nlistf = *argv;
409			if (*++argv)
410				memf = *argv;
411		}
412	}
413#endif
414
415	/*
416	 * Discard setgid privileges if not the running kernel so that bad
417	 * guys can't print interesting stuff from kernel memory.
418	 */
419	if (nlistf != NULL || memf != NULL)
420		setgid(getgid());
421
422	if (mflag) {
423		mbpr();
424		exit(0);
425	}
426	if (pflag) {
427		if (iflag && tp->pr_istats) {
428			kread(0, 0, 0);
429			intpr(interval, nl[N_IFNET].n_value, tp->pr_istats);
430			exit(0);
431		}
432		if (!tp->pr_stats) {
433			printf("%s: no stats routine\n", tp->pr_name);
434			exit(0);
435		}
436		if (tp->pr_usesysctl) {
437			(*tp->pr_stats)(tp->pr_usesysctl, tp->pr_name);
438		} else {
439			kread(0, 0, 0);
440			(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
441					tp->pr_name);
442		}
443		exit(0);
444	}
445#if 0
446	/*
447	 * Keep file descriptors open to avoid overhead
448	 * of open/close on each call to get* routines.
449	 */
450	sethostent(1);
451	setnetent(1);
452#else
453	/*
454	 * This does not make sense any more with DNS being default over
455	 * the files.  Doing a setXXXXent(1) causes a tcp connection to be
456	 * used for the queries, which is slower.
457	 */
458#endif
459	if (iflag) {
460		if (af != AF_UNSPEC)
461			goto protostat;
462
463		kread(0, 0, 0);
464		intpr(interval, nl[N_IFNET].n_value, NULL);
465		exit(0);
466	}
467	if (rflag) {
468		kread(0, 0, 0);
469		if (sflag)
470			rt_stats(nl[N_RTSTAT].n_value);
471		else
472			routepr(nl[N_RTREE].n_value);
473		exit(0);
474	}
475	if (gflag) {
476		kread(0, 0, 0);
477		if (sflag) {
478			if (af == AF_INET || af == AF_UNSPEC)
479				mrt_stats(nl[N_MRTSTAT].n_value);
480#ifdef INET6
481#ifdef notyet
482			if (af == AF_INET6 || af == AF_UNSPEC)
483				mrt6_stats(nl[N_MRT6STAT].n_value);
484#endif
485#endif
486		} else {
487			if (af == AF_INET || af == AF_UNSPEC)
488				mroutepr(nl[N_MFCTABLE].n_value,
489					 nl[N_VIFTABLE].n_value);
490#ifdef INET6
491#ifdef notyet
492			if (af == AF_INET6 || af == AF_UNSPEC)
493				mroute6pr(nl[N_MF6CTABLE].n_value,
494					  nl[N_MIF6TABLE].n_value);
495#endif
496#endif
497		}
498		exit(0);
499	}
500
501  protostat:
502	kread(0, 0, 0);
503	if (af == AF_INET || af == AF_UNSPEC)
504		for (tp = protox; tp->pr_name; tp++)
505			printproto(tp, tp->pr_name);
506#ifdef INET6
507	if (af == AF_INET6 || af == AF_UNSPEC)
508		for (tp = ip6protox; tp->pr_name; tp++)
509			printproto(tp, tp->pr_name);
510#endif /*INET6*/
511	if (af == AF_IPX || af == AF_UNSPEC) {
512		kread(0, 0, 0);
513		for (tp = ipxprotox; tp->pr_name; tp++)
514			printproto(tp, tp->pr_name);
515	}
516	if (af == AF_APPLETALK || af == AF_UNSPEC)
517		for (tp = atalkprotox; tp->pr_name; tp++)
518			printproto(tp, tp->pr_name);
519	if (af == AF_NETGRAPH || af == AF_UNSPEC)
520		for (tp = netgraphprotox; tp->pr_name; tp++)
521			printproto(tp, tp->pr_name);
522#ifdef NS
523	if (af == AF_NS || af == AF_UNSPEC)
524		for (tp = nsprotox; tp->pr_name; tp++)
525			printproto(tp, tp->pr_name);
526#endif
527#ifdef ISO
528	if (af == AF_ISO || af == AF_UNSPEC)
529		for (tp = isoprotox; tp->pr_name; tp++)
530			printproto(tp, tp->pr_name);
531#endif
532	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
533		unixpr();
534	exit(0);
535}
536
537/*
538 * Print out protocol statistics or control blocks (per sflag).
539 * If the interface was not specifically requested, and the symbol
540 * is not in the namelist, ignore this one.
541 */
542static void
543printproto(tp, name)
544	register struct protox *tp;
545	char *name;
546{
547	void (*pr)();
548	u_long off;
549
550	if (sflag) {
551		if (iflag) {
552			if (tp->pr_istats)
553				intpr(interval, nl[N_IFNET].n_value,
554				      tp->pr_istats);
555			return;
556		}
557		else {
558			pr = tp->pr_stats;
559			off = tp->pr_usesysctl ? tp->pr_usesysctl
560				: nl[tp->pr_sindex].n_value;
561		}
562	} else {
563		pr = tp->pr_cblocks;
564		off = tp->pr_usesysctl ? tp->pr_usesysctl
565			: nl[tp->pr_index].n_value;
566	}
567	if (pr != NULL && (off || af != AF_UNSPEC))
568		(*pr)(off, name, af);
569}
570
571/*
572 * Read kernel memory, return 0 on success.
573 */
574int
575kread(addr, buf, size)
576	u_long addr;
577	char *buf;
578	int size;
579{
580	if (kvmd == 0) {
581		/*
582		 * XXX.
583		 */
584		kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
585		if (kvmd != NULL) {
586			if (kvm_nlist(kvmd, nl) < 0) {
587				if(nlistf)
588					errx(1, "%s: kvm_nlist: %s", nlistf,
589					     kvm_geterr(kvmd));
590				else
591					errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
592			}
593
594			if (nl[0].n_type == 0) {
595				if(nlistf)
596					errx(1, "%s: no namelist", nlistf);
597				else
598					errx(1, "no namelist");
599			}
600		} else {
601			warnx("kvm not available");
602			return(-1);
603		}
604	}
605	if (!buf)
606		return (0);
607	if (kvm_read(kvmd, addr, buf, size) != size) {
608		warnx("%s", kvm_geterr(kvmd));
609		return (-1);
610	}
611	return (0);
612}
613
614char *
615plural(n)
616	int n;
617{
618	return (n != 1 ? "s" : "");
619}
620
621char *
622plurales(n)
623	int n;
624{
625	return (n != 1 ? "es" : "");
626}
627
628/*
629 * Find the protox for the given "well-known" name.
630 */
631static struct protox *
632knownname(name)
633	char *name;
634{
635	struct protox **tpp, *tp;
636
637	for (tpp = protoprotox; *tpp; tpp++)
638		for (tp = *tpp; tp->pr_name; tp++)
639			if (strcmp(tp->pr_name, name) == 0)
640				return (tp);
641	return (NULL);
642}
643
644/*
645 * Find the protox corresponding to name.
646 */
647static struct protox *
648name2protox(name)
649	char *name;
650{
651	struct protox *tp;
652	char **alias;			/* alias from p->aliases */
653	struct protoent *p;
654
655	/*
656	 * Try to find the name in the list of "well-known" names. If that
657	 * fails, check if name is an alias for an Internet protocol.
658	 */
659	if ((tp = knownname(name)) != NULL)
660		return (tp);
661
662	setprotoent(1);			/* make protocol lookup cheaper */
663	while ((p = getprotoent()) != NULL) {
664		/* assert: name not same as p->name */
665		for (alias = p->p_aliases; *alias; alias++)
666			if (strcmp(name, *alias) == 0) {
667				endprotoent();
668				return (knownname(p->p_name));
669			}
670	}
671	endprotoent();
672	return (NULL);
673}
674
675static void
676usage()
677{
678	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
679"usage: netstat [-Aan] [-f address_family] [-M core] [-N system]",
680"       netstat [-abdghimnrs] [-f address_family] [-M core] [-N system]",
681"       netstat [-bdn] [-I interface] [-M core] [-N system] [-w wait]",
682"       netstat [-M core] [-N system] [-p protocol]");
683	exit(1);
684}
685
686void
687trimdomain(cp)
688	char *cp;
689{
690	static char domain[MAXHOSTNAMELEN + 1];
691	static int first = 1;
692	char *s;
693
694	if (first) {
695		first = 0;
696		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
697		    (s = strchr(domain, '.')))
698			(void) strcpy(domain, s + 1);
699		else
700			domain[0] = 0;
701	}
702
703	if (domain[0]) {
704		while ((cp = strchr(cp, '.'))) {
705			if (!strcasecmp(cp + 1, domain)) {
706				*cp = 0;	/* hit it */
707				break;
708			} else {
709				cp++;
710			}
711		}
712	}
713}
714
715