From: David van Moolenbroek Date: Wed, 15 Feb 2017 12:35:47 +0000 (+0000) Subject: Import NetBSD netstat(1) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/doc/roff.7.ps?a=commitdiff_plain;h=66dfcc8533c70ea68511edc95c03189c8eb0a7a2;p=minix.git Import NetBSD netstat(1) The port forces the use of sysctl(7), as obtaining information through KVM is not and will never be viable. The sysctl mode of netstat(1) is currently somewhat limited and buggy, though. We fix a few minimal issues, but more improvements will have to come from NetBSD reimports. Some of netstat(1)'s views are currently not supported by the operating system. Later improvements on this point will not require changes to the imported code, though. Change-Id: If74a6811f0fc81bd1ecc31010a28379b14b2a0eb --- 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 */