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