diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index d50db4cfd..8e9ff7ad2 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -454,6 +454,7 @@ ./usr/bin/netpgp minix-base crypto ./usr/bin/netpgpkeys minix-base crypto ./usr/bin/netpgpverify minix-base crypto +./usr/bin/netstat minix-base ./usr/bin/newgrp minix-base ./usr/bin/nice minix-base ./usr/bin/nl minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index 823cc4047..287f2e692 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -360,6 +360,7 @@ ./usr/libdata/debug/usr/bin/netpgp.debug minix-debug debug ./usr/libdata/debug/usr/bin/netpgpkeys.debug minix-debug debug ./usr/libdata/debug/usr/bin/netpgpverify.debug minix-debug debug +./usr/libdata/debug/usr/bin/netstat.debug minix-debug debug ./usr/libdata/debug/usr/bin/newgrp.debug minix-debug debug ./usr/libdata/debug/usr/bin/nice.debug minix-debug debug ./usr/libdata/debug/usr/bin/nl.debug minix-debug debug diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index 58e2aaba0..94c511ec6 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -238,6 +238,7 @@ ./usr/man/man1/netpgp.1 minix-man crypto ./usr/man/man1/netpgpkeys.1 minix-man crypto ./usr/man/man1/netpgpverify.1 minix-man crypto +./usr/man/man1/netstat.1 minix-man ./usr/man/man1/newfs_mfs.1 minix-man ./usr/man/man1/newgrp.1 minix-man ./usr/man/man1/nice.1 minix-man diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 2cd2ad802..a58fbaed3 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -19,7 +19,7 @@ SUBDIR= asa \ machine make man menuc mesg \ mkcsmapper mkdep mkesdb mkfifo mklocale mkstr mktemp \ msgc \ - nbperf newgrp nice nl nohup \ + nbperf netstat newgrp nice nl nohup \ pagesize passwd paste patch pathchk pr \ printenv printf pwhash \ renice rev rsh \ diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile new file mode 100644 index 000000000..56c7192c3 --- /dev/null +++ b/usr.bin/netstat/Makefile @@ -0,0 +1,46 @@ +# $NetBSD: Makefile,v 1.43 2015/06/06 13:48:37 joerg Exp $ +# from: @(#)Makefile 8.1 (Berkeley) 6/12/93 + +.include + +USE_FORT?= yes # setgid + +RUMPPRG=netstat +#SRCS= atalk.c bpf.c fast_ipsec.c if.c inet.c inet6.c +SRCS= atalk.c bpf.c if.c inet.c inet6.c \ + main.c mbuf.c mroute.c mroute6.c pfkey.c pfsync.c route.c \ + unix.c vtw.c rtutil.c +.if !defined(__MINIX) +BINGRP= kmem +BINMODE=2555 +.endif # !defined(__MINIX) +LDADD= -lutil -lkvm +DPADD= ${LIBUTIL} ${LIBKVM} +.if !defined(__MINIX) +CPPFLAGS+= -DIPSEC -I${.CURDIR} +.else # defined(__MINIX) +CPPFLAGS+= -I${.CURDIR} +.endif # defined(__MINIX) +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/dist/pf +CPPFLAGS+= -I${NETBSDSRCDIR}/sbin/route + +.PATH: ${.CURDIR}/../../lib/libc/gen +.PATH: ${.CURDIR}/../../lib/libc/net +.PATH: ${.CURDIR}/../../sbin/route +CPPFLAGS+= -DRUMP_ACTION +RUMPSRCS+= sysctlbyname.c sysctlgetmibinfo.c sysctlnametomib.c +RUMPSRCS+= if_indextoname.c getifaddrs.c getnameinfo.c + +.if (${USE_INET6} != "no") +CPPFLAGS+= -DINET6 +.endif + +.if defined(__MINIX) +# FIXME: compilation on ARM fails on a format string warning because of a +# mismatch between the PRIxPTR format and the uintptr_t type, which should not +# be possible. This is a bug in the ARM port, but I have no idea how to solve +# that. For now, simply do not fail the build on this error. --dcvmoole +CPPFLAGS+= -Wno-error=format +.endif # defined(__MINIX) + +.include diff --git a/usr.bin/netstat/atalk.c b/usr.bin/netstat/atalk.c new file mode 100644 index 000000000..0c91362e3 --- /dev/null +++ b/usr.bin/netstat/atalk.c @@ -0,0 +1,314 @@ +/* $NetBSD: atalk.c,v 1.16 2015/06/06 13:08:31 joerg Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from @(#)atalk.c 1.1 (Whistle) 6/6/96"; +#else +__RCSID("$NetBSD: atalk.c,v 1.16 2015/06/06 13:08:31 joerg Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "netstat.h" + +struct ddpcb ddpcb; +struct socket sockb; + +static int first = 1; + +/* + * Print a summary of connections related to a Network Systems + * protocol. For XXX, also give state of connection. + * Listening processes (aflag) are suppressed unless the + * -a (all) flag is specified. + */ + +static const char * +at_pr_net(const struct sockaddr_at *sat, int numeric) +{ + static char mybuf[50]; + + if (!numeric) { + switch (sat->sat_addr.s_net) { + case 0xffff: + return "????"; + case ATADDR_ANYNET: + return ("*"); + } + } + (void)snprintf(mybuf, sizeof(mybuf), "%hu", ntohs(sat->sat_addr.s_net)); + return (mybuf); +} + +static const char * +at_pr_host(const struct sockaddr_at *sat, int numeric) +{ + static char mybuf[50]; + + if (!numeric) { + switch (sat->sat_addr.s_node) { + case ATADDR_BCAST: + return "bcast"; + case ATADDR_ANYNODE: + return ("*"); + } + } + (void)snprintf(mybuf, sizeof(mybuf), "%d", + (unsigned int)sat->sat_addr.s_node); + return (mybuf); +} + +static const char * +at_pr_port(const struct sockaddr_at *sat) +{ + static char mybuf[50]; + + switch (sat->sat_port) { + case ATADDR_ANYPORT: + return ("*"); + case 0xff: + return "????"; + default: + (void)snprintf(mybuf, sizeof(mybuf), "%d", + (unsigned int)sat->sat_port); + return (mybuf); + } +} + +static const char * +at_pr_range(const struct sockaddr_at *sat) +{ + static char mybuf[50]; + + if (sat->sat_range.r_netrange.nr_firstnet + != sat->sat_range.r_netrange.nr_lastnet) { + (void)snprintf(mybuf, sizeof(mybuf), "%d-%d", + ntohs(sat->sat_range.r_netrange.nr_firstnet), + ntohs(sat->sat_range.r_netrange.nr_lastnet)); + } else { + (void)snprintf(mybuf, sizeof(mybuf), "%d", + ntohs(sat->sat_range.r_netrange.nr_firstnet)); + } + return (mybuf); +} + + +/* what == 0 for addr only == 3 + * 1 for net + * 2 for host + * 4 for port + * 8 for numeric only + */ +const char * +atalk_print(const struct sockaddr *sa, int what) +{ + const struct sockaddr_at *sat = (const struct sockaddr_at *) sa; + static char mybuf[50]; + int numeric = (what & 0x08); + + mybuf[0] = 0; + switch (what & 0x13) { + case 0: + mybuf[0] = 0; + break; + case 1: + (void)snprintf(mybuf, sizeof(mybuf), "%s", + at_pr_net(sat, numeric)); + break; + case 2: + (void)snprintf(mybuf, sizeof(mybuf), "%s", + at_pr_host(sat, numeric)); + break; + case 3: + (void)snprintf(mybuf, sizeof(mybuf), "%s.%s", + at_pr_net(sat, numeric), + at_pr_host(sat, numeric)); + break; + case 0x10: + (void)snprintf(mybuf, sizeof(mybuf), "%s", at_pr_range(sat)); + } + if (what & 4) { + (void)snprintf(mybuf + strlen(mybuf), + sizeof(mybuf) - strlen(mybuf), ".%s", at_pr_port(sat)); + } + return (mybuf); +} + +const char * +atalk_print2(const struct sockaddr *sa, const struct sockaddr *mask, int what) +{ + int n, l; + static char buf[100]; + const struct sockaddr_at *sat1, *sat2; + struct sockaddr_at thesockaddr; + struct sockaddr *sa2; + + sat1 = (const struct sockaddr_at *) sa; + sat2 = (const struct sockaddr_at *) mask; + sa2 = (struct sockaddr *) & thesockaddr; + + thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net & + sat2->sat_addr.s_net; + n = snprintf(buf, sizeof(buf), "%s", atalk_print(sa2, 1 | (what & 8))); + if (n >= (int)sizeof(buf)) + n = sizeof(buf) - 1; + else if (n == -1) + n = 0; /* What else can be done ? */ + if (sat2->sat_addr.s_net != 0xFFFF) { + thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net | + ~sat2->sat_addr.s_net; + l = snprintf(buf + n, sizeof(buf) - n, + "-%s", atalk_print(sa2, 1 | (what & 8))); + if (l >= (int)(sizeof(buf) - n)) + l = sizeof(buf) - n - 1; + if (l > 0) + n += l; + } + if (what & 2) { + l = snprintf(buf + n, sizeof(buf) - n, ".%s", + atalk_print(sa, what & (~1))); + if (l >= (int)(sizeof(buf) - n)) + l = sizeof(buf) - n - 1; + if (l > 0) + n += l; + } + return (buf); +} + +void +atalkprotopr(u_long off, const char *name) +{ + struct ddpcb *next; + struct ddpcb *initial; + int width = 22; + if (off == 0) + return; + if (kread(off, (char *)&initial, sizeof(struct ddpcb *)) < 0) + return; + for (next = initial; next != NULL;) { + u_long ppcb = (u_long)next; + + if (kread((u_long)next, (char *)&ddpcb, sizeof(ddpcb)) < 0) + return; + next = ddpcb.ddp_next; +#if 0 + if (!aflag && atalk_nullhost(ddpcb.ddp_lsat)) { + continue; + } +#endif + if (kread((u_long)ddpcb.ddp_socket, + (char *)&sockb, sizeof(sockb)) < 0) + return; + if (first) { + printf("Active ATALK connections"); + if (aflag) + printf(" (including servers)"); + putchar('\n'); + if (Aflag) { + width = 18; + printf("%-8.8s ", "PCB"); + } + printf("%-5.5s %-6.6s %-6.6s %*.*s %*.*s %s\n", + "Proto", "Recv-Q", "Send-Q", + -width, width, "Local Address", + -width, width, "Foreign Address", "(state)"); + first = 0; + } + if (Aflag) + printf("%8lx ", ppcb); + printf("%-5.5s %6ld %6ld ", name, sockb.so_rcv.sb_cc, + sockb.so_snd.sb_cc); + printf(" %*.*s", -width, width, + atalk_print((struct sockaddr *)&ddpcb.ddp_lsat, 7)); + printf(" %*.*s", -width, width, + atalk_print((struct sockaddr *)&ddpcb.ddp_fsat, 7)); + putchar('\n'); + } +} +#define ANY(x,y,z) \ + ((sflag==1 || (x)) ? printf("\t%llu %s%s%s\n",(unsigned long long)x,y,plural(x),z) : 0) + +/* + * Dump DDP statistics structure. + */ +void +ddp_stats(u_long off, const char *name) +{ + uint64_t ddpstat[DDP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(ddpstat); + + if (sysctlbyname("net.atalk.ddp.stats", ddpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + + ANY(ddpstat[DDP_STAT_SHORT], "packet", " with short headers "); + ANY(ddpstat[DDP_STAT_LONG], "packet", " with long headers "); + ANY(ddpstat[DDP_STAT_NOSUM], "packet", " with no checksum "); + ANY(ddpstat[DDP_STAT_TOOSHORT], "packet", " too short "); + ANY(ddpstat[DDP_STAT_BADSUM], "packet", " with bad checksum "); + ANY(ddpstat[DDP_STAT_TOOSMALL], "packet", " with not enough data "); + ANY(ddpstat[DDP_STAT_FORWARD], "packet", " forwarded "); + ANY(ddpstat[DDP_STAT_ENCAP], "packet", " encapsulated "); + ANY(ddpstat[DDP_STAT_CANTFORWARD], "packet", " rcvd for unreachable dest "); + ANY(ddpstat[DDP_STAT_NOSOCKSPACE], "packet", " dropped due to no socket space "); +} +#undef ANY diff --git a/usr.bin/netstat/bpf.c b/usr.bin/netstat/bpf.c new file mode 100644 index 000000000..6790dbed7 --- /dev/null +++ b/usr.bin/netstat/bpf.c @@ -0,0 +1,175 @@ +/* $NetBSD: bpf.c,v 1.11 2012/12/14 08:15:44 msaitoh Exp $ */ + +/* + * Copyright (c) 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Rui Paulo. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "prog_ops.h" + +void +bpf_stats(void) +{ + struct bpf_stat bpf_s; + size_t len = sizeof(bpf_s); + + if (use_sysctl) { + if (sysctlbyname("net.bpf.stats", &bpf_s, &len, NULL, 0) == -1) + err(1, "net.bpf.stats"); + + printf("bpf:\n"); + printf("\t%" PRIu64 " total packets received\n", + bpf_s.bs_recv); + printf("\t%" PRIu64 " total packets captured\n", + bpf_s.bs_capt); + printf("\t%" PRIu64 " total packets dropped\n", + bpf_s.bs_drop); + } else { + warnx("BPF stats not available via KVM."); + } +} + +void +bpf_dump(const char *bpfif) +{ + struct bpf_d_ext *dpe; + + if (use_sysctl) { + int name[CTL_MAXNAME], rc; + size_t i, sz, szproc; + u_int namelen; + void *v; + struct kinfo_proc2 p; + + /* adapted from sockstat.c by Andrew Brown */ + + sz = CTL_MAXNAME; + if (sysctlnametomib("net.bpf.peers", &name[0], &sz) == -1) + err(1, "sysctlnametomib: net.bpf.peers"); + namelen = sz; + + name[namelen++] = sizeof(*dpe); + name[namelen++] = INT_MAX; + + v = NULL; + sz = 0; + do { + rc = prog_sysctl(&name[0], namelen, + v, &sz, NULL, 0); + if (rc == -1 && errno != ENOMEM) + err(1, "sysctl: net.bpf.peers"); + if (rc == -1 && v != NULL) { + free(v); + v = NULL; + } + if (v == NULL) { + v = malloc(sz); + rc = -1; + } + if (v == NULL) + err(1, "malloc"); + } while (rc == -1); + + dpe = v; + + puts("Active BPF peers\nPID\tInt\tRecv Drop Capt" \ + " Flags Bufsize Comm"); + +#define BPFEXT(entry) dpe->entry + + for (i = 0; i < (sz / sizeof(*dpe)); i++, dpe++) { + if (bpfif && + strncmp(BPFEXT(bde_ifname), bpfif, IFNAMSIZ)) + continue; + + printf("%-7d ", BPFEXT(bde_pid)); + printf("%-7s ", + (BPFEXT(bde_ifname)[0] == '\0') ? "-" : + BPFEXT(bde_ifname)); + + printf("%-8" PRIu64 " %-8" PRIu64 " %-8" PRIu64 " ", + BPFEXT(bde_rcount), BPFEXT(bde_dcount), + BPFEXT(bde_ccount)); + + switch (BPFEXT(bde_state)) { + case BPF_IDLE: + printf("I"); + break; + case BPF_WAITING: + printf("W"); + break; + case BPF_TIMED_OUT: + printf("T"); + break; + default: + printf("-"); + break; + } + + printf("%c", BPFEXT(bde_promisc) ? 'P' : '-'); + printf("%c", BPFEXT(bde_immediate) ? 'R' : '-'); + printf("%c", BPFEXT(bde_seesent) ? 'S' : '-'); + printf("%c", BPFEXT(bde_hdrcmplt) ? 'H' : '-'); + printf(" %-8d ", BPFEXT(bde_bufsize)); + + szproc = sizeof(p); + namelen = 0; + name[namelen++] = CTL_KERN; + name[namelen++] = KERN_PROC2; + name[namelen++] = KERN_PROC_PID; + name[namelen++] = BPFEXT(bde_pid); + name[namelen++] = szproc; + name[namelen++] = 1; + + if (prog_sysctl(&name[0], namelen, &p, &szproc, + NULL, 0) == -1) + printf("-\n"); + else + printf("%s\n", p.p_comm); +#undef BPFEXT + } + free(v); + } else { + /* XXX */ + errx(1, "bpf_dump not implemented using kvm"); + } +} diff --git a/usr.bin/netstat/fast_ipsec.c b/usr.bin/netstat/fast_ipsec.c new file mode 100644 index 000000000..06e4e374f --- /dev/null +++ b/usr.bin/netstat/fast_ipsec.c @@ -0,0 +1,307 @@ +/* $NetBSD: fast_ipsec.c,v 1.20 2013/04/15 21:20:39 christos Exp $ */ +/* $FreeBSD: src/tools/tools/crypto/ipsecstats.c,v 1.1.4.1 2003/06/03 00:13:13 sam Exp $ */ + +/*- + * Copyright (c) 2003, 2004 Jonathan Stone + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/tools/tools/crypto/ipsecstats.c,v 1.1.4.1 2003/06/03 00:13:13 sam Exp $ + */ + +#include +#ifndef lint +#ifdef __NetBSD__ +__RCSID("$NetBSD: fast_ipsec.c,v 1.20 2013/04/15 21:20:39 christos Exp $"); +#endif +#endif /* not lint*/ + +/* Kernel headers required, but not included, by netstat.h */ +#include +#include + +/* Kernel headers for sysctl(3). */ +#include +#include + +/* Kernel headers for FAST_IPSEC statistics */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "netstat.h" + +/* + * Table-driven mapping from SADB algorithm codes to string names. + */ +struct alg { + int a; + const char *name; +}; +static const struct alg aalgs[] = { + { SADB_AALG_NONE, "none", }, + { SADB_AALG_MD5HMAC, "hmac-md5", }, + { SADB_AALG_SHA1HMAC, "hmac-sha1", }, + { SADB_X_AALG_MD5, "md5", }, + { SADB_X_AALG_SHA, "sha", }, + { SADB_X_AALG_NULL, "null", }, + { SADB_X_AALG_SHA2_256, "hmac-sha2-256", }, + { SADB_X_AALG_SHA2_384, "hmac-sha2-384", }, + { SADB_X_AALG_SHA2_512, "hmac-sha2-512", }, + { SADB_X_AALG_AES_XCBC_MAC, "aes-xcbc-mac", }, + { SADB_X_AALG_AES128GMAC, "aes-128-gmac", }, + { SADB_X_AALG_AES192GMAC, "aes-192-gmac", }, + { SADB_X_AALG_AES256GMAC, "aes-256-gmac", }, +}; +static const struct alg espalgs[] = { + { SADB_EALG_NONE, "none", }, + { SADB_EALG_DESCBC, "des-cbc", }, + { SADB_EALG_3DESCBC, "3des-cbc", }, + { SADB_EALG_NULL, "null", }, + { SADB_X_EALG_CAST128CBC, "cast128-cbc", }, + { SADB_X_EALG_BLOWFISHCBC, "blowfish-cbc", }, + { SADB_X_EALG_RIJNDAELCBC, "aes-cbc", }, + { SADB_X_EALG_CAMELLIACBC, "camellia-cbc", }, + { SADB_X_EALG_AESCTR, "aes-ctr", }, + { SADB_X_EALG_AESGCM16, "aes-gcm-16", }, + { SADB_X_EALG_AESGMAC, "aes-gmac", }, +}; +static const struct alg ipcompalgs[] = { + { SADB_X_CALG_NONE, "none", }, + { SADB_X_CALG_OUI, "oui", }, + { SADB_X_CALG_DEFLATE, "deflate", }, + { SADB_X_CALG_LZS, "lzs", }, +}; +#define N(a) (sizeof(a)/sizeof(a[0])) + +static const char* +algname(int a, const struct alg algs[], int nalgs) +{ + static char buf[80]; + int i; + + for (i = 0; i < nalgs; i++) + if (algs[i].a == a) + return algs[i].name; + snprintf(buf, sizeof(buf), "alg#%u", a); + return buf; +} + +/* + * Print the fast_ipsec statistics. + * Since NetBSD's netstat(1) seems not to find us for "netstat -s", + * but does(?) find KAME, be prepared to be called explicitly from + * netstat's main program for "netstat -s"; but silently do nothing + * if that happens when we are running on KAME IPsec. + */ +void +fast_ipsec_stats(u_long off, const char *name) +{ + uint64_t ipsecstats[IPSEC_NSTATS]; + uint64_t ahstats[AH_NSTATS]; + uint64_t espstats[ESP_NSTATS]; + uint64_t ipcs[IPCOMP_NSTATS]; + uint64_t ipips[IPIP_NSTATS]; + int status; + size_t slen; + int i; + + if (! use_sysctl) { + warnx("IPsec stats not available via KVM."); + return; + } + + memset(ipsecstats, 0, sizeof(ipsecstats)); + memset(ahstats, 0, sizeof(ahstats)); + memset(espstats, 0, sizeof(espstats)); + memset(ipcs, 0, sizeof(ipcs)); + memset(ipips, 0, sizeof(ipips)); + + slen = sizeof(ipsecstats); + status = sysctlbyname("net.inet.ipsec.ipsecstats", ipsecstats, &slen, + NULL, 0); + if (status < 0) { + if (errno == ENOENT) + return; + if (errno != ENOMEM) + err(1, "net.inet.ipsec.ipsecstats"); + } + + slen = sizeof (ahstats); + status = sysctlbyname("net.inet.ah.ah_stats", ahstats, &slen, NULL, 0); + if (status < 0 && errno != ENOMEM) + err(1, "net.inet.ah.ah_stats"); + + slen = sizeof (espstats); + status = sysctlbyname("net.inet.esp.esp_stats", espstats, &slen, NULL, 0); + if (status < 0 && errno != ENOMEM) + err(1, "net.inet.esp.esp_stats"); + + slen = sizeof(ipcs); + status = sysctlbyname("net.inet.ipcomp.ipcomp_stats", ipcs, &slen, NULL, 0); + if (status < 0 && errno != ENOMEM) + err(1, "net.inet.ipcomp.ipcomp_stats"); + + slen = sizeof(ipips); + status = sysctlbyname("net.inet.ipip.ipip_stats", ipips, &slen, NULL, 0); + if (status < 0 && errno != ENOMEM) + err(1, "net.inet.ipip.ipip_stats"); + + printf("(Fast) IPsec:\n"); + +#define STAT(x,fmt) if ((x) || sflag <= 1) printf("\t%"PRIu64" " fmt "\n", x) + if (ipsecstats[IPSEC_STAT_IN_POLVIO]+ipsecstats[IPSEC_STAT_OUT_POLVIO]) + printf("\t%"PRIu64" policy violations: %"PRIu64" input %"PRIu64" output\n", + ipsecstats[IPSEC_STAT_IN_POLVIO] + ipsecstats[IPSEC_STAT_OUT_POLVIO], + ipsecstats[IPSEC_STAT_IN_POLVIO], ipsecstats[IPSEC_STAT_OUT_POLVIO]); + STAT(ipsecstats[IPSEC_STAT_OUT_NOSA], "no SA found (output)"); + STAT(ipsecstats[IPSEC_STAT_OUT_NOMEM], "no memory available (output)"); + STAT(ipsecstats[IPSEC_STAT_OUT_NOROUTE], "no route available (output)"); + STAT(ipsecstats[IPSEC_STAT_OUT_INVAL], "generic errors (output)"); + STAT(ipsecstats[IPSEC_STAT_OUT_BUNDLESA], "bundled SA processed (output)"); + STAT(ipsecstats[IPSEC_STAT_SPDCACHELOOKUP], "SPD cache lookups"); + STAT(ipsecstats[IPSEC_STAT_SPDCACHEMISS], "SPD cache misses"); +#undef STAT + printf("\n"); + + printf("IPsec ah:\n"); +#define AHSTAT(x,fmt) if ((x) || sflag <= 1) printf("\t%"PRIu64" ah " fmt "\n", x) + AHSTAT(ahstats[AH_STAT_INPUT], "input packets processed"); + AHSTAT(ahstats[AH_STAT_OUTPUT], "output packets processed"); + AHSTAT(ahstats[AH_STAT_HDROPS], "headers too short"); + AHSTAT(ahstats[AH_STAT_NOPF], "headers for unsupported address family"); + AHSTAT(ahstats[AH_STAT_NOTDB], "packets with no SA"); + AHSTAT(ahstats[AH_STAT_BADKCR], "packets dropped by crypto returning NULL mbuf"); + AHSTAT(ahstats[AH_STAT_BADAUTH], "packets with bad authentication"); + AHSTAT(ahstats[AH_STAT_NOXFORM], "packets with no xform"); + AHSTAT(ahstats[AH_STAT_QFULL], "packets dropped due to queue full"); + AHSTAT(ahstats[AH_STAT_WRAP], "packets dropped for replay counter wrap"); + AHSTAT(ahstats[AH_STAT_REPLAY], "packets dropped for possible replay"); + AHSTAT(ahstats[AH_STAT_BADAUTHL],"packets dropped for bad authenticator length"); + AHSTAT(ahstats[AH_STAT_INVALID], "packets with an invalid SA"); + AHSTAT(ahstats[AH_STAT_TOOBIG], "packets too big"); + AHSTAT(ahstats[AH_STAT_PDROPS], "packets blocked due to policy"); + AHSTAT(ahstats[AH_STAT_CRYPTO], "failed crypto requests"); + AHSTAT(ahstats[AH_STAT_TUNNEL], "tunnel sanity check failures"); + + printf("\tah histogram:\n"); + for (i = 0; i < AH_ALG_MAX; i++) + if (ahstats[AH_STAT_HIST + i]) + printf("\t\tah packets with %s: %"PRIu64"\n" + , algname(i, aalgs, N(aalgs)) + , ahstats[AH_STAT_HIST + i] + ); + AHSTAT(ahstats[AH_STAT_IBYTES], "bytes received"); + AHSTAT(ahstats[AH_STAT_OBYTES], "bytes transmitted"); +#undef AHSTAT + printf("\n"); + + printf("IPsec esp:\n"); +#define ESPSTAT(x,fmt) if ((x) || sflag <= 1) printf("\t%"PRIu64" esp " fmt "\n", x) + ESPSTAT(espstats[ESP_STAT_INPUT],"input packets processed"); + ESPSTAT(espstats[ESP_STAT_OUTPUT],"output packets processed"); + ESPSTAT(espstats[ESP_STAT_HDROPS],"headers too short"); + ESPSTAT(espstats[ESP_STAT_NOPF], "headers for unsupported address family"); + ESPSTAT(espstats[ESP_STAT_NOTDB],"packets with no SA"); + ESPSTAT(espstats[ESP_STAT_BADKCR],"packets dropped by crypto returning NULL mbuf"); + ESPSTAT(espstats[ESP_STAT_QFULL],"packets dropped due to queue full"); + ESPSTAT(espstats[ESP_STAT_NOXFORM],"packets with no xform"); + ESPSTAT(espstats[ESP_STAT_BADILEN],"packets with bad ilen"); + ESPSTAT(espstats[ESP_STAT_BADENC],"packets with bad encryption"); + ESPSTAT(espstats[ESP_STAT_BADAUTH],"packets with bad authentication"); + ESPSTAT(espstats[ESP_STAT_WRAP], "packets dropped for replay counter wrap"); + ESPSTAT(espstats[ESP_STAT_REPLAY],"packets dropped for possible replay"); + ESPSTAT(espstats[ESP_STAT_INVALID],"packets with an invalid SA"); + ESPSTAT(espstats[ESP_STAT_TOOBIG],"packets too big"); + ESPSTAT(espstats[ESP_STAT_PDROPS],"packets blocked due to policy"); + ESPSTAT(espstats[ESP_STAT_CRYPTO],"failed crypto requests"); + ESPSTAT(espstats[ESP_STAT_TUNNEL],"tunnel sanity check failures"); + printf("\tesp histogram:\n"); + for (i = 0; i < ESP_ALG_MAX; i++) + if (espstats[ESP_STAT_HIST + i]) + printf("\t\tesp packets with %s: %"PRIu64"\n" + , algname(i, espalgs, N(espalgs)) + , espstats[ESP_STAT_HIST + i] + ); + ESPSTAT(espstats[ESP_STAT_IBYTES], "bytes received"); + ESPSTAT(espstats[ESP_STAT_OBYTES], "bytes transmitted"); +#undef ESPSTAT + printf("IPsec ipip:\n"); + +#define IPIPSTAT(x,fmt) \ + if ((x) || sflag <= 1) printf("\t%"PRIu64" ipip " fmt "\n", x) + IPIPSTAT(ipips[IPIP_STAT_IPACKETS],"total input packets"); + IPIPSTAT(ipips[IPIP_STAT_OPACKETS],"total output packets"); + IPIPSTAT(ipips[IPIP_STAT_HDROPS],"packets too short for header length"); + IPIPSTAT(ipips[IPIP_STAT_QFULL],"packets dropped due to queue full"); + IPIPSTAT(ipips[IPIP_STAT_PDROPS],"packets blocked due to policy"); + IPIPSTAT(ipips[IPIP_STAT_SPOOF],"IP spoofing attempts"); + IPIPSTAT(ipips[IPIP_STAT_FAMILY],"protocol family mismatched"); + IPIPSTAT(ipips[IPIP_STAT_UNSPEC],"missing tunnel-endpoint address"); + IPIPSTAT(ipips[IPIP_STAT_IBYTES],"input bytes received"); + IPIPSTAT(ipips[IPIP_STAT_OBYTES],"output bytes processed"); +#undef IPIPSTAT + + printf("IPsec ipcomp:\n"); +#define IPCOMP(x,fmt) \ + if ((x) || sflag <= 1) printf("\t%"PRIu64" ipcomp " fmt "\n", x) + + IPCOMP(ipcs[IPCOMP_STAT_HDROPS],"packets too short for header length"); + IPCOMP(ipcs[IPCOMP_STAT_NOPF], "protocol family not supported"); + IPCOMP(ipcs[IPCOMP_STAT_NOTDB], "packets with no SA"); + IPCOMP(ipcs[IPCOMP_STAT_BADKCR],"packets dropped by crypto returning NULL mbuf"); + IPCOMP(ipcs[IPCOMP_STAT_QFULL], "queue full"); + IPCOMP(ipcs[IPCOMP_STAT_NOXFORM],"no support for transform"); + IPCOMP(ipcs[IPCOMP_STAT_WRAP], "packets dropped for replay counter wrap"); + IPCOMP(ipcs[IPCOMP_STAT_INPUT], "input IPcomp packets"); + IPCOMP(ipcs[IPCOMP_STAT_OUTPUT],"output IPcomp packets"); + IPCOMP(ipcs[IPCOMP_STAT_INVALID],"packets with an invalid SA"); + IPCOMP(ipcs[IPCOMP_STAT_TOOBIG],"packets decompressed as too big"); + IPCOMP(ipcs[IPCOMP_STAT_MINLEN], "packets too short to be compressed"); + IPCOMP(ipcs[IPCOMP_STAT_USELESS],"packet for which compression was useless"); + IPCOMP(ipcs[IPCOMP_STAT_PDROPS],"packets blocked due to policy"); + IPCOMP(ipcs[IPCOMP_STAT_CRYPTO],"failed crypto requests"); + + printf("\tIPcomp histogram:\n"); + for (i = 0; i < IPCOMP_ALG_MAX; i++) + if (ipcs[IPCOMP_STAT_HIST + i]) + printf("\t\tIPcomp packets with %s: %"PRIu64"\n" + , algname(i, ipcompalgs, N(ipcompalgs)) + , ipcs[IPCOMP_STAT_HIST + i] + ); + IPCOMP(ipcs[IPCOMP_STAT_IBYTES],"input bytes"); + IPCOMP(ipcs[IPCOMP_STAT_OBYTES],"output bytes"); +#undef IPCOMP +} diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c new file mode 100644 index 000000000..3c3766d89 --- /dev/null +++ b/usr.bin/netstat/if.c @@ -0,0 +1,1050 @@ +/* $NetBSD: if.c,v 1.82 2015/09/20 00:30:04 mrg Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)if.c 8.2 (Berkeley) 2/21/94"; +#else +__RCSID("$NetBSD: if.c,v 1.82 2015/09/20 00:30:04 mrg Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netstat.h" +#include "rtutil.h" +#include "prog_ops.h" + +#define MAXIF 100 + +#define HUMBUF_SIZE 7 + +struct iftot { + char ift_name[IFNAMSIZ]; /* interface name */ + u_quad_t ift_ip; /* input packets */ + u_quad_t ift_ib; /* input bytes */ + u_quad_t ift_ie; /* input errors */ + u_quad_t ift_op; /* output packets */ + u_quad_t ift_ob; /* output bytes */ + u_quad_t ift_oe; /* output errors */ + u_quad_t ift_co; /* collisions */ + int ift_dr; /* drops */ +}; + +static void set_lines(void); +static void print_addr(struct sockaddr *, struct sockaddr **, struct if_data *, + struct ifnet *); +static void sidewaysintpr(u_int, u_long); + +static void iftot_banner(struct iftot *); +static void iftot_print_sum(struct iftot *, struct iftot *); +static void iftot_print(struct iftot *, struct iftot *); + +static void catchalarm __P((int)); +static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +static void fetchifs(void); + +static void intpr_sysctl(void); +static void intpr_kvm(u_long, void (*)(const char *)); + +struct iftot iftot[MAXIF], ip_cur, ip_old, sum_cur, sum_old; +bool signalled; /* set if alarm goes off "early" */ + +static unsigned redraw_lines = 21; + +static void +set_lines(void) +{ + static bool first = true; + struct ttysize ts; + + if (!first) + return; + first = false; + if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1 && ts.ts_lines) + redraw_lines = ts.ts_lines - 3; +} + + +/* + * Print a description of the network interfaces. + * NOTE: ifnetaddr is the location of the kernel global "ifnet", + * which is a TAILQ_HEAD. + */ +void +intpr(int interval, u_long ifnetaddr, void (*pfunc)(const char *)) +{ + + if (interval) { + sidewaysintpr((unsigned)interval, ifnetaddr); + return; + } + + if (use_sysctl) { + intpr_sysctl(); + } else { + intpr_kvm(ifnetaddr, pfunc); + } + +} + +static void +intpr_header(void) +{ + + if (!sflag & !pflag) { + if (bflag) { + printf("%-5.5s %-5.5s %-13.13s %-17.17s " + "%10.10s %10.10s", + "Name", "Mtu", "Network", "Address", + "Ibytes", "Obytes"); + } else { + printf("%-5.5s %-5.5s %-13.13s %-17.17s " + "%8.8s %5.5s %8.8s %5.5s %5.5s", + "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs", + "Opkts", "Oerrs", "Colls"); + } + if (tflag) + printf(" %4.4s", "Time"); + if (dflag) + printf(" %5.5s", "Drops"); + putchar('\n'); + } +} + +static void +intpr_sysctl(void) +{ + struct if_msghdr *ifm; + int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; + char *buf = NULL, *next, *lim, *cp; + struct rt_msghdr *rtm; + struct ifa_msghdr *ifam; + struct if_data *ifd = NULL; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl; + uint64_t total = 0; + size_t len; + char name[IFNAMSIZ + 1]; /* + 1 for `*' */ + + if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1) + err(1, "sysctl"); + if ((buf = malloc(len)) == NULL) + err(1, NULL); + if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1) + err(1, "sysctl"); + + intpr_header(); + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + total = 0; + ifm = (struct if_msghdr *)next; + ifd = &ifm->ifm_data; + + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + + sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; + if (sdl == NULL || sdl->sdl_family != AF_LINK) { + continue; + } + bzero(name, sizeof(name)); + if (sdl->sdl_nlen >= IFNAMSIZ) + memcpy(name, sdl->sdl_data, IFNAMSIZ - 1); + else if (sdl->sdl_nlen > 0) + memcpy(name, sdl->sdl_data, sdl->sdl_nlen); + + if (interface != 0 && strcmp(name, interface) != 0) + continue; + + /* mark inactive interfaces with a '*' */ + cp = strchr(name, '\0'); + if ((ifm->ifm_flags & IFF_UP) == 0) + *cp++ = '*'; + *cp = '\0'; + + if (qflag) { + total = ifd->ifi_ibytes + ifd->ifi_obytes + + ifd->ifi_ipackets + ifd->ifi_ierrors + + ifd->ifi_opackets + ifd->ifi_oerrors + + ifd->ifi_collisions; + if (tflag) + total += 0; // XXX-elad ifnet.if_timer; + if (dflag) + total += 0; // XXX-elad ifnet.if_snd.ifq_drops; + if (total == 0) + continue; + } + + printf("%-5s %-5" PRIu64, name, ifd->ifi_mtu); +#ifdef __minix + /* There should be a space between Mtu and Network. */ + printf(" "); +#endif /* __minix */ + print_addr(rti_info[RTAX_IFP], rti_info, ifd, NULL); + break; + + case RTM_NEWADDR: + if (qflag && total == 0) + continue; + if (interface != 0 && strcmp(name, interface) != 0) + continue; + ifam = (struct ifa_msghdr *)next; + if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | + RTA_BRD)) == 0) + break; + + sa = (struct sockaddr *)(ifam + 1); + + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + + printf("%-5s %-5" PRIu64, name, ifd->ifi_mtu); +#ifdef __minix + /* + * There should be a space between Mtu and Network. + * Maintainer's note: this has been fixed in NetBSD + * upstream already; drop this change as soon as it + * creates any conflict at all! --dcvmoole + */ + printf(" "); +#endif /* __minix */ + print_addr(rti_info[RTAX_IFA], rti_info, ifd, NULL); + break; + } + } +} + +union ifaddr_u { + struct ifaddr ifa; + struct in_ifaddr in; +#ifdef INET6 + struct in6_ifaddr in6; +#endif /* INET6 */ +}; + +static void +intpr_kvm(u_long ifnetaddr, void (*pfunc)(const char *)) +{ + struct ifnet ifnet; + union ifaddr_u ifaddr; + u_long ifaddraddr; + struct ifnet_head ifhead; /* TAILQ_HEAD */ + char name[IFNAMSIZ + 1]; /* + 1 for `*' */ + + if (ifnetaddr == 0) { + printf("ifnet: symbol not defined\n"); + return; + } + + /* + * Find the pointer to the first ifnet structure. Replace + * the pointer to the TAILQ_HEAD with the actual pointer + * to the first list element. + */ + if (kread(ifnetaddr, (char *)&ifhead, sizeof ifhead)) + return; + ifnetaddr = (u_long)ifhead.tqh_first; + + intpr_header(); + + ifaddraddr = 0; + while (ifnetaddr || ifaddraddr) { + char *cp; + int n; + + if (ifaddraddr == 0) { + if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet)) + return; + memmove(name, ifnet.if_xname, IFNAMSIZ); + name[IFNAMSIZ - 1] = '\0'; /* sanity */ + ifnetaddr = (u_long)ifnet.if_list.tqe_next; + if (interface != 0 && strcmp(name, interface) != 0) + continue; + cp = strchr(name, '\0'); + + if (pfunc) { + (*pfunc)(name); + continue; + } + + if ((ifnet.if_flags & IFF_UP) == 0) + *cp++ = '*'; + *cp = '\0'; + ifaddraddr = (u_long)ifnet.if_addrlist.tqh_first; + } + if (vflag) + n = strlen(name) < 5 ? 5 : strlen(name); + else + n = 5; + printf("%-*.*s %-5llu ", n, n, name, + (unsigned long long)ifnet.if_mtu); + if (ifaddraddr == 0) { + printf("%-13.13s ", "none"); + printf("%-17.17s ", "none"); + } else { + struct sockaddr *sa; + + if (kread(ifaddraddr, (char *)&ifaddr, sizeof ifaddr)) { + ifaddraddr = 0; + continue; + } +#define CP(x) ((char *)(x)) + cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) + + CP(&ifaddr); + sa = (struct sockaddr *)cp; + print_addr(sa, (void *)&ifaddr, &ifnet.if_data, &ifnet); + } + ifaddraddr = (u_long)ifaddr.ifa.ifa_list.tqe_next; + } + +} + +static void +print_addr(struct sockaddr *sa, struct sockaddr **rtinfo, struct if_data *ifd, + struct ifnet *ifnet) +{ + char hexsep = '.'; /* for hexprint */ + static const char hexfmt[] = "%02x%c"; /* for hexprint */ + char hbuf[NI_MAXHOST]; /* for getnameinfo() */ +#ifdef INET6 + const int niflag = NI_NUMERICHOST; + struct sockaddr_in6 *sin6, *netmask6; +#endif + struct sockaddr_in netmask; + struct sockaddr_in *sin; + char *cp; + int n, m; + + switch (sa->sa_family) { + case AF_UNSPEC: + printf("%-13.13s ", "none"); + printf("%-17.17s ", "none"); + break; + case AF_INET: + sin = (struct sockaddr_in *)sa; + if (use_sysctl) { + netmask = *((struct sockaddr_in *)rtinfo[RTAX_NETMASK]); + } else { + struct in_ifaddr *ifaddr_in = (void *)rtinfo; + netmask.sin_addr.s_addr = ifaddr_in->ia_subnetmask; + } + cp = netname4(sin, &netmask, nflag); + if (vflag) + n = strlen(cp) < 13 ? 13 : strlen(cp); + else + n = 13; + printf("%-*.*s ", n, n, cp); + cp = routename4(sin->sin_addr.s_addr, nflag); + if (vflag) + n = strlen(cp) < 17 ? 17 : strlen(cp); + else + n = 17; + printf("%-*.*s ", n, n, cp); + + if (aflag && ifnet) { + u_long multiaddr; + struct in_multi inm; + union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo; + + multiaddr = (u_long) + ifaddr->in.ia_multiaddrs.lh_first; + while (multiaddr != 0) { + kread(multiaddr, (char *)&inm, + sizeof inm); + printf("\n%25s %-17.17s ", "", + routename4( + inm.inm_addr.s_addr, nflag)); + multiaddr = + (u_long)inm.inm_list.le_next; + } + } + break; +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL); +#ifdef __KAME__ + if (!vflag) + sin6->sin6_scope_id = 0; +#endif + + if (use_sysctl) { + netmask6 = (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK]; + } else { + struct in6_ifaddr *ifaddr_in6 = (void *)rtinfo; + netmask6 = &ifaddr_in6->ia_prefixmask; + } + + cp = netname6(sin6, netmask6, nflag); + if (vflag) + n = strlen(cp) < 13 ? 13 : strlen(cp); + else + n = 13; + printf("%-*.*s ", n, n, cp); + if (getnameinfo((struct sockaddr *)sin6, + sin6->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, + niflag) != 0) { + strlcpy(hbuf, "?", sizeof(hbuf)); + } + cp = hbuf; + if (vflag) + n = strlen(cp) < 17 ? 17 : strlen(cp); + else + n = 17; + printf("%-*.*s ", n, n, cp); + + if (aflag && ifnet) { + u_long multiaddr; + struct in6_multi inm; + struct sockaddr_in6 as6; + union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo; + + multiaddr = (u_long) + ifaddr->in6.ia6_multiaddrs.lh_first; + while (multiaddr != 0) { + kread(multiaddr, (char *)&inm, + sizeof inm); + memset(&as6, 0, sizeof(as6)); + as6.sin6_len = sizeof(struct sockaddr_in6); + as6.sin6_family = AF_INET6; + as6.sin6_addr = inm.in6m_addr; + inet6_getscopeid(&as6, + INET6_IS_ADDR_MC_LINKLOCAL); + if (getnameinfo((struct sockaddr *)&as6, + as6.sin6_len, hbuf, + sizeof(hbuf), NULL, 0, + niflag) != 0) { + strlcpy(hbuf, "??", + sizeof(hbuf)); + } + cp = hbuf; + if (vflag) + n = strlen(cp) < 17 + ? 17 : strlen(cp); + else + n = 17; + printf("\n%25s %-*.*s ", "", + n, n, cp); + multiaddr = + (u_long)inm.in6m_entry.le_next; + } + } + break; +#endif /*INET6*/ +#ifndef SMALL + case AF_APPLETALK: + printf("atalk:%-7.7s ", + atalk_print(sa,0x10)); + printf("%-17.17s ", atalk_print(sa,0x0b)); + break; +#endif + case AF_LINK: + printf("%-13.13s ", ""); + if (getnameinfo(sa, sa->sa_len, + hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST) != 0) { + strlcpy(hbuf, "?", sizeof(hbuf)); + } + cp = hbuf; + if (vflag) + n = strlen(cp) < 17 ? 17 : strlen(cp); + else + n = 17; + printf("%-*.*s ", n, n, cp); + break; + + default: + m = printf("(%d)", sa->sa_family); + for (cp = sa->sa_len + (char *)sa; + --cp > sa->sa_data && (*cp == 0);) {} + n = cp - sa->sa_data + 1; + cp = sa->sa_data; + + while (--n >= 0) + m += printf(hexfmt, *cp++ & 0xff, + n > 0 ? hexsep : ' '); + m = 32 - m; + while (m-- > 0) + putchar(' '); + break; + } + + if (bflag) { + char humbuf[HUMBUF_SIZE]; + + if (hflag && humanize_number(humbuf, sizeof(humbuf), + ifd->ifi_ibytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf("%10s ", humbuf); + else + printf("%10llu ", (unsigned long long)ifd->ifi_ibytes); + + if (hflag && humanize_number(humbuf, sizeof(humbuf), + ifd->ifi_obytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf("%10s", humbuf); + else + printf("%10llu", (unsigned long long)ifd->ifi_obytes); + } else { + printf("%8llu %5llu %8llu %5llu %5llu", + (unsigned long long)ifd->ifi_ipackets, + (unsigned long long)ifd->ifi_ierrors, + (unsigned long long)ifd->ifi_opackets, + (unsigned long long)ifd->ifi_oerrors, + (unsigned long long)ifd->ifi_collisions); + } + if (tflag) + printf(" %4d", ifnet ? ifnet->if_timer : 0); + if (dflag) + printf(" %5d", ifnet ? ifnet->if_snd.ifq_drops : 0); + putchar('\n'); +} + +static void +iftot_banner(struct iftot *ift) +{ + if (bflag) + printf("%7.7s in %8.8s %6.6s out %5.5s", + ift->ift_name, " ", + ift->ift_name, " "); + else + printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s", + ift->ift_name, " ", + ift->ift_name, " ", " "); + if (dflag) + printf(" %5.5s", " "); + + if (bflag) + printf(" %7.7s in %8.8s %6.6s out %5.5s", + "total", " ", "total", " "); + else + printf(" %5.5s in %5.5s%5.5s out %5.5s %5.5s", + "total", " ", "total", " ", " "); + if (dflag) + printf(" %5.5s", " "); + putchar('\n'); + if (bflag) + printf("%10.10s %8.8s %10.10s %5.5s", + "bytes", " ", "bytes", " "); + else + printf("%8.8s %5.5s %8.8s %5.5s %5.5s", + "packets", "errs", "packets", "errs", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + + if (bflag) + printf(" %10.10s %8.8s %10.10s %5.5s", + "bytes", " ", "bytes", " "); + else + printf(" %8.8s %5.5s %8.8s %5.5s %5.5s", + "packets", "errs", "packets", "errs", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + putchar('\n'); + fflush(stdout); +} + +static void +iftot_print(struct iftot *cur, struct iftot *old) +{ + if (bflag) + printf("%10" PRIu64 " %8.8s %10" PRIu64 " %5.5s", + cur->ift_ib - old->ift_ib, " ", + cur->ift_ob - old->ift_ob, " "); + else + printf("%8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64, + cur->ift_ip - old->ift_ip, + cur->ift_ie - old->ift_ie, + cur->ift_op - old->ift_op, + cur->ift_oe - old->ift_oe, + cur->ift_co - old->ift_co); + if (dflag) + printf(" %5llu", + /* XXX ifnet.if_snd.ifq_drops - ip->ift_dr); */ + 0LL); +} + +static void +iftot_print_sum(struct iftot *cur, struct iftot *old) +{ + if (bflag) + printf(" %10" PRIu64 " %8.8s %10" PRIu64 " %5.5s", + cur->ift_ib - old->ift_ib, " ", + cur->ift_ob - old->ift_ob, " "); + else + printf(" %8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64, + cur->ift_ip - old->ift_ip, + cur->ift_ie - old->ift_ie, + cur->ift_op - old->ift_op, + cur->ift_oe - old->ift_oe, + cur->ift_co - old->ift_co); + + if (dflag) + printf(" %5llu", (unsigned long long)(cur->ift_dr - old->ift_dr)); +} + +__dead static void +sidewaysintpr_sysctl(unsigned interval) +{ + sigset_t emptyset; + unsigned line; + + set_lines(); + + fetchifs(); + if (ip_cur.ift_name[0] == '\0') { + fprintf(stderr, "%s: %s: unknown interface\n", + getprogname(), interface); + exit(1); + } + + (void)signal(SIGALRM, catchalarm); + signalled = 0; + (void)alarm(interval); +banner: + iftot_banner(&ip_cur); + + line = 0; + bzero(&ip_old, sizeof(ip_old)); + bzero(&sum_old, sizeof(sum_old)); +loop: + bzero(&sum_cur, sizeof(sum_cur)); + + fetchifs(); + + iftot_print(&ip_cur, &ip_old); + + ip_old = ip_cur; + + iftot_print_sum(&sum_cur, &sum_old); + + sum_old = sum_cur; + + putchar('\n'); + fflush(stdout); + line++; + sigemptyset(&emptyset); + if (!signalled) + sigsuspend(&emptyset); + signalled = 0; + (void)alarm(interval); + if (line == redraw_lines) + goto banner; + goto loop; + /*NOTREACHED*/ +} + +static void +sidewaysintpr_kvm(unsigned interval, u_long off) +{ + struct itimerval it; + struct ifnet ifnet; + u_long firstifnet; + struct iftot *ip, *total; + unsigned line; + struct iftot *lastif, *sum, *interesting; + struct ifnet_head ifhead; /* TAILQ_HEAD */ + int oldmask; + + set_lines(); + + /* + * Find the pointer to the first ifnet structure. Replace + * the pointer to the TAILQ_HEAD with the actual pointer + * to the first list element. + */ + if (kread(off, (char *)&ifhead, sizeof ifhead)) + return; + firstifnet = (u_long)ifhead.tqh_first; + + lastif = iftot; + sum = iftot + MAXIF - 1; + total = sum - 1; + interesting = (interface == NULL) ? iftot : NULL; + for (off = firstifnet, ip = iftot; off;) { + if (kread(off, (char *)&ifnet, sizeof ifnet)) + break; + memset(ip->ift_name, 0, sizeof(ip->ift_name)); + snprintf(ip->ift_name, IFNAMSIZ, "%s", ifnet.if_xname); + if (interface && strcmp(ifnet.if_xname, interface) == 0) + interesting = ip; + ip++; + if (ip >= iftot + MAXIF - 2) + break; + off = (u_long)ifnet.if_list.tqe_next; + } + if (interesting == NULL) { + fprintf(stderr, "%s: %s: unknown interface\n", + getprogname(), interface); + exit(1); + } + lastif = ip; + + (void)signal(SIGALRM, catchalarm); + signalled = false; + + it.it_interval.tv_sec = it.it_value.tv_sec = interval; + it.it_interval.tv_usec = it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + +banner: + if (bflag) + printf("%7.7s in %8.8s %6.6s out %5.5s", + interesting->ift_name, " ", + interesting->ift_name, " "); + else + printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s", + interesting->ift_name, " ", + interesting->ift_name, " ", " "); + if (dflag) + printf(" %5.5s", " "); + if (lastif - iftot > 0) { + if (bflag) + printf(" %7.7s in %8.8s %6.6s out %5.5s", + "total", " ", "total", " "); + else + printf(" %5.5s in %5.5s%5.5s out %5.5s %5.5s", + "total", " ", "total", " ", " "); + if (dflag) + printf(" %5.5s", " "); + } + for (ip = iftot; ip < iftot + MAXIF; ip++) { + ip->ift_ip = 0; + ip->ift_ib = 0; + ip->ift_ie = 0; + ip->ift_op = 0; + ip->ift_ob = 0; + ip->ift_oe = 0; + ip->ift_co = 0; + ip->ift_dr = 0; + } + putchar('\n'); + if (bflag) + printf("%10.10s %8.8s %10.10s %5.5s", + "bytes", " ", "bytes", " "); + else + printf("%8.8s %5.5s %8.8s %5.5s %5.5s", + "packets", "errs", "packets", "errs", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + if (lastif - iftot > 0) { + if (bflag) + printf(" %10.10s %8.8s %10.10s %5.5s", + "bytes", " ", "bytes", " "); + else + printf(" %8.8s %5.5s %8.8s %5.5s %5.5s", + "packets", "errs", "packets", "errs", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + } + putchar('\n'); + fflush(stdout); + line = 0; +loop: + sum->ift_ip = 0; + sum->ift_ib = 0; + sum->ift_ie = 0; + sum->ift_op = 0; + sum->ift_ob = 0; + sum->ift_oe = 0; + sum->ift_co = 0; + sum->ift_dr = 0; + for (off = firstifnet, ip = iftot; off && ip < lastif; ip++) { + if (kread(off, (char *)&ifnet, sizeof ifnet)) { + off = 0; + continue; + } + if (ip == interesting) { + if (bflag) { + char humbuf[HUMBUF_SIZE]; + + if (hflag && humanize_number(humbuf, + sizeof(humbuf), + ifnet.if_ibytes - ip->ift_ib, "", + HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf("%10s %8.8s ", humbuf, " "); + else + printf("%10llu %8.8s ", + (unsigned long long) + (ifnet.if_ibytes-ip->ift_ib), " "); + + if (hflag && humanize_number(humbuf, + sizeof(humbuf), + ifnet.if_obytes - ip->ift_ob, "", + HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf("%10s %5.5s", humbuf, " "); + else + printf("%10llu %5.5s", + (unsigned long long) + (ifnet.if_obytes-ip->ift_ob), " "); + } else { + printf("%8llu %5llu %8llu %5llu %5llu", + (unsigned long long) + (ifnet.if_ipackets - ip->ift_ip), + (unsigned long long) + (ifnet.if_ierrors - ip->ift_ie), + (unsigned long long) + (ifnet.if_opackets - ip->ift_op), + (unsigned long long) + (ifnet.if_oerrors - ip->ift_oe), + (unsigned long long) + (ifnet.if_collisions - ip->ift_co)); + } + if (dflag) + printf(" %5llu", + (unsigned long long) + (ifnet.if_snd.ifq_drops - ip->ift_dr)); + } + ip->ift_ip = ifnet.if_ipackets; + ip->ift_ib = ifnet.if_ibytes; + ip->ift_ie = ifnet.if_ierrors; + ip->ift_op = ifnet.if_opackets; + ip->ift_ob = ifnet.if_obytes; + ip->ift_oe = ifnet.if_oerrors; + ip->ift_co = ifnet.if_collisions; + ip->ift_dr = ifnet.if_snd.ifq_drops; + sum->ift_ip += ip->ift_ip; + sum->ift_ib += ip->ift_ib; + sum->ift_ie += ip->ift_ie; + sum->ift_op += ip->ift_op; + sum->ift_ob += ip->ift_ob; + sum->ift_oe += ip->ift_oe; + sum->ift_co += ip->ift_co; + sum->ift_dr += ip->ift_dr; + off = (u_long)ifnet.if_list.tqe_next; + } + if (lastif - iftot > 0) { + if (bflag) { + char humbuf[HUMBUF_SIZE]; + + if (hflag && humanize_number(humbuf, + sizeof(humbuf), sum->ift_ib - total->ift_ib, "", + HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf(" %10s %8.8s ", humbuf, " "); + else + printf(" %10llu %8.8s ", + (unsigned long long) + (sum->ift_ib - total->ift_ib), " "); + + if (hflag && humanize_number(humbuf, + sizeof(humbuf), sum->ift_ob - total->ift_ob, "", + HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0) + printf("%10s %5.5s", humbuf, " "); + else + printf("%10llu %5.5s", + (unsigned long long) + (sum->ift_ob - total->ift_ob), " "); + } else { + printf(" %8llu %5llu %8llu %5llu %5llu", + (unsigned long long) + (sum->ift_ip - total->ift_ip), + (unsigned long long) + (sum->ift_ie - total->ift_ie), + (unsigned long long) + (sum->ift_op - total->ift_op), + (unsigned long long) + (sum->ift_oe - total->ift_oe), + (unsigned long long) + (sum->ift_co - total->ift_co)); + } + if (dflag) + printf(" %5llu", + (unsigned long long)(sum->ift_dr - total->ift_dr)); + } + *total = *sum; + putchar('\n'); + fflush(stdout); + line++; + oldmask = sigblock(sigmask(SIGALRM)); + if (! signalled) { + sigpause(0); + } + sigsetmask(oldmask); + signalled = false; + if (line == redraw_lines) + goto banner; + goto loop; + /*NOTREACHED*/ +} + +/* + * Print a running summary of interface statistics. + * Repeat display every interval seconds, showing statistics + * collected over that interval. Assumes that interval is non-zero. + * First line printed at top of screen is always cumulative. + */ +static void +sidewaysintpr(unsigned int interval, u_long off) +{ + + if (use_sysctl) { + sidewaysintpr_sysctl(interval); + } else { + sidewaysintpr_kvm(interval, off); + } +} + +/* + * Called if an interval expires before sidewaysintpr has completed a loop. + * Sets a flag to not wait for the alarm. + */ +static void +catchalarm(int signo) +{ + + signalled = true; +} + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + RT_ROUNDUP(sa->sa_len)); + } else + rti_info[i] = NULL; + } +} + +static void +fetchifs(void) +{ + struct if_msghdr *ifm; + int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; + struct rt_msghdr *rtm; + struct if_data *ifd = NULL; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl; + char *buf, *next, *lim; + char name[IFNAMSIZ]; + size_t len; + + if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1) + err(1, "sysctl"); + if ((buf = malloc(len)) == NULL) + err(1, NULL); + if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1) + err(1, "sysctl"); + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)next; + ifd = &ifm->ifm_data; + + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + + sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; + if (sdl == NULL || sdl->sdl_family != AF_LINK) + continue; + bzero(name, sizeof(name)); + if (sdl->sdl_nlen >= IFNAMSIZ) + memcpy(name, sdl->sdl_data, IFNAMSIZ - 1); + else if (sdl->sdl_nlen > 0) + memcpy(name, sdl->sdl_data, sdl->sdl_nlen); + + if (interface != 0 && !strcmp(name, interface)) { + strlcpy(ip_cur.ift_name, name, + sizeof(ip_cur.ift_name)); + ip_cur.ift_ip = ifd->ifi_ipackets; + ip_cur.ift_ib = ifd->ifi_ibytes; + ip_cur.ift_ie = ifd->ifi_ierrors; + ip_cur.ift_op = ifd->ifi_opackets; + ip_cur.ift_ob = ifd->ifi_obytes; + ip_cur.ift_oe = ifd->ifi_oerrors; + ip_cur.ift_co = ifd->ifi_collisions; + ip_cur.ift_dr = 0; + /* XXX-elad ifnet.if_snd.ifq_drops */ + } + + sum_cur.ift_ip += ifd->ifi_ipackets; + sum_cur.ift_ib += ifd->ifi_ibytes; + sum_cur.ift_ie += ifd->ifi_ierrors; + sum_cur.ift_op += ifd->ifi_opackets; + sum_cur.ift_ob += ifd->ifi_obytes; + sum_cur.ift_oe += ifd->ifi_oerrors; + sum_cur.ift_co += ifd->ifi_collisions; + sum_cur.ift_dr += 0; /* XXX-elad ifnet.if_snd.ifq_drops */ + break; + } + } + if (interface == NULL) { + strlcpy(ip_cur.ift_name, name, + sizeof(ip_cur.ift_name)); + ip_cur.ift_ip = ifd->ifi_ipackets; + ip_cur.ift_ib = ifd->ifi_ibytes; + ip_cur.ift_ie = ifd->ifi_ierrors; + ip_cur.ift_op = ifd->ifi_opackets; + ip_cur.ift_ob = ifd->ifi_obytes; + ip_cur.ift_oe = ifd->ifi_oerrors; + ip_cur.ift_co = ifd->ifi_collisions; + ip_cur.ift_dr = 0; + /* XXX-elad ifnet.if_snd.ifq_drops */ + } +} diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c new file mode 100644 index 000000000..120d2744e --- /dev/null +++ b/usr.bin/netstat/inet.c @@ -0,0 +1,1037 @@ +/* $NetBSD: inet.c,v 1.106 2015/02/08 15:09:45 christos Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)inet.c 8.4 (Berkeley) 4/20/94"; +#else +__RCSID("$NetBSD: inet.c,v 1.106 2015/02/08 15:09:45 christos Exp $"); +#endif +#endif /* not lint */ + +#define _CALLOUT_PRIVATE /* for defs in sys/callout.h */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define ICMP_STRINGS +#include + +#ifdef INET6 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#define TCPSTATES +#include +#define TCPTIMERS +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "vtw.h" +#include "prog_ops.h" + +char *inetname(struct in_addr *); +void inetprint(struct in_addr *, u_int16_t, const char *, int); + +void print_vtw_v4(const vtw_t *); + +/* + * Print a summary of connections related to an Internet + * protocol. For TCP, also give state of connection. + * Listening processes (aflag) are suppressed unless the + * -a (all) flag is specified. + */ +static int width; +static int compact; + +/* VTW-related variables. */ +static struct timeval now; + +static void +protoprhdr(void) +{ + printf("Active Internet connections"); + if (aflag) + printf(" (including servers)"); + putchar('\n'); + if (Aflag) + printf("%-8.8s ", "PCB"); + printf( + Vflag ? "%-5.5s %-6.6s %-6.6s %s%-*.*s %-*.*s %-13.13s Expires\n" + : "%-5.5s %-6.6s %-6.6s %s%-*.*s %-*.*s %s\n", + "Proto", "Recv-Q", "Send-Q", compact ? "" : " ", + width, width, "Local Address", + width, width, "Foreign Address", + "State"); +} + +static void +protopr0(intptr_t ppcb, u_long rcv_sb_cc, u_long snd_sb_cc, + struct in_addr *laddr, u_int16_t lport, + struct in_addr *faddr, u_int16_t fport, + short t_state, const char *name, int inp_flags, + const struct timeval *expires) +{ + static const char *shorttcpstates[] = { + "CLOSED", "LISTEN", "SYNSEN", "SYSRCV", + "ESTABL", "CLWAIT", "FWAIT1", "CLOSNG", + "LASTAK", "FWAIT2", "TMWAIT", + }; + int istcp; + + istcp = strcmp(name, "tcp") == 0; + + if (Aflag) { + printf("%8" PRIxPTR " ", ppcb); + } + printf("%-5.5s %6ld %6ld%s", name, rcv_sb_cc, snd_sb_cc, + compact ? "" : " "); + if (numeric_port) { + inetprint(laddr, lport, name, 1); + inetprint(faddr, fport, name, 1); + } else if (inp_flags & INP_ANONPORT) { + inetprint(laddr, lport, name, 1); + inetprint(faddr, fport, name, 0); + } else { + inetprint(laddr, lport, name, 0); + inetprint(faddr, fport, name, 0); + } + if (istcp) { + if (t_state < 0 || t_state >= TCP_NSTATES) + printf(" %d", t_state); + else + printf(" %s", compact ? shorttcpstates[t_state] : + tcpstates[t_state]); + } + if (Vflag && expires != NULL) { + if (expires->tv_sec == 0 && expires->tv_usec == -1) + printf(" reclaimed"); + else { + struct timeval delta; + + timersub(expires, &now, &delta); + printf(" %.3fms", + delta.tv_sec * 1000.0 + delta.tv_usec / 1000.0); + } + } + putchar('\n'); +} + +static void +dbg_printf(const char *fmt, ...) +{ + return; +} + +void +print_vtw_v4(const vtw_t *vtw) +{ + const vtw_v4_t *v4 = (const vtw_v4_t *)vtw; + struct timeval delta; + struct in_addr la, fa; + char buf[2][32]; + static const struct timeval zero = {.tv_sec = 0, .tv_usec = 0}; + + la.s_addr = v4->laddr; + fa.s_addr = v4->faddr; + + snprintf(&buf[0][0], 32, "%s", inet_ntoa(la)); + snprintf(&buf[1][0], 32, "%s", inet_ntoa(fa)); + + timersub(&vtw->expire, &now, &delta); + + if (vtw->expire.tv_sec == 0 && vtw->expire.tv_usec == -1) { + dbg_printf("%15.15s:%d %15.15s:%d reclaimed\n" + ,buf[0], ntohs(v4->lport) + ,buf[1], ntohs(v4->fport)); + if (!(Vflag && vflag)) + return; + } else if (vtw->expire.tv_sec == 0) + return; + else if (timercmp(&delta, &zero, <) && !(Vflag && vflag)) { + dbg_printf("%15.15s:%d %15.15s:%d expired\n" + ,buf[0], ntohs(v4->lport) + ,buf[1], ntohs(v4->fport)); + return; + } else { + dbg_printf("%15.15s:%d %15.15s:%d expires in %.3fms\n" + ,buf[0], ntohs(v4->lport) + ,buf[1], ntohs(v4->fport) + ,delta.tv_sec * 1000.0 + delta.tv_usec / 1000.0); + } + protopr0(0, 0, 0, + &la, v4->lport, + &fa, v4->fport, + TCPS_TIME_WAIT, "tcp", 0, &vtw->expire); +} + +struct kinfo_pcb * +getpcblist_sysctl(const char *name, size_t *len) { + int mib[8]; + size_t namelen = 0, size = 0; + char *mibname = NULL; + struct kinfo_pcb *pcblist; + + memset(mib, 0, sizeof(mib)); + + if (asprintf(&mibname, "net.inet%s.%s.pcblist", name + 3, name) == -1) + err(1, "asprintf"); + + /* get dynamic pcblist node */ + if (sysctlnametomib(mibname, mib, &namelen) == -1) + err(1, "sysctlnametomib: %s", mibname); + + free(mibname); + + if (prog_sysctl(mib, __arraycount(mib), NULL, &size, NULL, 0) == -1) + err(1, "sysctl (query)"); + + if ((pcblist = malloc(size)) == NULL) + err(1, "malloc"); + memset(pcblist, 0, size); + + mib[6] = sizeof(*pcblist); + mib[7] = size / sizeof(*pcblist); + + if (prog_sysctl(mib, __arraycount(mib), pcblist, &size, NULL, 0) == -1) + err(1, "sysctl (copy)"); + + *len = size / sizeof(*pcblist); + return pcblist; + +} + +static struct kinfo_pcb * +getpcblist_kmem(u_long off, const char *name, size_t *len) { + struct inpcbtable table; + struct inpcb_hdr *next, *prev; + struct inpcb inpcb; + struct tcpcb tcpcb; + struct socket sockb; + int istcp = strcmp(name, "tcp") == 0; + struct kinfo_pcb *pcblist; + size_t size = 100, i; + struct sockaddr_in sin; + struct inpcbqueue *head; + + if (off == 0) { + *len = 0; + return NULL; + } + + kread(off, (char *)&table, sizeof table); + head = &table.inpt_queue; + next = TAILQ_FIRST(head); + prev = TAILQ_END(head); + + if ((pcblist = malloc(size * sizeof(*pcblist))) == NULL) + err(1, "malloc"); + + i = 0; + while (next != TAILQ_END(head)) { + kread((u_long)next, (char *)&inpcb, sizeof inpcb); + prev = next; + next = TAILQ_NEXT(&inpcb, inp_queue); + + if (inpcb.inp_af != AF_INET) + continue; + + kread((u_long)inpcb.inp_socket, (char *)&sockb, sizeof(sockb)); + if (istcp) { + kread((u_long)inpcb.inp_ppcb, + (char *)&tcpcb, sizeof (tcpcb)); + } + pcblist[i].ki_ppcbaddr = + istcp ? (uintptr_t) inpcb.inp_ppcb : (uintptr_t) prev; + pcblist[i].ki_rcvq = (uint64_t)sockb.so_rcv.sb_cc; + pcblist[i].ki_sndq = (uint64_t)sockb.so_snd.sb_cc; + + sin.sin_addr = inpcb.inp_laddr; + sin.sin_port = inpcb.inp_lport; + memcpy(&pcblist[i].ki_s, &sin, sizeof(sin)); + sin.sin_addr = inpcb.inp_faddr; + sin.sin_port = inpcb.inp_fport; + memcpy(&pcblist[i].ki_d, &sin, sizeof(sin)); + pcblist[i].ki_tstate = tcpcb.t_state; + pcblist[i].ki_pflags = inpcb.inp_flags; + if (i++ == size) { + size += 100; + struct kinfo_pcb *n = realloc(pcblist, + size * sizeof(*pcblist)); + if (n == NULL) + err(1, "realloc"); + pcblist = n; + } + } + *len = i; + return pcblist; +} + +void +protopr(u_long off, const char *name) +{ + static int first = 1; + struct kinfo_pcb *pcblist; + size_t i, len; + + compact = 0; + if (Aflag) { + if (!numeric_addr) + width = 18; + else { + width = 21; + compact = 1; + } + } else + width = 22; + + if (use_sysctl) + pcblist = getpcblist_sysctl(name, &len); + else + pcblist = getpcblist_kmem(off, name, &len); + + for (i = 0; i < len; i++) { + struct sockaddr_in src, dst; + + memcpy(&src, &pcblist[i].ki_s, sizeof(src)); + memcpy(&dst, &pcblist[i].ki_d, sizeof(dst)); + + if (!aflag && + inet_lnaof(dst.sin_addr) == INADDR_ANY) + continue; + + if (first) { + protoprhdr(); + first = 0; + } + protopr0((intptr_t) pcblist[i].ki_ppcbaddr, + pcblist[i].ki_rcvq, pcblist[i].ki_sndq, + &src.sin_addr, src.sin_port, + &dst.sin_addr, dst.sin_port, + pcblist[i].ki_tstate, name, + pcblist[i].ki_pflags, NULL); + } + + free(pcblist); + +#ifndef __minix + if (strcmp(name, "tcp") == 0) { + struct timeval t; + timebase(&t); + gettimeofday(&now, NULL); + timersub(&now, &t, &now); + show_vtw_v4(print_vtw_v4); + } +#endif /* !__minix */ +} + +/* + * Dump TCP statistics structure. + */ +void +tcp_stats(u_long off, const char *name) +{ + uint64_t tcpstat[TCP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(tcpstat); + + if (sysctlbyname("net.inet.tcp.stats", tcpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf ("%s:\n", name); + +#define ps(f, m) if (tcpstat[f] || sflag <= 1) \ + printf(m, tcpstat[f]) +#define p(f, m) if (tcpstat[f] || sflag <= 1) \ + printf(m, tcpstat[f], plural(tcpstat[f])) +#define p2(f1, f2, m) if (tcpstat[f1] || tcpstat[f2] || sflag <= 1) \ + printf(m, tcpstat[f1], plural(tcpstat[f1]), \ + tcpstat[f2], plural(tcpstat[f2])) +#define p2s(f1, f2, m) if (tcpstat[f1] || tcpstat[f2] || sflag <= 1) \ + printf(m, tcpstat[f1], plural(tcpstat[f1]), \ + tcpstat[f2]) +#define p3(f, m) if (tcpstat[f] || sflag <= 1) \ + printf(m, tcpstat[f], plurales(tcpstat[f])) + + p(TCP_STAT_SNDTOTAL, "\t%" PRIu64 " packet%s sent\n"); + p2(TCP_STAT_SNDPACK,TCP_STAT_SNDBYTE, + "\t\t%" PRIu64 " data packet%s (%" PRIu64 " byte%s)\n"); + p2(TCP_STAT_SNDREXMITPACK, TCP_STAT_SNDREXMITBYTE, + "\t\t%" PRIu64 " data packet%s (%" PRIu64 " byte%s) retransmitted\n"); + p2s(TCP_STAT_SNDACKS, TCP_STAT_DELACK, + "\t\t%" PRIu64 " ack-only packet%s (%" PRIu64 " delayed)\n"); + p(TCP_STAT_SNDURG, "\t\t%" PRIu64 " URG only packet%s\n"); + p(TCP_STAT_SNDPROBE, "\t\t%" PRIu64 " window probe packet%s\n"); + p(TCP_STAT_SNDWINUP, "\t\t%" PRIu64 " window update packet%s\n"); + p(TCP_STAT_SNDCTRL, "\t\t%" PRIu64 " control packet%s\n"); + p(TCP_STAT_SELFQUENCH, + "\t\t%" PRIu64 " send attempt%s resulted in self-quench\n"); + p(TCP_STAT_RCVTOTAL, "\t%" PRIu64 " packet%s received\n"); + p2(TCP_STAT_RCVACKPACK, TCP_STAT_RCVACKBYTE, + "\t\t%" PRIu64 " ack%s (for %" PRIu64 " byte%s)\n"); + p(TCP_STAT_RCVDUPACK, "\t\t%" PRIu64 " duplicate ack%s\n"); + p(TCP_STAT_RCVACKTOOMUCH, "\t\t%" PRIu64 " ack%s for unsent data\n"); + p2(TCP_STAT_RCVPACK, TCP_STAT_RCVBYTE, + "\t\t%" PRIu64 " packet%s (%" PRIu64 " byte%s) received in-sequence\n"); + p2(TCP_STAT_RCVDUPPACK, TCP_STAT_RCVDUPBYTE, + "\t\t%" PRIu64 " completely duplicate packet%s (%" PRIu64 " byte%s)\n"); + p(TCP_STAT_PAWSDROP, "\t\t%" PRIu64 " old duplicate packet%s\n"); + p2(TCP_STAT_RCVPARTDUPPACK, TCP_STAT_RCVPARTDUPBYTE, + "\t\t%" PRIu64 " packet%s with some dup. data (%" PRIu64 " byte%s duped)\n"); + p2(TCP_STAT_RCVOOPACK, TCP_STAT_RCVOOBYTE, + "\t\t%" PRIu64 " out-of-order packet%s (%" PRIu64 " byte%s)\n"); + p2(TCP_STAT_RCVPACKAFTERWIN, TCP_STAT_RCVBYTEAFTERWIN, + "\t\t%" PRIu64 " packet%s (%" PRIu64 " byte%s) of data after window\n"); + p(TCP_STAT_RCVWINPROBE, "\t\t%" PRIu64 " window probe%s\n"); + p(TCP_STAT_RCVWINUPD, "\t\t%" PRIu64 " window update packet%s\n"); + p(TCP_STAT_RCVAFTERCLOSE, "\t\t%" PRIu64 " packet%s received after close\n"); + p(TCP_STAT_RCVBADSUM, "\t\t%" PRIu64 " discarded for bad checksum%s\n"); + p(TCP_STAT_RCVBADOFF, "\t\t%" PRIu64 " discarded for bad header offset field%s\n"); + ps(TCP_STAT_RCVSHORT, "\t\t%" PRIu64 " discarded because packet too short\n"); + p(TCP_STAT_CONNATTEMPT, "\t%" PRIu64 " connection request%s\n"); + p(TCP_STAT_ACCEPTS, "\t%" PRIu64 " connection accept%s\n"); + p(TCP_STAT_CONNECTS, + "\t%" PRIu64 " connection%s established (including accepts)\n"); + p2(TCP_STAT_CLOSED, TCP_STAT_DROPS, + "\t%" PRIu64 " connection%s closed (including %" PRIu64 " drop%s)\n"); + p(TCP_STAT_CONNDROPS, "\t%" PRIu64 " embryonic connection%s dropped\n"); + p(TCP_STAT_DELAYED_FREE, "\t%" PRIu64 " delayed free%s of tcpcb\n"); + p2(TCP_STAT_RTTUPDATED, TCP_STAT_SEGSTIMED, + "\t%" PRIu64 " segment%s updated rtt (of %" PRIu64 " attempt%s)\n"); + p(TCP_STAT_REXMTTIMEO, "\t%" PRIu64 " retransmit timeout%s\n"); + p(TCP_STAT_TIMEOUTDROP, + "\t\t%" PRIu64 " connection%s dropped by rexmit timeout\n"); + p2(TCP_STAT_PERSISTTIMEO, TCP_STAT_PERSISTDROPS, + "\t%" PRIu64 " persist timeout%s (resulting in %" PRIu64 " dropped " + "connection%s)\n"); + p(TCP_STAT_KEEPTIMEO, "\t%" PRIu64 " keepalive timeout%s\n"); + p(TCP_STAT_KEEPPROBE, "\t\t%" PRIu64 " keepalive probe%s sent\n"); + p(TCP_STAT_KEEPDROPS, "\t\t%" PRIu64 " connection%s dropped by keepalive\n"); + p(TCP_STAT_PREDACK, "\t%" PRIu64 " correct ACK header prediction%s\n"); + p(TCP_STAT_PREDDAT, "\t%" PRIu64 " correct data packet header prediction%s\n"); + p3(TCP_STAT_PCBHASHMISS, "\t%" PRIu64 " PCB hash miss%s\n"); + ps(TCP_STAT_NOPORT, "\t%" PRIu64 " dropped due to no socket\n"); + p(TCP_STAT_CONNSDRAINED, "\t%" PRIu64 " connection%s drained due to memory " + "shortage\n"); + p(TCP_STAT_PMTUBLACKHOLE, "\t%" PRIu64 " PMTUD blackhole%s detected\n"); + + p(TCP_STAT_BADSYN, "\t%" PRIu64 " bad connection attempt%s\n"); + ps(TCP_STAT_SC_ADDED, "\t%" PRIu64 " SYN cache entries added\n"); + p(TCP_STAT_SC_COLLISIONS, "\t\t%" PRIu64 " hash collision%s\n"); + ps(TCP_STAT_SC_COMPLETED, "\t\t%" PRIu64 " completed\n"); + ps(TCP_STAT_SC_ABORTED, "\t\t%" PRIu64 " aborted (no space to build PCB)\n"); + ps(TCP_STAT_SC_TIMED_OUT, "\t\t%" PRIu64 " timed out\n"); + ps(TCP_STAT_SC_OVERFLOWED, "\t\t%" PRIu64 " dropped due to overflow\n"); + ps(TCP_STAT_SC_BUCKETOVERFLOW, "\t\t%" PRIu64 " dropped due to bucket overflow\n"); + ps(TCP_STAT_SC_RESET, "\t\t%" PRIu64 " dropped due to RST\n"); + ps(TCP_STAT_SC_UNREACH, "\t\t%" PRIu64 " dropped due to ICMP unreachable\n"); + ps(TCP_STAT_SC_DELAYED_FREE, "\t\t%" PRIu64 " delayed free of SYN cache " + "entries\n"); + p(TCP_STAT_SC_RETRANSMITTED, "\t%" PRIu64 " SYN,ACK%s retransmitted\n"); + p(TCP_STAT_SC_DUPESYN, "\t%" PRIu64 " duplicate SYN%s received for entries " + "already in the cache\n"); + p(TCP_STAT_SC_DROPPED, "\t%" PRIu64 " SYN%s dropped (no route or no space)\n"); + p(TCP_STAT_BADSIG, "\t%" PRIu64 " packet%s with bad signature\n"); + p(TCP_STAT_GOODSIG, "\t%" PRIu64 " packet%s with good signature\n"); + + p(TCP_STAT_ECN_SHS, "\t%" PRIu64 " successful ECN handshake%s\n"); + p(TCP_STAT_ECN_CE, "\t%" PRIu64 " packet%s with ECN CE bit\n"); + p(TCP_STAT_ECN_ECT, "\t%" PRIu64 " packet%s ECN ECT(0) bit\n"); +#undef p +#undef ps +#undef p2 +#undef p2s +#undef p3 + show_vtw_stats(); +} + +/* + * Dump UDP statistics structure. + */ +void +udp_stats(u_long off, const char *name) +{ + uint64_t udpstat[UDP_NSTATS]; + u_quad_t delivered; + + if (use_sysctl) { + size_t size = sizeof(udpstat); + + if (sysctlbyname("net.inet.udp.stats", udpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf ("%s:\n", name); + +#define ps(f, m) if (udpstat[f] || sflag <= 1) \ + printf(m, udpstat[f]) +#define p(f, m) if (udpstat[f] || sflag <= 1) \ + printf(m, udpstat[f], plural(udpstat[f])) +#define p3(f, m) if (udpstat[f] || sflag <= 1) \ + printf(m, udpstat[f], plurales(udpstat[f])) + + p(UDP_STAT_IPACKETS, "\t%" PRIu64 " datagram%s received\n"); + ps(UDP_STAT_HDROPS, "\t%" PRIu64 " with incomplete header\n"); + ps(UDP_STAT_BADLEN, "\t%" PRIu64 " with bad data length field\n"); + ps(UDP_STAT_BADSUM, "\t%" PRIu64 " with bad checksum\n"); + ps(UDP_STAT_NOPORT, "\t%" PRIu64 " dropped due to no socket\n"); + p(UDP_STAT_NOPORTBCAST, + "\t%" PRIu64 " broadcast/multicast datagram%s dropped due to no socket\n"); + ps(UDP_STAT_FULLSOCK, "\t%" PRIu64 " dropped due to full socket buffers\n"); + delivered = udpstat[UDP_STAT_IPACKETS] - + udpstat[UDP_STAT_HDROPS] - + udpstat[UDP_STAT_BADLEN] - + udpstat[UDP_STAT_BADSUM] - + udpstat[UDP_STAT_NOPORT] - + udpstat[UDP_STAT_NOPORTBCAST] - + udpstat[UDP_STAT_FULLSOCK]; + if (delivered || sflag <= 1) + printf("\t%" PRIu64 " delivered\n", delivered); + p3(UDP_STAT_PCBHASHMISS, "\t%" PRIu64 " PCB hash miss%s\n"); + p(UDP_STAT_OPACKETS, "\t%" PRIu64 " datagram%s output\n"); + +#undef ps +#undef p +#undef p3 +} + +/* + * Dump IP statistics structure. + */ +void +ip_stats(u_long off, const char *name) +{ + uint64_t ipstat[IP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(ipstat); + + if (sysctlbyname("net.inet.ip.stats", ipstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define ps(f, m) if (ipstat[f] || sflag <= 1) \ + printf(m, ipstat[f]) +#define p(f, m) if (ipstat[f] || sflag <= 1) \ + printf(m, ipstat[f], plural(ipstat[f])) + + p(IP_STAT_TOTAL, "\t%" PRIu64 " total packet%s received\n"); + p(IP_STAT_BADSUM, "\t%" PRIu64 " bad header checksum%s\n"); + ps(IP_STAT_TOOSMALL, "\t%" PRIu64 " with size smaller than minimum\n"); + ps(IP_STAT_TOOSHORT, "\t%" PRIu64 " with data size < data length\n"); + ps(IP_STAT_TOOLONG, "\t%" PRIu64 " with length > max ip packet size\n"); + ps(IP_STAT_BADHLEN, "\t%" PRIu64 " with header length < data size\n"); + ps(IP_STAT_BADLEN, "\t%" PRIu64 " with data length < header length\n"); + ps(IP_STAT_BADOPTIONS, "\t%" PRIu64 " with bad options\n"); + ps(IP_STAT_BADVERS, "\t%" PRIu64 " with incorrect version number\n"); + p(IP_STAT_FRAGMENTS, "\t%" PRIu64 " fragment%s received\n"); + p(IP_STAT_FRAGDROPPED, "\t%" PRIu64 " fragment%s dropped (dup or out of space)\n"); + p(IP_STAT_RCVMEMDROP, "\t%" PRIu64 " fragment%s dropped (out of ipqent)\n"); + p(IP_STAT_BADFRAGS, "\t%" PRIu64 " malformed fragment%s dropped\n"); + p(IP_STAT_FRAGTIMEOUT, "\t%" PRIu64 " fragment%s dropped after timeout\n"); + p(IP_STAT_REASSEMBLED, "\t%" PRIu64 " packet%s reassembled ok\n"); + p(IP_STAT_DELIVERED, "\t%" PRIu64 " packet%s for this host\n"); + p(IP_STAT_NOPROTO, "\t%" PRIu64 " packet%s for unknown/unsupported protocol\n"); + p(IP_STAT_FORWARD, "\t%" PRIu64 " packet%s forwarded"); + p(IP_STAT_FASTFORWARD, " (%" PRIu64 " packet%s fast forwarded)"); + if (ipstat[IP_STAT_FORWARD] || sflag <= 1) + putchar('\n'); + p(IP_STAT_CANTFORWARD, "\t%" PRIu64 " packet%s not forwardable\n"); + p(IP_STAT_REDIRECTSENT, "\t%" PRIu64 " redirect%s sent\n"); + p(IP_STAT_NOGIF, "\t%" PRIu64 " packet%s no matching gif found\n"); + p(IP_STAT_LOCALOUT, "\t%" PRIu64 " packet%s sent from this host\n"); + p(IP_STAT_RAWOUT, "\t%" PRIu64 " packet%s sent with fabricated ip header\n"); + p(IP_STAT_ODROPPED, "\t%" PRIu64 " output packet%s dropped due to no bufs, etc.\n"); + p(IP_STAT_NOROUTE, "\t%" PRIu64 " output packet%s discarded due to no route\n"); + p(IP_STAT_FRAGMENTED, "\t%" PRIu64 " output datagram%s fragmented\n"); + p(IP_STAT_OFRAGMENTS, "\t%" PRIu64 " fragment%s created\n"); + p(IP_STAT_CANTFRAG, "\t%" PRIu64 " datagram%s that can't be fragmented\n"); + p(IP_STAT_BADADDR, "\t%" PRIu64 " datagram%s with bad address in header\n"); +#undef ps +#undef p +} + +/* + * Dump ICMP statistics. + */ +void +icmp_stats(u_long off, const char *name) +{ + uint64_t icmpstat[ICMP_NSTATS]; + int i, first; + + if (use_sysctl) { + size_t size = sizeof(icmpstat); + + if (sysctlbyname("net.inet.icmp.stats", icmpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (icmpstat[f] || sflag <= 1) \ + printf(m, icmpstat[f], plural(icmpstat[f])) + + p(ICMP_STAT_ERROR, "\t%" PRIu64 " call%s to icmp_error\n"); + p(ICMP_STAT_OLDICMP, + "\t%" PRIu64 " error%s not generated because old message was icmp\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (icmpstat[ICMP_STAT_OUTHIST + i] != 0) { + if (first) { + printf("\tOutput histogram:\n"); + first = 0; + } + printf("\t\t%s: %" PRIu64 "\n", icmp_type[i], + icmpstat[ICMP_STAT_OUTHIST + i]); + } + p(ICMP_STAT_BADCODE, "\t%" PRIu64 " message%s with bad code fields\n"); + p(ICMP_STAT_TOOSHORT, "\t%" PRIu64 " message%s < minimum length\n"); + p(ICMP_STAT_CHECKSUM, "\t%" PRIu64 " bad checksum%s\n"); + p(ICMP_STAT_BADLEN, "\t%" PRIu64 " message%s with bad length\n"); + p(ICMP_STAT_BMCASTECHO, "\t%" PRIu64 " multicast echo request%s ignored\n"); + p(ICMP_STAT_BMCASTTSTAMP, "\t%" PRIu64 " multicast timestamp request%s ignored\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (icmpstat[ICMP_STAT_INHIST + i] != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %" PRIu64 "\n", icmp_type[i], + icmpstat[ICMP_STAT_INHIST + i]); + } + p(ICMP_STAT_REFLECT, "\t%" PRIu64 " message response%s generated\n"); + p(ICMP_STAT_PMTUCHG, "\t%" PRIu64 " path MTU change%s\n"); +#undef p +} + +/* + * Dump IGMP statistics structure. + */ +void +igmp_stats(u_long off, const char *name) +{ + uint64_t igmpstat[IGMP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(igmpstat); + + if (sysctlbyname("net.inet.igmp.stats", igmpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (igmpstat[f] || sflag <= 1) \ + printf(m, igmpstat[f], plural(igmpstat[f])) +#define py(f, m) if (igmpstat[f] || sflag <= 1) \ + printf(m, igmpstat[f], igmpstat[f] != 1 ? "ies" : "y") + p(IGMP_STAT_RCV_TOTAL, "\t%" PRIu64 " message%s received\n"); + p(IGMP_STAT_RCV_TOOSHORT, "\t%" PRIu64 " message%s received with too few bytes\n"); + p(IGMP_STAT_RCV_BADSUM, "\t%" PRIu64 " message%s received with bad checksum\n"); + py(IGMP_STAT_RCV_QUERIES, "\t%" PRIu64 " membership quer%s received\n"); + py(IGMP_STAT_RCV_BADQUERIES, "\t%" PRIu64 " membership quer%s received with invalid field(s)\n"); + p(IGMP_STAT_RCV_REPORTS, "\t%" PRIu64 " membership report%s received\n"); + p(IGMP_STAT_RCV_BADREPORTS, "\t%" PRIu64 " membership report%s received with invalid field(s)\n"); + p(IGMP_STAT_RCV_OURREPORTS, "\t%" PRIu64 " membership report%s received for groups to which we belong\n"); + p(IGMP_STAT_SND_REPORTS, "\t%" PRIu64 " membership report%s sent\n"); +#undef p +#undef py +} + +/* + * Dump CARP statistics structure. + */ +void +carp_stats(u_long off, const char *name) +{ + uint64_t carpstat[CARP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(carpstat); + + if (sysctlbyname("net.inet.carp.stats", carpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (carpstat[f] || sflag <= 1) \ + printf(m, carpstat[f], plural(carpstat[f])) +#define p2(f, m) if (carpstat[f] || sflag <= 1) \ + printf(m, carpstat[f]) + + p(CARP_STAT_IPACKETS, "\t%" PRIu64 " packet%s received (IPv4)\n"); + p(CARP_STAT_IPACKETS6, "\t%" PRIu64 " packet%s received (IPv6)\n"); + p(CARP_STAT_BADIF, + "\t\t%" PRIu64 " packet%s discarded for bad interface\n"); + p(CARP_STAT_BADTTL, + "\t\t%" PRIu64 " packet%s discarded for wrong TTL\n"); + p(CARP_STAT_HDROPS, "\t\t%" PRIu64 " packet%s shorter than header\n"); + p(CARP_STAT_BADSUM, "\t\t%" PRIu64 + " packet%s discarded for bad checksum\n"); + p(CARP_STAT_BADVER, + "\t\t%" PRIu64 " packet%s discarded with a bad version\n"); + p2(CARP_STAT_BADLEN, + "\t\t%" PRIu64 " discarded because packet was too short\n"); + p(CARP_STAT_BADAUTH, + "\t\t%" PRIu64 " packet%s discarded for bad authentication\n"); + p(CARP_STAT_BADVHID, "\t\t%" PRIu64 " packet%s discarded for bad vhid\n"); + p(CARP_STAT_BADADDRS, "\t\t%" PRIu64 + " packet%s discarded because of a bad address list\n"); + p(CARP_STAT_OPACKETS, "\t%" PRIu64 " packet%s sent (IPv4)\n"); + p(CARP_STAT_OPACKETS6, "\t%" PRIu64 " packet%s sent (IPv6)\n"); + p2(CARP_STAT_ONOMEM, + "\t\t%" PRIu64 " send failed due to mbuf memory error\n"); +#undef p +#undef p2 +} + +/* + * Dump PIM statistics structure. + */ +void +pim_stats(u_long off, const char *name) +{ + struct pimstat pimstat; + + if (off == 0) + return; + if (kread(off, (char *)&pimstat, sizeof (pimstat)) != 0) { + /* XXX: PIM is probably not enabled in the kernel */ + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (pimstat.f || sflag <= 1) \ + printf(m, pimstat.f, plural(pimstat.f)) + + p(pims_rcv_total_msgs, "\t%" PRIu64 " message%s received\n"); + p(pims_rcv_total_bytes, "\t%" PRIu64 " byte%s received\n"); + p(pims_rcv_tooshort, "\t%" PRIu64 " message%s received with too few bytes\n"); + p(pims_rcv_badsum, "\t%" PRIu64 " message%s received with bad checksum\n"); + p(pims_rcv_badversion, "\t%" PRIu64 " message%s received with bad version\n"); + p(pims_rcv_registers_msgs, "\t%" PRIu64 " data register message%s received\n"); + p(pims_rcv_registers_bytes, "\t%" PRIu64 " data register byte%s received\n"); + p(pims_rcv_registers_wrongiif, "\t%" PRIu64 " data register message%s received on wrong iif\n"); + p(pims_rcv_badregisters, "\t%" PRIu64 " bad register%s received\n"); + p(pims_snd_registers_msgs, "\t%" PRIu64 " data register message%s sent\n"); + p(pims_snd_registers_bytes, "\t%" PRIu64 " data register byte%s sent\n"); +#undef p +} + +/* + * Dump the ARP statistics structure. + */ +void +arp_stats(u_long off, const char *name) +{ + uint64_t arpstat[ARP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(arpstat); + + if (sysctlbyname("net.inet.arp.stats", arpstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define ps(f, m) if (arpstat[f] || sflag <= 1) \ + printf(m, arpstat[f]) +#define p(f, m) if (arpstat[f] || sflag <= 1) \ + printf(m, arpstat[f], plural(arpstat[f])) + + p(ARP_STAT_SNDTOTAL, "\t%" PRIu64 " packet%s sent\n"); + p(ARP_STAT_SNDREPLY, "\t\t%" PRIu64 " reply packet%s\n"); + p(ARP_STAT_SENDREQUEST, "\t\t%" PRIu64 " request packet%s\n"); + + p(ARP_STAT_RCVTOTAL, "\t%" PRIu64 " packet%s received\n"); + p(ARP_STAT_RCVREPLY, "\t\t%" PRIu64 " reply packet%s\n"); + p(ARP_STAT_RCVREQUEST, "\t\t%" PRIu64 " valid request packet%s\n"); + p(ARP_STAT_RCVMCAST, "\t\t%" PRIu64 " broadcast/multicast packet%s\n"); + p(ARP_STAT_RCVBADPROTO, "\t\t%" PRIu64 " packet%s with unknown protocol type\n"); + p(ARP_STAT_RCVBADLEN, "\t\t%" PRIu64 " packet%s with bad (short) length\n"); + p(ARP_STAT_RCVZEROTPA, "\t\t%" PRIu64 " packet%s with null target IP address\n"); + p(ARP_STAT_RCVZEROSPA, "\t\t%" PRIu64 " packet%s with null source IP address\n"); + ps(ARP_STAT_RCVNOINT, "\t\t%" PRIu64 " could not be mapped to an interface\n"); + p(ARP_STAT_RCVLOCALSHA, "\t\t%" PRIu64 " packet%s sourced from a local hardware " + "address\n"); + p(ARP_STAT_RCVBCASTSHA, "\t\t%" PRIu64 " packet%s with a broadcast " + "source hardware address\n"); + p(ARP_STAT_RCVLOCALSPA, "\t\t%" PRIu64 " duplicate%s for a local IP address\n"); + p(ARP_STAT_RCVOVERPERM, "\t\t%" PRIu64 " attempt%s to overwrite a static entry\n"); + p(ARP_STAT_RCVOVERINT, "\t\t%" PRIu64 " packet%s received on wrong interface\n"); + p(ARP_STAT_RCVOVER, "\t\t%" PRIu64 " entry%s overwritten\n"); + p(ARP_STAT_RCVLENCHG, "\t\t%" PRIu64 " change%s in hardware address length\n"); + + p(ARP_STAT_DFRTOTAL, "\t%" PRIu64 " packet%s deferred pending ARP resolution\n"); + ps(ARP_STAT_DFRSENT, "\t\t%" PRIu64 " sent\n"); + ps(ARP_STAT_DFRDROPPED, "\t\t%" PRIu64 " dropped\n"); + + p(ARP_STAT_ALLOCFAIL, "\t%" PRIu64 " failure%s to allocate llinfo\n"); + +#undef ps +#undef p +} + +/* + * Pretty print an Internet address (net address + port). + * Take numeric_addr and numeric_port into consideration. + */ +void +inetprint(struct in_addr *in, uint16_t port, const char *proto, + int port_numeric) +{ + struct servent *sp = 0; + char line[80], *cp; + size_t space; + + (void)snprintf(line, sizeof line, "%.*s.", + (Aflag && !numeric_addr) ? 12 : 16, inetname(in)); + cp = strchr(line, '\0'); + if (!port_numeric && port) + sp = getservbyport((int)port, proto); + space = sizeof line - (cp-line); + if (sp || port == 0) + (void)snprintf(cp, space, "%s", sp ? sp->s_name : "*"); + else + (void)snprintf(cp, space, "%u", ntohs(port)); + (void)printf(" %-*.*s", width, width, line); +} + +/* + * Construct an Internet address representation. + * If numeric_addr has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +char * +inetname(struct in_addr *inp) +{ + char *cp; + static char line[50]; + struct hostent *hp; + struct netent *np; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + + if (first && !numeric_addr) { + first = 0; + if (gethostname(domain, sizeof domain) == 0) { + domain[sizeof(domain) - 1] = '\0'; + if ((cp = strchr(domain, '.'))) + (void) strlcpy(domain, cp + 1, sizeof(domain)); + else + domain[0] = 0; + } else + domain[0] = 0; + } + cp = 0; + if (!numeric_addr && inp->s_addr != INADDR_ANY) { + int net = inet_netof(*inp); + int lna = inet_lnaof(*inp); + + if (lna == INADDR_ANY) { + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + if (cp == 0) { + hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + } + if (inp->s_addr == INADDR_ANY) + strlcpy(line, "*", sizeof line); + else if (cp) + strlcpy(line, cp, sizeof line); + else { + inp->s_addr = ntohl(inp->s_addr); +#define C(x) ((x) & 0xff) + (void)snprintf(line, sizeof line, "%u.%u.%u.%u", + C(inp->s_addr >> 24), C(inp->s_addr >> 16), + C(inp->s_addr >> 8), C(inp->s_addr)); +#undef C + } + return (line); +} + +/* + * Dump the contents of a TCP PCB. + */ +void +tcp_dump(u_long off, const char *name, u_long pcbaddr) +{ + callout_impl_t *ci; + struct tcpcb tcpcb; + int i, hardticks; + struct kinfo_pcb *pcblist; + size_t j, len; + + if (use_sysctl) + pcblist = getpcblist_sysctl(name, &len); + else + pcblist = getpcblist_kmem(off, name, &len); + + for (j = 0; j < len; j++) + if (pcblist[j].ki_ppcbaddr == pcbaddr) + break; + free(pcblist); + + if (j == len) + errx(1, "0x%lx is not a valid pcb address", pcbaddr); + + kread(pcbaddr, (char *)&tcpcb, sizeof(tcpcb)); + hardticks = get_hardticks(); + + printf("TCP Protocol Control Block at 0x%08lx:\n\n", pcbaddr); + + printf("Timers:\n"); + for (i = 0; i < TCPT_NTIMERS; i++) { + char buf[128]; + ci = (callout_impl_t *)&tcpcb.t_timer[i]; + snprintb(buf, sizeof(buf), CALLOUT_FMT, ci->c_flags); + printf("\t%s\t%s", tcptimers[i], buf); + if (ci->c_flags & CALLOUT_PENDING) + printf("\t%d\n", ci->c_time - hardticks); + else + printf("\n"); + } + printf("\n\n"); + + if (tcpcb.t_state < 0 || tcpcb.t_state >= TCP_NSTATES) + printf("State: %d", tcpcb.t_state); + else + printf("State: %s", tcpstates[tcpcb.t_state]); + printf(", flags 0x%x, inpcb 0x%lx, in6pcb 0x%lx\n\n", tcpcb.t_flags, + (u_long)tcpcb.t_inpcb, (u_long)tcpcb.t_in6pcb); + + printf("rxtshift %d, rxtcur %d, dupacks %d\n", tcpcb.t_rxtshift, + tcpcb.t_rxtcur, tcpcb.t_dupacks); + printf("peermss %u, ourmss %u, segsz %u, segqlen %u\n\n", + tcpcb.t_peermss, tcpcb.t_ourmss, tcpcb.t_segsz, tcpcb.t_segqlen); + + printf("snd_una %u, snd_nxt %u, snd_up %u\n", + tcpcb.snd_una, tcpcb.snd_nxt, tcpcb.snd_up); + printf("snd_wl1 %u, snd_wl2 %u, iss %u, snd_wnd %lu\n\n", + tcpcb.snd_wl1, tcpcb.snd_wl2, tcpcb.iss, tcpcb.snd_wnd); + + printf("rcv_wnd %lu, rcv_nxt %u, rcv_up %u, irs %u\n\n", + tcpcb.rcv_wnd, tcpcb.rcv_nxt, tcpcb.rcv_up, tcpcb.irs); + + printf("rcv_adv %u, snd_max %u, snd_cwnd %lu, snd_ssthresh %lu\n", + tcpcb.rcv_adv, tcpcb.snd_max, tcpcb.snd_cwnd, tcpcb.snd_ssthresh); + + printf("rcvtime %u, rtttime %u, rtseq %u, srtt %d, rttvar %d, " + "rttmin %d, max_sndwnd %lu\n\n", tcpcb.t_rcvtime, tcpcb.t_rtttime, + tcpcb.t_rtseq, tcpcb.t_srtt, tcpcb.t_rttvar, tcpcb.t_rttmin, + tcpcb.max_sndwnd); + + printf("oobflags %d, iobc %d, softerror %d\n\n", tcpcb.t_oobflags, + tcpcb.t_iobc, tcpcb.t_softerror); + + printf("snd_scale %d, rcv_scale %d, req_r_scale %d, req_s_scale %d\n", + tcpcb.snd_scale, tcpcb.rcv_scale, tcpcb.request_r_scale, + tcpcb.requested_s_scale); + printf("ts_recent %u, ts_regent_age %d, last_ack_sent %u\n", + tcpcb.ts_recent, tcpcb.ts_recent_age, tcpcb.last_ack_sent); +} diff --git a/usr.bin/netstat/inet6.c b/usr.bin/netstat/inet6.c new file mode 100644 index 000000000..6e4442aef --- /dev/null +++ b/usr.bin/netstat/inet6.c @@ -0,0 +1,1533 @@ +/* $NetBSD: inet6.c,v 1.68 2015/02/08 15:09:45 christos Exp $ */ +/* BSDI inet.c,v 2.3 1995/10/24 02:19:29 prb Exp */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)inet.c 8.4 (Berkeley) 4/20/94"; +#else +__RCSID("$NetBSD: inet6.c,v 1.68 2015/02/08 15:09:45 christos Exp $"); +#endif +#endif /* not lint */ + +#define _CALLOUT_PRIVATE + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifndef TCP6 +#include +#include +#endif +#include +#include +#include +#ifdef TCP6 +#include +#include +#define TCP6STATES +#include +#define TCP6TIMERS +#include +#include +#include +#else +#define TCP6T_NTIMERS TCPT_NTIMERS +#define tcp6timers tcptimers +#define tcp6states tcpstates +#define TCP6_NSTATES TCP_NSTATES +#define tcp6cb tcpcb +#include +#include +#include +#include +extern const char * const tcpstates[]; +extern const char * const tcptimers[]; +#include +#include +#include +#endif /*TCP6*/ +#include +#include +#include +#include +#include + +#include +#if 0 +#include "gethostbyname2.h" +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "vtw.h" +#include "prog_ops.h" + +#ifdef INET6 + +struct in6pcb in6pcb; +#ifdef TCP6 +struct tcp6cb tcp6cb; +#else +struct tcpcb tcpcb; +#endif +struct socket sockb; + +char *inet6name(const struct in6_addr *); +void inet6print(const struct in6_addr *, int, const char *); +void print_vtw_v6(const vtw_t *); + +/* + * Print a summary of connections related to an Internet + * protocol. For TCP, also give state of connection. + * Listening processes (aflag) are suppressed unless the + * -a (all) flag is specified. + */ +static int width; +static int compact; + +/* VTW-related variables. */ +static struct timeval now; + +static void +ip6protoprhdr(void) +{ + + printf("Active Internet6 connections"); + + if (aflag) + printf(" (including servers)"); + putchar('\n'); + + if (Aflag) { + printf("%-8.8s ", "PCB"); + width = 18; + } + printf( + Vflag ? "%-5.5s %-6.6s %-6.6s %*.*s %*.*s %-13.13s Expires\n" + : "%-5.5s %-6.6s %-6.6s %*.*s %*.*s %s\n", + "Proto", "Recv-Q", "Send-Q", + -width, width, "Local Address", + -width, width, "Foreign Address", "(state)"); +} + +static void +ip6protopr0(intptr_t ppcb, u_long rcv_sb_cc, u_long snd_sb_cc, + const struct in6_addr *laddr, u_int16_t lport, + const struct in6_addr *faddr, u_int16_t fport, + short t_state, const char *name, const struct timeval *expires) +{ + static const char *shorttcpstates[] = { + "CLOSED", "LISTEN", "SYNSEN", "SYSRCV", + "ESTABL", "CLWAIT", "FWAIT1", "CLOSNG", + "LASTAK", "FWAIT2", "TMWAIT", + }; + int istcp; + + istcp = strcmp(name, "tcp6") == 0; + if (Aflag) + printf("%8" PRIxPTR " ", ppcb); + + printf("%-5.5s %6ld %6ld%s", name, rcv_sb_cc, snd_sb_cc, + compact ? "" : " "); + + inet6print(laddr, (int)lport, name); + inet6print(faddr, (int)fport, name); + if (istcp) { +#ifdef TCP6 + if (t_state < 0 || t_state >= TCP6_NSTATES) + printf(" %d", t_state); + else + printf(" %s", tcp6states[t_state]); +#else + if (t_state < 0 || t_state >= TCP_NSTATES) + printf(" %d", t_state); + else + printf(" %s", compact ? shorttcpstates[t_state] : + tcpstates[t_state]); +#endif + } + if (Vflag && expires != NULL) { + if (expires->tv_sec == 0 && expires->tv_usec == -1) + printf(" reclaimed"); + else { + struct timeval delta; + + timersub(expires, &now, &delta); + printf(" %.3fms", + delta.tv_sec * 1000.0 + delta.tv_usec / 1000.0); + } + } + putchar('\n'); +} + +static void +dbg_printf(const char *fmt, ...) +{ + return; +} + +void +print_vtw_v6(const vtw_t *vtw) +{ + const vtw_v6_t *v6 = (const vtw_v6_t *)vtw; + struct timeval delta; + char buf[2][128]; + static const struct timeval zero = {.tv_sec = 0, .tv_usec = 0}; + + inet_ntop(AF_INET6, &v6->laddr, buf[0], sizeof(buf[0])); + inet_ntop(AF_INET6, &v6->faddr, buf[1], sizeof(buf[1])); + + timersub(&vtw->expire, &now, &delta); + + if (vtw->expire.tv_sec == 0 && vtw->expire.tv_usec == -1) { + dbg_printf("%15.15s:%d %15.15s:%d reclaimed\n" + ,buf[0], ntohs(v6->lport) + ,buf[1], ntohs(v6->fport)); + if (!(Vflag && vflag)) + return; + } else if (vtw->expire.tv_sec == 0) + return; + else if (timercmp(&delta, &zero, <) && !(Vflag && vflag)) { + dbg_printf("%15.15s:%d %15.15s:%d expired\n" + ,buf[0], ntohs(v6->lport) + ,buf[1], ntohs(v6->fport)); + return; + } else { + dbg_printf("%15.15s:%d %15.15s:%d expires in %.3fms\n" + ,buf[0], ntohs(v6->lport) + ,buf[1], ntohs(v6->fport) + ,delta.tv_sec * 1000.0 + delta.tv_usec / 1000.0); + } + ip6protopr0(0, 0, 0, + &v6->laddr, v6->lport, + &v6->faddr, v6->fport, + TCPS_TIME_WAIT, "tcp6", &vtw->expire); +} + + +static struct kinfo_pcb * +getpcblist_kmem(u_long off, const char *name, size_t *len) { + + struct inpcbtable table; + struct inpcb_hdr *next, *prev; + int istcp = strcmp(name, "tcp6") == 0; + struct kinfo_pcb *pcblist; + size_t size = 100, i; + struct sockaddr_in6 sin6; + struct inpcbqueue *head; + + if (off == 0) { + *len = 0; + return NULL; + } + kread(off, (char *)&table, sizeof (table)); + head = &table.inpt_queue; + next = TAILQ_FIRST(head); + prev = TAILQ_END(head); + + if ((pcblist = malloc(size * sizeof(*pcblist))) == NULL) + err(1, "malloc"); + + i = 0; + while (next != TAILQ_END(head)) { + kread((u_long)next, (char *)&in6pcb, sizeof in6pcb); + next = TAILQ_NEXT(&in6pcb, in6p_queue); + prev = next; + + if (in6pcb.in6p_af != AF_INET6) + continue; + + kread((u_long)in6pcb.in6p_socket, (char *)&sockb, + sizeof (sockb)); + if (istcp) { +#ifdef TCP6 + kread((u_long)in6pcb.in6p_ppcb, + (char *)&tcp6cb, sizeof (tcp6cb)); +#else + kread((u_long)in6pcb.in6p_ppcb, + (char *)&tcpcb, sizeof (tcpcb)); +#endif + } + pcblist[i].ki_ppcbaddr = + istcp ? (uintptr_t) in6pcb.in6p_ppcb : (uintptr_t) prev; + pcblist[i].ki_rcvq = (uint64_t)sockb.so_rcv.sb_cc; + pcblist[i].ki_sndq = (uint64_t)sockb.so_snd.sb_cc; + sin6.sin6_addr = in6pcb.in6p_laddr; + sin6.sin6_port = in6pcb.in6p_lport; + memcpy(&pcblist[i].ki_s, &sin6, sizeof(sin6)); + sin6.sin6_addr = in6pcb.in6p_faddr; + sin6.sin6_port = in6pcb.in6p_fport; + memcpy(&pcblist[i].ki_d, &sin6, sizeof(sin6)); + pcblist[i].ki_tstate = tcpcb.t_state; + if (i++ == size) { + size += 100; + struct kinfo_pcb *n = realloc(pcblist, + size * sizeof(*pcblist)); + if (n == NULL) + err(1, "realloc"); + pcblist = n; + } + } + *len = i; + return pcblist; +} + +void +ip6protopr(u_long off, const char *name) +{ + struct kinfo_pcb *pcblist; + size_t i, len; + static int first = 1; + + compact = 0; + if (Aflag) { + if (!numeric_addr) + width = 18; + else { + width = 21; + compact = 1; + } + } else + width = 22; + + if (use_sysctl) + pcblist = getpcblist_sysctl(name, &len); + else + pcblist = getpcblist_kmem(off, name, &len); + + for (i = 0; i < len; i++) { + struct sockaddr_in6 src, dst; + + memcpy(&src, &pcblist[i].ki_s, sizeof(src)); + memcpy(&dst, &pcblist[i].ki_d, sizeof(dst)); + + if (!aflag && IN6_IS_ADDR_UNSPECIFIED(&dst.sin6_addr)) + continue; + + if (first) { + ip6protoprhdr(); + first = 0; + } + + ip6protopr0((intptr_t) pcblist[i].ki_ppcbaddr, + pcblist[i].ki_rcvq, pcblist[i].ki_sndq, + &src.sin6_addr, src.sin6_port, + &dst.sin6_addr, dst.sin6_port, + pcblist[i].ki_tstate, name, NULL); + } + + free(pcblist); + +#ifndef __minix + if (strcmp(name, "tcp6") == 0) { + struct timeval t; + timebase(&t); + gettimeofday(&now, NULL); + timersub(&now, &t, &now); + show_vtw_v6(print_vtw_v6); + } +#endif /* !__minix */ +} + +#ifdef TCP6 +/* + * Dump TCP6 statistics structure. + */ +void +tcp6_stats(u_long off, const char *name) +{ + struct tcp6stat tcp6stat; + + if (use_sysctl) { + size_t size = sizeof(tcp6stat); + + if (sysctlbyname("net.inet6.tcp6.stats", &tcp6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf ("%s:\n", name); + +#define p(f, m) if (tcp6stat.f || sflag <= 1) \ + printf(m, tcp6stat.f, plural(tcp6stat.f)) +#define p2(f1, f2, m) if (tcp6stat.f1 || tcp6stat.f2 || sflag <= 1) \ + printf(m, tcp6stat.f1, plural(tcp6stat.f1), tcp6stat.f2, plural(tcp6stat.f2)) +#define p3(f, m) if (tcp6stat.f || sflag <= 1) \ + printf(m, tcp6stat.f, plurales(tcp6stat.f)) + + p(tcp6s_sndtotal, "\t%ld packet%s sent\n"); + p2(tcp6s_sndpack,tcp6s_sndbyte, + "\t\t%ld data packet%s (%ld byte%s)\n"); + p2(tcp6s_sndrexmitpack, tcp6s_sndrexmitbyte, + "\t\t%ld data packet%s (%ld byte%s) retransmitted\n"); + p2(tcp6s_sndacks, tcp6s_delack, + "\t\t%ld ack-only packet%s (%ld packet%s delayed)\n"); + p(tcp6s_sndurg, "\t\t%ld URG only packet%s\n"); + p(tcp6s_sndprobe, "\t\t%ld window probe packet%s\n"); + p(tcp6s_sndwinup, "\t\t%ld window update packet%s\n"); + p(tcp6s_sndctrl, "\t\t%ld control packet%s\n"); + p(tcp6s_rcvtotal, "\t%ld packet%s received\n"); + p2(tcp6s_rcvackpack, tcp6s_rcvackbyte, "\t\t%ld ack%s (for %ld byte%s)\n"); + p(tcp6s_rcvdupack, "\t\t%ld duplicate ack%s\n"); + p(tcp6s_rcvacktoomuch, "\t\t%ld ack%s for unsent data\n"); + p2(tcp6s_rcvpack, tcp6s_rcvbyte, + "\t\t%ld packet%s (%ld byte%s) received in-sequence\n"); + p2(tcp6s_rcvduppack, tcp6s_rcvdupbyte, + "\t\t%ld completely duplicate packet%s (%ld byte%s)\n"); + p(tcp6s_pawsdrop, "\t\t%ld old duplicate packet%s\n"); + p2(tcp6s_rcvpartduppack, tcp6s_rcvpartdupbyte, + "\t\t%ld packet%s with some dup. data (%ld byte%s duped)\n"); + p2(tcp6s_rcvoopack, tcp6s_rcvoobyte, + "\t\t%ld out-of-order packet%s (%ld byte%s)\n"); + p2(tcp6s_rcvpackafterwin, tcp6s_rcvbyteafterwin, + "\t\t%ld packet%s (%ld byte%s) of data after window\n"); + p(tcp6s_rcvwinprobe, "\t\t%ld window probe%s\n"); + p(tcp6s_rcvwinupd, "\t\t%ld window update packet%s\n"); + p(tcp6s_rcvafterclose, "\t\t%ld packet%s received after close\n"); + p(tcp6s_rcvbadsum, "\t\t%ld discarded for bad checksum%s\n"); + p(tcp6s_rcvbadoff, "\t\t%ld discarded for bad header offset field%s\n"); + p(tcp6s_rcvshort, "\t\t%ld discarded because packet%s too short\n"); + p(tcp6s_connattempt, "\t%ld connection request%s\n"); + p(tcp6s_accepts, "\t%ld connection accept%s\n"); + p(tcp6s_badsyn, "\t%ld bad connection attempt%s\n"); + p(tcp6s_connects, "\t%ld connection%s established (including accepts)\n"); + p2(tcp6s_closed, tcp6s_drops, + "\t%ld connection%s closed (including %ld drop%s)\n"); + p(tcp6s_conndrops, "\t%ld embryonic connection%s dropped\n"); + p2(tcp6s_rttupdated, tcp6s_segstimed, + "\t%ld segment%s updated rtt (of %ld attempt%s)\n"); + p(tcp6s_rexmttimeo, "\t%ld retransmit timeout%s\n"); + p(tcp6s_timeoutdrop, "\t\t%ld connection%s dropped by rexmit timeout\n"); + p(tcp6s_persisttimeo, "\t%ld persist timeout%s\n"); + p(tcp6s_persistdrop, "\t%ld connection%s timed out in persist\n"); + p(tcp6s_keeptimeo, "\t%ld keepalive timeout%s\n"); + p(tcp6s_keepprobe, "\t\t%ld keepalive probe%s sent\n"); + p(tcp6s_keepdrops, "\t\t%ld connection%s dropped by keepalive\n"); + p(tcp6s_predack, "\t%ld correct ACK header prediction%s\n"); + p(tcp6s_preddat, "\t%ld correct data packet header prediction%s\n"); + p3(tcp6s_pcbcachemiss, "\t%ld PCB cache miss%s\n"); +#undef p +#undef p2 +#undef p3 +} +#endif + +/* + * Dump UDP6 statistics structure. + */ +void +udp6_stats(u_long off, const char *name) +{ + uint64_t udp6stat[UDP6_NSTATS]; + u_quad_t delivered; + + if (use_sysctl) { + size_t size = sizeof(udp6stat); + + if (sysctlbyname("net.inet6.udp6.stats", udp6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + printf("%s:\n", name); +#define p(f, m) if (udp6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)udp6stat[f], plural(udp6stat[f])) +#define p1(f, m) if (udp6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)udp6stat[f]) + p(UDP6_STAT_IPACKETS, "\t%llu datagram%s received\n"); + p1(UDP6_STAT_HDROPS, "\t%llu with incomplete header\n"); + p1(UDP6_STAT_BADLEN, "\t%llu with bad data length field\n"); + p1(UDP6_STAT_BADSUM, "\t%llu with bad checksum\n"); + p1(UDP6_STAT_NOSUM, "\t%llu with no checksum\n"); + p1(UDP6_STAT_NOPORT, "\t%llu dropped due to no socket\n"); + p(UDP6_STAT_NOPORTMCAST, + "\t%llu multicast datagram%s dropped due to no socket\n"); + p1(UDP6_STAT_FULLSOCK, "\t%llu dropped due to full socket buffers\n"); + delivered = udp6stat[UDP6_STAT_IPACKETS] - + udp6stat[UDP6_STAT_HDROPS] - + udp6stat[UDP6_STAT_BADLEN] - + udp6stat[UDP6_STAT_BADSUM] - + udp6stat[UDP6_STAT_NOPORT] - + udp6stat[UDP6_STAT_NOPORTMCAST] - + udp6stat[UDP6_STAT_FULLSOCK]; + if (delivered || sflag <= 1) + printf("\t%llu delivered\n", (unsigned long long)delivered); + p(UDP6_STAT_OPACKETS, "\t%llu datagram%s output\n"); +#undef p +#undef p1 +} + +static const char *ip6nh[] = { +/*0*/ "hop by hop", + "ICMP", + "IGMP", + NULL, + "IP", +/*5*/ NULL, + "TCP", + NULL, + NULL, + NULL, +/*10*/ NULL, NULL, NULL, NULL, NULL, +/*15*/ NULL, + NULL, + "UDP", + NULL, + NULL, +/*20*/ NULL, + NULL, + "IDP", + NULL, + NULL, +/*25*/ NULL, + NULL, + NULL, + NULL, + NULL, +/*30*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*40*/ NULL, + "IP6", + NULL, + "routing", + "fragment", +/*45*/ NULL, NULL, NULL, NULL, NULL, +/*50*/ "ESP", + "AH", + NULL, + NULL, + NULL, +/*55*/ NULL, + NULL, + NULL, + "ICMP6", + "no next header", +/*60*/ "destination option", + NULL, + NULL, + NULL, + NULL, +/*65*/ NULL, NULL, NULL, NULL, NULL, +/*70*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*80*/ NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "OSPF", +/*90*/ NULL, NULL, NULL, NULL, NULL, +/*95*/ NULL, + NULL, + "Ethernet", + NULL, + NULL, +/*100*/ NULL, + NULL, + NULL, + "PIM", + NULL, +/*105*/ NULL, NULL, NULL, NULL, NULL, +/*110*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*120*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*130*/ NULL, + NULL, + "SCTP", + NULL, + NULL, +/*135*/ NULL, NULL, NULL, NULL, NULL, +/*140*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*160*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*180*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*200*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*220*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/*240*/ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* + * Dump IP6 statistics structure. + */ +void +ip6_stats(u_long off, const char *name) +{ + uint64_t ip6stat[IP6_NSTATS]; + int first, i; + struct protoent *ep; + const char *n; + + if (use_sysctl) { + size_t size = sizeof(ip6stat); + + if (sysctlbyname("net.inet6.ip6.stats", ip6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + printf("%s:\n", name); + +#define p(f, m) if (ip6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)ip6stat[f], plural(ip6stat[f])) +#define p1(f, m) if (ip6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)ip6stat[f]) + + p(IP6_STAT_TOTAL, "\t%llu total packet%s received\n"); + p1(IP6_STAT_TOOSMALL, "\t%llu with size smaller than minimum\n"); + p1(IP6_STAT_TOOSHORT, "\t%llu with data size < data length\n"); + p1(IP6_STAT_BADOPTIONS, "\t%llu with bad options\n"); + p1(IP6_STAT_BADVERS, "\t%llu with incorrect version number\n"); + p(IP6_STAT_FRAGMENTS, "\t%llu fragment%s received\n"); + p(IP6_STAT_FRAGDROPPED, + "\t%llu fragment%s dropped (dup or out of space)\n"); + p(IP6_STAT_FRAGTIMEOUT, "\t%llu fragment%s dropped after timeout\n"); + p(IP6_STAT_FRAGOVERFLOW, "\t%llu fragment%s that exceeded limit\n"); + p(IP6_STAT_REASSEMBLED, "\t%llu packet%s reassembled ok\n"); + p(IP6_STAT_DELIVERED, "\t%llu packet%s for this host\n"); + p(IP6_STAT_FORWARD, "\t%llu packet%s forwarded\n"); + p(IP6_STAT_FASTFORWARD, "\t%llu packet%s fast forwarded\n"); + p1(IP6_STAT_FASTFORWARDFLOWS, "\t%llu fast forward flows\n"); + p(IP6_STAT_CANTFORWARD, "\t%llu packet%s not forwardable\n"); + p(IP6_STAT_REDIRECTSENT, "\t%llu redirect%s sent\n"); + p(IP6_STAT_LOCALOUT, "\t%llu packet%s sent from this host\n"); + p(IP6_STAT_RAWOUT, "\t%llu packet%s sent with fabricated ip header\n"); + p(IP6_STAT_ODROPPED, + "\t%llu output packet%s dropped due to no bufs, etc.\n"); + p(IP6_STAT_NOROUTE, "\t%llu output packet%s discarded due to no route\n"); + p(IP6_STAT_FRAGMENTED, "\t%llu output datagram%s fragmented\n"); + p(IP6_STAT_OFRAGMENTS, "\t%llu fragment%s created\n"); + p(IP6_STAT_CANTFRAG, "\t%llu datagram%s that can't be fragmented\n"); + p(IP6_STAT_BADSCOPE, "\t%llu packet%s that violated scope rules\n"); + p(IP6_STAT_NOTMEMBER, "\t%llu multicast packet%s which we don't join\n"); + for (first = 1, i = 0; i < 256; i++) + if (ip6stat[IP6_STAT_NXTHIST + i] != 0) { + if (first) { + printf("\tInput packet histogram:\n"); + first = 0; + } + n = NULL; + if (ip6nh[i]) + n = ip6nh[i]; + else if ((ep = getprotobynumber(i)) != NULL) + n = ep->p_name; + if (n) + printf("\t\t%s: %llu\n", n, + (unsigned long long)ip6stat[IP6_STAT_NXTHIST + i]); + else + printf("\t\t#%d: %llu\n", i, + (unsigned long long)ip6stat[IP6_STAT_NXTHIST + i]); + } + printf("\tMbuf statistics:\n"); + p(IP6_STAT_M1, "\t\t%llu one mbuf%s\n"); + for (first = 1, i = 0; i < 32; i++) { + char ifbuf[IFNAMSIZ]; + if (ip6stat[IP6_STAT_M2M + i] != 0) { + if (first) { + printf("\t\ttwo or more mbuf:\n"); + first = 0; + } + printf("\t\t\t%s = %llu\n", + if_indextoname(i, ifbuf), + (unsigned long long)ip6stat[IP6_STAT_M2M + i]); + } + } + p(IP6_STAT_MEXT1, "\t\t%llu one ext mbuf%s\n"); + p(IP6_STAT_MEXT2M, "\t\t%llu two or more ext mbuf%s\n"); + p(IP6_STAT_EXTHDRTOOLONG, + "\t%llu packet%s whose headers are not continuous\n"); + p(IP6_STAT_NOGIF, "\t%llu tunneling packet%s that can't find gif\n"); + p(IP6_STAT_TOOMANYHDR, + "\t%llu packet%s discarded due to too many headers\n"); + + /* for debugging source address selection */ +#define PRINT_SCOPESTAT(s,i) do {\ + switch(i) { /* XXX hardcoding in each case */\ + case 1:\ + p(s, "\t\t%llu node-local%s\n");\ + break;\ + case 2:\ + p(s, "\t\t%llu link-local%s\n");\ + break;\ + case 5:\ + p(s, "\t\t%llu site-local%s\n");\ + break;\ + case 14:\ + p(s, "\t\t%llu global%s\n");\ + break;\ + default:\ + printf("\t\t%llu addresses scope=%x\n",\ + (unsigned long long)ip6stat[s], i);\ + }\ + } while(/*CONSTCOND*/0); + + p(IP6_STAT_SOURCES_NONE, + "\t%llu failure%s of source address selection\n"); + for (first = 1, i = 0; i < 16; i++) { + if (ip6stat[IP6_STAT_SOURCES_SAMEIF + i]) { + if (first) { + printf("\tsource addresses on an outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(IP6_STAT_SOURCES_SAMEIF + i, i); + } + } + for (first = 1, i = 0; i < 16; i++) { + if (ip6stat[IP6_STAT_SOURCES_OTHERIF + i]) { + if (first) { + printf("\tsource addresses on a non-outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(IP6_STAT_SOURCES_OTHERIF + i, i); + } + } + for (first = 1, i = 0; i < 16; i++) { + if (ip6stat[IP6_STAT_SOURCES_SAMESCOPE + i]) { + if (first) { + printf("\tsource addresses of same scope\n"); + first = 0; + } + PRINT_SCOPESTAT(IP6_STAT_SOURCES_SAMESCOPE + i, i); + } + } + for (first = 1, i = 0; i < 16; i++) { + if (ip6stat[IP6_STAT_SOURCES_OTHERSCOPE + i]) { + if (first) { + printf("\tsource addresses of a different scope\n"); + first = 0; + } + PRINT_SCOPESTAT(IP6_STAT_SOURCES_OTHERSCOPE + i, i); + } + } + for (first = 1, i = 0; i < 16; i++) { + if (ip6stat[IP6_STAT_SOURCES_DEPRECATED + i]) { + if (first) { + printf("\tdeprecated source addresses\n"); + first = 0; + } + PRINT_SCOPESTAT(IP6_STAT_SOURCES_DEPRECATED + i, i); + } + } + + p1(IP6_STAT_FORWARD_CACHEHIT, "\t%llu forward cache hit\n"); + p1(IP6_STAT_FORWARD_CACHEMISS, "\t%llu forward cache miss\n"); +#undef p +#undef p1 +} + +/* + * Dump IPv6 per-interface statistics based on RFC 2465. + */ +void +ip6_ifstats(const char *ifname) +{ + struct in6_ifreq ifr; + int s; +#define p(f, m) if (ifr.ifr_ifru.ifru_stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ifr.ifr_ifru.ifru_stat.f, \ + plural(ifr.ifr_ifru.ifru_stat.f)) +#define p_5(f, m) if (ifr.ifr_ifru.ifru_stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ip6stat.f) + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET6)"); + return; + } + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("ip6 on %s:\n", ifname); + + if (ioctl(s, SIOCGIFSTAT_IN6, (char *)&ifr) < 0) { + perror("Warning: ioctl(SIOCGIFSTAT_IN6)"); + goto end; + } + + p(ifs6_in_receive, "\t%llu total input datagram%s\n"); + p(ifs6_in_hdrerr, "\t%llu datagram%s with invalid header received\n"); + p(ifs6_in_toobig, "\t%llu datagram%s exceeded MTU received\n"); + p(ifs6_in_noroute, "\t%llu datagram%s with no route received\n"); + p(ifs6_in_addrerr, "\t%llu datagram%s with invalid dst received\n"); + p(ifs6_in_truncated, "\t%llu truncated datagram%s received\n"); + p(ifs6_in_protounknown, "\t%llu datagram%s with unknown proto received\n"); + p(ifs6_in_discard, "\t%llu input datagram%s discarded\n"); + p(ifs6_in_deliver, + "\t%llu datagram%s delivered to an upper layer protocol\n"); + p(ifs6_out_forward, "\t%llu datagram%s forwarded to this interface\n"); + p(ifs6_out_request, + "\t%llu datagram%s sent from an upper layer protocol\n"); + p(ifs6_out_discard, "\t%llu total discarded output datagram%s\n"); + p(ifs6_out_fragok, "\t%llu output datagram%s fragmented\n"); + p(ifs6_out_fragfail, "\t%llu output datagram%s failed on fragment\n"); + p(ifs6_out_fragcreat, "\t%llu output datagram%s succeeded on fragment\n"); + p(ifs6_reass_reqd, "\t%llu incoming datagram%s fragmented\n"); + p(ifs6_reass_ok, "\t%llu datagram%s reassembled\n"); + p(ifs6_reass_fail, "\t%llu datagram%s failed on reassembling\n"); + p(ifs6_in_mcast, "\t%llu multicast datagram%s received\n"); + p(ifs6_out_mcast, "\t%llu multicast datagram%s sent\n"); + + end: + close(s); + +#undef p +#undef p_5 +} + +static const char *icmp6names[] = { + "#0", + "unreach", + "packet too big", + "time exceed", + "parameter problem", + "#5", + "#6", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "#17", + "#18", + "#19", + "#20", + "#21", + "#22", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "#29", + "#30", + "#31", + "#32", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "#41", + "#42", + "#43", + "#44", + "#45", + "#46", + "#47", + "#48", + "#49", + "#50", + "#51", + "#52", + "#53", + "#54", + "#55", + "#56", + "#57", + "#58", + "#59", + "#60", + "#61", + "#62", + "#63", + "#64", + "#65", + "#66", + "#67", + "#68", + "#69", + "#70", + "#71", + "#72", + "#73", + "#74", + "#75", + "#76", + "#77", + "#78", + "#79", + "#80", + "#81", + "#82", + "#83", + "#84", + "#85", + "#86", + "#87", + "#88", + "#89", + "#80", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "#97", + "#98", + "#99", + "#100", + "#101", + "#102", + "#103", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "echo", + "echo reply", + "multicast listener query", + "multicast listener report", + "multicast listener done", + "router solicitation", + "router advertisement", + "neighbor solicitation", + "neighbor advertisement", + "redirect", + "router renumbering", + "node information request", + "node information reply", + "#141", + "#142", + "#143", + "#144", + "#145", + "#146", + "#147", + "#148", + "#149", + "#150", + "#151", + "#152", + "#153", + "#154", + "#155", + "#156", + "#157", + "#158", + "#159", + "#160", + "#161", + "#162", + "#163", + "#164", + "#165", + "#166", + "#167", + "#168", + "#169", + "#170", + "#171", + "#172", + "#173", + "#174", + "#175", + "#176", + "#177", + "#178", + "#179", + "#180", + "#181", + "#182", + "#183", + "#184", + "#185", + "#186", + "#187", + "#188", + "#189", + "#180", + "#191", + "#192", + "#193", + "#194", + "#195", + "#196", + "#197", + "#198", + "#199", + "#200", + "#201", + "#202", + "#203", + "#204", + "#205", + "#206", + "#207", + "#208", + "#209", + "#210", + "#211", + "#212", + "#213", + "#214", + "#215", + "#216", + "#217", + "#218", + "#219", + "#220", + "#221", + "#222", + "#223", + "#224", + "#225", + "#226", + "#227", + "#228", + "#229", + "#230", + "#231", + "#232", + "#233", + "#234", + "#235", + "#236", + "#237", + "#238", + "#239", + "#240", + "#241", + "#242", + "#243", + "#244", + "#245", + "#246", + "#247", + "#248", + "#249", + "#250", + "#251", + "#252", + "#253", + "#254", + "#255", +}; + +/* + * Dump ICMPv6 statistics. + */ +void +icmp6_stats(u_long off, const char *name) +{ + uint64_t icmp6stat[ICMP6_NSTATS]; + int i, first; + + if (use_sysctl) { + size_t size = sizeof(icmp6stat); + + if (sysctlbyname("net.inet6.icmp6.stats", icmp6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (icmp6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)icmp6stat[f], plural(icmp6stat[f])) +#define p_oerr(f, m) if (icmp6stat[ICMP6_STAT_OUTERRHIST + f] || sflag <= 1) \ + printf(m, (unsigned long long)icmp6stat[ICMP6_STAT_OUTERRHIST + f]) + + p(ICMP6_STAT_ERROR, "\t%llu call%s to icmp6_error\n"); + p(ICMP6_STAT_CANTERROR, + "\t%llu error%s not generated because old message was icmp6 or so\n"); + p(ICMP6_STAT_TOOFREQ, + "\t%llu error%s not generated because of rate limitation\n"); + for (first = 1, i = 0; i < 256; i++) + if (icmp6stat[ICMP6_STAT_OUTHIST + i] != 0) { + if (first) { + printf("\tOutput packet histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)icmp6stat[ICMP6_STAT_OUTHIST + i]); + } + p(ICMP6_STAT_BADCODE, "\t%llu message%s with bad code fields\n"); + p(ICMP6_STAT_TOOSHORT, "\t%llu message%s < minimum length\n"); + p(ICMP6_STAT_CHECKSUM, "\t%llu bad checksum%s\n"); + p(ICMP6_STAT_BADLEN, "\t%llu message%s with bad length\n"); + for (first = 1, i = 0; i < ICMP6_MAXTYPE; i++) + if (icmp6stat[ICMP6_STAT_INHIST + i] != 0) { + if (first) { + printf("\tInput packet histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)icmp6stat[ICMP6_STAT_INHIST + i]); + } + printf("\tHistogram of error messages to be generated:\n"); + p_oerr(ICMP6_ERRSTAT_DST_UNREACH_NOROUTE, "\t\t%llu no route\n"); + p_oerr(ICMP6_ERRSTAT_DST_UNREACH_ADMIN, "\t\t%llu administratively prohibited\n"); + p_oerr(ICMP6_ERRSTAT_DST_UNREACH_BEYONDSCOPE, "\t\t%llu beyond scope\n"); + p_oerr(ICMP6_ERRSTAT_DST_UNREACH_ADDR, "\t\t%llu address unreachable\n"); + p_oerr(ICMP6_ERRSTAT_DST_UNREACH_NOPORT, "\t\t%llu port unreachable\n"); + p_oerr(ICMP6_ERRSTAT_PACKET_TOO_BIG, "\t\t%llu packet too big\n"); + p_oerr(ICMP6_ERRSTAT_TIME_EXCEED_TRANSIT, "\t\t%llu time exceed transit\n"); + p_oerr(ICMP6_ERRSTAT_TIME_EXCEED_REASSEMBLY, "\t\t%llu time exceed reassembly\n"); + p_oerr(ICMP6_ERRSTAT_PARAMPROB_HEADER, "\t\t%llu erroneous header field\n"); + p_oerr(ICMP6_ERRSTAT_PARAMPROB_NEXTHEADER, "\t\t%llu unrecognized next header\n"); + p_oerr(ICMP6_ERRSTAT_PARAMPROB_OPTION, "\t\t%llu unrecognized option\n"); + p_oerr(ICMP6_ERRSTAT_REDIRECT, "\t\t%llu redirect\n"); + p_oerr(ICMP6_ERRSTAT_UNKNOWN, "\t\t%llu unknown\n"); + + p(ICMP6_STAT_REFLECT, "\t%llu message response%s generated\n"); + p(ICMP6_STAT_ND_TOOMANYOPT, "\t%llu message%s with too many ND options\n"); + p(ICMP6_STAT_ND_BADOPT, "\t%llu message%s with bad ND options\n"); + p(ICMP6_STAT_BADNS, "\t%llu bad neighbor solicitation message%s\n"); + p(ICMP6_STAT_BADNA, "\t%llu bad neighbor advertisement message%s\n"); + p(ICMP6_STAT_BADRS, "\t%llu bad router solicitation message%s\n"); + p(ICMP6_STAT_BADRA, "\t%llu bad router advertisement message%s\n"); + p(ICMP6_STAT_DROPPED_RAROUTE, "\t%llu router advertisement route%s dropped\n"); + p(ICMP6_STAT_BADREDIRECT, "\t%llu bad redirect message%s\n"); + p(ICMP6_STAT_PMTUCHG, "\t%llu path MTU change%s\n"); +#undef p +#undef p_oerr +} + +/* + * Dump ICMPv6 per-interface statistics based on RFC 2466. + */ +void +icmp6_ifstats(const char *ifname) +{ + struct in6_ifreq ifr; + int s; +#define p(f, m) if (ifr.ifr_ifru.ifru_icmp6stat.f || sflag <= 1) \ + printf(m, (unsigned long long)ifr.ifr_ifru.ifru_icmp6stat.f, \ + plural(ifr.ifr_ifru.ifru_icmp6stat.f)) + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET6)"); + return; + } + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("icmp6 on %s:\n", ifname); + + if (ioctl(s, SIOCGIFSTAT_ICMP6, (char *)&ifr) < 0) { + perror("Warning: ioctl(SIOCGIFSTAT_ICMP6)"); + goto end; + } + + p(ifs6_in_msg, "\t%llu total input message%s\n"); + p(ifs6_in_error, "\t%llu total input error message%s\n"); + p(ifs6_in_dstunreach, "\t%llu input destination unreachable error%s\n"); + p(ifs6_in_adminprohib, "\t%llu input administratively prohibited error%s\n"); + p(ifs6_in_timeexceed, "\t%llu input time exceeded error%s\n"); + p(ifs6_in_paramprob, "\t%llu input parameter problem error%s\n"); + p(ifs6_in_pkttoobig, "\t%llu input packet too big error%s\n"); + p(ifs6_in_echo, "\t%llu input echo request%s\n"); + p(ifs6_in_echoreply, "\t%llu input echo reply%s\n"); + p(ifs6_in_routersolicit, "\t%llu input router solicitation%s\n"); + p(ifs6_in_routeradvert, "\t%llu input router advertisement%s\n"); + p(ifs6_in_neighborsolicit, "\t%llu input neighbor solicitation%s\n"); + p(ifs6_in_neighboradvert, "\t%llu input neighbor advertisement%s\n"); + p(ifs6_in_redirect, "\t%llu input redirect%s\n"); + p(ifs6_in_mldquery, "\t%llu input MLD query%s\n"); + p(ifs6_in_mldreport, "\t%llu input MLD report%s\n"); + p(ifs6_in_mlddone, "\t%llu input MLD done%s\n"); + + p(ifs6_out_msg, "\t%llu total output message%s\n"); + p(ifs6_out_error, "\t%llu total output error message%s\n"); + p(ifs6_out_dstunreach, "\t%llu output destination unreachable error%s\n"); + p(ifs6_out_adminprohib, "\t%llu output administratively prohibited error%s\n"); + p(ifs6_out_timeexceed, "\t%llu output time exceeded error%s\n"); + p(ifs6_out_paramprob, "\t%llu output parameter problem error%s\n"); + p(ifs6_out_pkttoobig, "\t%llu output packet too big error%s\n"); + p(ifs6_out_echo, "\t%llu output echo request%s\n"); + p(ifs6_out_echoreply, "\t%llu output echo reply%s\n"); + p(ifs6_out_routersolicit, "\t%llu output router solicitation%s\n"); + p(ifs6_out_routeradvert, "\t%llu output router advertisement%s\n"); + p(ifs6_out_neighborsolicit, "\t%llu output neighbor solicitation%s\n"); + p(ifs6_out_neighboradvert, "\t%llu output neighbor advertisement%s\n"); + p(ifs6_out_redirect, "\t%llu output redirect%s\n"); + p(ifs6_out_mldquery, "\t%llu output MLD query%s\n"); + p(ifs6_out_mldreport, "\t%llu output MLD report%s\n"); + p(ifs6_out_mlddone, "\t%llu output MLD done%s\n"); + + end: + close(s); +#undef p +} + +/* + * Dump PIM statistics structure. + */ +void +pim6_stats(u_long off, const char *name) +{ + uint64_t pim6stat[PIM6_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(pim6stat); + + if (sysctlbyname("net.inet6.pim6.stats", pim6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + printf("%s:\n", name); + +#define p(f, m) if (pim6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)pim6stat[f], plural(pim6stat[f])) + p(PIM6_STAT_RCV_TOTAL, "\t%llu message%s received\n"); + p(PIM6_STAT_RCV_TOOSHORT, "\t%llu message%s received with too few bytes\n"); + p(PIM6_STAT_RCV_BADSUM, "\t%llu message%s received with bad checksum\n"); + p(PIM6_STAT_RCV_BADVERSION, "\t%llu message%s received with bad version\n"); + p(PIM6_STAT_RCV_REGISTERS, "\t%llu register%s received\n"); + p(PIM6_STAT_RCV_BADREGISTERS, "\t%llu bad register%s received\n"); + p(PIM6_STAT_SND_REGISTERS, "\t%llu register%s sent\n"); +#undef p +} + +/* + * Dump raw ip6 statistics structure. + */ +void +rip6_stats(u_long off, const char *name) +{ + uint64_t rip6stat[RIP6_NSTATS]; + u_quad_t delivered; + + if (use_sysctl) { + size_t size = sizeof(rip6stat); + + if (sysctlbyname("net.inet6.raw6.stats", rip6stat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + printf("%s:\n", name); + +#define p(f, m) if (rip6stat[f] || sflag <= 1) \ + printf(m, (unsigned long long)rip6stat[f], plural(rip6stat[f])) + p(RIP6_STAT_IPACKETS, "\t%llu message%s received\n"); + p(RIP6_STAT_ISUM, "\t%llu checksum calculation%s on inbound\n"); + p(RIP6_STAT_BADSUM, "\t%llu message%s with bad checksum\n"); + p(RIP6_STAT_NOSOCK, "\t%llu message%s dropped due to no socket\n"); + p(RIP6_STAT_NOSOCKMCAST, + "\t%llu multicast message%s dropped due to no socket\n"); + p(RIP6_STAT_FULLSOCK, + "\t%llu message%s dropped due to full socket buffers\n"); + delivered = rip6stat[RIP6_STAT_IPACKETS] - + rip6stat[RIP6_STAT_BADSUM] - + rip6stat[RIP6_STAT_NOSOCK] - + rip6stat[RIP6_STAT_NOSOCKMCAST] - + rip6stat[RIP6_STAT_FULLSOCK]; + if (delivered || sflag <= 1) + printf("\t%llu delivered\n", (unsigned long long)delivered); + p(RIP6_STAT_OPACKETS, "\t%llu datagram%s output\n"); +#undef p +} + +/* + * Pretty print an Internet address (net address + port). + * Take numeric_addr and numeric_port into consideration. + */ +void +inet6print(const struct in6_addr *in6, int port, const char *proto) +{ +#define GETSERVBYPORT6(port, proto, ret)\ +do {\ + if (strcmp((proto), "tcp6") == 0)\ + (ret) = getservbyport((int)(port), "tcp");\ + else if (strcmp((proto), "udp6") == 0)\ + (ret) = getservbyport((int)(port), "udp");\ + else\ + (ret) = getservbyport((int)(port), (proto));\ +} while (0) + struct servent *sp = 0; + char line[80], *cp; + int lwidth; + + lwidth = Aflag ? 12 : 16; + if (vflag && lwidth < (int)strlen(inet6name(in6))) + lwidth = strlen(inet6name(in6)); + snprintf(line, sizeof(line), "%.*s.", lwidth, inet6name(in6)); + cp = strchr(line, '\0'); + if (!numeric_port && port) + GETSERVBYPORT6(port, proto, sp); + if (sp || port == 0) + snprintf(cp, sizeof(line) - (cp - line), + "%s", sp ? sp->s_name : "*"); + else + snprintf(cp, sizeof(line) - (cp - line), + "%d", ntohs((u_short)port)); + lwidth = Aflag ? 18 : 22; + if (vflag && lwidth < (int)strlen(line)) + lwidth = strlen(line); + printf(" %-*.*s", lwidth, lwidth, line); +} + +/* + * Construct an Internet address representation. + * If the numeric_addr has been supplied, give + * numeric value, otherwise try for symbolic name. + */ + +char * +inet6name(const struct in6_addr *in6p) +{ + char *cp; + static char line[NI_MAXHOST]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char hbuf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + const int niflag = NI_NUMERICHOST; + + if (first && !numeric_addr) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) + (void) strlcpy(domain, cp + 1, sizeof(domain)); + else + domain[0] = 0; + } + cp = 0; + if (!numeric_addr && !IN6_IS_ADDR_UNSPECIFIED(in6p)) { + hp = gethostbyaddr((const char *)in6p, sizeof(*in6p), AF_INET6); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (IN6_IS_ADDR_UNSPECIFIED(in6p)) + strlcpy(line, "*", sizeof(line)); + else if (cp) + strlcpy(line, cp, sizeof(line)); + else { + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *in6p; + inet6_getscopeid(&sin6, INET6_IS_ADDR_LINKLOCAL| + INET6_IS_ADDR_MC_LINKLOCAL); + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + hbuf, sizeof(hbuf), NULL, 0, niflag) != 0) + strlcpy(hbuf, "?", sizeof(hbuf)); + strlcpy(line, hbuf, sizeof(line)); + } + return (line); +} + +/* + * Dump the contents of a TCP6 PCB. + */ +void +tcp6_dump(u_long off, const char *name, u_long pcbaddr) +{ + callout_impl_t *ci; + int i, hardticks; + struct kinfo_pcb *pcblist; +#ifdef TCP6 +#define mypcb tcp6cb +#else +#define mypcb tcpcb +#endif + size_t j, len; + + if (use_sysctl) + pcblist = getpcblist_sysctl(name, &len); + else + pcblist = getpcblist_kmem(off, name, &len); + + for (j = 0; j < len; j++) + if (pcblist[j].ki_ppcbaddr == pcbaddr) + break; + free(pcblist); + + if (j == len) + errx(1, "0x%lx is not a valid pcb address", pcbaddr); + + kread(pcbaddr, (char *)&mypcb, sizeof(mypcb)); + hardticks = get_hardticks(); + + printf("TCP Protocol Control Block at 0x%08lx:\n\n", pcbaddr); + printf("Timers:\n"); + for (i = 0; i < TCP6T_NTIMERS; i++) { + char buf[128]; + ci = (callout_impl_t *)&tcpcb.t_timer[i]; + snprintb(buf, sizeof(buf), CALLOUT_FMT, ci->c_flags); + printf("\t%s\t%s", tcptimers[i], buf); + if (ci->c_flags & CALLOUT_PENDING) + printf("\t%d\n", ci->c_time - hardticks); + else + printf("\n"); + } + printf("\n\n"); + + if (mypcb.t_state < 0 || mypcb.t_state >= TCP6_NSTATES) + printf("State: %d", mypcb.t_state); + else + printf("State: %s", tcp6states[mypcb.t_state]); + printf(", flags 0x%x, in6pcb 0x%lx\n\n", mypcb.t_flags, + (u_long)mypcb.t_in6pcb); + + printf("rxtshift %d, rxtcur %d, dupacks %d\n", mypcb.t_rxtshift, + mypcb.t_rxtcur, mypcb.t_dupacks); +#ifdef TCP6 + printf("peermaxseg %u, maxseg %u, force %d\n\n", mypcb.t_peermaxseg, + mypcb.t_maxseg, mypcb.t_force); +#endif + + printf("snd_una %u, snd_nxt %u, snd_up %u\n", + mypcb.snd_una, mypcb.snd_nxt, mypcb.snd_up); + printf("snd_wl1 %u, snd_wl2 %u, iss %u, snd_wnd %llu\n\n", + mypcb.snd_wl1, mypcb.snd_wl2, mypcb.iss, + (unsigned long long)mypcb.snd_wnd); + + printf("rcv_wnd %llu, rcv_nxt %u, rcv_up %u, irs %u\n\n", + (unsigned long long)mypcb.rcv_wnd, mypcb.rcv_nxt, + mypcb.rcv_up, mypcb.irs); + + printf("rcv_adv %u, snd_max %u, snd_cwnd %llu, snd_ssthresh %llu\n", + mypcb.rcv_adv, mypcb.snd_max, (unsigned long long)mypcb.snd_cwnd, + (unsigned long long)mypcb.snd_ssthresh); + +#ifdef TCP6 + printf("idle %d, rtt %d, " mypcb.t_idle, mypcb.t_rtt) +#endif + printf("rtseq %u, srtt %d, rttvar %d, rttmin %d, " + "max_sndwnd %llu\n\n", mypcb.t_rtseq, + mypcb.t_srtt, mypcb.t_rttvar, mypcb.t_rttmin, + (unsigned long long)mypcb.max_sndwnd); + + printf("oobflags %d, iobc %d, softerror %d\n\n", mypcb.t_oobflags, + mypcb.t_iobc, mypcb.t_softerror); + + printf("snd_scale %d, rcv_scale %d, req_r_scale %d, req_s_scale %d\n", + mypcb.snd_scale, mypcb.rcv_scale, mypcb.request_r_scale, + mypcb.requested_s_scale); + printf("ts_recent %u, ts_regent_age %d, last_ack_sent %u\n", + mypcb.ts_recent, mypcb.ts_recent_age, mypcb.last_ack_sent); +} + +#endif /*INET6*/ diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c new file mode 100644 index 000000000..04d93aa97 --- /dev/null +++ b/usr.bin/netstat/main.c @@ -0,0 +1,883 @@ +/* $NetBSD: main.c,v 1.95 2014/11/12 03:34:59 christos Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\ + Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)main.c 8.4 (Berkeley) 3/1/94"; +#else +__RCSID("$NetBSD: main.c,v 1.95 2014/11/12 03:34:59 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "rtutil.h" +#include "prog_ops.h" + +struct nlist nl[] = { +#define N_MBSTAT 0 + { "_mbstat", 0, 0, 0, 0 }, +#define N_IPSTAT 1 + { "_ipstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_TCBTABLE 2 + { "_tcbtable", 0, 0, 0, 0 }, +#define N_TCPSTAT 3 + { "_tcpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_UDBTABLE 4 + { "_udbtable", 0, 0, 0, 0 }, +#define N_UDPSTAT 5 + { "_udpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_IFNET_LIST 6 + { "_ifnet_list", 0, 0, 0, 0 }, +#define N_ICMPSTAT 7 + { "_icmpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_RTSTAT 8 + { "_rtstat", 0, 0, 0, 0 }, +#define N_UNIXSW 9 + { "_unixsw", 0, 0, 0, 0 }, +#define N_RTREE 10 + { "_rt_tables", 0, 0, 0, 0 }, +#define N_NFILE 11 + { "_nfile", 0, 0, 0, 0 }, +#define N_IGMPSTAT 12 + { "_igmpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_MRTPROTO 13 + { "_ip_mrtproto", 0, 0, 0, 0 }, +#define N_MRTSTAT 14 + { "_mrtstat", 0, 0, 0, 0 }, +#define N_MFCHASHTBL 15 + { "_mfchashtbl", 0, 0, 0, 0 }, +#define N_MFCHASH 16 + { "_mfchash", 0, 0, 0, 0 }, +#define N_VIFTABLE 17 + { "_viftable", 0, 0, 0, 0 }, +#define N_MSIZE 18 + { "_msize", 0, 0, 0, 0 }, +#define N_MCLBYTES 19 + { "_mclbytes", 0, 0, 0, 0 }, +#define N_DDPSTAT 20 + { "_ddpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_DDPCB 21 + { "_ddpcb", 0, 0, 0, 0 }, +#define N_MBPOOL 22 + { "_mbpool", 0, 0, 0, 0 }, +#define N_MCLPOOL 23 + { "_mclpool", 0, 0, 0, 0 }, +#define N_IP6STAT 24 + { "_ip6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_TCP6STAT 25 + { "_tcp6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_UDP6STAT 26 + { "_udp6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_ICMP6STAT 27 + { "_icmp6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_IPSECSTAT 28 + { "_ipsecstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_IPSEC6STAT 29 + { "_ipsec6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_PIM6STAT 30 + { "_pim6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_MRT6PROTO 31 + { "_ip6_mrtproto", 0, 0, 0, 0 }, +#define N_MRT6STAT 32 + { "_mrt6stat", 0, 0, 0, 0 }, +#define N_MF6CTABLE 33 + { "_mf6ctable", 0, 0, 0, 0 }, +#define N_MIF6TABLE 34 + { "_mif6table", 0, 0, 0, 0 }, +#define N_PFKEYSTAT 35 + { "_pfkeystat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_ARPSTAT 36 + { "_arpstat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_RIP6STAT 37 + { "_rip6stat", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_ARPINTRQ 38 + { "_arpintrq", 0, 0, 0, 0 }, +#define N_IPINTRQ 39 + { "_ipintrq", 0, 0, 0, 0 }, +#define N_IP6INTRQ 40 + { "_ip6intrq", 0, 0, 0, 0 }, +#define N_ATINTRQ1 41 + { "_atintrq1", 0, 0, 0, 0 }, +#define N_ATINTRQ2 42 + { "_atintrq2", 0, 0, 0, 0 }, +#define N_NSINTRQ 43 + { "_nsintrq", 0, 0, 0, 0 }, +#define N_LLCINTRQ 44 + { "_llcintrq", 0, 0, 0, 0 }, +#define N_HDINTRQ 45 + { "_hdintrq", 0, 0, 0, 0 }, +#define N_NATMINTRQ 46 + { "_natmintrq", 0, 0, 0, 0 }, +#define N_PPPOEDISCINQ 47 + { "_ppoediscinq", 0, 0, 0, 0 }, +#define N_PPPOEINQ 48 + { "_ppoeinq", 0, 0, 0, 0 }, +#define N_PKINTRQ 49 + { "_pkintrq", 0, 0, 0, 0 }, +#define N_HARDCLOCK_TICKS 50 + { "_hardclock_ticks", 0, 0, 0, 0 }, +#define N_PIMSTAT 51 + { "_pimstat", 0, 0, 0, 0 }, +#define N_CARPSTAT 52 + { "_carpstats", 0, 0, 0, 0 }, /* not available via kvm */ +#define N_PFSYNCSTAT 53 + { "_pfsyncstats", 0, 0, 0, 0}, /* not available via kvm */ + { "", 0, 0, 0, 0 }, +}; + +struct protox { + u_char pr_index; /* index into nlist of cb head */ + u_char pr_sindex; /* index into nlist of stat block */ + u_char pr_wanted; /* 1 if wanted, 0 otherwise */ + void (*pr_cblocks) /* control blocks printing routine */ + __P((u_long, const char *)); + void (*pr_stats) /* statistics printing routine */ + __P((u_long, const char *)); + void (*pr_istats) + __P((const char *)); /* per/if statistics printing routine */ + void (*pr_dump) /* PCB state dump routine */ + __P((u_long, const char *, u_long)); + const char *pr_name; /* well-known name */ +} protox[] = { + { N_TCBTABLE, N_TCPSTAT, 1, protopr, + tcp_stats, NULL, tcp_dump, "tcp" }, + { N_UDBTABLE, N_UDPSTAT, 1, protopr, + udp_stats, NULL, 0, "udp" }, + { -1, N_IPSTAT, 1, 0, + ip_stats, NULL, 0, "ip" }, + { -1, N_ICMPSTAT, 1, 0, + icmp_stats, NULL, 0, "icmp" }, + { -1, N_IGMPSTAT, 1, 0, + igmp_stats, NULL, 0, "igmp" }, + { -1, N_CARPSTAT, 1, 0, + carp_stats, NULL, 0, "carp" }, +#ifdef IPSEC + { -1, N_IPSECSTAT, 1, 0, + fast_ipsec_stats, NULL, 0, "ipsec" }, +#endif + { -1, N_PIMSTAT, 1, 0, + pim_stats, NULL, 0, "pim" }, + { -1, N_PFSYNCSTAT, 1, 0, + pfsync_stats, NULL, 0, "pfsync" }, + { -1, -1, 0, 0, + 0, NULL, 0, 0 } +}; + +#ifdef INET6 +struct protox ip6protox[] = { + { -1, N_IP6STAT, 1, 0, + ip6_stats, ip6_ifstats, 0, "ip6" }, + { -1, N_ICMP6STAT, 1, 0, + icmp6_stats, icmp6_ifstats, 0, "icmp6" }, +#ifdef TCP6 + { N_TCBTABLE, N_TCP6STAT, 1, ip6protopr, + tcp6_stats, NULL, tcp6_dump, "tcp6" }, +#else + { N_TCBTABLE, N_TCP6STAT, 1, ip6protopr, + tcp_stats, NULL, tcp6_dump, "tcp6" }, +#endif + { N_UDBTABLE, N_UDP6STAT, 1, ip6protopr, + udp6_stats, NULL, 0, "udp6" }, +#ifdef IPSEC + { -1, N_IPSEC6STAT, 1, 0, + fast_ipsec_stats, NULL, 0, "ipsec6" }, +#endif + { -1, N_PIM6STAT, 1, 0, + pim6_stats, NULL, 0, "pim6" }, + { -1, N_RIP6STAT, 1, 0, + rip6_stats, NULL, 0, "rip6" }, + { -1, -1, 0, 0, + 0, NULL, 0, 0 } +}; +#endif + +struct protox arpprotox[] = { + { -1, N_ARPSTAT, 1, 0, + arp_stats, NULL, 0, "arp" }, + { -1, -1, 0, 0, + 0, NULL, 0, 0 } +}; + +#ifdef IPSEC +struct protox pfkeyprotox[] = { + { -1, N_PFKEYSTAT, 1, 0, + pfkey_stats, NULL, 0, "pfkey" }, + { -1, -1, 0, 0, + 0, NULL, 0, 0 } +}; +#endif + +#ifndef SMALL +struct protox atalkprotox[] = { + { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, + ddp_stats, NULL, 0, "ddp" }, + { -1, -1, 0, 0, + 0, NULL, 0, NULL } +}; +#endif + +struct protox *protoprotox[] = { protox, +#ifdef INET6 + ip6protox, +#endif + arpprotox, +#ifdef IPSEC + pfkeyprotox, +#endif +#ifndef SMALL + atalkprotox, +#endif + NULL }; + +const struct softintrq { + const char *siq_name; + int siq_index; +} softintrq[] = { + { "arpintrq", N_ARPINTRQ }, + { "ipintrq", N_IPINTRQ }, + { "ip6intrq", N_IP6INTRQ }, + { "atintrq1", N_ATINTRQ1 }, + { "atintrq2", N_ATINTRQ2 }, + { "llcintrq", N_LLCINTRQ }, + { "hdintrq", N_HDINTRQ }, + { "natmintrq", N_NATMINTRQ }, + { "ppoediscinq", N_PPPOEDISCINQ }, + { "ppoeinq", N_PPPOEINQ }, + { "pkintrq", N_PKINTRQ }, + { NULL, -1 }, +}; + +int main __P((int, char *[])); +static void printproto __P((struct protox *, const char *)); +static void print_softintrq __P((void)); +__dead static void usage(void); +static struct protox *name2protox __P((const char *)); +static struct protox *knownname __P((const char *)); +static void prepare(const char *, const char *, struct protox *tp); +static kvm_t *prepare_kvmd(const char *, const char *, char *); + +static kvm_t *kvmd = NULL; +gid_t egid; +int interval; /* repeat interval for i/f stats */ +static const char *nlistf = NULL, *memf = NULL; + +kvm_t * +get_kvmd(void) +{ + char buf[_POSIX2_LINE_MAX]; + + if (kvmd != NULL) + return kvmd; + if ((kvmd = prepare_kvmd(nlistf, memf, buf)) == NULL) + errx(1, "kvm error: %s", buf); + return kvmd; +} + +static kvm_t * +prepare_kvmd(const char *nf, const char *mf, char *errbuf) +{ + kvm_t *k; + + (void)setegid(egid); + k = kvm_openfiles(nf, mf, NULL, O_RDONLY, errbuf); + (void)setgid(getgid()); + return k; +} + +void +prepare(const char *nf, const char *mf, struct protox *tp) +{ + char buf[_POSIX2_LINE_MAX]; + /* + * Try to figure out if we can use sysctl or not. + */ + if (nf != NULL || mf != NULL) { + /* Of course, we can't use sysctl with dumps. */ + if (force_sysctl) + errx(EXIT_FAILURE, "can't use sysctl with dumps"); + + /* + * If we have -M or -N, we're not dealing with live memory + * or want to use kvm interface explicitly. It is sometimes + * useful to dig inside of kernel without extending + * sysctl interface (i.e., without rebuilding kernel). + */ + use_sysctl = 0; + } else if (qflag || + iflag || +#ifndef SMALL + gflag || +#endif + (pflag && tp->pr_sindex == N_PIMSTAT) || + Pflag) { + /* These flags are not yet supported via sysctl(3). */ + use_sysctl = 0; + } else { + /* We can use sysctl(3). */ + use_sysctl = 1; + } + + if (force_sysctl && !use_sysctl) { + /* Let the user know what's about to happen. */ + warnx("forcing sysctl usage even though it might not be "\ + "supported"); + use_sysctl = 1; + } + +#ifdef __minix + use_sysctl = 1; +#endif /* __minix */ + + kvmd = prepare_kvmd(nf, mf, buf); + + if (!use_sysctl) { + + if (kvmd == NULL) + errx(1, "kvm error: %s", buf); + if (kvm_nlist(kvmd, nl) < 0 || nl[0].n_type == 0) { + if (nf) + errx(1, "%s: no namelist", nf); + else + errx(1, "no namelist"); + } + } else + (void)setgid(getgid()); +} + +int +main(int argc, char *argv[]) +{ + struct protoent *p; + struct protox *tp; /* for printing cblocks & stats */ + int ch; + char *cp; + char *afname, *afnames; + u_long pcbaddr; + + if (prog_init) { + if (prog_init() == -1) + err(1, "init failed"); + force_sysctl = 1; /* cheap trick */ + } + + egid = getegid(); + (void)setegid(getgid()); + tp = NULL; + af = AF_UNSPEC; + afnames = NULL; + pcbaddr = 0; + + while ((ch = getopt(argc, argv, + "AabBdf:ghI:LliM:mN:nP:p:qrsStTuVvw:X")) != -1) + switch (ch) { + case 'A': + Aflag = RT_AFLAG; + break; + case 'a': + aflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'B': + Bflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'f': + afnames = optarg; + break; +#ifndef SMALL + case 'g': + gflag = 1; + break; +#endif + case 'h': + hflag = 1; + break; + case 'I': + iflag = 1; + interface = optarg; + break; + case 'i': + iflag = 1; + break; + case 'L': + Lflag = RT_LFLAG; + break; + case 'l': + lflag = 1; + break; + case 'M': + memf = optarg; + break; + case 'm': + mflag = 1; + break; + case 'N': + nlistf = optarg; + break; + case 'n': + numeric_addr = numeric_port = nflag = RT_NFLAG; + break; + case 'P': + errno = 0; + pcbaddr = strtoul(optarg, &cp, 16); + if (*cp != '\0' || errno == ERANGE) + errx(1, "invalid PCB address %s", + optarg); + Pflag = 1; + break; + case 'p': + if ((tp = name2protox(optarg)) == NULL) + errx(1, "%s: unknown or uninstrumented protocol", + optarg); + pflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + ++sflag; + break; + case 'S': + numeric_addr = 1; + break; + case 't': + tflag = 1; + break; + case 'T': + tagflag = RT_TFLAG; + break; + case 'u': + af = AF_LOCAL; + break; + case 'V': + Vflag++; + break; + case 'v': + vflag = RT_VFLAG; + break; + case 'w': + interval = atoi(optarg); + iflag = 1; + break; + case 'X': + force_sysctl = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + if (isdigit((unsigned char)**argv)) { + interval = atoi(*argv); + if (interval <= 0) + usage(); + ++argv; + iflag = 1; + } + if (*argv) { + nlistf = *argv; + if (*++argv) + memf = *argv; + } + } +#endif + + prepare(nlistf, memf, tp); + +#ifndef SMALL + if (Bflag) { + if (sflag) + bpf_stats(); + else + bpf_dump(interface); + exit(0); + } +#endif + + if (mflag) { + mbpr(nl[N_MBSTAT].n_value, nl[N_MSIZE].n_value, + nl[N_MCLBYTES].n_value, nl[N_MBPOOL].n_value, + nl[N_MCLPOOL].n_value); + exit(0); + } + if (Pflag) { + if (tp == NULL) { + /* Default to TCP. */ + tp = name2protox("tcp"); + } + if (tp->pr_dump) + (*tp->pr_dump)(nl[tp->pr_index].n_value, tp->pr_name, + pcbaddr); + else + printf("%s: no PCB dump routine\n", tp->pr_name); + exit(0); + } + if (pflag) { + if (iflag && tp->pr_istats) + intpr(interval, nl[N_IFNET_LIST].n_value, tp->pr_istats); + else if (tp->pr_stats) + (*tp->pr_stats)(nl[tp->pr_sindex].n_value, + tp->pr_name); + else + printf("%s: no stats routine\n", tp->pr_name); + exit(0); + } + if (qflag) { + print_softintrq(); + exit(0); + } + /* + * Keep file descriptors open to avoid overhead + * of open/close on each call to get* routines. + */ + sethostent(1); + setnetent(1); + /* + * If -f was used afnames != NULL, loop over the address families. + * Otherwise do this at least once (with af == AF_UNSPEC). + */ + afname = NULL; + do { + if (afnames != NULL) { + afname = strsep(&afnames, ","); + if (afname == NULL) + break; /* Exit early */ + if (strcmp(afname, "inet") == 0) + af = AF_INET; + else if (strcmp(afname, "inet6") == 0) + af = AF_INET6; + else if (strcmp(afname, "arp") == 0) + af = AF_ARP; + else if (strcmp(afname, "pfkey") == 0) + af = PF_KEY; + else if (strcmp(afname, "unix") == 0 + || strcmp(afname, "local") == 0) + af = AF_LOCAL; + else if (strcmp(afname, "atalk") == 0) + af = AF_APPLETALK; + else if (strcmp(afname, "mpls") == 0) + af = AF_MPLS; + else { + warnx("%s: unknown address family", + afname); + continue; + } + } + + if (iflag) { + if (af != AF_UNSPEC) + goto protostat; + + intpr(interval, nl[N_IFNET_LIST].n_value, NULL); + break; + } + if (rflag) { + if (sflag) + rt_stats(use_sysctl ? 0 : nl[N_RTSTAT].n_value); + else { + if (use_sysctl) + p_rttables(af, + nflag|tagflag|vflag|Lflag, 0, ~0); + else + routepr(nl[N_RTREE].n_value); + } + break; + } +#ifndef SMALL + if (gflag) { + if (sflag) { + if (af == AF_INET || af == AF_UNSPEC) + mrt_stats(nl[N_MRTPROTO].n_value, + nl[N_MRTSTAT].n_value); +#ifdef INET6 + if (af == AF_INET6 || af == AF_UNSPEC) + mrt6_stats(nl[N_MRT6PROTO].n_value, + nl[N_MRT6STAT].n_value); +#endif + } + else { + if (af == AF_INET || af == AF_UNSPEC) + mroutepr(nl[N_MRTPROTO].n_value, + nl[N_MFCHASHTBL].n_value, + nl[N_MFCHASH].n_value, + nl[N_VIFTABLE].n_value); +#ifdef INET6 + if (af == AF_INET6 || af == AF_UNSPEC) + mroute6pr(nl[N_MRT6PROTO].n_value, + nl[N_MF6CTABLE].n_value, + nl[N_MIF6TABLE].n_value); +#endif + } + break; + } +#endif + protostat: + if (af == AF_INET || af == AF_UNSPEC) { + setprotoent(1); + setservent(1); + /* ugh, this is O(MN) ... why do we do this? */ + while ((p = getprotoent()) != NULL) { + for (tp = protox; tp->pr_name; tp++) + if (strcmp(tp->pr_name, p->p_name) == 0) + break; + if (tp->pr_name == 0 || tp->pr_wanted == 0) + continue; + printproto(tp, p->p_name); + tp->pr_wanted = 0; + } + endprotoent(); + for (tp = protox; tp->pr_name; tp++) + if (tp->pr_wanted) + printproto(tp, tp->pr_name); + } +#ifdef INET6 + if (af == AF_INET6 || af == AF_UNSPEC) + for (tp = ip6protox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif + if (af == AF_ARP || af == AF_UNSPEC) + for (tp = arpprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#ifdef IPSEC + if (af == PF_KEY || af == AF_UNSPEC) + for (tp = pfkeyprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif +#ifndef SMALL + if (af == AF_APPLETALK || af == AF_UNSPEC) + for (tp = atalkprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); + if ((af == AF_LOCAL || af == AF_UNSPEC) && !sflag) + unixpr(nl[N_UNIXSW].n_value); +#endif + } while (afnames != NULL && afname != NULL); + exit(0); +} + +/* + * Print out protocol statistics or control blocks (per sflag). + * If the interface was not specifically requested, and the symbol + * is not in the namelist, ignore this one. + */ +static void +printproto(struct protox *tp, const char *name) +{ + void (*pr) __P((u_long, const char *)); + u_long off; + + if (sflag) { + if (iflag) { + if (tp->pr_istats) + intpr(interval, nl[N_IFNET_LIST].n_value, + tp->pr_istats); + return; + } + else { + pr = tp->pr_stats; + off = nl[tp->pr_sindex].n_value; + } + } else { + pr = tp->pr_cblocks; + off = nl[tp->pr_index].n_value; + } + if (pr != NULL && ((off || af != AF_UNSPEC) || use_sysctl)) { + (*pr)(off, name); + } +} + +/* + * Print softintrq status. + */ +void +print_softintrq(void) +{ + struct ifqueue intrq, *ifq = &intrq; + const struct softintrq *siq; + u_long off; + + for (siq = softintrq; siq->siq_name != NULL; siq++) { + off = nl[siq->siq_index].n_value; + if (off == 0) + continue; + + kread(off, (char *)ifq, sizeof(*ifq)); + printf("%s:\n", siq->siq_name); + printf("\tqueue length: %d\n", ifq->ifq_len); + printf("\tmaximum queue length: %d\n", ifq->ifq_maxlen); + printf("\tpackets dropped: %d\n", ifq->ifq_drops); + } +} + +/* + * Read kernel memory, return 0 on success. + */ +int +kread(u_long addr, char *buf, int size) +{ + + if (kvm_read(kvmd, addr, buf, size) != size) { + warnx("%s", kvm_geterr(kvmd)); + return (-1); + } + return (0); +} + +const char * +plural(int n) +{ + + return (n != 1 ? "s" : ""); +} + +const char * +plurales(int n) +{ + + return (n != 1 ? "es" : ""); +} + +int +get_hardticks(void) +{ + int hardticks; + + kread(nl[N_HARDCLOCK_TICKS].n_value, (char *)&hardticks, + sizeof(hardticks)); + return (hardticks); +} + +/* + * Find the protox for the given "well-known" name. + */ +static struct protox * +knownname(const char *name) +{ + struct protox **tpp, *tp; + + for (tpp = protoprotox; *tpp; tpp++) + for (tp = *tpp; tp->pr_name; tp++) + if (strcmp(tp->pr_name, name) == 0) + return (tp); + return (NULL); +} + +/* + * Find the protox corresponding to name. + */ +static struct protox * +name2protox(const char *name) +{ + struct protox *tp; + char **alias; /* alias from p->aliases */ + struct protoent *p; + + /* + * Try to find the name in the list of "well-known" names. If that + * fails, check if name is an alias for an Internet protocol. + */ + if ((tp = knownname(name)) != NULL) + return (tp); + + setprotoent(1); /* make protocol lookup cheaper */ + while ((p = getprotoent()) != NULL) { + /* assert: name not same as p->name */ + for (alias = p->p_aliases; *alias; alias++) + if (strcmp(name, *alias) == 0) { + endprotoent(); + return (knownname(p->p_name)); + } + } + endprotoent(); + return (NULL); +} + +static void +usage(void) +{ + const char *progname = getprogname(); + + (void)fprintf(stderr, +"usage: %s [-Aan] [-f address_family[,family ...]] [-M core] [-N system]\n", progname); + (void)fprintf(stderr, +" %s [-bdgiLmnqrsSv] [-f address_family[,family ...]] [-M core] [-N system]\n", + progname); + (void)fprintf(stderr, +" %s [-dn] [-I interface] [-M core] [-N system] [-w wait]\n", progname); + (void)fprintf(stderr, +" %s [-p protocol] [-M core] [-N system]\n", progname); + (void)fprintf(stderr, +" %s [-p protocol] [-M core] [-N system] -P pcbaddr\n", progname); + (void)fprintf(stderr, +" %s [-p protocol] [-i] [-I Interface] \n", progname); + (void)fprintf(stderr, +" %s [-s] [-f address_family[,family ...]] [-i] [-I Interface]\n", progname); + (void)fprintf(stderr, +" %s [-s] [-B] [-I interface]\n", progname); + exit(1); +} diff --git a/usr.bin/netstat/mbuf.c b/usr.bin/netstat/mbuf.c new file mode 100644 index 000000000..06317d3d5 --- /dev/null +++ b/usr.bin/netstat/mbuf.c @@ -0,0 +1,273 @@ +/* $NetBSD: mbuf.c,v 1.33 2015/07/28 19:46:42 christos Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)mbuf.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: mbuf.c,v 1.33 2015/07/28 19:46:42 christos Exp $"); +#endif +#endif /* not lint */ + +#define __POOL_EXPOSE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "prog_ops.h" + +#define YES 1 + +struct mbstat mbstat; +struct pool mbpool, mclpool; +struct pool_allocator mbpa, mclpa; + +static struct mbtypes { + int mt_type; + const char *mt_name; +} mbtypes[] = { + { MT_DATA, "data" }, + { MT_OOBDATA, "oob data" }, + { MT_CONTROL, "ancillary data" }, + { MT_HEADER, "packet headers" }, + { MT_FTABLE, "fragment reassembly queue headers" }, /* XXX */ + { MT_SONAME, "socket names and addresses" }, + { MT_SOOPTS, "socket options" }, + { 0, 0 } +}; + +const int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short); +bool seen[256]; /* "have we seen this type yet?" */ + +int mbstats_ctl[] = { CTL_KERN, KERN_MBUF, MBUF_STATS }; +int mowners_ctl[] = { CTL_KERN, KERN_MBUF, MBUF_MOWNERS }; + +/* + * Print mbuf statistics. + */ +void +mbpr(u_long mbaddr, u_long msizeaddr, u_long mclbaddr, u_long mbpooladdr, + u_long mclpooladdr) +{ + u_long totmem, totused, totpct; + u_int totmbufs; + int i, lines; + struct mbtypes *mp; + size_t len; + void *data; + struct mowner_user *mo; + int mclbytes, msize; + + if (nmbtypes != 256) { + fprintf(stderr, + "%s: unexpected change to mbstat; check source\n", + getprogname()); + return; + } + + if (use_sysctl) { + size_t mbstatlen = sizeof(mbstat); + if (prog_sysctl(mbstats_ctl, + sizeof(mbstats_ctl) / sizeof(mbstats_ctl[0]), + &mbstat, &mbstatlen, NULL, 0) < 0) { + warn("mbstat: sysctl failed"); + return; + } + goto printit; + } + + if (mbaddr == 0) { + fprintf(stderr, "%s: mbstat: symbol not in namelist\n", + getprogname()); + return; + } +/*XXX*/ + if (msizeaddr != 0) + kread(msizeaddr, (char *)&msize, sizeof (msize)); + else + msize = MSIZE; + if (mclbaddr != 0) + kread(mclbaddr, (char *)&mclbytes, sizeof (mclbytes)); + else + mclbytes = MCLBYTES; +/*XXX*/ + + if (kread(mbaddr, (char *)&mbstat, sizeof (mbstat))) + return; + + if (kread(mbpooladdr, (char *)&mbpool, sizeof (mbpool))) + return; + + if (kread(mclpooladdr, (char *)&mclpool, sizeof (mclpool))) + return; + + mbpooladdr = (u_long) mbpool.pr_alloc; + mclpooladdr = (u_long) mclpool.pr_alloc; + + if (kread(mbpooladdr, (char *)&mbpa, sizeof (mbpa))) + return; + + if (kread(mclpooladdr, (char *)&mclpa, sizeof (mclpa))) + return; + + printit: + totmbufs = 0; + for (mp = mbtypes; mp->mt_name; mp++) + totmbufs += mbstat.m_mtypes[mp->mt_type]; + printf("%u mbufs in use:\n", totmbufs); + for (mp = mbtypes; mp->mt_name; mp++) + if (mbstat.m_mtypes[mp->mt_type]) { + seen[mp->mt_type] = YES; + printf("\t%u mbufs allocated to %s\n", + mbstat.m_mtypes[mp->mt_type], mp->mt_name); + } + seen[MT_FREE] = YES; + for (i = 0; i < nmbtypes; i++) + if (!seen[i] && mbstat.m_mtypes[i]) { + printf("\t%u mbufs allocated to \n", + mbstat.m_mtypes[i], i); + } + + if (use_sysctl) /* XXX */ + goto dump_drain; + + printf("%lu/%lu mapped pages in use\n", + (u_long)(mclpool.pr_nget - mclpool.pr_nput), + ((u_long)mclpool.pr_npages * mclpool.pr_itemsperpage)); + totmem = (mbpool.pr_npages << mbpa.pa_pageshift) + + (mclpool.pr_npages << mclpa.pa_pageshift); + totused = (mbpool.pr_nget - mbpool.pr_nput) * mbpool.pr_size + + (mclpool.pr_nget - mclpool.pr_nput) * mclpool.pr_size; + if (totmem == 0) + totpct = 0; + else if (totused < (ULONG_MAX/100)) + totpct = (totused * 100)/totmem; + else { + u_long totmem1 = totmem/100; + u_long totused1 = totused/100; + totpct = (totused1 * 100)/totmem1; + } + + printf("%lu Kbytes allocated to network (%lu%% in use)\n", + totmem / 1024, totpct); + +dump_drain: + printf("%lu calls to protocol drain routines\n", mbstat.m_drain); + + if (sflag < 2) + return; + + if (!use_sysctl) + return; + + if (prog_sysctl(mowners_ctl, + sizeof(mowners_ctl)/sizeof(mowners_ctl[0]), + NULL, &len, NULL, 0) < 0) { + if (errno == ENOENT) + return; + warn("mowners: sysctl test"); + return; + } + len += 10 * sizeof(*mo); /* add some slop */ + data = malloc(len); + if (data == NULL) { + warn("malloc(%lu)", (u_long)len); + return; + } + + if (prog_sysctl(mowners_ctl, + sizeof(mowners_ctl)/sizeof(mowners_ctl[0]), + data, &len, NULL, 0) < 0) { + warn("mowners: sysctl get"); + free(data); + return; + } + + for (mo = (void *) data, lines = 0; len >= sizeof(*mo); + len -= sizeof(*mo), mo++) { + char buf[32]; + if (vflag == 1 && + mo->mo_counter[MOWNER_COUNTER_CLAIMS] == 0 && + mo->mo_counter[MOWNER_COUNTER_EXT_CLAIMS] == 0 && + mo->mo_counter[MOWNER_COUNTER_CLUSTER_CLAIMS] == 0) + continue; + if (vflag == 0 && + mo->mo_counter[MOWNER_COUNTER_CLAIMS] == + mo->mo_counter[MOWNER_COUNTER_RELEASES] && + mo->mo_counter[MOWNER_COUNTER_EXT_CLAIMS] == + mo->mo_counter[MOWNER_COUNTER_EXT_RELEASES] && + mo->mo_counter[MOWNER_COUNTER_CLUSTER_CLAIMS] == + mo->mo_counter[MOWNER_COUNTER_CLUSTER_RELEASES]) + continue; + snprintf(buf, sizeof(buf), "%16s %-13s", + mo->mo_name, mo->mo_descr); + if ((lines % 24) == 0 || lines > 24) { + printf("%30s %-8s %10s %10s %10s\n", + "", "", "small", "ext", "cluster"); + lines = 1; + } + printf("%30s %-8s %10lu %10lu %10lu\n", + buf, "inuse", + mo->mo_counter[MOWNER_COUNTER_CLAIMS] - + mo->mo_counter[MOWNER_COUNTER_RELEASES], + mo->mo_counter[MOWNER_COUNTER_EXT_CLAIMS] - + mo->mo_counter[MOWNER_COUNTER_EXT_RELEASES], + mo->mo_counter[MOWNER_COUNTER_CLUSTER_CLAIMS] - + mo->mo_counter[MOWNER_COUNTER_CLUSTER_RELEASES]); + lines++; + if (vflag) { + printf("%30s %-8s %10lu %10lu %10lu\n", + "", "claims", + mo->mo_counter[MOWNER_COUNTER_CLAIMS], + mo->mo_counter[MOWNER_COUNTER_EXT_CLAIMS], + mo->mo_counter[MOWNER_COUNTER_CLUSTER_CLAIMS]); + printf("%30s %-8s %10lu %10lu %10lu\n", + "", "releases", + mo->mo_counter[MOWNER_COUNTER_RELEASES], + mo->mo_counter[MOWNER_COUNTER_EXT_RELEASES], + mo->mo_counter[MOWNER_COUNTER_CLUSTER_RELEASES]); + lines += 2; + } + } + free(data); +} diff --git a/usr.bin/netstat/mroute.c b/usr.bin/netstat/mroute.c new file mode 100644 index 000000000..5665641de --- /dev/null +++ b/usr.bin/netstat/mroute.c @@ -0,0 +1,397 @@ +/* $NetBSD: mroute.c,v 1.25 2014/11/06 21:30:09 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)mroute.c 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1989 Stephen Deering + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)mroute.c 8.1 (Berkeley) 6/6/93 + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)mroute.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: mroute.c,v 1.25 2014/11/06 21:30:09 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * Print multicast routing structures and statistics. + * + * MROUTING 1.0 + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#define _KERNEL +#include +#undef _KERNEL + +#include +#include +#include +#include "netstat.h" +#include "rtutil.h" + +static char *pktscale(u_long); +static void print_bw_meter(struct bw_meter *, int *); + +static char * +pktscale(u_long n) +{ + static char buf[20]; + char t; + + if (n < 1024) + t = ' '; + else if (n < 1024 * 1024) { + t = 'k'; + n /= 1024; + } else { + t = 'm'; + n /= 1048576; + } + + (void)snprintf(buf, sizeof buf, "%lu%c", n, t); + return (buf); +} + +void +mroutepr(u_long mrpaddr, u_long mfchashtbladdr, u_long mfchashaddr, + u_long vifaddr) +{ + u_int mrtproto; + LIST_HEAD(, mfc) *mfchashtbl; + u_long mfchash, i; + struct vif viftable[MAXVIFS]; + struct mfc *mfcp, mfc; + struct vif *v; + vifi_t vifi; + int banner_printed; + int saved_numeric_addr; + int numvifs; + int nmfc; /* No. of cache entries */ + + if (mrpaddr == 0) { + printf("ip_mrtproto: symbol not in namelist\n"); + return; + } + + kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto)); + switch (mrtproto) { + case 0: + printf("no multicast routing compiled into this system\n"); + return; + + case IGMP_DVMRP: + break; + + default: + printf("multicast routing protocol %u, unknown\n", mrtproto); + return; + } + + if (mfchashtbladdr == 0) { + printf("mfchashtbl: symbol not in namelist\n"); + return; + } + if (mfchashaddr == 0) { + printf("mfchash: symbol not in namelist\n"); + return; + } + if (vifaddr == 0) { + printf("viftable: symbol not in namelist\n"); + return; + } + + saved_numeric_addr = numeric_addr; + numeric_addr = 1; + + kread(vifaddr, (char *)&viftable, sizeof(viftable)); + banner_printed = 0; + numvifs = 0; + + for (vifi = 0, v = viftable; vifi < MAXVIFS; ++vifi, ++v) { + if (v->v_lcl_addr.s_addr == 0) + continue; + numvifs = vifi; + + if (!banner_printed) { + printf("\nVirtual Interface Table\n %s%s", + "Vif Thresh Limit Local-Address ", + "Remote-Address Pkt_in Pkt_out\n"); + banner_printed = 1; + } + + printf(" %3u %3u %5u %-15.15s", + vifi, v->v_threshold, v->v_rate_limit, + routename4(v->v_lcl_addr.s_addr, nflag)); + printf(" %-15.15s %6lu %7lu\n", (v->v_flags & VIFF_TUNNEL) ? + routename4(v->v_rmt_addr.s_addr, nflag) : "", + v->v_pkt_in, v->v_pkt_out); + } + if (!banner_printed) + printf("\nVirtual Interface Table is empty\n"); + + kread(mfchashtbladdr, (char *)&mfchashtbl, sizeof(mfchashtbl)); + kread(mfchashaddr, (char *)&mfchash, sizeof(mfchash)); + banner_printed = 0; + nmfc = 0; + + if (mfchashtbl != 0) + for (i = 0; i <= mfchash; ++i) { + kread((u_long)&mfchashtbl[i], (char *)&mfcp, sizeof(mfcp)); + + for (; mfcp != 0; mfcp = mfc.mfc_hash.le_next) { + if (!banner_printed) { + printf("\nMulticast Forwarding Cache\n %s%s", + "Hash Origin Mcastgroup ", + "Traffic In-Vif Out-Vifs/Forw-ttl\n"); + banner_printed = 1; + } + + kread((u_long)mfcp, (char *)&mfc, sizeof(mfc)); + printf(" %3lu %-15.15s", + i, routename4(mfc.mfc_origin.s_addr, nflag)); + printf(" %-15.15s %7s %3u ", + routename4(mfc.mfc_mcastgrp.s_addr, nflag), + pktscale(mfc.mfc_pkt_cnt), mfc.mfc_parent); + for (vifi = 0; vifi <= numvifs; ++vifi) + if (mfc.mfc_ttls[vifi]) + printf(" %u/%u", vifi, mfc.mfc_ttls[vifi]); + + printf("\n"); + + /* Print the bw meter information */ + { + struct bw_meter bw_meter, *bwm; + int banner_printed2 = 0; + + bwm = mfc.mfc_bw_meter; + while (bwm) { + kread((u_long)bwm, + (char *)&bw_meter, + sizeof bw_meter); + print_bw_meter(&bw_meter, + &banner_printed2); + bwm = bw_meter.bm_mfc_next; + } +#if 0 /* Don't ever print it? */ + if (! banner_printed2) + printf("\n No Bandwidth Meters\n"); +#endif + } + + nmfc++; + } + } + if (!banner_printed) + printf("\nMulticast Forwarding Cache is empty\n"); + else + printf("\nTotal no. of entries in cache: %d\n", nmfc); + + printf("\n"); + numeric_addr = saved_numeric_addr; +} + +static void +print_bw_meter(struct bw_meter *bw_meter, int *banner_printed) +{ + char s0[256], s1[256], s2[256], s3[256]; + struct timeval now, end, delta; + + gettimeofday(&now, NULL); + + if (! *banner_printed) { + printf(" Bandwidth Meters\n"); + printf(" %-30s", "Measured(Start|Packets|Bytes)"); + printf(" %s", "Type"); + printf(" %-30s", "Thresh(Interval|Packets|Bytes)"); + printf(" Remain"); + printf("\n"); + *banner_printed = 1; + } + + /* The measured values */ + if (bw_meter->bm_flags & BW_METER_UNIT_PACKETS) + sprintf(s1, "%llu", (unsigned long long)bw_meter->bm_measured.b_packets); + else + sprintf(s1, "?"); + if (bw_meter->bm_flags & BW_METER_UNIT_BYTES) + sprintf(s2, "%llu", (unsigned long long)bw_meter->bm_measured.b_bytes); + else + sprintf(s2, "?"); + sprintf(s0, "%lld.%ld|%s|%s", + (long long)bw_meter->bm_start_time.tv_sec, + (long)bw_meter->bm_start_time.tv_usec, + s1, s2); + printf(" %-30s", s0); + + /* The type of entry */ + sprintf(s0, "%s", "?"); + if (bw_meter->bm_flags & BW_METER_GEQ) + sprintf(s0, "%s", ">="); + else if (bw_meter->bm_flags & BW_METER_LEQ) + sprintf(s0, "%s", "<="); + printf(" %-3s", s0); + + /* The threshold values */ + if (bw_meter->bm_flags & BW_METER_UNIT_PACKETS) + sprintf(s1, "%llu", (unsigned long long)bw_meter->bm_threshold.b_packets); + else + sprintf(s1, "?"); + if (bw_meter->bm_flags & BW_METER_UNIT_BYTES) + sprintf(s2, "%llu", (unsigned long long)bw_meter->bm_threshold.b_bytes); + else + sprintf(s2, "?"); + sprintf(s0, "%lld.%ld|%s|%s", + (long long)bw_meter->bm_threshold.b_time.tv_sec, + (long)bw_meter->bm_threshold.b_time.tv_usec, + s1, s2); + printf(" %-30s", s0); + + /* Remaining time */ + timeradd(&bw_meter->bm_start_time, + &bw_meter->bm_threshold.b_time, &end); + if (timercmp(&now, &end, <=)) { + timersub(&end, &now, &delta); + sprintf(s3, "%lld.%ld", + (long long)delta.tv_sec, (long)delta.tv_usec); + } else { + /* Negative time */ + timersub(&now, &end, &delta); + sprintf(s3, "-%lld.%ld", + (long long)delta.tv_sec, (long)delta.tv_usec); + } + printf(" %s", s3); + + printf("\n"); +} + +void +mrt_stats(u_long mrpaddr, u_long mstaddr) +{ + u_int mrtproto; + struct mrtstat mrtstat; + + if (mrpaddr == 0) { + printf("ip_mrtproto: symbol not in namelist\n"); + return; + } + + kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto)); + switch (mrtproto) { + case 0: + printf("no multicast routing compiled into this system\n"); + return; + + case IGMP_DVMRP: + break; + + default: + printf("multicast routing protocol %u, unknown\n", mrtproto); + return; + } + + if (mstaddr == 0) { + printf("mrtstat: symbol not in namelist\n"); + return; + } + + kread(mstaddr, (char *)&mrtstat, sizeof(mrtstat)); + printf("multicast routing:\n"); + printf("\t%lu datagram%s with no route for origin\n", + mrtstat.mrts_no_route, plural(mrtstat.mrts_no_route)); + printf("\t%lu upcall%s made to mrouted\n", + mrtstat.mrts_upcalls, plural(mrtstat.mrts_upcalls)); + printf("\t%lu datagram%s with malformed tunnel options\n", + mrtstat.mrts_bad_tunnel, plural(mrtstat.mrts_bad_tunnel)); + printf("\t%lu datagram%s with no room for tunnel options\n", + mrtstat.mrts_cant_tunnel, plural(mrtstat.mrts_cant_tunnel)); + printf("\t%lu datagram%s arrived on wrong interface\n", + mrtstat.mrts_wrong_if, plural(mrtstat.mrts_wrong_if)); + printf("\t%lu datagram%s dropped due to upcall Q overflow\n", + mrtstat.mrts_upq_ovflw, plural(mrtstat.mrts_upq_ovflw)); + printf("\t%lu datagram%s dropped due to upcall socket overflow\n", + mrtstat.mrts_upq_sockfull, plural(mrtstat.mrts_upq_sockfull)); + printf("\t%lu datagram%s cleaned up by the cache\n", + mrtstat.mrts_cache_cleanups, plural(mrtstat.mrts_cache_cleanups)); + printf("\t%lu datagram%s dropped selectively by ratelimiter\n", + mrtstat.mrts_drop_sel, plural(mrtstat.mrts_drop_sel)); + printf("\t%lu datagram%s dropped - bucket Q overflow\n", + mrtstat.mrts_q_overflow, plural(mrtstat.mrts_q_overflow)); + printf("\t%lu datagram%s dropped - larger than bkt size\n", + mrtstat.mrts_pkt2large, plural(mrtstat.mrts_pkt2large)); +} diff --git a/usr.bin/netstat/mroute6.c b/usr.bin/netstat/mroute6.c new file mode 100644 index 000000000..039f60cc7 --- /dev/null +++ b/usr.bin/netstat/mroute6.c @@ -0,0 +1,301 @@ +/* $NetBSD: mroute6.c,v 1.15 2014/11/06 21:30:09 christos Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mroute.c 8.2 (Berkeley) 4/28/95 + */ + +/* + * Copyright (c) 1989 Stephen Deering + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mroute.c 8.2 (Berkeley) 4/28/95 + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define _KERNEL 1 +#include +#undef _KERNEL + +#include +#include +#include "netstat.h" +#include "rtutil.h" + +#ifdef INET6 + +#define WID_ORG (lflag ? 39 : (numeric_addr ? 29 : 18)) /* width of origin column */ +#define WID_GRP (lflag ? 18 : (numeric_addr ? 16 : 18)) /* width of group column */ + +void +mroute6pr(u_long mrpaddr, u_long mfcaddr, u_long mifaddr) +{ + u_int mrtproto; + struct mf6c *mf6ctable[MF6CTBLSIZ], *mfcp; + struct mif6 mif6table[MAXMIFS]; + struct mf6c mfc; + struct rtdetq rte, *rtep; + register struct mif6 *mifp; + register mifi_t mifi; + register int i; + register int banner_printed; + register int saved_numeric_addr; + int waitings; + + if (mrpaddr == 0) { + printf("mroute6pr: symbol not in namelist\n"); + return; + } + + kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto)); + switch (mrtproto) { + case 0: + printf("no IPv6 multicast routing compiled into this system\n"); + return; + + case IPPROTO_PIM: + break; + + default: + printf("IPv6 multicast routing protocol %u, unknown\n", + mrtproto); + return; + } + + if (mfcaddr == 0) { + printf("mf6ctable: symbol not in namelist\n"); + return; + } + if (mifaddr == 0) { + printf("miftable: symbol not in namelist\n"); + return; + } + + saved_numeric_addr = numeric_addr; + numeric_addr = 1; + + kread(mifaddr, (char *)&mif6table, sizeof(mif6table)); + banner_printed = 0; + for (mifi = 0, mifp = mif6table; mifi < MAXMIFS; ++mifi, ++mifp) { + struct ifnet ifnet; + char ifname[IFNAMSIZ]; + + if (mifp->m6_ifp == NULL) + continue; + + kread((u_long)mifp->m6_ifp, (char *)&ifnet, sizeof(ifnet)); + if (!banner_printed) { + printf("\nIPv6 Multicast Interface Table\n" + " Mif Rate PhyIF Pkts-In Pkts-Out\n"); + banner_printed = 1; + } + + printf(" %2u %4d", mifi, mifp->m6_rate_limit); + printf(" %5s", (mifp->m6_flags & MIFF_REGISTER) ? + "reg0" : if_indextoname(ifnet.if_index, ifname)); + + printf(" %9llu %9llu\n", (unsigned long long)mifp->m6_pkt_in, + (unsigned long long)mifp->m6_pkt_out); + } + if (!banner_printed) + printf("\nIPv6 Multicast Interface Table is empty\n"); + + kread(mfcaddr, (char *)&mf6ctable, sizeof(mf6ctable)); + banner_printed = 0; + for (i = 0; i < MF6CTBLSIZ; ++i) { + mfcp = mf6ctable[i]; + while(mfcp) { + kread((u_long)mfcp, (char *)&mfc, sizeof(mfc)); + if (!banner_printed) { + printf ("\nIPv6 Multicast Forwarding Cache\n"); + printf(" %-*.*s %-*.*s %s", + WID_ORG, WID_ORG, "Origin", + WID_GRP, WID_GRP, "Group", + " Packets Waits In-Mif Out-Mifs\n"); + banner_printed = 1; + } + + printf(" %-*.*s", WID_ORG, WID_ORG, + routename6(&mfc.mf6c_origin, nflag)); + printf(" %-*.*s", WID_GRP, WID_GRP, + routename6(&mfc.mf6c_mcastgrp, nflag)); + printf(" %9llu", (unsigned long long)mfc.mf6c_pkt_cnt); + + for (waitings = 0, rtep = mfc.mf6c_stall; rtep; ) { + waitings++; + kread((u_long)rtep, (char *)&rte, sizeof(rte)); + rtep = rte.next; + } + printf(" %3d", waitings); + + if (mfc.mf6c_parent == MF6C_INCOMPLETE_PARENT) + printf(" --- "); + else + printf(" %3d ", mfc.mf6c_parent); + for (mifi = 0; mifi <= MAXMIFS; mifi++) { + if (IF_ISSET(mifi, &mfc.mf6c_ifset)) + printf(" %u", mifi); + } + printf("\n"); + + mfcp = mfc.mf6c_next; + } + } + if (!banner_printed) + printf("\nIPv6 Multicast Routing Table is empty\n"); + + printf("\n"); + numeric_addr = saved_numeric_addr; +} + +void +mrt6_stats(u_long mrpaddr, u_long mstaddr) +{ +#define p(f, m) printf(m, (unsigned long long)mrtstat.f, plural(mrtstat.f)) +#define pes(f, m) printf(m, (unsigned long long)mrtstat.f, plurales(mrtstat.f)) + u_int mrtproto; + struct mrt6stat mrtstat; + + if (mrpaddr == 0) { + printf("mrt6_stats: symbol not in namelist\n"); + return; + } + + kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto)); + switch (mrtproto) { + case 0: + printf("no IPv6 multicast routing compiled into this system\n"); + return; + + case IPPROTO_PIM: + break; + + default: + printf("IPv6 multicast routing protocol %u, unknown\n", + mrtproto); + return; + } + + if (mstaddr == 0) { + printf("mrt6_stats: symbol not in namelist\n"); + return; + } + + kread(mstaddr, (char *)&mrtstat, sizeof(mrtstat)); + printf("multicast forwarding:\n"); + p(mrt6s_mfc_lookups, " %10llu multicast forwarding cache lookup%s\n"); + pes(mrt6s_mfc_misses, " %10llu multicast forwarding cache miss%s\n"); + p(mrt6s_upcalls, " %10llu upcall%s to mrouted\n"); + p(mrt6s_upq_ovflw, " %10llu upcall queue overflow%s\n"); + p(mrt6s_upq_sockfull, + " %10llu upcall%s dropped due to full socket buffer\n"); + p(mrt6s_cache_cleanups, " %10llu cache cleanup%s\n"); + p(mrt6s_no_route, " %10llu datagram%s with no route for origin\n"); + p(mrt6s_bad_tunnel, " %10llu datagram%s arrived with bad tunneling\n"); + p(mrt6s_cant_tunnel, " %10llu datagram%s could not be tunneled\n"); + p(mrt6s_wrong_if, " %10llu datagram%s arrived on wrong interface\n"); + p(mrt6s_drop_sel, " %10llu datagram%s selectively dropped\n"); + p(mrt6s_q_overflow, + " %10llu datagram%s dropped due to queue overflow\n"); + p(mrt6s_pkt2large, " %10llu datagram%s dropped for being too large\n"); +#undef p +#undef pes +} +#endif /*INET6*/ diff --git a/usr.bin/netstat/netstat.1 b/usr.bin/netstat/netstat.1 new file mode 100644 index 000000000..6cea79947 --- /dev/null +++ b/usr.bin/netstat/netstat.1 @@ -0,0 +1,476 @@ +.\" $NetBSD: netstat.1,v 1.72 2015/03/23 18:33:17 roy Exp $ +.\" +.\" Copyright (c) 1983, 1990, 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)netstat.1 8.8 (Berkeley) 4/18/94 +.\" +.Dd March 19, 2015 +.Dt NETSTAT 1 +.Os +.Sh NAME +.Nm netstat +.Nd show network status +.Sh SYNOPSIS +.ds address_family Fl f Ar address_family Ns Op , Ns Ar family ... +.Nm +.Op Fl Aan +.Op \*[address_family] +.Op Fl M Ar core +.Op Fl N Ar system +.Nm +.Op Fl bdghiLlmnqrSsTtv +.Op \*[address_family] +.Op Fl M Ar core +.Op Fl N Ar system +.Nm +.Op Fl dn +.Op Fl I Ar interface +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl w Ar wait +.Nm +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl p Ar protocol +.Nm +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl p Ar protocol +.Fl P Ar pcbaddr +.Nm +.Op Fl i +.Op Fl I Ar Interface +.Op Fl p Ar protocol +.Nm +.Op Fl is +.Op \*[address_family] +.Op Fl I Ar Interface +.Nm +.Op Fl s +.Op Fl I Ar Interface +.Fl B +.Sh DESCRIPTION +The +.Nm +command symbolically displays the contents of various network-related +data structures. +There are a number of output formats, +depending on the options for the information presented. +The first form of the command displays a list of active sockets for +each protocol. +The second form presents the contents of one of the other network +data structures according to the option selected. +Using the third form, with a +.Ar wait +interval specified, +.Nm +will continuously display the information regarding packet +traffic on the configured network interfaces. +The fourth form displays statistics about the named protocol. +The fifth and sixth forms display per interface statistics for +the specified protocol or address family. +.Pp +The options have the following meaning: +.Bl -tag -width flag +.It Fl A +With the default display, +show the address of any protocol control blocks associated with sockets; used +for debugging. +.It Fl a +With the default display, +show the state of all sockets; normally sockets used by +server processes are not shown. +.It Fl B +With the default display, +show the current +.Xr bpf 4 +peers. +To show only the peers listening to a specific interface, +use the +.Fl I +option. +If the +.Fl s +option is present, show the current +.Xr bpf 4 +statistics. +.It Fl b +With the interface display (option +.Fl i ) , +show bytes in and out, instead of packets in and out. +.It Fl d +With either interface display (option +.Fl i +or an interval, as described below), +show the number of dropped packets. +.It \*[address_family] +Limit statistics or address control block reports to those +of the specified +.Ar address_families . +The following address families +are recognized: +.Ar inet , +for +.Dv AF_INET ; +.Ar inet6 , +for +.Dv AF_INET6 ; +.Ar arp , +for +.Dv AF_ARP ; +.Ar ns , +for +.Dv AF_NS ; +.Ar atalk , +for +.Dv AF_APPLETALK ; +.Ar mpls , +for +.Dv AF_MPLS ; +and +.Ar local +or +.Ar unix , +for +.Dv AF_LOCAL . +.It Fl g +Show information related to multicast (group address) routing. +By default, show the IP Multicast virtual-interface and routing tables. +If the +.Fl s +option is also present, show multicast routing statistics. +.It Fl h +When used with +.Fl b +in combination with either +.Fl i +or +.Fl I , +output "human-readable" byte counts. +.It Fl I Ar interface +Show information about the specified interface; +used with a +.Ar wait +interval as described below. +If the +.Fl f Ar address_family +option (with the +.Fl s +option) or the +.Fl p Ar protocol +option is present, show per-interface statistics on the +.Ar interface +for the specified +.Ar address_family +or +.Ar protocol , +respectively. +.It Fl i +Show the state of interfaces which have been auto-configured +(interfaces statically configured into a system, but not +located at boot time are not shown). +If the +.Fl a +options is also present, multicast addresses currently in use are shown +for each Ethernet interface and for each IP interface address. +Multicast addresses are shown on separate lines following the interface +address with which they are associated. +If the +.Fl f Ar address_family +option (with the +.Fl s +option) or the +.Fl p Ar protocol +option is present, show per-interface statistics on all interfaces +for the specified +.Ar address_family +or +.Ar protocol , +respectively. +.It Fl L +Don't show link-level routes (e.g., IPv4 ARP or IPv6 neighbour cache). +.It Fl l +With the +.Fl g +option, display wider fields for the IPv6 multicast routing table +.Qq Origin +and +.Qq Group +columns. +.It Fl M Ar core +Use +.Xr kvm 3 +instead of +.Xr sysctl 3 +to retrieve information and +extract values associated with the name list from the specified core. +If the +.Fl M +option is not given but the +.Fl N +option is given, the default +.Pa /dev/mem +is used. +.It Fl m +Show statistics recorded by the mbuf memory management routines +(the network manages a private pool of memory buffers). +.It Fl N Ar system +Use +.Xr kvm 3 +instead of +.Xr sysctl 3 +to retrieve information and extract the name list from the specified system. +For the default behavior when only +.Fl M +option is given, see the description about when +.Fa execfile +is +.Dv NULL +in +.Xr kvm_openfiles 3 . +.It Fl n +Show network addresses and ports as numbers (normally +.Nm +interprets addresses and ports and attempts to display them +symbolically). +This option may be used with any of the display formats. +.It Fl P Ar pcbaddr +Dump the contents of the protocol control block (PCB) located at kernel +virtual address +.Ar pcbaddr . +This address may be obtained using the +.Fl A +flag. +The default protocol is TCP, but may be overridden using the +.Fl p +flag. +.It Fl p Ar protocol +Show statistics about +.Ar protocol , +which is either a well-known name for a protocol or an alias for it. +Some protocol names and aliases are listed in the file +.Pa /etc/protocols . +A null response typically means that there are no interesting numbers to +report. +The program will complain if +.Ar protocol +is unknown or if there is no statistics routine for it. +.It Fl q +Show software interrupt queue setting/statistics for all protocols. +.It Fl r +Show the routing tables. +When +.Fl s +is also present, show routing statistics instead. +.It Fl S +Show network addresses as numbers (as with +.Fl n , +but show ports symbolically). +.It Fl s +Show per-protocol statistics. +If this option is repeated, counters with a value of zero are suppressed. +.It Fl T +Show MPLS Tags for the routing tables. +If multiple tags exists, they will +be comma separated, first tag being the BoS one. +.It Fl t +With the +.Fl i +option, display the current value of the watchdog timer function. +.It Fl v +Show extra (verbose) detail for the routing tables +.Pq Fl r , +or avoid truncation of long addresses. +.It Fl w Ar wait +Show network interface statistics at intervals of +.Ar wait +seconds. +.It Fl X +Force use of +.Xr sysctl 3 +when retrieving information. +Some features of +.Nm +may not be (fully) supported when using +.Xr sysctl 3 . +This flag forces the use of the latter regardless, and emits a message if a +not yet fully supported feature is used in conjunction with it. +This flag might be removed at any time; do not rely on its presence. +.El +.Pp +The default display, for active sockets, shows the local +and remote addresses, send and receive queue sizes (in bytes), protocol, +and the internal state of the protocol. +Address formats are of the form ``host.port'' or ``network.port'' +if a socket's address specifies a network but no specific host address. +When known the host and network addresses are displayed symbolically +according to the data bases +.Pa /etc/hosts +and +.Pa /etc/networks , +respectively. +If a symbolic name for an address is unknown, or if +the +.Fl n +option is specified, the address is printed numerically, according +to the address family. +For more information regarding +the Internet ``dot format,'' +refer to +.Xr inet 3 ) . +Unspecified, +or ``wildcard'', addresses and ports appear as ``*''. +You can use the +.Xr fstat 1 +command to find out which process or processes hold references to a socket. +.Pp +The interface display provides a table of cumulative +statistics regarding packets transferred, errors, and collisions. +The network addresses of the interface +and the maximum transmission unit (``mtu'') are also displayed. +.Pp +The routing table display indicates the available routes and +their status. +Each route consists of a destination host or network +and a gateway to use in forwarding packets. +The flags field shows +a collection of information about the route stored as +binary choices. +The individual flags are discussed in more +detail in the +.Xr route 8 +and +.Xr route 4 +manual pages. +The mapping between letters and flags is: +.Bl -column XXXX RTF_BLACKHOLE +.It 1 RTF_PROTO1 Protocol specific routing flag #1 +.It 2 RTF_PROTO2 Protocol specific routing flag #2 +.It B RTF_BLACKHOLE Just discard pkts (during updates) +.It b RTF_BROADCAST Route represents a broadcast address +.It C RTF_CLONING Generate new routes on use +.It c RTF_CLONED Cloned routes (generated from RTF_CLONING) +.It D RTF_DYNAMIC Created dynamically (by redirect) +.It G RTF_GATEWAY Destination requires forwarding by intermediary +.It H RTF_HOST Host entry (net otherwise) +.It L RTF_LLINFO Valid protocol to link address translation. +.It l RTF_LOCAL Route represents a local address +.It M RTF_MODIFIED Modified dynamically (by redirect) +.It p RTF_ANNOUNCE Link level proxy +.It R RTF_REJECT Host or net unreachable +.It S RTF_STATIC Manually added +.It U RTF_UP Route usable +.It X RTF_XRESOLVE External daemon translates proto to link address +.El +.Pp +Direct routes are created for each +interface attached to the local host; +the gateway field for such entries shows the address of the outgoing interface. +The refcnt field gives the +current number of active uses of the route. +Connection oriented +protocols normally hold on to a single route for the duration of +a connection while connectionless protocols obtain a route while sending +to the same destination. +The use field provides a count of the number of packets +sent using that route. +The mtu entry shows the mtu associated with +that route. +This mtu value is used as the basis for the TCP maximum +segment size. +The 'L' flag appended to the mtu value indicates that +the value is locked, and that path mtu discovery is turned off for +that route. +A +.Sq - +indicates that the mtu for this route has not been set, and a default +TCP maximum segment size will be used. +The interface entry indicates +the network interface used for the route. +.Pp +When +.Nm +is invoked with the +.Fl w +option and a +.Ar wait +interval argument, it displays a running count of statistics related to +network interfaces. +An obsolescent version of this option used a numeric parameter +with no option, and is currently supported for backward compatibility. +This display consists of a column for the primary interface (the first +interface found during autoconfiguration) and a column summarizing +information for all interfaces. +The primary interface may be replaced with another interface with the +.Fl I +option. +The first line of each screen of information contains a summary since the +system was last rebooted. +Subsequent lines of output show values +accumulated over the preceding interval. +.Pp +The first character of the flags column in the +.Fl B +option shows the status of the +.Xr bpf 4 +descriptor which has three different values: +Idle ('I'), Waiting ('W') and Timed Out ('T'). +The second character indicates whether the promisc flag is set. +The third character indicates the status of the immediate mode. +The fourth character indicates whether the peer will have the ability +to see the packets sent. +And the fifth character shows the header complete flag status. +.Sh SEE ALSO +.Xr fstat 1 , +.Xr nfsstat 1 , +.Xr ps 1 , +.Xr sockstat 1 , +.Xr vmstat 1 , +.Xr inet 3 , +.Xr bpf 4 , +.Xr hosts 5 , +.Xr networks 5 , +.Xr protocols 5 , +.Xr services 5 , +.Xr iostat 8 , +.Xr trpt 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +IPv6 support was added by WIDE/KAME project. +.\" .Sh FILES +.\" .Bl -tag -width /dev/mem -compact +.\" .It Pa /netbsd +.\" default kernel namelist +.\" .It Pa /dev/mem +.\" default memory file +.\" .El +.Sh BUGS +The notion of errors is ill-defined. diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h new file mode 100644 index 000000000..b35bb18ed --- /dev/null +++ b/usr.bin/netstat/netstat.h @@ -0,0 +1,149 @@ +/* $NetBSD: netstat.h,v 1.51 2014/11/06 21:30:09 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)netstat.h 8.2 (Berkeley) 1/4/94 + */ + +#include +#include + +int Aflag; /* show addresses of protocol control block */ +int aflag; /* show all sockets (including servers) */ +int Bflag; /* show Berkeley Packet Filter information */ +int bflag; /* show i/f byte stats */ +int dflag; /* show i/f dropped packets */ +#ifndef SMALL +int gflag; /* show group (multicast) routing or stats */ +#endif +int hflag; /* humanize byte counts */ +int iflag; /* show interfaces */ +int Lflag; /* don't show LLINFO entries */ +int lflag; /* show routing table with use and ref */ +int mflag; /* show memory stats */ +int numeric_addr; /* show addresses numerically */ +int numeric_port; /* show ports numerically */ +int nflag; /* same as above, for show.c compat */ +int Pflag; /* dump a PCB */ +int pflag; /* show given protocol */ +int qflag; /* show softintrq */ +int rflag; /* show routing tables (or routing stats) */ +int sflag; /* show protocol statistics */ +int tagflag; /* show route tags */ +int tflag; /* show i/f watchdog timers */ +int Vflag; /* show Vestigial TIME_WAIT (VTW) information */ +int vflag; /* verbose route information or don't truncate names */ + +char *interface; /* desired i/f for stats, or NULL for all i/fs */ + +int af; /* address family */ +int use_sysctl; /* use sysctl instead of kmem */ +int force_sysctl; /* force use of sysctl (or exit) - for testing */ + + +int kread(u_long addr, char *buf, int size); +const char *plural(int); +const char *plurales(int); +int get_hardticks(void); + +void protopr(u_long, const char *); +void tcp_stats(u_long, const char *); +void tcp_dump(u_long, const char *, u_long); +void udp_stats(u_long, const char *); +void ip_stats(u_long, const char *); +void icmp_stats(u_long, const char *); +void igmp_stats(u_long, const char *); +void pim_stats(u_long, const char *); +void arp_stats(u_long, const char *); +void carp_stats(u_long, const char *); +void pfsync_stats(u_long, const char*); +#ifdef IPSEC +void fast_ipsec_stats(u_long, const char *); +#endif + +#ifdef INET6 +struct sockaddr_in6; +struct in6_addr; +void ip6protopr(u_long, const char *); +void tcp6_stats(u_long, const char *); +void tcp6_dump(u_long, const char *, u_long); +void udp6_stats(u_long, const char *); +void ip6_stats(u_long, const char *); +void ip6_ifstats(const char *); +void icmp6_stats(u_long, const char *); +void icmp6_ifstats(const char *); +void pim6_stats(u_long, const char *); +void rip6_stats(u_long, const char *); +void mroute6pr(u_long, u_long, u_long); +void mrt6_stats(u_long, u_long); +#endif /*INET6*/ + +#ifdef IPSEC +void pfkey_stats(u_long, const char *); +#endif + +void mbpr(u_long, u_long, u_long, u_long, u_long); + +void hostpr(u_long, u_long); +void impstats(u_long, u_long); + +void rt_stats(u_long); +char *ns_phost(struct sockaddr *); + +const char *atalk_print(const struct sockaddr *, int); +const char *atalk_print2(const struct sockaddr *, const struct sockaddr *, + int); +char *ns_print(struct sockaddr *); + +void nsprotopr(u_long, const char *); +void spp_stats(u_long, const char *); +void idp_stats(u_long, const char *); +void nserr_stats(u_long, const char *); + +void atalkprotopr(u_long, const char *); +void ddp_stats(u_long, const char *); + +void intpr(int, u_long, void (*)(const char *)); + +void unixpr(u_long); + +void routepr(u_long); +void mroutepr(u_long, u_long, u_long, u_long); +void mrt_stats(u_long, u_long); + +void bpf_stats(void); +void bpf_dump(const char *); + +kvm_t *get_kvmd(void); + +char *mpls_ntoa(const struct sockaddr *); + +struct kinfo_pcb *getpcblist_sysctl(const char *, size_t *); + +#define PLEN (LONG_BIT / 4 + 2) diff --git a/usr.bin/netstat/netstat_hostops.c b/usr.bin/netstat/netstat_hostops.c new file mode 100644 index 000000000..be38f914b --- /dev/null +++ b/usr.bin/netstat/netstat_hostops.c @@ -0,0 +1,41 @@ +/* $NetBSD: netstat_hostops.c,v 1.1 2010/12/13 21:15:30 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: netstat_hostops.c,v 1.1 2010/12/13 21:15:30 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_sysctl = sysctl, +}; diff --git a/usr.bin/netstat/netstat_rumpops.c b/usr.bin/netstat/netstat_rumpops.c new file mode 100644 index 000000000..92ad0e10a --- /dev/null +++ b/usr.bin/netstat/netstat_rumpops.c @@ -0,0 +1,46 @@ +/* $NetBSD: netstat_rumpops.c,v 1.1 2010/12/13 21:15:30 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: netstat_rumpops.c,v 1.1 2010/12/13 21:15:30 pooka Exp $"); +#endif /* !lint */ + +#include + +#include +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_init = rumpclient_init, + + .op_sysctl = rump_sys___sysctl, +}; diff --git a/usr.bin/netstat/pfkey.c b/usr.bin/netstat/pfkey.c new file mode 100644 index 000000000..9ad0089df --- /dev/null +++ b/usr.bin/netstat/pfkey.c @@ -0,0 +1,180 @@ +/* $NetBSD: pfkey.c,v 1.1 2012/01/06 14:21:16 drochner Exp $ */ +/* $KAME: ipsec.c,v 1.33 2003/07/25 09:54:32 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)inet.c 8.4 (Berkeley) 4/20/94"; +#else +#ifdef __NetBSD__ +__RCSID("$NetBSD: pfkey.c,v 1.1 2012/01/06 14:21:16 drochner Exp $"); +#endif +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#ifdef IPSEC +#include +#endif + +#include +#include +#include +#include +#include "netstat.h" + +#ifdef IPSEC + +static const char *pfkey_msgtypenames[] = { + "reserved", "getspi", "update", "add", "delete", + "get", "acquire", "register", "expire", "flush", + "dump", "x_promisc", "x_pchange", "x_spdupdate", "x_spdadd", + "x_spddelete", "x_spdget", "x_spdacquire", "x_spddump", "x_spdflush", + "x_spdsetidx", "x_spdexpire", "x_spddelete2" +}; + +static const char *pfkey_msgtype_names(int); + +static const char * +pfkey_msgtype_names(int x) +{ + const int max = + sizeof(pfkey_msgtypenames)/sizeof(pfkey_msgtypenames[0]); + static char buf[20]; + + if (x < max && pfkey_msgtypenames[x]) + return pfkey_msgtypenames[x]; + snprintf(buf, sizeof(buf), "#%d", x); + return buf; +} + +void +pfkey_stats(u_long off, const char *name) +{ + uint64_t pfkeystat[PFKEY_NSTATS]; + int first, type; + + if (use_sysctl) { + size_t size = sizeof(pfkeystat); + + if (sysctlbyname("net.key.stats", pfkeystat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf ("%s:\n", name); + +#define p(f, m) if (pfkeystat[f] || sflag <= 1) \ + printf(m, (unsigned long long)pfkeystat[f], plural(pfkeystat[f])) + + /* userland -> kernel */ + p(PFKEY_STAT_OUT_TOTAL, "\t%llu request%s sent from userland\n"); + p(PFKEY_STAT_OUT_BYTES, "\t%llu byte%s sent from userland\n"); + for (first = 1, type = 0; type < 256; type++) { + if (pfkeystat[PFKEY_STAT_OUT_MSGTYPE + type] == 0) + continue; + if (first) { + printf("\thistogram by message type:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", pfkey_msgtype_names(type), + (unsigned long long)pfkeystat[PFKEY_STAT_OUT_MSGTYPE + type]); + } + p(PFKEY_STAT_OUT_INVLEN, "\t%llu message%s with invalid length field\n"); + p(PFKEY_STAT_OUT_INVVER, "\t%llu message%s with invalid version field\n"); + p(PFKEY_STAT_OUT_INVMSGTYPE, "\t%llu message%s with invalid message type field\n"); + p(PFKEY_STAT_OUT_TOOSHORT, "\t%llu message%s too short\n"); + p(PFKEY_STAT_OUT_NOMEM, "\t%llu message%s with memory allocation failure\n"); + p(PFKEY_STAT_OUT_DUPEXT, "\t%llu message%s with duplicate extension\n"); + p(PFKEY_STAT_OUT_INVEXTTYPE, "\t%llu message%s with invalid extension type\n"); + p(PFKEY_STAT_OUT_INVSATYPE, "\t%llu message%s with invalid sa type\n"); + p(PFKEY_STAT_OUT_INVADDR, "\t%llu message%s with invalid address extension\n"); + + /* kernel -> userland */ + p(PFKEY_STAT_IN_TOTAL, "\t%llu request%s sent to userland\n"); + p(PFKEY_STAT_IN_BYTES, "\t%llu byte%s sent to userland\n"); + for (first = 1, type = 0; type < 256; type++) { + if (pfkeystat[PFKEY_STAT_IN_MSGTYPE + type] == 0) + continue; + if (first) { + printf("\thistogram by message type:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", pfkey_msgtype_names(type), + (unsigned long long)pfkeystat[PFKEY_STAT_IN_MSGTYPE + type]); + } + p(PFKEY_STAT_IN_MSGTARGET + KEY_SENDUP_ONE, + "\t%llu message%s toward single socket\n"); + p(PFKEY_STAT_IN_MSGTARGET + KEY_SENDUP_ALL, + "\t%llu message%s toward all sockets\n"); + p(PFKEY_STAT_IN_MSGTARGET + KEY_SENDUP_REGISTERED, + "\t%llu message%s toward registered sockets\n"); + p(PFKEY_STAT_IN_NOMEM, "\t%llu message%s with memory allocation failure\n"); +#undef p +} +#endif /*IPSEC*/ diff --git a/usr.bin/netstat/pfsync.c b/usr.bin/netstat/pfsync.c new file mode 100644 index 000000000..ca1813fb5 --- /dev/null +++ b/usr.bin/netstat/pfsync.c @@ -0,0 +1,119 @@ +/* $NetBSD: pfsync.c,v 1.1 2011/03/01 19:01:59 dyoung Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: pfsync.c,v 1.1 2011/03/01 19:01:59 dyoung Exp $"); +#endif /* not lint */ + +#define _CALLOUT_PRIVATE /* for defs in sys/callout.h */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "prog_ops.h" + +/* + * Dump PFSYNC statistics structure. + */ +void +pfsync_stats(u_long off, const char *name) +{ + uint64_t pfsyncstat[PFSYNC_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(pfsyncstat); + + if (sysctlbyname("net.inet.pfsync.stats", pfsyncstat, &size, + NULL, 0) == -1) + return; + } else { + warnx("%s stats not available via KVM.", name); + return; + } + + printf("%s:\n", name); + +#define p(f, m) if (pfsyncstat[f] || sflag <= 1) \ + printf(m, pfsyncstat[f], plural(pfsyncstat[f])) +#define p2(f, m) if (pfsyncstat[f] || sflag <= 1) \ + printf(m, pfsyncstat[f]) + + p(PFSYNC_STAT_IPACKETS, "\t%" PRIu64 " packet%s received (IPv4)\n"); + p(PFSYNC_STAT_IPACKETS6,"\t%" PRIu64 " packet%s received (IPv6)\n"); + p(PFSYNC_STAT_BADIF, "\t\t%" PRIu64 " packet%s discarded for bad interface\n"); + p(PFSYNC_STAT_BADTTL, "\t\t%" PRIu64 " packet%s discarded for bad ttl\n"); + p(PFSYNC_STAT_HDROPS, "\t\t%" PRIu64 " packet%s shorter than header\n"); + p(PFSYNC_STAT_BADVER, "\t\t%" PRIu64 " packet%s discarded for bad version\n"); + p(PFSYNC_STAT_BADAUTH, "\t\t%" PRIu64 " packet%s discarded for bad HMAC\n"); + p(PFSYNC_STAT_BADACT,"\t\t%" PRIu64 " packet%s discarded for bad action\n"); + p(PFSYNC_STAT_BADLEN, "\t\t%" PRIu64 " packet%s discarded for short packet\n"); + p(PFSYNC_STAT_BADVAL, "\t\t%" PRIu64 " state%s discarded for bad values\n"); + p(PFSYNC_STAT_STALE, "\t\t%" PRIu64 " stale state%s\n"); + p(PFSYNC_STAT_BADSTATE, "\t\t%" PRIu64 " failed state lookup/insert%s\n"); + p(PFSYNC_STAT_OPACKETS, "\t%" PRIu64 " packet%s sent (IPv4)\n"); + p(PFSYNC_STAT_OPACKETS6, "\t%" PRIu64 " packet%s sent (IPv6)\n"); + p2(PFSYNC_STAT_ONOMEM, "\t\t%" PRIu64 " send failed due to mbuf memory error\n"); + p2(PFSYNC_STAT_OERRORS, "\t\t%" PRIu64 " send error\n"); +#undef p +#undef p2 +} + + diff --git a/usr.bin/netstat/prog_ops.h b/usr.bin/netstat/prog_ops.h new file mode 100644 index 000000000..ec8f76282 --- /dev/null +++ b/usr.bin/netstat/prog_ops.h @@ -0,0 +1,50 @@ +/* $NetBSD: prog_ops.h,v 1.2 2010/12/15 11:22:41 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROG_OPS_H_ +#define _PROG_OPS_H_ + +#include + +#ifndef CRUNCHOPS +struct prog_ops { + int (*op_init)(void); + + int (*op_sysctl)(const int *, u_int, void *, size_t *, + const void *, size_t); +}; +extern const struct prog_ops prog_ops; + +#define prog_init prog_ops.op_init +#define prog_sysctl prog_ops.op_sysctl +#else +#define prog_init ((int (*)(void))NULL) +#define prog_sysctl sysctl +#endif + +#endif /* _PROG_OPS_H_ */ diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c new file mode 100644 index 000000000..ffa99ee18 --- /dev/null +++ b/usr.bin/netstat/route.c @@ -0,0 +1,334 @@ +/* $NetBSD: route.c,v 1.84 2015/05/25 03:56:20 manu Exp $ */ + +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)route.c 8.3 (Berkeley) 3/9/94"; +#else +__RCSID("$NetBSD: route.c,v 1.84 2015/05/25 03:56:20 manu Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "netstat.h" +#include "rtutil.h" + +#define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d))) + +/* + * XXX we put all of the sockaddr types in here to force the alignment + * to be correct. + */ +static union sockaddr_union { + struct sockaddr u_sa; + struct sockaddr_in u_in; + struct sockaddr_un u_un; + struct sockaddr_at u_at; + struct sockaddr_dl u_dl; + u_short u_data[128]; + int u_dummy; /* force word-alignment */ +} pt_u; + +int do_rtent = 0; +struct rtentry rtentry; +struct radix_node rnode; +struct radix_mask rmask; + +static struct sockaddr *kgetsa(const struct sockaddr *); +static void p_tree(struct radix_node *); +static void p_rtnode(void); +static void p_krtentry(struct rtentry *); + +/* + * Print routing tables. + */ +void +routepr(u_long rtree) +{ + struct radix_node_head *rnh, head; + struct radix_node_head *rt_nodes[AF_MAX+1]; + int i; + + printf("Routing tables\n"); + + if (rtree == 0) { + printf("rt_tables: symbol not in namelist\n"); + return; + } + + kget(rtree, rt_nodes); + for (i = 0; i <= AF_MAX; i++) { + if ((rnh = rt_nodes[i]) == 0) + continue; + kget(rnh, head); + if (i == AF_UNSPEC) { + if (Aflag && (af == 0 || af == 0xff)) { + printf("Netmasks:\n"); + p_tree(head.rnh_treetop); + } + } else if (af == AF_UNSPEC || af == i) { + p_family(i); + do_rtent = 1; + p_rthdr(i, Aflag); + p_tree(head.rnh_treetop); + } + } +} + +static struct sockaddr * +kgetsa(const struct sockaddr *dst) +{ + + kget(dst, pt_u.u_sa); + if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa)) + kread((u_long)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len); + return (&pt_u.u_sa); +} + +static void +p_tree(struct radix_node *rn) +{ + +again: + kget(rn, rnode); + if (rnode.rn_b < 0) { + if (Aflag) + printf("%-8.8lx ", (u_long) rn); + if (rnode.rn_flags & RNF_ROOT) { + if (Aflag) + printf("(root node)%s", + rnode.rn_dupedkey ? " =>\n" : "\n"); + } else if (do_rtent) { + kget(rn, rtentry); + p_krtentry(&rtentry); + if (Aflag) + p_rtnode(); + } else { + p_sockaddr(kgetsa((const struct sockaddr *)rnode.rn_key), + NULL, 0, 44, nflag); + putchar('\n'); + } + if ((rn = rnode.rn_dupedkey) != NULL) + goto again; + } else { + if (Aflag && do_rtent) { + printf("%-8.8lx ", (u_long) rn); + p_rtnode(); + } + rn = rnode.rn_r; + p_tree(rnode.rn_l); + p_tree(rn); + } +} + +static void +p_rtnode(void) +{ + struct radix_mask *rm = rnode.rn_mklist; + char nbuf[20]; + + if (rnode.rn_b < 0) { + if (rnode.rn_mask) { + printf("\t mask "); + p_sockaddr(kgetsa((const struct sockaddr *)rnode.rn_mask), + NULL, 0, -1, nflag); + } else if (rm == 0) + return; + } else { + (void)snprintf(nbuf, sizeof nbuf, "(%d)", rnode.rn_b); + printf("%6.6s %8.8lx : %8.8lx", nbuf, (u_long) rnode.rn_l, + (u_long) rnode.rn_r); + } + while (rm) { + kget(rm, rmask); + (void)snprintf(nbuf, sizeof nbuf, " %d refs, ", rmask.rm_refs); + printf(" mk = %8.8lx {(%d),%s", (u_long) rm, + -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " "); + if (rmask.rm_flags & RNF_NORMAL) { + struct radix_node rnode_aux; + printf(" , "); + kget(rmask.rm_leaf, rnode_aux); + p_sockaddr(kgetsa((const struct sockaddr *)rnode_aux.rn_mask), + NULL, 0, -1, nflag); + } else + p_sockaddr(kgetsa((const struct sockaddr *)rmask.rm_mask), + NULL, 0, -1, nflag); + putchar('}'); + if ((rm = rmask.rm_mklist) != NULL) + printf(" ->"); + } + putchar('\n'); +} + +static struct sockaddr *sockcopy(struct sockaddr *, union sockaddr_union *); + +/* + * copy a sockaddr into an allocated region, allocate at least sockaddr + * bytes and zero unused + */ +static struct sockaddr * +sockcopy(struct sockaddr *sp, union sockaddr_union *dp) +{ + int len; + + if (sp == 0 || sp->sa_len == 0) + (void)memset(dp, 0, sizeof (*sp)); + else { + len = (sp->sa_len >= sizeof (*sp)) ? sp->sa_len : sizeof (*sp); + (void)memcpy(dp, sp, len); + } + return ((struct sockaddr *)dp); +} + +static void +p_krtentry(struct rtentry *rt) +{ + static struct ifnet ifnet, *lastif; + union sockaddr_union addr_un, mask_un; + struct sockaddr *addr, *mask; + + if (Lflag && (rt->rt_flags & RTF_LLINFO)) + return; + + memset(&addr_un, 0, sizeof(addr_un)); + memset(&mask_un, 0, sizeof(mask_un)); + addr = sockcopy(kgetsa(rt_getkey(rt)), &addr_un); + if (rt_mask(rt)) + mask = sockcopy(kgetsa(rt_mask(rt)), &mask_un); + else + mask = sockcopy(NULL, &mask_un); + p_addr(addr, mask, rt->rt_flags, nflag); + p_gwaddr(kgetsa(rt->rt_gateway), kgetsa(rt->rt_gateway)->sa_family, nflag); + p_flags(rt->rt_flags); + printf("%6d %8"PRIu64" ", rt->rt_refcnt, rt->rt_use); + if (rt->rt_rmx.rmx_mtu) + printf("%6"PRIu64, rt->rt_rmx.rmx_mtu); + else + printf("%6s", "-"); + putchar((rt->rt_rmx.rmx_locks & RTV_MTU) ? 'L' : ' '); + if (tagflag == 1) { +#ifndef SMALL + if (rt->rt_tag != NULL) { + const struct sockaddr *tagsa = kgetsa(rt->rt_tag); + char *tagstr; + + if (tagsa->sa_family == AF_MPLS) { + tagstr = mpls_ntoa(tagsa); + if (strlen(tagstr) < 7) + printf("%7s", tagstr); + else + printf("%s", tagstr); + } + else + printf("%7s", "-"); + } else +#endif + printf("%7s", "-"); + } + if (rt->rt_ifp) { + if (rt->rt_ifp != lastif) { + kget(rt->rt_ifp, ifnet); + lastif = rt->rt_ifp; + } + printf(" %.16s%s", ifnet.if_xname, + rt->rt_nodes[0].rn_dupedkey ? " =>" : ""); + } + putchar('\n'); +#ifndef SMALL + if (vflag) + p_rtrmx(&rt->rt_rmx); +#endif +} + +/* + * Print routing statistics + */ +void +rt_stats(u_long off) +{ + struct rtstat rtstats; + + if (use_sysctl) { + size_t rtsize = sizeof(rtstats); + + if (sysctlbyname("net.route.stats", &rtstats, &rtsize, + NULL, 0) == -1) + err(1, "rt_stats: sysctl"); + } else if (off == 0) { + printf("rtstat: symbol not in namelist\n"); + return; + } else + kread(off, (char *)&rtstats, sizeof(rtstats)); + + printf("routing:\n"); + printf("\t%llu bad routing redirect%s\n", + (unsigned long long)rtstats.rts_badredirect, + plural(rtstats.rts_badredirect)); + printf("\t%llu dynamically created route%s\n", + (unsigned long long)rtstats.rts_dynamic, + plural(rtstats.rts_dynamic)); + printf("\t%llu new gateway%s due to redirects\n", + (unsigned long long)rtstats.rts_newgateway, + plural(rtstats.rts_newgateway)); + printf("\t%llu destination%s found unreachable\n", + (unsigned long long)rtstats.rts_unreach, + plural(rtstats.rts_unreach)); + printf("\t%llu use%s of a wildcard route\n", + (unsigned long long)rtstats.rts_wildcard, + plural(rtstats.rts_wildcard)); +} diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c new file mode 100644 index 000000000..d26a7486d --- /dev/null +++ b/usr.bin/netstat/unix.c @@ -0,0 +1,231 @@ +/* $NetBSD: unix.c,v 1.34 2012/03/20 20:34:58 matt Exp $ */ + +/*- + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)unix.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: unix.c,v 1.34 2012/03/20 20:34:58 matt Exp $"); +#endif +#endif /* not lint */ + +/* + * Display protocol blocks in the unix domain. + */ +#define _KERNEL +#include +#undef _KERNEL +#include +#include +#include +#include +#include +#include +#include +#include +#define _KERNEL +#include +#undef _KERNEL + +#include + +#include +#include +#include +#include +#include +#include "netstat.h" +#include "prog_ops.h" + +static void unixdomainprhdr(void); +static void unixdomainpr0(u_long, u_long, u_long, u_long, u_long, u_long, + u_long, u_long, u_long, struct sockaddr_un *, int); +static void unixdomainpr(struct socket *, void *); + +static struct file *file, *fileNFILE; +static int ns_nfiles; + +static void +unixdomainprhdr(void) +{ + printf("Active UNIX domain sockets\n"); + printf("%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n", + "Address", "Type", "Recv-Q", "Send-Q", "Inode", "Conn", "Refs", + "Nextref"); +} + +static const char * const socktype[] = + { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" }; + +static void +unixdomainpr0(u_long so_pcb, u_long so_type, u_long rcvq, u_long sndq, + u_long inode, u_long conn, u_long refs, u_long nextref, + u_long addr, struct sockaddr_un *sun, int remote) +{ + printf("%8lx %-6.6s %6ld %6ld %8lx %8lx %8lx %8lx", + so_pcb, socktype[so_type], rcvq, sndq, inode, conn, refs, + nextref); + if (addr || remote) + printf((remote ? " -> %.*s" : " %.*s"), + (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))), + sun->sun_path); + putchar('\n'); +} + +static void +unixdomainpr(struct socket *so, void *soaddr) +{ + struct unpcb unp, runp; + struct sockaddr_un sun, rsun; + static int first = 1; + int remote = 0; + + if (kread((u_long)so->so_pcb, (char *)&unp, sizeof (unp))) + return; + if (unp.unp_addr) + if (kread((u_long)unp.unp_addr, (char *)&sun, sizeof (sun))) + unp.unp_addr = 0; + + if (!unp.unp_addr) { + memset(&rsun, 0, sizeof(rsun)); + if (unp.unp_conn && + kread((u_long)unp.unp_conn, (char *)&runp, sizeof (runp)) == 0 && + runp.unp_addr && + kread((u_long)runp.unp_addr, (char *)&rsun, sizeof (rsun)) == 0 && + rsun.sun_path[0] != '\0') + remote = 1; + } + + if (first) { + unixdomainprhdr(); + first = 0; + } + + unixdomainpr0((u_long)so->so_pcb, so->so_type, so->so_rcv.sb_cc, + so->so_snd.sb_cc, (u_long)unp.unp_vnode, + (u_long)unp.unp_conn, (u_long)unp.unp_refs, + (u_long)unp.unp_nextref, (u_long)unp.unp_addr, + remote ? &rsun : &sun, remote); +} + +void +unixpr(u_long off) +{ + struct file *fp; + struct socket sock, *so = &sock; + char *filebuf; + struct protosw *unixsw = (struct protosw *)off; + + if (use_sysctl) { + struct kinfo_pcb *pcblist; + int mib[8]; + size_t namelen = 0, size = 0, i; + const char *mibnames[] = { + "net.local.stream.pcblist", + "net.local.dgram.pcblist", + "net.local.seqpacket.pcblist", + NULL, + }; + const char **mibname; + static int first = 1; + + for (mibname = mibnames; *mibname; mibname++) { + memset(mib, 0, sizeof(mib)); + + if (sysctlnametomib(*mibname, mib, + &namelen) == -1) + err(1, "sysctlnametomib: %s", *mibname); + + if (prog_sysctl(mib, sizeof(mib) / sizeof(*mib), + NULL, &size, NULL, 0) == -1) + err(1, "sysctl (query)"); + + if ((pcblist = malloc(size)) == NULL) + err(1, "malloc"); + memset(pcblist, 0, size); + + mib[6] = sizeof(*pcblist); + mib[7] = size / sizeof(*pcblist); + + if (prog_sysctl(mib, sizeof(mib) / sizeof(*mib), + pcblist, &size, NULL, 0) == -1) + err(1, "sysctl (copy)"); + + for (i = 0; i < size / sizeof(*pcblist); i++) { + struct kinfo_pcb *ki = &pcblist[i]; + struct sockaddr_un *sun; + int remote = 0; + + if (first) { + unixdomainprhdr(); + first = 0; + } + + sun = (struct sockaddr_un *)&ki->ki_dst; + if (sun->sun_path[0] != '\0') { + remote = 1; + } else { + sun = (struct sockaddr_un *)&ki->ki_src; + } + + unixdomainpr0(ki->ki_pcbaddr, ki->ki_type, + ki->ki_rcvq, ki->ki_sndq, + ki->ki_vnode, ki->ki_conn, + ki->ki_refs, ki->ki_nextref, + ki->ki_sockaddr, sun, remote); + } + + free(pcblist); + } + + } else { + filebuf = (char *)kvm_getfiles(get_kvmd(), KERN_FILE, + 0, &ns_nfiles); + if (filebuf == 0) { + printf("file table read error: %s", + kvm_geterr(get_kvmd())); + return; + } + file = (struct file *)(filebuf + sizeof(fp)); + fileNFILE = file + ns_nfiles; + for (fp = file; fp < fileNFILE; fp++) { + if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET) + continue; + if (kread((u_long)fp->f_data, (char *)so, sizeof (*so))) + continue; + /* kludge */ + if (so->so_proto >= unixsw && so->so_proto <= unixsw + 2) + if (so->so_pcb) + unixdomainpr(so, fp->f_data); + } + } +} diff --git a/usr.bin/netstat/vtw.c b/usr.bin/netstat/vtw.c new file mode 100644 index 000000000..08ed43f19 --- /dev/null +++ b/usr.bin/netstat/vtw.c @@ -0,0 +1,439 @@ +/* $NetBSD: vtw.c,v 1.8 2015/06/16 22:54:10 christos Exp $ */ + +/* + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Coyote Point Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)inet.c 8.4 (Berkeley) 4/20/94"; +#else +__RCSID("$NetBSD: vtw.c,v 1.8 2015/06/16 22:54:10 christos Exp $"); +#endif +#endif /* not lint */ + +#define _CALLOUT_PRIVATE /* for defs in sys/callout.h */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "netstat.h" +#include "vtw.h" +#include "prog_ops.h" + +static void snarf(const void *, void *, size_t); +static void *lookup(const char *); +static void process_vtw(const vtw_ctl_t *, void (*)(const vtw_t *)); + +static void +snarf(const void *addr, void *buf, size_t len) +{ + size_t cc; + + memset(buf, 0, len); + + cc = kvm_read(get_kvmd(), (unsigned long) addr, buf, len); + + if (cc != len) { + warnx("%s: short read at %p, len %zx cc %zx", __func__, addr, + len, cc); + } +} + +static void * +lookup(const char *name) +{ + kvm_t *k; + struct nlist nl[2]; + + nl[0].n_name = name; + nl[0].n_value = 0; + nl[1].n_name = NULL; + + if ((k = get_kvmd()) == NULL) { + if (Vflag) + errx(EXIT_FAILURE, "kvm not available"); + return NULL; + } + switch (kvm_nlist(k, &nl[0])) { + case -1: + err(EXIT_FAILURE, "kvm_nlist"); + break; + + case 0: + return (void *)nl[0].n_value; + + default: + if (Vflag) + errx(EXIT_FAILURE, "%s missing in symbol table", name); + break; + } + + return NULL; +} + +void +timebase(struct timeval *tv) +{ + void *p; + struct bintime timebasebin; + + p = lookup("timebasebin"); + if (!p) + return; + snarf(p, &timebasebin, sizeof(timebasebin)); + bintime2timeval(&timebasebin, tv); +} + +static void +process_vtw(const vtw_ctl_t * ctl, void (*print)(const vtw_t *)) +{ + vtw_t *vp; + + for (vp = ctl->base.v; vp && vp <= ctl->lim.v;) { + + (*print)(vp); + + if (ctl->is_v4) { + vtw_v4_t *v4 = (vtw_v4_t *)vp; + + vp = &(++v4)->common; + } else if (ctl->is_v6) { + vtw_v6_t *v6 = (vtw_v6_t *)vp; + + vp = &(++v6)->common; + } + } +} + +void +show_vtw_stats(void) +{ + vtw_stats_t stats; + void *p; + + if (!Vflag) + return; + + if ((p = lookup("vtw_stats")) == NULL) + return; + snarf(p, &stats, sizeof(stats)); + + printf("\t\t%" PRIu64 " inserts\n", stats.ins); + printf("\t\t%" PRIu64 " deletes\n", stats.del); + printf("\t\t%" PRIu64 " assassinations\n", stats.kill); + printf("\tvestigial time-wait lookup_connect\n"); + printf("\t\t%" PRIu64 " look\n", stats.look[0]); + printf("\t\t%" PRIu64 " hit\n", stats.hit[0]); + printf("\t\t%" PRIu64 " miss\n", stats.miss[0]); + printf("\t\t%" PRIu64 " probe\n", stats.probe[0]); + printf("\t\t%" PRIu64 " losing\n", stats.losing[0]); + printf("\t\t%" PRIu64 " max_chain\n", stats.max_chain[0]); + printf("\t\t%" PRIu64 " max_probe\n", stats.max_probe[0]); + printf("\t\t%" PRIu64 " max_loss\n", stats.max_loss[0]); + printf("\tvestigial time-wait lookup_port\n"); + printf("\t\t%" PRIu64 " look\n", stats.look[1]); + printf("\t\t%" PRIu64 " hit\n", stats.hit[1]); + printf("\t\t%" PRIu64 " miss\n", stats.miss[1]); + printf("\t\t%" PRIu64 " probe\n", stats.probe[1]); + printf("\t\t%" PRIu64 " losing\n", stats.losing[1]); + printf("\t\t%" PRIu64 " max_chain\n", stats.max_chain[1]); + printf("\t\t%" PRIu64 " max_probe\n", stats.max_probe[1]); + printf("\t\t%" PRIu64 " max_loss\n", stats.max_loss[1]); +} + +void +show_vtw_v4(void (*print)(const vtw_t *)) +{ + fatp_t *base, *lim; + fatp_t **hash, **port; + size_t n; + fatp_ctl_t fat_tcpv4; + vtw_ctl_t vtw_tcpv4[VTW_NCLASS]; + int i; + int mem = 0; + void *p; + + if ((p = lookup("fat_tcpv4")) == NULL) + return; + snarf(p, &fat_tcpv4, sizeof(fat_tcpv4)); + + if ((p = lookup("vtw_tcpv4")) == NULL) + return; + snarf(p, &vtw_tcpv4[0], sizeof(vtw_tcpv4)); + + mem += sizeof(fat_tcpv4); + mem += sizeof(vtw_tcpv4); + + /* snarf/adjust vtw_ctl */ + for (i = 0; i < VTW_NCLASS; ++i) { + vtw_v4_t *kbase, *klim; + vtw_v4_t *ubase; + ptrdiff_t delta; + + kbase = vtw_tcpv4[i].base.v4; + klim = vtw_tcpv4[i].lim.v4; + + if (!kbase | !klim) + continue; + + n = (klim - kbase + 1); + + if (!i) { + if ((ubase = malloc(n * sizeof(*kbase))) == NULL) + err(EXIT_FAILURE, NULL); + snarf(kbase, ubase, n * sizeof(*ubase)); + + mem += n * sizeof(*ubase); + } else { + ubase = vtw_tcpv4[0].base.v4; + } + + delta = ubase - kbase; + + vtw_tcpv4[i].base.v4 += delta; + vtw_tcpv4[i].lim.v4 += delta; + vtw_tcpv4[i].alloc.v4 += delta; + vtw_tcpv4[i].fat = &fat_tcpv4; + + if (vtw_tcpv4[i].oldest.v4) + vtw_tcpv4[i].oldest.v4 += delta; + } + + /* snarf/adjust fat_ctl */ + + base = fat_tcpv4.base; + lim = fat_tcpv4.lim; + + if (!base | !lim) + goto end; + + mem += (lim - base + 1) * sizeof(*base); + + fat_tcpv4.base = malloc((lim - base + 1) * sizeof(*base)); + if (fat_tcpv4.base == NULL) + err(EXIT_FAILURE, NULL); + fat_tcpv4.lim = fat_tcpv4.base + (lim - base); + + snarf(base, fat_tcpv4.base, sizeof(*base) * (lim - base + 1)); + + fat_tcpv4.vtw = &vtw_tcpv4[0]; + fat_tcpv4.free = fat_tcpv4.base + (fat_tcpv4.free - base); + + n = fat_tcpv4.mask + 1; + hash = fat_tcpv4.hash; + port = fat_tcpv4.port; + + fat_tcpv4.hash = malloc(n * sizeof(*hash)); + fat_tcpv4.port = malloc(n * sizeof(*port)); + if (fat_tcpv4.hash == NULL || fat_tcpv4.port == NULL) + err(EXIT_FAILURE, NULL); + + snarf(hash, fat_tcpv4.hash, n * sizeof(*hash)); + snarf(port, fat_tcpv4.port, n * sizeof(*port)); + +end: + process_vtw(&vtw_tcpv4[0], print); + +#if 0 + if (Vflag && vflag) { + printf("total memory for VTW in current config: %d bytes %f MB\n" + ,mem + ,mem / (1024.0 * 1024)); + } +#endif +} + +void +show_vtw_v6(void (*print)(const vtw_t *)) +{ + fatp_t *base, *lim; + fatp_t **hash, **port; + size_t n; + fatp_ctl_t fat_tcpv6; + vtw_ctl_t vtw_tcpv6[VTW_NCLASS]; + int i; + int mem = 0; + void *p; + + if ((p = lookup("fat_tcpv6")) == NULL) + return; + snarf(p, &fat_tcpv6, sizeof(fat_tcpv6)); + if ((p = lookup("vtw_tcpv6")) == NULL) + return; + snarf(p, &vtw_tcpv6[0], sizeof(vtw_tcpv6)); + + mem += sizeof(fat_tcpv6); + mem += sizeof(vtw_tcpv6); + + for (i = 0; i < VTW_NCLASS; ++i) { + vtw_v6_t *kbase, *klim; + vtw_v6_t *ubase; + ptrdiff_t delta; + + kbase = vtw_tcpv6[i].base.v6; + klim = vtw_tcpv6[i].lim.v6; + + if (!kbase | !klim) + continue; + + n = (klim - kbase + 1); + + if (!i) { + if ((ubase = malloc(n * sizeof(*kbase))) == NULL) + err(EXIT_FAILURE, NULL); + + snarf(kbase, ubase, n * sizeof(*ubase)); + + mem += n * sizeof(*ubase); + } else { + ubase = vtw_tcpv6[0].base.v6; + } + + delta = ubase - kbase; + + vtw_tcpv6[i].base.v6 += delta; + vtw_tcpv6[i].lim.v6 += delta; + vtw_tcpv6[i].alloc.v6 += delta; + vtw_tcpv6[i].fat = &fat_tcpv6; + + if (vtw_tcpv6[i].oldest.v6) + vtw_tcpv6[i].oldest.v6 += delta; + } + + base = fat_tcpv6.base; + lim = fat_tcpv6.lim; + + if (!base | !lim) + goto end; + + mem += (lim - base + 1) * sizeof(*base); + + fat_tcpv6.base = malloc((lim - base + 1) * sizeof(*base)); + if (fat_tcpv6.base == NULL) + err(EXIT_FAILURE, NULL); + fat_tcpv6.lim = fat_tcpv6.base + (lim - base); + + snarf(base, fat_tcpv6.base, sizeof(*base) * (lim - base + 1)); + + fat_tcpv6.vtw = &vtw_tcpv6[0]; + fat_tcpv6.free = fat_tcpv6.base + (fat_tcpv6.free - base); + + n = fat_tcpv6.mask + 1; + hash = fat_tcpv6.hash; + port = fat_tcpv6.port; + + fat_tcpv6.hash = malloc(n * sizeof(*hash)); + fat_tcpv6.port = malloc(n * sizeof(*port)); + if (fat_tcpv6.hash == NULL || fat_tcpv6.port == NULL) + err(EXIT_FAILURE, NULL); + + snarf(hash, fat_tcpv6.hash, n * sizeof(*hash)); + snarf(port, fat_tcpv6.port, n * sizeof(*port)); + +end: + + process_vtw(&vtw_tcpv6[0], print); +#if 0 + if (Vflag && vflag) { + printf("total memory for VTW in current config: %d bytes %f MB\n" + ,mem + ,mem / (1024.0 * 1024)); + } +#endif +} diff --git a/usr.bin/netstat/vtw.h b/usr.bin/netstat/vtw.h new file mode 100644 index 000000000..8cb197da8 --- /dev/null +++ b/usr.bin/netstat/vtw.h @@ -0,0 +1,9 @@ +#ifndef _NETSTAT_VTW_H +#define _NETSTAT_VTW_H + +void show_vtw_stats(void); +void show_vtw_v4(void (*)(const vtw_t *)); +void show_vtw_v6(void (*)(const vtw_t *)); +void timebase(struct timeval *); + +#endif /* _NETSTAT_VTW_H */