xref: /original-bsd/usr.bin/netstat/main.c (revision b9df2d9d)
1 /*
2  * Copyright (c) 1983, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)main.c	5.28 (Berkeley) 05/27/92";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/protosw.h>
20 #include <sys/socket.h>
21 #include <sys/file.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <nlist.h>
25 #include <kvm.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <paths.h>
32 #include "netstat.h"
33 
34 struct nlist nl[] = {
35 #define	N_MBSTAT	0
36 	{ "_mbstat" },
37 #define	N_IPSTAT	1
38 	{ "_ipstat" },
39 #define	N_TCB		2
40 	{ "_tcb" },
41 #define	N_TCPSTAT	3
42 	{ "_tcpstat" },
43 #define	N_UDB		4
44 	{ "_udb" },
45 #define	N_UDPSTAT	5
46 	{ "_udpstat" },
47 #define	N_IFNET		6
48 	{ "_ifnet" },
49 #define	N_IMP		7
50 	{ "_imp_softc" },
51 #define	N_ICMPSTAT	8
52 	{ "_icmpstat" },
53 #define	N_RTSTAT	9
54 	{ "_rtstat" },
55 #define	N_UNIXSW	10
56 	{ "_unixsw" },
57 #define N_IDP		11
58 	{ "_nspcb"},
59 #define N_IDPSTAT	12
60 	{ "_idpstat"},
61 #define N_SPPSTAT	13
62 	{ "_spp_istat"},
63 #define N_NSERR		14
64 	{ "_ns_errstat"},
65 #define	N_CLNPSTAT	15
66 	{ "_clnp_stat"},
67 #define	IN_NOTUSED	16
68 	{ "_tp_inpcb" },
69 #define	ISO_TP		17
70 	{ "_tp_refinfo" },
71 #define	N_TPSTAT	18
72 	{ "_tp_stat" },
73 #define	N_ESISSTAT	19
74 	{ "_esis_stat"},
75 #define N_NIMP		20
76 	{ "_nimp"},
77 #define N_RTREE		21
78 	{ "_rt_tables"},
79 #define N_CLTP		22
80 	{ "_cltb"},
81 #define N_CLTPSTAT	23
82 	{ "_cltpstat"},
83 	"",
84 };
85 
86 struct protox {
87 	u_char	pr_index;		/* index into nlist of cb head */
88 	u_char	pr_sindex;		/* index into nlist of stat block */
89 	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
90 	void	(*pr_cblocks)();	/* control blocks printing routine */
91 	void	(*pr_stats)();		/* statistics printing routine */
92 	char	*pr_name;		/* well-known name */
93 } protox[] = {
94 	{ N_TCB,	N_TCPSTAT,	1,	protopr,
95 	  tcp_stats,	"tcp" },
96 	{ N_UDB,	N_UDPSTAT,	1,	protopr,
97 	  udp_stats,	"udp" },
98 	{ -1,		N_IPSTAT,	1,	0,
99 	  ip_stats,	"ip" },
100 	{ -1,		N_ICMPSTAT,	1,	0,
101 	  icmp_stats,	"icmp" },
102 	{ -1,		-1,		0,	0,
103 	  0,		0 }
104 };
105 
106 struct protox nsprotox[] = {
107 	{ N_IDP,	N_IDPSTAT,	1,	nsprotopr,
108 	  idp_stats,	"idp" },
109 	{ N_IDP,	N_SPPSTAT,	1,	nsprotopr,
110 	  spp_stats,	"spp" },
111 	{ -1,		N_NSERR,	1,	0,
112 	  nserr_stats,	"ns_err" },
113 	{ -1,		-1,		0,	0,
114 	  0,		0 }
115 };
116 
117 struct protox isoprotox[] = {
118 	{ ISO_TP,	N_TPSTAT,	1,	iso_protopr,
119 	  tp_stats,	"tp" },
120 	{ N_CLTP,	N_CLTPSTAT,	1,	iso_protopr,
121 	  cltp_stats,	"cltp" },
122 	{ -1,		N_CLNPSTAT,	1,	 0,
123 	  clnp_stats,	"clnp"},
124 	{ -1,		N_ESISSTAT,	1,	 0,
125 	  esis_stats,	"esis"},
126 	{ -1,		-1,		0,	0,
127 	  0,		0 }
128 };
129 
130 struct protox *protoprotox[] = { protox, nsprotox, isoprotox, NULL };
131 
132 static void printproto __P((struct protox *, char *));
133 static void usage __P(());
134 static struct protox *name2protox __P((char *));
135 static struct protox *knownname __P((char *));
136 
137 kvm_t *kvmd;
138 
139 int
140 main(argc, argv)
141 	int argc;
142 	char **argv;
143 {
144 	extern char *optarg;
145 	extern int optind;
146 	register struct protoent *p;
147 	register struct protox *tp;	/* for printing cblocks & stats */
148 	register char *cp;
149 	int ch;
150 	char *nlistf = NULL, *memf = NULL;
151 	char buf[_POSIX2_LINE_MAX];
152 
153 	if (cp = rindex(argv[0], '/'))
154 		prog = cp + 1;
155 	else
156 		prog = argv[0];
157 	af = AF_UNSPEC;
158 
159 	while ((ch = getopt(argc, argv, "Aadf:hI:iM:mN:np:rstuw")) != EOF)
160 		switch((char)ch) {
161 		case 'A':
162 			Aflag = 1;
163 			break;
164 		case 'a':
165 			aflag = 1;
166 			break;
167 		case 'd':
168 			dflag = 1;
169 			break;
170 		case 'f':
171 			if (strcmp(optarg, "ns") == 0)
172 				af = AF_NS;
173 			else if (strcmp(optarg, "inet") == 0)
174 				af = AF_INET;
175 			else if (strcmp(optarg, "unix") == 0)
176 				af = AF_UNIX;
177 			else if (strcmp(optarg, "iso") == 0)
178 				af = AF_ISO;
179 			else {
180 				(void)fprintf(stderr,
181 				    "%s: %s: unknown address family\n",
182 				    prog, optarg);
183 				exit(1);
184 			}
185 			break;
186 		case 'h':
187 			hflag = 1;
188 			break;
189 		case 'I': {
190 			char *cp;
191 
192 			iflag = 1;
193 			for (cp = interface = optarg; isalpha(*cp); cp++)
194 				continue;
195 			unit = atoi(cp);
196 			*cp = '\0';
197 			break;
198 		}
199 		case 'i':
200 			iflag = 1;
201 			break;
202 		case 'M':
203 			memf = optarg;
204 			break;
205 		case 'm':
206 			mflag = 1;
207 			break;
208 		case 'N':
209 			nlistf = optarg;
210 			break;
211 		case 'n':
212 			nflag = 1;
213 			break;
214 		case 'p':
215 			if ((tp = name2protox(optarg)) == NULL) {
216 				(void)fprintf(stderr,
217 				    "%s: %s: unknown or uninstrumented protocol\n",
218 				    prog, optarg);
219 				exit(1);
220 			}
221 			pflag = 1;
222 			break;
223 		case 'r':
224 			rflag = 1;
225 			break;
226 		case 's':
227 			++sflag;
228 			break;
229 		case 't':
230 			tflag = 1;
231 			break;
232 		case 'u':
233 			af = AF_UNIX;
234 			break;
235 		case 'w':
236 			interval = atoi(optarg);
237 			break;
238 		case '?':
239 		default:
240 			usage();
241 		}
242 	argv += optind;
243 	argc -= optind;
244 
245 #define	BACKWARD_COMPATIBILITY
246 #ifdef	BACKWARD_COMPATIBILITY
247 	if (*argv) {
248 		if (isdigit(**argv)) {
249 			interval = atoi(*argv);
250 			if (interval <= 0)
251 				usage();
252 			++argv;
253 			iflag = 1;
254 		}
255 		if (*argv) {
256 			nlistf = *argv;
257 			if (*++argv)
258 				memf = *argv;
259 		}
260 	}
261 #endif
262 
263 	/*
264 	 * Discard setgid privileges if not the running kernel so that bad
265 	 * guys can't print interesting stuff from kernel memory.
266 	 */
267 	if (nlistf != NULL || memf != NULL)
268 		setgid(getgid());
269 
270 	if ((kvmd = kvm_open(nlistf, memf, NULL, O_RDONLY, prog)) == NULL) {
271 		fprintf(stderr, "%s: kvm_open: %s\n", prog, buf);
272 		exit(1);
273 	}
274 	if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) {
275 		if (nlistf)
276 			fprintf(stderr, "%s: %s: no namelist\n", prog, nlistf);
277 		else
278 			fprintf(stderr, "%s: no namelist\n", prog);
279 		exit(1);
280 	}
281 	if (mflag) {
282 		mbpr((off_t)nl[N_MBSTAT].n_value);
283 		exit(0);
284 	}
285 	if (pflag) {
286 		if (tp->pr_stats)
287 			(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
288 				tp->pr_name);
289 		else
290 			printf("%s: no stats routine\n", tp->pr_name);
291 		exit(0);
292 	}
293 	if (hflag) {
294 		hostpr(nl[N_IMP].n_value, nl[N_NIMP].n_value);
295 		exit(0);
296 	}
297 	/*
298 	 * Keep file descriptors open to avoid overhead
299 	 * of open/close on each call to get* routines.
300 	 */
301 	sethostent(1);
302 	setnetent(1);
303 	if (iflag) {
304 		intpr(interval, nl[N_IFNET].n_value);
305 		exit(0);
306 	}
307 	if (rflag) {
308 		if (sflag)
309 			rt_stats((off_t)nl[N_RTSTAT].n_value);
310 		else
311 			routepr((off_t)nl[N_RTREE].n_value);
312 		exit(0);
313 	}
314 	if (af == AF_INET || af == AF_UNSPEC) {
315 		setprotoent(1);
316 		setservent(1);
317 		/* ugh, this is O(MN) ... why do we do this? */
318 		while (p = getprotoent()) {
319 			for (tp = protox; tp->pr_name; tp++)
320 				if (strcmp(tp->pr_name, p->p_name) == 0)
321 					break;
322 			if (tp->pr_name == 0 || tp->pr_wanted == 0)
323 				continue;
324 			printproto(tp, p->p_name);
325 		}
326 		endprotoent();
327 	}
328 	if (af == AF_NS || af == AF_UNSPEC)
329 		for (tp = nsprotox; tp->pr_name; tp++)
330 			printproto(tp, tp->pr_name);
331 	if (af == AF_ISO || af == AF_UNSPEC)
332 		for (tp = isoprotox; tp->pr_name; tp++)
333 			printproto(tp, tp->pr_name);
334 	if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
335 		unixpr((struct protosw *)nl[N_UNIXSW].n_value);
336 	if (af == AF_UNSPEC && sflag)
337 		impstats(nl[N_IMP].n_value, nl[N_NIMP].n_value);
338 	exit(0);
339 }
340 
341 /*
342  * Print out protocol statistics or control blocks (per sflag).
343  * If the interface was not specifically requested, and the symbol
344  * is not in the namelist, ignore this one.
345  */
346 static void
347 printproto(tp, name)
348 	register struct protox *tp;
349 	char *name;
350 {
351 	void (*pr)();
352 	off_t off;
353 
354 	if (sflag) {
355 		pr = tp->pr_stats;
356 		off = nl[tp->pr_sindex].n_value;
357 	} else {
358 		pr = tp->pr_cblocks;
359 		off = nl[tp->pr_index].n_value;
360 	}
361 	if (pr != NULL && (off || af != AF_UNSPEC))
362 		(*pr)(off, name);
363 }
364 
365 /*
366  * Read kernel memory, return 0 on success.
367  */
368 int
369 kread(addr, buf, size)
370 	off_t addr;
371 	char *buf;
372 	int size;
373 {
374 
375 	if (kvm_read(kvmd, addr, buf, size) != size) {
376 		/* XXX this duplicates kvm_read's error printout */
377 		(void)fprintf(stderr, "%s: kvm_read %s\n", prog,
378 		    kvm_geterr(kvmd));
379 		return (-1);
380 	}
381 	return (0);
382 }
383 
384 char *
385 plural(n)
386 	int n;
387 {
388 	return (n != 1 ? "s" : "");
389 }
390 
391 /*
392  * Find the protox for the given "well-known" name.
393  */
394 static struct protox *
395 knownname(name)
396 	char *name;
397 {
398 	struct protox **tpp, *tp;
399 
400 	for (tpp = protoprotox; *tpp; tpp++)
401 		for (tp = *tpp; tp->pr_name; tp++)
402 			if (strcmp(tp->pr_name, name) == 0)
403 				return (tp);
404 	return (NULL);
405 }
406 
407 /*
408  * Find the protox corresponding to name.
409  */
410 static struct protox *
411 name2protox(name)
412 	char *name;
413 {
414 	struct protox *tp;
415 	char **alias;			/* alias from p->aliases */
416 	struct protoent *p;
417 
418 	/*
419 	 * Try to find the name in the list of "well-known" names. If that
420 	 * fails, check if name is an alias for an Internet protocol.
421 	 */
422 	if (tp = knownname(name))
423 		return (tp);
424 
425 	setprotoent(1);			/* make protocol lookup cheaper */
426 	while (p = getprotoent()) {
427 		/* assert: name not same as p->name */
428 		for (alias = p->p_aliases; *alias; alias++)
429 			if (strcmp(name, *alias) == 0) {
430 				endprotoent();
431 				return (knownname(p->p_name));
432 			}
433 	}
434 	endprotoent();
435 	return (NULL);
436 }
437 
438 static void
439 usage()
440 {
441 	(void)fprintf(stderr,
442 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", prog);
443 	(void)fprintf(stderr,
444 "               [-himnrs] [-f address_family] [-M core] [-N system]\n");
445 	(void)fprintf(stderr,
446 "               [-n] [-I interface] [-M core] [-N system] [-w wait]\n");
447 	(void)fprintf(stderr,
448 "               [-M core] [-N system] [-p protocol]\n");
449 	exit(1);
450 }
451