1 /* $OpenBSD: main.c,v 1.49 2004/06/29 08:18:20 henning 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.49 2004/06/29 08:18:20 henning 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 <errno.h>
56 #include <kvm.h>
57 #include <limits.h>
58 #include <netdb.h>
59 #include <nlist.h>
60 #include <paths.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include "netstat.h"
66
67 struct nlist nl[] = {
68 #define N_MBSTAT 0
69 { "_mbstat" },
70 #define N_IPSTAT 1
71 { "_ipstat" },
72 #define N_TCBTABLE 2
73 { "_tcbtable" },
74 #define N_TCPSTAT 3
75 { "_tcpstat" },
76 #define N_UDBTABLE 4
77 { "_udbtable" },
78 #define N_UDPSTAT 5
79 { "_udpstat" },
80 #define N_IFNET 6
81 { "_ifnet" },
82 #define N_IMP 7
83 { "_imp_softc" },
84 #define N_ICMPSTAT 8
85 { "_icmpstat" },
86 #define N_RTSTAT 9
87 { "_rtstat" },
88 #define N_UNIXSW 10
89 { "_unixsw" },
90 #define N_IDP 11
91 { "_nspcb"},
92 #define N_IDPSTAT 12
93 { "_idpstat"},
94 #define N_SPPSTAT 13
95 { "_spp_istat"},
96 #define N_NSERR 14
97 { "_ns_errstat"},
98 #define N_CLNPSTAT 15
99 { "_clnp_stat"},
100 #define IN_NOTUSED 16
101 { "_tp_inpcb" },
102 #define ISO_NOTUSED 16
103 { "_tp_refinfo" },
104 #define N_TPSTAT 18
105 { "_tp_stat" },
106 #define N_ESISSTAT 19
107 { "_esis_stat"},
108 #define N_NIMP 20
109 { "_nimp"},
110 #define N_RTREE 21
111 { "_rt_tables"},
112 #define N_CLTP 22
113 { "_cltb"},
114 #define N_CLTPSTAT 23
115 { "_cltpstat"},
116 #define N_NFILE 24
117 { "_nfile" },
118 #define N_FILE 25
119 { "_file" },
120 #define N_IGMPSTAT 26
121 { "_igmpstat" },
122 #define N_MRTPROTO 27
123 { "_ip_mrtproto" },
124 #define N_MRTSTAT 28
125 { "_mrtstat" },
126 #define N_MFCHASHTBL 29
127 { "_mfchashtbl" },
128 #define N_MFCHASH 30
129 { "_mfchash" },
130 #define N_VIFTABLE 31
131 { "_viftable" },
132 #define N_IPX 32
133 { "_ipxcbtable"},
134 #define N_IPXSTAT 33
135 { "_ipxstat"},
136 #define N_SPXSTAT 34
137 { "_spx_istat"},
138 #define N_IPXERR 35
139 { "_ipx_errstat"},
140 #define N_AHSTAT 36
141 { "_ahstat"},
142 #define N_ESPSTAT 37
143 { "_espstat"},
144 #define N_IP4STAT 38
145 { "_ipipstat"},
146 #define N_DDPSTAT 39
147 { "_ddpstat"},
148 #define N_DDPCB 40
149 { "_ddpcb"},
150 #define N_ETHERIPSTAT 41
151 { "_etheripstat"},
152 #define N_IP6STAT 42
153 { "_ip6stat" },
154 #define N_ICMP6STAT 43
155 { "_icmp6stat" },
156 #define N_IPSECSTAT 44
157 { "_ipsecstat" },
158 #define N_IPSEC6STAT 45
159 { "_ipsec6stat" },
160 #define N_PIM6STAT 46
161 { "_pim6stat" },
162 #define N_MRT6PROTO 47
163 { "_ip6_mrtproto" },
164 #define N_MRT6STAT 48
165 { "_mrt6stat" },
166 #define N_MF6CTABLE 49
167 { "_mf6ctable" },
168 #define N_MIF6TABLE 50
169 { "_mif6table" },
170 #define N_MBPOOL 51
171 { "_mbpool" },
172 #define N_MCLPOOL 52
173 { "_mclpool" },
174 #define N_IPCOMPSTAT 53
175 { "_ipcompstat" },
176 #define N_RIP6STAT 54
177 { "_rip6stat" },
178 #define N_CARPSTAT 55
179 { "_carpstats" },
180 #define N_RAWIPTABLE 56
181 { "_rawcbtable" },
182 #define N_RAWIP6TABLE 57
183 { "_rawin6pcbtable" },
184 #define N_PFSYNCSTAT 58
185 { "_pfsyncstats" },
186 { ""},
187 };
188
189 struct protox {
190 u_char pr_index; /* index into nlist of cb head */
191 u_char pr_sindex; /* index into nlist of stat block */
192 u_char pr_wanted; /* 1 if wanted, 0 otherwise */
193 void (*pr_cblocks)(u_long, char *); /* control blocks printing routine */
194 void (*pr_stats)(u_long, char *); /* statistics printing routine */
195 char *pr_name; /* well-known name */
196 } protox[] = {
197 { N_TCBTABLE, N_TCPSTAT, 1, protopr,
198 tcp_stats, "tcp" },
199 { N_UDBTABLE, N_UDPSTAT, 1, protopr,
200 udp_stats, "udp" },
201 { N_RAWIPTABLE, N_IPSTAT, 1, protopr,
202 ip_stats, "ip" },
203 { -1, N_ICMPSTAT, 1, 0,
204 icmp_stats, "icmp" },
205 { -1, N_IGMPSTAT, 1, 0,
206 igmp_stats, "igmp" },
207 { -1, N_AHSTAT, 1, 0,
208 ah_stats, "ah" },
209 { -1, N_ESPSTAT, 1, 0,
210 esp_stats, "esp" },
211 { -1, N_IP4STAT, 1, 0,
212 ipip_stats, "ipencap" },
213 { -1, N_ETHERIPSTAT, 1, 0,
214 etherip_stats,"etherip" },
215 { -1, N_IPCOMPSTAT, 1, 0,
216 ipcomp_stats, "ipcomp" },
217 { -1, N_CARPSTAT, 1, 0,
218 carp_stats, "carp" },
219 { -1, N_PFSYNCSTAT, 1, 0,
220 pfsync_stats, "pfsync" },
221 { -1, -1, 0, 0,
222 0, 0 }
223 };
224
225 #ifdef INET6
226 struct protox ip6protox[] = {
227 { N_TCBTABLE, N_TCPSTAT, 1, ip6protopr,
228 0, "tcp" },
229 { N_UDBTABLE, N_UDPSTAT, 1, ip6protopr,
230 0, "udp" },
231 { N_RAWIP6TABLE,N_IP6STAT, 1, ip6protopr,
232 ip6_stats, "ip6" },
233 { -1, N_ICMP6STAT, 1, 0,
234 icmp6_stats, "icmp6" },
235 { -1, N_PIM6STAT, 1, 0,
236 pim6_stats, "pim6" },
237 { -1, N_RIP6STAT, 1, 0,
238 rip6_stats, "rip6" },
239 { -1, -1, 0, 0,
240 0, 0 }
241 };
242 #endif
243
244 struct protox ipxprotox[] = {
245 { N_IPX, N_IPXSTAT, 1, ipxprotopr,
246 ipx_stats, "ipx" },
247 { N_IPX, N_SPXSTAT, 1, ipxprotopr,
248 spx_stats, "spx" },
249 { -1, -1, 0, 0,
250 0, 0 }
251 };
252
253 struct protox atalkprotox[] = {
254 { N_DDPCB, N_DDPSTAT, 1, atalkprotopr,
255 ddp_stats, "ddp" },
256 { -1, -1, 0, 0,
257 0, 0 }
258 };
259
260 #ifndef INET6
261 struct protox *protoprotox[] = {
262 protox, ipxprotox, atalkprotox, NULL
263 };
264 #else
265 struct protox *protoprotox[] = {
266 protox, ip6protox, ipxprotox, atalkprotox, NULL
267 };
268 #endif
269
270 static void printproto(struct protox *, char *);
271 static void usage(void);
272 static struct protox *name2protox(char *);
273 static struct protox *knownname(char *);
274
275 kvm_t *kvmd;
276
277 int
main(int argc,char * argv[])278 main(int argc, char *argv[])
279 {
280 extern char *optarg;
281 extern int optind;
282 struct protoent *p;
283 struct protox *tp = NULL; /* for printing cblocks & stats */
284 int ch;
285 char *nlistf = NULL, *memf = NULL;
286 char buf[_POSIX2_LINE_MAX];
287
288 af = AF_UNSPEC;
289
290 while ((ch = getopt(argc, argv, "Aabdf:gI:ilM:mN:np:qrSstuvw:")) != -1)
291 switch (ch) {
292 case 'A':
293 Aflag = 1;
294 break;
295 case 'a':
296 aflag = 1;
297 break;
298 case 'b':
299 bflag = 1;
300 break;
301 case 'd':
302 dflag = 1;
303 break;
304 case 'f':
305 if (strcmp(optarg, "inet") == 0)
306 af = AF_INET;
307 else if (strcmp(optarg, "inet6") == 0)
308 af = AF_INET6;
309 else if (strcmp(optarg, "local") == 0)
310 af = AF_LOCAL;
311 else if (strcmp(optarg, "unix") == 0)
312 af = AF_UNIX;
313 else if (strcmp(optarg, "ipx") == 0)
314 af = AF_IPX;
315 else if (strcmp(optarg, "ns") == 0)
316 af = AF_NS;
317 else if (strcmp(optarg, "encap") == 0)
318 af = PF_KEY;
319 else if (strcmp(optarg, "atalk") == 0)
320 af = AF_APPLETALK;
321 else {
322 (void)fprintf(stderr,
323 "%s: %s: unknown address family\n",
324 __progname, optarg);
325 exit(1);
326 }
327 break;
328 case 'g':
329 gflag = 1;
330 break;
331 case 'I':
332 iflag = 1;
333 interface = optarg;
334 break;
335 case 'i':
336 iflag = 1;
337 break;
338 case 'l':
339 lflag = 1;
340 break;
341 case 'M':
342 memf = optarg;
343 break;
344 case 'm':
345 mflag = 1;
346 break;
347 case 'N':
348 nlistf = optarg;
349 break;
350 case 'n':
351 nflag = 1;
352 break;
353 case 'p':
354 if ((tp = name2protox(optarg)) == NULL) {
355 (void)fprintf(stderr,
356 "%s: %s: unknown protocol\n",
357 __progname, optarg);
358 exit(1);
359 }
360 pflag = 1;
361 break;
362 case 'q':
363 qflag = 1;
364 break;
365 case 'r':
366 rflag = 1;
367 break;
368 case 'S':
369 Sflag = 1;
370 break;
371 case 's':
372 ++sflag;
373 break;
374 case 't':
375 tflag = 1;
376 break;
377 case 'u':
378 af = AF_UNIX;
379 break;
380 case 'v':
381 vflag = 1;
382 break;
383 case 'w':
384 interval = atoi(optarg);
385 iflag = 1;
386 break;
387 case '?':
388 default:
389 usage();
390 }
391 argv += optind;
392 argc -= optind;
393
394 /*
395 * Discard setgid privileges if not the running kernel so that bad
396 * guys can't print interesting stuff from kernel memory.
397 */
398 if (nlistf != NULL || memf != NULL) {
399 setegid(getgid());
400 setgid(getgid());
401 }
402
403 if ((kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
404 buf)) == NULL) {
405 fprintf(stderr, "%s: kvm_open: %s\n", __progname, buf);
406 exit(1);
407 }
408 setegid(getgid());
409 setgid(getgid());
410
411 #define BACKWARD_COMPATIBILITY
412 #ifdef BACKWARD_COMPATIBILITY
413 if (*argv) {
414 if (isdigit(**argv)) {
415 interval = atoi(*argv);
416 if (interval <= 0)
417 usage();
418 ++argv;
419 iflag = 1;
420 }
421 if (*argv) {
422 nlistf = *argv;
423 if (*++argv)
424 memf = *argv;
425 }
426 }
427 #endif
428
429 if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) {
430 if (nlistf)
431 fprintf(stderr, "%s: %s: no namelist\n", __progname,
432 nlistf);
433 else
434 fprintf(stderr, "%s: no namelist\n", __progname);
435 exit(1);
436 }
437 if (mflag) {
438 mbpr(nl[N_MBSTAT].n_value, nl[N_MBPOOL].n_value,
439 nl[N_MCLPOOL].n_value);
440 exit(0);
441 }
442 if (pflag) {
443 printproto(tp, tp->pr_name);
444 exit(0);
445 }
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 if (iflag) {
453 intpr(interval, nl[N_IFNET].n_value);
454 exit(0);
455 }
456 if (rflag) {
457 if (sflag)
458 rt_stats(nl[N_RTSTAT].n_value);
459 else
460 routepr(nl[N_RTREE].n_value);
461 exit(0);
462 }
463 if (gflag) {
464 if (sflag) {
465 if (af == AF_INET || af == AF_UNSPEC)
466 mrt_stats(nl[N_MRTPROTO].n_value,
467 nl[N_MRTSTAT].n_value);
468 #ifdef INET6
469 if (af == AF_INET6 || af == AF_UNSPEC)
470 mrt6_stats(nl[N_MRT6PROTO].n_value,
471 nl[N_MRT6STAT].n_value);
472 #endif
473 }
474 else {
475 if (af == AF_INET || af == AF_UNSPEC)
476 mroutepr(nl[N_MRTPROTO].n_value,
477 nl[N_MFCHASHTBL].n_value,
478 nl[N_MFCHASH].n_value,
479 nl[N_VIFTABLE].n_value);
480 #ifdef INET6
481 if (af == AF_INET6 || af == AF_UNSPEC)
482 mroute6pr(nl[N_MRT6PROTO].n_value,
483 nl[N_MF6CTABLE].n_value,
484 nl[N_MIF6TABLE].n_value);
485 #endif
486 }
487 exit(0);
488 }
489 if (af == AF_INET || af == AF_UNSPEC) {
490 setprotoent(1);
491 setservent(1);
492 /* ugh, this is O(MN) ... why do we do this? */
493 while ((p = getprotoent())) {
494 for (tp = protox; tp->pr_name; tp++)
495 if (strcmp(tp->pr_name, p->p_name) == 0)
496 break;
497 if (tp->pr_name == 0 || tp->pr_wanted == 0)
498 continue;
499 printproto(tp, p->p_name);
500 }
501 endprotoent();
502 }
503 #ifdef INET6
504 if (af == AF_INET6 || af == AF_UNSPEC)
505 for (tp = ip6protox; tp->pr_name; tp++)
506 printproto(tp, tp->pr_name);
507 #endif
508 if (af == AF_IPX || af == AF_UNSPEC)
509 for (tp = ipxprotox; tp->pr_name; tp++)
510 printproto(tp, tp->pr_name);
511 if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
512 unixpr(nl[N_UNIXSW].n_value);
513 if (af == AF_APPLETALK || af == AF_UNSPEC)
514 for (tp = atalkprotox; tp->pr_name; tp++)
515 printproto(tp, tp->pr_name);
516 exit(0);
517 }
518
519 /*
520 * Print out protocol statistics or control blocks (per sflag).
521 * If the interface was not specifically requested, and the symbol
522 * is not in the namelist, ignore this one.
523 */
524 static void
printproto(struct protox * tp,char * name)525 printproto(struct protox *tp, char *name)
526 {
527 void (*pr)(u_long, char *);
528 u_char i;
529
530 if (sflag) {
531 pr = tp->pr_stats;
532 i = tp->pr_sindex;
533 } else {
534 pr = tp->pr_cblocks;
535 i = tp->pr_index;
536 }
537 if (pr != NULL && i < sizeof(nl) / sizeof(nl[0]) &&
538 (nl[i].n_value || af != AF_UNSPEC))
539 (*pr)(nl[i].n_value, name);
540 }
541
542 /*
543 * Read kernel memory, return 0 on success.
544 */
545 int
kread(u_long addr,char * buf,int size)546 kread(u_long addr, char *buf, int size)
547 {
548
549 if (kvm_read(kvmd, addr, buf, size) != size) {
550 (void)fprintf(stderr, "%s: %s\n", __progname,
551 kvm_geterr(kvmd));
552 return (-1);
553 }
554 return (0);
555 }
556
557 char *
plural(int n)558 plural(int n)
559 {
560 return (n != 1 ? "s" : "");
561 }
562
563 char *
plurales(int n)564 plurales(int n)
565 {
566 return (n != 1 ? "es" : "");
567 }
568
569 /*
570 * Find the protox for the given "well-known" name.
571 */
572 static struct protox *
knownname(char * name)573 knownname(char *name)
574 {
575 struct protox **tpp, *tp;
576
577 for (tpp = protoprotox; *tpp; tpp++)
578 for (tp = *tpp; tp->pr_name; tp++)
579 if (strcmp(tp->pr_name, name) == 0)
580 return (tp);
581 return (NULL);
582 }
583
584 /*
585 * Find the protox corresponding to name.
586 */
587 static struct protox *
name2protox(char * name)588 name2protox(char *name)
589 {
590 struct protox *tp;
591 char **alias; /* alias from p->aliases */
592 struct protoent *p;
593
594 /*
595 * Try to find the name in the list of "well-known" names. If that
596 * fails, check if name is an alias for an Internet protocol.
597 */
598 if ((tp = knownname(name)))
599 return (tp);
600
601 setprotoent(1); /* make protocol lookup cheaper */
602 while ((p = getprotoent())) {
603 /* assert: name not same as p->name */
604 for (alias = p->p_aliases; *alias; alias++)
605 if (strcmp(name, *alias) == 0) {
606 endprotoent();
607 return (knownname(p->p_name));
608 }
609 }
610 endprotoent();
611 return (NULL);
612 }
613
614 static void
usage(void)615 usage(void)
616 {
617 (void)fprintf(stderr,
618 "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", __progname);
619 (void)fprintf(stderr,
620 " %s [-bdgilmnqrSstu] [-f address_family] [-M core] [-N system]\n", __progname);
621 (void)fprintf(stderr,
622 " %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n", __progname);
623 (void)fprintf(stderr,
624 " %s [-s] [-M core] [-N system] [-p protocol]\n", __progname);
625 (void)fprintf(stderr,
626 " %s [-a] [-f address_family] [-i | -I interface]\n", __progname);
627 exit(1);
628 }
629