diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /network_cmds/netstat.tproj | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'network_cmds/netstat.tproj')
-rw-r--r-- | network_cmds/netstat.tproj/DERIVED_FILES | 1 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/data.c | 28 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/if.c | 2259 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/inet.c | 1361 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/inet6.c | 1333 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/ipsec.c | 376 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/main.c | 638 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/mbuf.c | 476 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/mcast.c | 887 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/misc.c | 122 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/mptcp.c | 170 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/netstat.1 | 445 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/netstat.h | 170 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/route.c | 795 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/systm.c | 503 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/tp_astring.c | 74 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/unix.c | 231 | ||||
-rw-r--r-- | network_cmds/netstat.tproj/vsock.c | 187 |
18 files changed, 10056 insertions, 0 deletions
diff --git a/network_cmds/netstat.tproj/DERIVED_FILES b/network_cmds/netstat.tproj/DERIVED_FILES new file mode 100644 index 0000000..a4fa6c9 --- /dev/null +++ b/network_cmds/netstat.tproj/DERIVED_FILES @@ -0,0 +1 @@ +unix/bsd/netiso/tp_astring.c diff --git a/network_cmds/netstat.tproj/data.c b/network_cmds/netstat.tproj/data.c new file mode 100644 index 0000000..9e0bbe2 --- /dev/null +++ b/network_cmds/netstat.tproj/data.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* NeXT */ +#ifndef EXTERN +#define EXTERN +#endif +#include "netstat.h" diff --git a/network_cmds/netstat.tproj/if.c b/network_cmds/netstat.tproj/if.c new file mode 100644 index 0000000..2fe6182 --- /dev/null +++ b/network_cmds/netstat.tproj/if.c @@ -0,0 +1,2259 @@ +/* + * Copyright (c) 2008-2019 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * 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. 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/kern_control.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_mib.h> +#include <net/if_llreach.h> +#include <net/ethernet.h> +#include <net/route.h> +#include <net/ntstat.h> + +#include <net/pktsched/pktsched.h> +#include <net/classq/if_classq.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> + +#include <arpa/inet.h> + +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stddef.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> + +#include <assert.h> + +#include "netstat.h" + +#define YES 1 +#define NO 0 + +#define ROUNDUP(a, size) (((a) & ((size) - 1)) ? (1 + ((a)|(size - 1))) : (a)) + +#define NEXT_SA(p) (struct sockaddr *) \ + ((caddr_t)p + (p->sa_len ? ROUNDUP(p->sa_len, sizeof(uint32_t)) : \ + sizeof(uint32_t))) + +static void sidewaysintpr (); +static void catchalarm (int); +static char *sec2str(time_t); +static void llreach_sysctl(uint32_t); +static char *nsec_to_str(unsigned long long); +static char *sched2str(unsigned int); +static char *pri2str(unsigned int i); + +#define AVGN_MAX 8 + +struct queue_stats { + int avgn; + double avg_bytes; + double avg_packets; + u_int64_t prev_bytes; + u_int64_t prev_packets; + unsigned int handle; +}; + +static void update_avg(struct if_ifclassq_stats *, struct queue_stats *); +static void print_fq_codel_stats(int slot, struct fq_codel_classstats *, + struct queue_stats *); + +struct queue_stats qstats[IFCQ_SC_MAX]; + +#ifdef INET6 +char *netname6 (struct sockaddr_in6 *, struct sockaddr *); +static char ntop_buf[INET6_ADDRSTRLEN]; /* for inet_ntop() */ +#endif + +/* + * Display a formatted value, or a '-' in the same space. + */ +static void +show_stat(const char *fmt, int width, u_int64_t value, short showvalue) +{ + char newfmt[32]; + + /* Construct the format string */ + if (showvalue) { + snprintf(newfmt, sizeof(newfmt), "%%%d%s", width, fmt); + printf(newfmt, value); + } else { + snprintf(newfmt, sizeof(newfmt), "%%%ds", width); + printf(newfmt, "-"); + } +} + +size_t +get_rti_info(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + size_t len = 0; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + if (sa->sa_len < sizeof(struct sockaddr)) + len += sizeof(struct sockaddr); + else + len += sa->sa_len; + sa = NEXT_SA(sa); + } else { + rti_info[i] = NULL; + } + } + return len; +} + +static void +multipr(int family, char *buf, char *lim) +{ + char *next; + + for (next = buf; next < lim; ) { + struct ifma_msghdr2 *ifmam = (struct ifma_msghdr2 *)next; + struct sockaddr *rti_info[RTAX_MAX]; + struct sockaddr *sa; + const char *fmt = 0; + + next += ifmam->ifmam_msglen; + if (ifmam->ifmam_type == RTM_IFINFO2) + break; + else if (ifmam->ifmam_type != RTM_NEWMADDR2) + continue; + get_rti_info(ifmam->ifmam_addrs, (struct sockaddr*)(ifmam + 1), rti_info); + sa = rti_info[RTAX_IFA]; + + if (sa->sa_family != family) + continue; + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + fmt = routename(sin->sin_addr.s_addr); + break; + } + #ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 sin6; + + memcpy(&sin6, sa, sizeof(struct sockaddr_in6)); + + if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) { + sin6.sin6_scope_id = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + + printf("%23s %-19.19s(refs: %d)\n", "", + inet_ntop(AF_INET6, &sin6.sin6_addr, + ntop_buf, sizeof(ntop_buf)), + ifmam->ifmam_refcount); + break; + } + #endif /* INET6 */ + case AF_LINK: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + + switch (sdl->sdl_type) { + case IFT_ETHER: + case IFT_FDDI: + fmt = ether_ntoa((struct ether_addr *) + LLADDR(sdl)); + break; + } + break; + } + } + if (fmt) + printf("%23s %s\n", "", fmt); + } +} + +/* + * Print a description of the network interfaces. + */ +void +intpr(void (*pfunc)(char *)) +{ + u_int64_t opackets = 0; + u_int64_t ipackets = 0; + u_int64_t obytes = 0; + u_int64_t ibytes = 0; + u_int64_t oerrors = 0; + u_int64_t ierrors = 0; + u_int64_t collisions = 0; + u_int64_t fpackets = 0; + u_int64_t fbytes = 0; + uint32_t mtu = 0; + int timer = 0; + int drops = 0; + struct sockaddr *sa = NULL; + char name[32]; + short network_layer; + short link_layer; + int mib[6]; + char *buf = NULL, *lim, *next; + size_t len; + struct if_msghdr *ifm; + struct sockaddr *rti_info[RTAX_MAX]; + unsigned int ifindex = 0; + + if (interval) { + sidewaysintpr(); + return; + } + + if (interface != 0) + ifindex = if_nametoindex(interface); + + mib[0] = CTL_NET; // networking subsystem + mib[1] = PF_ROUTE; // type of information + mib[2] = 0; // protocol (IPPROTO_xxx) + mib[3] = 0; // address family + mib[4] = NET_RT_IFLIST2; // operation + mib[5] = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return; + if ((buf = malloc(len)) == NULL) { + printf("malloc failed\n"); + exit(1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + if (buf) + free(buf); + return; + } + + if (!pfunc) { + if (lflag) { + printf("%-10.10s %-5.5s %-39.39s %-39.39s %8.8s %5.5s", + "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs"); + } else { + printf("%-10.10s %-5.5s %-13.13s %-15.15s %8.8s %5.5s", + "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs"); + } + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Itcpkts", "Ipvpkts"); + if (bflag) { + printf(" %10.10s","Ibytes"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Itcbytes", "Ipvbytes"); + } + printf(" %8.8s %5.5s", "Opkts", "Oerrs"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Otcpkts", "Opvpkts"); + if (bflag) { + printf(" %10.10s","Obytes"); + if (prioflag >= 0) + printf(" %8.8s %8.8s", "Otcbytes", "Opvbytes"); + } + printf(" %5s", "Coll"); + if (tflag) + printf(" %s", "Time"); + if (dflag) + printf(" %s", "Drop"); + if (Fflag) { + printf(" %8.8s", "Fpkts"); + if (bflag) + printf(" %10.10s", "Fbytes"); + } + putchar('\n'); + } + lim = buf + len; + for (next = buf; next < lim; ) { + char *cp; + int n, m; + struct ifmibdata_supplemental ifmsupp; + u_int64_t ift_itcp = 0; /* input tc packets */ + u_int64_t ift_itcb = 0; /* input tc bytes */ + u_int64_t ift_otcp = 0; /* output tc packets */ + u_int64_t ift_otcb = 0; /* output tc bytes */ + u_int64_t ift_ipvp = 0; /* input priv tc packets */ + u_int64_t ift_ipvb = 0; /* input priv tc bytes */ + u_int64_t ift_opvp = 0; /* output priv tc packets */ + u_int64_t ift_opvb = 0; /* output priv tc bytes */ + + bzero(&ifmsupp, sizeof(struct ifmibdata_supplemental)); + + network_layer = 0; + link_layer = 0; + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + + if (ifm->ifm_type == RTM_IFINFO2) { + struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; + struct sockaddr_dl *sdl = + (struct sockaddr_dl *)(if2m + 1); + int mibname[6]; + size_t miblen = sizeof(struct ifmibdata_supplemental); + + if (interface != 0 && if2m->ifm_index != ifindex) + continue; + + /* The interface name is not a zero-ended string */ + memcpy(name, sdl->sdl_data, MIN(sizeof(name) - 1, sdl->sdl_nlen)); + name[MIN(sizeof(name) - 1, sdl->sdl_nlen)] = 0; + + if (pfunc) { + (*pfunc)(name); + continue; + } + + cp = index(name, '\0'); + if ((if2m->ifm_flags & IFF_UP) == 0) + *cp++ = '*'; + *cp = '\0'; + + /* + * Get the interface stats. These may get + * overriden below on a per-interface basis. + */ + opackets = if2m->ifm_data.ifi_opackets; + ipackets = if2m->ifm_data.ifi_ipackets; + obytes = if2m->ifm_data.ifi_obytes; + ibytes = if2m->ifm_data.ifi_ibytes; + oerrors =if2m->ifm_data.ifi_oerrors; + ierrors = if2m->ifm_data.ifi_ierrors; + collisions = if2m->ifm_data.ifi_collisions; + timer = if2m->ifm_timer; + drops = if2m->ifm_snd_drops; + mtu = if2m->ifm_data.ifi_mtu; + + /* Common OID prefix */ + mibname[0] = CTL_NET; + mibname[1] = PF_LINK; + mibname[2] = NETLINK_GENERIC; + mibname[3] = IFMIB_IFDATA; + mibname[4] = if2m->ifm_index; + mibname[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(mibname, 6, &ifmsupp, &miblen, NULL, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + fpackets = ifmsupp.ifmd_data_extended.ifi_fpackets; + fbytes = ifmsupp.ifmd_data_extended.ifi_fbytes; + + if (prioflag >= 0) { + switch (prioflag) { + case SO_TC_BE: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ibepackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ibebytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_obepackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ibkpackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ibkbytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_obkpackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ivipackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ivibytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_ovipackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + ift_itcp = ifmsupp.ifmd_traffic_class.ifi_ivopackets; + ift_itcb = ifmsupp.ifmd_traffic_class.ifi_ivobytes; + ift_otcp = ifmsupp.ifmd_traffic_class.ifi_ovopackets; + ift_otcb = ifmsupp.ifmd_traffic_class.ifi_ovobytes; + break; + default: + ift_itcp = 0; + ift_itcb = 0; + ift_otcp = 0; + ift_otcb = 0; + ift_ipvp = 0; + ift_ipvb = 0; + ift_opvp = 0; + ift_opvb = 0; + break; + } + ift_ipvp = ifmsupp.ifmd_traffic_class.ifi_ipvpackets; + ift_ipvb = ifmsupp.ifmd_traffic_class.ifi_ipvbytes; + ift_opvp = ifmsupp.ifmd_traffic_class.ifi_opvpackets; + ift_opvb = ifmsupp.ifmd_traffic_class.ifi_opvbytes; + } + + get_rti_info(if2m->ifm_addrs, + (struct sockaddr*)(if2m + 1), rti_info); + sa = rti_info[RTAX_IFP]; + } else if (ifm->ifm_type == RTM_NEWADDR) { + struct ifa_msghdr *ifam = (struct ifa_msghdr *)ifm; + + if (interface != 0 && ifam->ifam_index != ifindex) + continue; + get_rti_info(ifam->ifam_addrs, + (struct sockaddr*)(ifam + 1), rti_info); + sa = rti_info[RTAX_IFA]; + } else { + continue; + } + if (lflag) { + printf("%-10.10s %-5u ", name, mtu); + } else { + printf("%-5.5s %-5u ", name, mtu); + } + + if (sa == 0) { + printf(lflag ? "%-39.39s " : "%-13.13s ", "none"); + printf(lflag ? "%-39.39s " : "%-15.15s ", "none"); + } else { + switch (sa->sa_family) { + case AF_UNSPEC: + printf(lflag ? "%-39.39s " : "%-13.13s ", "none"); + printf(lflag ? "%-39.39s " : "%-15.15s ", "none"); + break; + + case AF_INET: { + struct sockaddr_in *sin = + (struct sockaddr_in *)sa; + struct sockaddr_in mask; + + mask.sin_addr.s_addr = 0; + memcpy(&mask, rti_info[RTAX_NETMASK], + ((struct sockaddr_in *) + rti_info[RTAX_NETMASK])->sin_len); + + printf(lflag ? "%-39.39s " : "%-13.13s ", + netname(sin->sin_addr.s_addr & + mask.sin_addr.s_addr, + ntohl(mask.sin_addr.s_addr))); + + printf(lflag ? "%-39.39s " : "%-15.15s ", + routename(sin->sin_addr.s_addr)); + + network_layer = 1; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)sa; + struct sockaddr *mask = + (struct sockaddr *)rti_info[RTAX_NETMASK]; + + printf(lflag ? "%-39.39s " : "%-11.11s ", netname6(sin6, mask)); + printf(lflag ? "%-39.39s " : "%-17.17s ", (char *)inet_ntop(AF_INET6, + &sin6->sin6_addr, ntop_buf, + sizeof(ntop_buf))); + + network_layer = 1; + break; + } +#endif /*INET6*/ + case AF_LINK: { + struct sockaddr_dl *sdl = + (struct sockaddr_dl *)sa; + char linknum[10]; + cp = (char *)LLADDR(sdl); + n = sdl->sdl_alen; + snprintf(linknum, sizeof(linknum), + "<Link#%d>", sdl->sdl_index); + m = printf(lflag ? "%-39.39s " : "%-11.11s ", linknum); + goto hexprint; + } + + 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; + hexprint: + while (--n >= 0) + m += printf("%02x%c", *cp++ & 0xff, + n > 0 ? ':' : ' '); + m = (lflag ? 80 : 30) - m; + while (m-- > 0) + putchar(' '); + + link_layer = 1; + break; + } + } + + show_stat("llu", 8, ipackets, link_layer|network_layer); + printf(" "); + show_stat("llu", 5, ierrors, link_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_itcp, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_ipvp, link_layer|network_layer); + printf(" "); + } + if (bflag) { + show_stat("llu", 10, ibytes, link_layer|network_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_itcb, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_ipvb, link_layer|network_layer); + printf(" "); + } + } + show_stat("llu", 8, opackets, link_layer|network_layer); + printf(" "); + show_stat("llu", 5, oerrors, link_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_otcp, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_opvp, link_layer|network_layer); + printf(" "); + } + if (bflag) { + show_stat("llu", 10, obytes, link_layer|network_layer); + printf(" "); + if (prioflag >= 0) { + show_stat("llu", 8, ift_otcb, link_layer|network_layer); + printf(" "); + show_stat("llu", 8, ift_opvb, link_layer|network_layer); + printf(" "); + } + } + show_stat("llu", 5, collisions, link_layer); + if (tflag) { + printf(" "); + show_stat("d", 3, timer, link_layer); + } + if (dflag) { + printf(" "); + show_stat("d", 3, drops, link_layer); + } + if (Fflag) { + printf(" "); + show_stat("llu", 8, fpackets, link_layer|network_layer); + if (bflag) { + printf(" "); + show_stat("llu", 10, fbytes, + link_layer|network_layer); + } + } + putchar('\n'); + + if (aflag) + multipr(sa->sa_family, next, lim); + } + free(buf); +} + +struct iftot { + SLIST_ENTRY(iftot) chain; + char ift_name[16]; /* interface name */ + u_int64_t ift_ip; /* input packets */ + u_int64_t ift_ie; /* input errors */ + u_int64_t ift_op; /* output packets */ + u_int64_t ift_oe; /* output errors */ + u_int64_t ift_co; /* collisions */ + u_int64_t ift_dr; /* drops */ + u_int64_t ift_ib; /* input bytes */ + u_int64_t ift_ob; /* output bytes */ + u_int64_t ift_itcp; /* input tc packets */ + u_int64_t ift_itcb; /* input tc bytes */ + u_int64_t ift_otcp; /* output tc packets */ + u_int64_t ift_otcb; /* output tc bytes */ + u_int64_t ift_ipvp; /* input priv tc packets */ + u_int64_t ift_ipvb; /* input priv tc bytes */ + u_int64_t ift_opvp; /* output priv tc packets */ + u_int64_t ift_opvb; /* output priv tc bytes */ + u_int64_t ift_fp; /* forwarded packets */ + u_int64_t ift_fb; /* forwarded bytes */ +}; + +u_char signalled; /* set if alarm goes off "early" */ + +/* + * 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. + * XXX - should be rewritten to use ifmib(4). + */ +static void +sidewaysintpr() +{ + struct iftot *total, *sum, *interesting; + register int line; + int first; + int name[6]; + size_t len; + unsigned int ifcount, i; + struct ifmibdata *ifmdall = 0; + int interesting_row; + sigset_t sigset, oldsigset; + struct itimerval timer_interval; + + /* Common OID prefix */ + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + + len = sizeof(int); + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + if (sysctl(name, 5, &ifcount, &len, 0, 0) == 1) + err(1, "sysctl IFMIB_IFCOUNT"); + + len = ifcount * sizeof(struct ifmibdata); + ifmdall = malloc(len); + if (ifmdall == 0) + err(1, "malloc failed"); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, ifmdall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA"); + + interesting = NULL; + interesting_row = 0; + for (i = 0; i < ifcount; i++) { + struct ifmibdata *ifmd = ifmdall + i; + + if (interface && strcmp(ifmd->ifmd_name, interface) == 0) { + if ((interesting = calloc(ifcount, + sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + interesting_row = if_nametoindex(interface); + snprintf(interesting->ift_name, 16, "(%s)", + ifmd->ifmd_name);; + } + } + if ((total = calloc(1, sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + + if ((sum = calloc(1, sizeof(struct iftot))) == NULL) + err(1, "malloc failed"); + + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void)signal(SIGALRM, catchalarm); + signalled = NO; + (void)setitimer(ITIMER_REAL, &timer_interval, NULL); + first = 1; +banner: + if (vflag > 0) + printf("%9s", " "); + + if (prioflag >= 0) + printf("%39s %39s %36s", "input", + interesting ? interesting->ift_name : "(Total)", "output"); + else + printf("%17s %14s %16s", "input", + interesting ? interesting->ift_name : "(Total)", "output"); + putchar('\n'); + + if (vflag > 0) + printf("%9s", " "); + + printf("%10s %5s %10s ", "packets", "errs", "bytes"); + if (prioflag >= 0) + printf(" %10s %10s %10s %10s", + "tcpkts", "tcbytes", "pvpkts", "pvbytes"); + printf("%10s %5s %10s %5s", "packets", "errs", "bytes", "colls"); + if (dflag) + printf(" %5.5s", "drops"); + if (prioflag >= 0) + printf(" %10s %10s %10s %10s", + "tcpkts", "tcbytes", "pvpkts", "pvbytes"); + if (Fflag) + printf(" %10s %10s", "fpackets", "fbytes"); + putchar('\n'); + fflush(stdout); + line = 0; +loop: + if (vflag && !first) + print_time(); + + if (interesting != NULL) { + struct ifmibdata ifmd; + struct ifmibdata_supplemental ifmsupp; + + len = sizeof(struct ifmibdata); + name[3] = IFMIB_IFDATA; + name[4] = interesting_row; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, &ifmd, &len, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_GENERAL %d", interesting_row); + + len = sizeof(struct ifmibdata_supplemental); + name[3] = IFMIB_IFDATA; + name[4] = interesting_row; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, &ifmsupp, &len, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL %d", + interesting_row); + + if (!first) { + printf("%10llu %5llu %10llu ", + ifmd.ifmd_data.ifi_ipackets - interesting->ift_ip, + ifmd.ifmd_data.ifi_ierrors - interesting->ift_ie, + ifmd.ifmd_data.ifi_ibytes - interesting->ift_ib); + switch (prioflag) { + case SO_TC_BE: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ibepackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ibebytes - + interesting->ift_itcb); + break; + case SO_TC_BK: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ibkpackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ibkbytes - + interesting->ift_itcb); + break; + case SO_TC_VI: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ivipackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ivibytes - + interesting->ift_itcb); + break; + case SO_TC_VO: + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ivopackets - + interesting->ift_itcp, + ifmsupp.ifmd_traffic_class.ifi_ivobytes - + interesting->ift_itcb); + break; + default: + break; + } + if (prioflag >= 0) { + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_ipvpackets - + interesting->ift_ipvp, + ifmsupp.ifmd_traffic_class.ifi_ipvbytes - + interesting->ift_ipvb); + } + printf("%10llu %5llu %10llu %5llu", + ifmd.ifmd_data.ifi_opackets - interesting->ift_op, + ifmd.ifmd_data.ifi_oerrors - interesting->ift_oe, + ifmd.ifmd_data.ifi_obytes - interesting->ift_ob, + ifmd.ifmd_data.ifi_collisions - interesting->ift_co); + if (dflag) + printf(" %5llu", + ifmd.ifmd_snd_drops - interesting->ift_dr); + switch (prioflag) { + case SO_TC_BE: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_obepackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_obebytes - + interesting->ift_otcb); + break; + case SO_TC_BK: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_obkpackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_obkbytes - + interesting->ift_otcb); + break; + case SO_TC_VI: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_ovipackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_ovibytes - + interesting->ift_otcb); + break; + case SO_TC_VO: + printf(" %10llu %10llu", + ifmsupp.ifmd_traffic_class.ifi_ovopackets - + interesting->ift_otcp, + ifmsupp.ifmd_traffic_class.ifi_ovobytes - + interesting->ift_otcb); + break; + default: + break; + } + if (prioflag >= 0) { + printf("%10llu %10llu ", + ifmsupp.ifmd_traffic_class.ifi_opvpackets - + interesting->ift_opvp, + ifmsupp.ifmd_traffic_class.ifi_opvbytes - + interesting->ift_opvb); + } + if (Fflag) { + printf("%10llu %10llu", + ifmsupp.ifmd_data_extended.ifi_fpackets - + interesting->ift_fp, + ifmsupp.ifmd_data_extended.ifi_fbytes - + interesting->ift_fb); + } + } + interesting->ift_ip = ifmd.ifmd_data.ifi_ipackets; + interesting->ift_ie = ifmd.ifmd_data.ifi_ierrors; + interesting->ift_ib = ifmd.ifmd_data.ifi_ibytes; + interesting->ift_op = ifmd.ifmd_data.ifi_opackets; + interesting->ift_oe = ifmd.ifmd_data.ifi_oerrors; + interesting->ift_ob = ifmd.ifmd_data.ifi_obytes; + interesting->ift_co = ifmd.ifmd_data.ifi_collisions; + interesting->ift_dr = ifmd.ifmd_snd_drops; + + /* private counters */ + switch (prioflag) { + case SO_TC_BE: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ibepackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ibebytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_obepackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ibkpackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ibkbytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_obkpackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ivipackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ivibytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_ovipackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + interesting->ift_itcp = + ifmsupp.ifmd_traffic_class.ifi_ivopackets; + interesting->ift_itcb = + ifmsupp.ifmd_traffic_class.ifi_ivobytes; + interesting->ift_otcp = + ifmsupp.ifmd_traffic_class.ifi_ovopackets; + interesting->ift_otcb = + ifmsupp.ifmd_traffic_class.ifi_ovobytes; + break; + default: + break; + } + if (prioflag >= 0) { + interesting->ift_ipvp = + ifmsupp.ifmd_traffic_class.ifi_ipvpackets; + interesting->ift_ipvb = + ifmsupp.ifmd_traffic_class.ifi_ipvbytes; + interesting->ift_opvp = + ifmsupp.ifmd_traffic_class.ifi_opvpackets; + interesting->ift_opvb = + ifmsupp.ifmd_traffic_class.ifi_opvbytes; + } + interesting->ift_fp = ifmsupp.ifmd_data_extended.ifi_fpackets; + interesting->ift_fb = ifmsupp.ifmd_data_extended.ifi_fbytes; + } else { + unsigned int latest_ifcount; + struct ifmibdata_supplemental *ifmsuppall = NULL; + + len = sizeof(int); + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + if (sysctl(name, 5, &latest_ifcount, &len, 0, 0) == 1) + err(1, "sysctl IFMIB_IFCOUNT"); + if (latest_ifcount > ifcount) { + ifcount = latest_ifcount; + len = ifcount * sizeof(struct ifmibdata); + free(ifmdall); + ifmdall = malloc(len); + if (ifmdall == 0) + err(1, "malloc ifmdall failed"); + } else if (latest_ifcount > ifcount) { + ifcount = latest_ifcount; + len = ifcount * sizeof(struct ifmibdata); + } + len = ifcount * sizeof(struct ifmibdata); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_GENERAL; + if (sysctl(name, 6, ifmdall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA"); + + len = ifcount * sizeof(struct ifmibdata_supplemental); + ifmsuppall = malloc(len); + if (ifmsuppall == NULL) + err(1, "malloc ifmsuppall failed"); + name[3] = IFMIB_IFALLDATA; + name[4] = 0; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, ifmsuppall, &len, (void *)0, 0) == -1) + err(1, "sysctl IFMIB_IFALLDATA SUPPLEMENTAL"); + + sum->ift_ip = 0; + sum->ift_ie = 0; + sum->ift_ib = 0; + sum->ift_op = 0; + sum->ift_oe = 0; + sum->ift_ob = 0; + sum->ift_co = 0; + sum->ift_dr = 0; + sum->ift_itcp = 0; + sum->ift_itcb = 0; + sum->ift_otcp = 0; + sum->ift_otcb = 0; + sum->ift_ipvp = 0; + sum->ift_ipvb = 0; + sum->ift_opvp = 0; + sum->ift_opvb = 0; + sum->ift_fp = 0; + sum->ift_fb = 0; + for (i = 0; i < ifcount; i++) { + struct ifmibdata *ifmd = ifmdall + i; + struct ifmibdata_supplemental *ifmsupp = ifmsuppall + i; + + sum->ift_ip += ifmd->ifmd_data.ifi_ipackets; + sum->ift_ie += ifmd->ifmd_data.ifi_ierrors; + sum->ift_ib += ifmd->ifmd_data.ifi_ibytes; + sum->ift_op += ifmd->ifmd_data.ifi_opackets; + sum->ift_oe += ifmd->ifmd_data.ifi_oerrors; + sum->ift_ob += ifmd->ifmd_data.ifi_obytes; + sum->ift_co += ifmd->ifmd_data.ifi_collisions; + sum->ift_dr += ifmd->ifmd_snd_drops; + /* private counters */ + if (prioflag >= 0) { + switch (prioflag) { + case SO_TC_BE: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ibepackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ibebytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_obepackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_obebytes; + break; + case SO_TC_BK: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ibkpackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ibkbytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_obkpackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_obkbytes; + break; + case SO_TC_VI: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ivipackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ivibytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_ovipackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_ovibytes; + break; + case SO_TC_VO: + sum->ift_itcp += ifmsupp->ifmd_traffic_class.ifi_ivopackets; + sum->ift_itcb += ifmsupp->ifmd_traffic_class.ifi_ivobytes; + sum->ift_otcp += ifmsupp->ifmd_traffic_class.ifi_ovopackets; + sum->ift_otcb += ifmsupp->ifmd_traffic_class.ifi_ovobytes; + break; + default: + break; + } + sum->ift_ipvp += ifmsupp->ifmd_traffic_class.ifi_ipvpackets; + sum->ift_ipvb += ifmsupp->ifmd_traffic_class.ifi_ipvbytes; + sum->ift_opvp += ifmsupp->ifmd_traffic_class.ifi_opvpackets; + sum->ift_opvb += ifmsupp->ifmd_traffic_class.ifi_opvbytes; + } + sum->ift_fp += ifmsupp->ifmd_data_extended.ifi_fpackets; + sum->ift_fb += ifmsupp->ifmd_data_extended.ifi_fbytes; + } + if (!first) { + printf("%10llu %5llu %10llu ", + sum->ift_ip - total->ift_ip, + sum->ift_ie - total->ift_ie, + sum->ift_ib - total->ift_ib); + if (prioflag >= 0) + printf(" %10llu %10llu %10llu %10llu", + sum->ift_itcp - total->ift_itcp, + sum->ift_itcb - total->ift_itcb, + sum->ift_ipvp - total->ift_ipvp, + sum->ift_ipvb - total->ift_ipvb); + printf("%10llu %5llu %10llu %5llu", + sum->ift_op - total->ift_op, + sum->ift_oe - total->ift_oe, + sum->ift_ob - total->ift_ob, + sum->ift_co - total->ift_co); + if (dflag) + printf(" %5llu", sum->ift_dr - total->ift_dr); + if (prioflag >= 0) + printf(" %10llu %10llu %10llu %10llu", + sum->ift_otcp - total->ift_otcp, + sum->ift_otcb - total->ift_otcb, + sum->ift_opvp - total->ift_opvp, + sum->ift_opvb - total->ift_opvb); + if (Fflag) + printf(" %10llu %10llu", + sum->ift_fp - total->ift_fp, + sum->ift_fb - total->ift_fb); + } + *total = *sum; + + free(ifmsuppall); + } + if (!first) + putchar('\n'); + fflush(stdout); + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void)sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void)sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + line++; + first = 0; + if (line == 21) + goto banner; + else + goto loop; + /*NOTREACHED*/ +} + +void +intervalpr(void (*pr)(uint32_t, char *, int), uint32_t off, char *name , int af) +{ + struct itimerval timer_interval; + sigset_t sigset, oldsigset; + + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + + for (;;) { + pr(off, name, af); + + fflush(stdout); + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + signalled = NO; + } +} + +/* + * 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 = YES; +} + +static char * +sec2str(total) + time_t total; +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + + return(result); +} + +void +intpr_ri(void (*pfunc)(char *)) +{ + int mib[6]; + char *buf = NULL, *lim, *next; + size_t len; + unsigned int ifindex = 0; + struct if_msghdr2 *if2m; + + if (interface != 0) { + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + printf("interface name is not valid: %s\n", interface); + exit(1); + } + } + + mib[0] = CTL_NET; /* networking subsystem */ + mib[1] = PF_ROUTE; /* type of information */ + mib[2] = 0; /* protocol (IPPROTO_xxx) */ + mib[3] = 0; /* address family */ + mib[4] = NET_RT_IFLIST2; /* operation */ + mib[5] = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return; + if ((buf = malloc(len)) == NULL) { + printf("malloc failed\n"); + exit(1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return; + } + + printf("%-6s %-17s %8.8s %-9.9s %4s %4s", + "Proto", "Linklayer Address", "Netif", "Expire", "Refs", + "Prbs"); + if (xflag) + printf(" %7s %7s %7s", "RSSI", "LQM", "NPM"); + printf("\n"); + + lim = buf + len; + if2m = (struct if_msghdr2 *)buf; + + for (next = buf; next < lim; ) { + if2m = (struct if_msghdr2 *)next; + next += if2m->ifm_msglen; + + if (if2m->ifm_type != RTM_IFINFO2) + continue; + else if (interface != 0 && if2m->ifm_index != ifindex) + continue; + + llreach_sysctl(if2m->ifm_index); + } + free(buf); +} + +static void +llreach_sysctl(uint32_t ifindex) +{ +#define MAX_SYSCTL_TRY 5 + int mib[6], i, ntry = 0; + size_t mibsize, len, needed, cnt; + struct if_llreach_info *lri; + struct timeval time; + char *buf; + char ifname[IF_NAMESIZE]; + + bzero(&mib, sizeof (mib)); + mibsize = sizeof (mib) / sizeof (mib[0]); + if (sysctlnametomib("net.link.generic.system.llreach_info", mib, + &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + + mibsize = sizeof (mib) / sizeof (mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.link.generic.system.llreach_info"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + cnt = len / sizeof (*lri); + lri = (struct if_llreach_info *)buf; + + gettimeofday(&time, 0); + if (if_indextoname(ifindex, ifname) == NULL) + snprintf(ifname, sizeof (ifname), "%s", "?"); + + for (i = 0; i < cnt; i++, lri++) { + printf("0x%-4x %-17s %8.8s ", lri->lri_proto, + ether_ntoa((struct ether_addr *)lri->lri_addr), ifname); + + if (lri->lri_expire > time.tv_sec) + printf("%-9.9s", sec2str(lri->lri_expire - time.tv_sec)); + else if (lri->lri_expire == 0) + printf("%-9.9s", "permanent"); + else + printf("%-9.9s", "expired"); + + printf(" %4d", lri->lri_refcnt); + if (lri->lri_probes) + printf(" %4d", lri->lri_probes); + + if (xflag) { + if (!lri->lri_probes) + printf(" %-4.4s", "none"); + + if (lri->lri_rssi != IFNET_RSSI_UNKNOWN) + printf(" %7d", lri->lri_rssi); + else + printf(" %-7.7s", "unknown"); + + switch (lri->lri_lqm) + { + case IFNET_LQM_THRESH_OFF: + printf(" %-7.7s", "off"); + break; + case IFNET_LQM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_LQM_THRESH_POOR: + printf(" %-7.7s", "poor"); + break; + case IFNET_LQM_THRESH_GOOD: + printf(" %-7.7s", "good"); + break; + default: + printf(" %7d", lri->lri_lqm); + break; + } + + switch (lri->lri_npm) + { + case IFNET_NPM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_NPM_THRESH_NEAR: + printf(" %-7.7s", "near"); + break; + case IFNET_NPM_THRESH_GENERAL: + printf(" %-7.7s", "general"); + break; + case IFNET_NPM_THRESH_FAR: + printf(" %-7.7s", "far"); + break; + default: + printf(" %7d", lri->lri_npm); + break; + } + } + + printf("\n"); + len -= sizeof (*lri); + } + + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.link.generic.system.llreach_info"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +void +aqstatpr(void) +{ + unsigned int ifindex; + struct itimerval timer_interval; + struct if_qstatsreq ifqr; + struct if_ifclassq_stats *ifcqs; + sigset_t sigset, oldsigset; + u_int32_t scheduler; + int s, n; + + if (cq < -1 || cq >= IFCQ_SC_MAX) { + fprintf(stderr, "Invalid classq index (range is 0-%d)\n", + IFCQ_SC_MAX-1); + return; + } + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + ifcqs = malloc(sizeof (*ifcqs)); + if (ifcqs == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + return; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("Warning: socket(AF_INET)"); + free(ifcqs); + return; + } + + bzero(&ifqr, sizeof (ifqr)); + strlcpy(ifqr.ifqr_name, interface, sizeof (ifqr.ifqr_name)); + ifqr.ifqr_buf = ifcqs; + ifqr.ifqr_len = sizeof (*ifcqs); + +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + ifqr.ifqr_slot = 0; + if (ioctl(s, SIOCGIFQUEUESTATS, (char *)&ifqr) < 0) { + if (errno == ENXIO) { + printf("Queue statistics are not available on %s\n", + interface); + } else { + perror("Warning: ioctl(SIOCGIFQUEUESTATS)"); + } + goto done; + } + scheduler = ifcqs->ifqs_scheduler; + + printf("%s:\n" + " [ sched: %9s qlength: %3d/%3d ]\n", + interface, sched2str(ifcqs->ifqs_scheduler), + ifcqs->ifqs_len, ifcqs->ifqs_maxlen); + printf(" [ pkts: %10llu bytes: %10llu " + " dropped pkts: %6llu bytes: %6llu ]\n", + ifcqs->ifqs_xmitcnt.packets, ifcqs->ifqs_xmitcnt.bytes, + ifcqs->ifqs_dropcnt.packets, ifcqs->ifqs_dropcnt.bytes); + + for (n = 0; n < IFCQ_SC_MAX && scheduler != PKTSCHEDT_NONE; n++) { + if (cq >= 0 && cq != n) + continue; + + ifqr.ifqr_slot = n; + if (ioctl(s, SIOCGIFQUEUESTATS, (char *)&ifqr) < 0) { + perror("Warning: ioctl(SIOCGIFQUEUESTATS)"); + goto done; + } + + update_avg(ifcqs, &qstats[n]); + + switch (scheduler) { + case PKTSCHEDT_FQ_CODEL: + print_fq_codel_stats(n, + &ifcqs->ifqs_fq_codel_stats, + &qstats[n]); + break; + case PKTSCHEDT_NONE: + default: + break; + } + } + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } + +done: + free(ifcqs); + close(s); +} + +static void +print_fq_codel_stats(int pri, struct fq_codel_classstats *fqst, + struct queue_stats *qs) +{ + int i = 0; + + if (fqst->fcls_service_class == 0 && fqst->fcls_pri == 0) + return; + printf("=====================================================\n"); + printf(" [ pri: %s (%d)\tsrv_cl: 0x%x\tquantum: %d\tdrr_max: %d ]\n", + pri2str(fqst->fcls_pri), fqst->fcls_pri, + fqst->fcls_service_class, fqst->fcls_quantum, + fqst->fcls_drr_max); + printf(" [ queued pkts: %llu\tbytes: %llu ]\n", + fqst->fcls_pkt_cnt, fqst->fcls_byte_cnt); + printf(" [ dequeued pkts: %llu\tbytes: %llu ]\n", + fqst->fcls_dequeue, fqst->fcls_dequeue_bytes); + printf(" [ budget: %lld\ttarget qdelay: %10s\t", + fqst->fcls_budget, nsec_to_str(fqst->fcls_target_qdelay)); + printf("update interval:%10s ]\n", + nsec_to_str(fqst->fcls_update_interval)); + printf(" [ flow control: %u\tfeedback: %u\tstalls: %u\tfailed: %u ]\n", + fqst->fcls_flow_control, fqst->fcls_flow_feedback, + fqst->fcls_dequeue_stall, fqst->fcls_flow_control_fail); + printf(" [ drop overflow: %llu\tearly: %llu\tmemfail: %u\tduprexmt:%u ]\n", + fqst->fcls_drop_overflow, fqst->fcls_drop_early, + fqst->fcls_drop_memfailure, fqst->fcls_dup_rexmts); + printf(" [ flows total: %u\tnew: %u\told: %u ]\n", + fqst->fcls_flows_cnt, + fqst->fcls_newflows_cnt, fqst->fcls_oldflows_cnt); + printf(" [ throttle on: %u\toff: %u\tdrop: %u ]\n", + fqst->fcls_throttle_on, fqst->fcls_throttle_off, + fqst->fcls_throttle_drops); + printf(" [ compressible pkts: %u compressed pkts: %u]\n", + fqst->fcls_pkts_compressible, fqst->fcls_pkts_compressed); + + if (qflag < 2) + return; + + if (fqst->fcls_flowstats_cnt > 0) { + printf("Flowhash\tBytes\tMin qdelay\tFlags\t\n"); + for (i = 0; i < fqst->fcls_flowstats_cnt; i++) { + printf("%u\t%u\t%14s\t", + fqst->fcls_flowstats[i].fqst_flowhash, + fqst->fcls_flowstats[i].fqst_bytes, + nsec_to_str(fqst->fcls_flowstats[i].fqst_min_qdelay)); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_OLD_FLOW) + printf("O"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_NEW_FLOW) + printf("N"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_LARGE_FLOW) + printf("L"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_DELAY_HIGH) + printf("D"); + if (fqst->fcls_flowstats[i].fqst_flags & + FQ_FLOWSTATS_FLOWCTL_ON) + printf("F"); + printf("\n"); + } + } +} + +static void +update_avg(struct if_ifclassq_stats *ifcqs, struct queue_stats *qs) +{ + u_int64_t b, p; + int n; + + n = qs->avgn; + + switch (ifcqs->ifqs_scheduler) { + case PKTSCHEDT_FQ_CODEL: + b = ifcqs->ifqs_fq_codel_stats.fcls_dequeue_bytes; + p = ifcqs->ifqs_fq_codel_stats.fcls_dequeue; + break; + default: + b = 0; + p = 0; + break; + } + + if (n == 0) { + qs->prev_bytes = b; + qs->prev_packets = p; + qs->avgn++; + return; + } + + if (b >= qs->prev_bytes) + qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + + (b - qs->prev_bytes)) / n; + + if (p >= qs->prev_packets) + qs->avg_packets = ((qs->avg_packets * (n - 1)) + + (p - qs->prev_packets)) / n; + + qs->prev_bytes = b; + qs->prev_packets = p; + if (n < AVGN_MAX) + qs->avgn++; +} + +#define NSEC_PER_SEC 1000000000 /* nanoseconds per second */ +#define USEC_PER_SEC 1000000 /* microseconds per second */ +#define MSEC_PER_SEC 1000 /* milliseconds per second */ + +static char * +nsec_to_str(unsigned long long nsec) +{ + static char buf[32]; + const char *u; + long double n = nsec, t; + + if (nsec >= NSEC_PER_SEC) { + t = n / NSEC_PER_SEC; + u = "sec "; + } else if (n >= USEC_PER_SEC) { + t = n / USEC_PER_SEC; + u = "msec"; + } else if (n >= MSEC_PER_SEC) { + t = n / MSEC_PER_SEC; + u = "usec"; + } else { + t = n; + u = "nsec"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +static char * +sched2str(unsigned int s) +{ + char *c; + + switch (s) { + case PKTSCHEDT_NONE: + c = "NONE"; + break; + case PKTSCHEDT_FQ_CODEL: + c = "FQ_CODEL"; + break; + default: + c = "UNKNOWN"; + break; + } + + return (c); +} + +static char * +pri2str(unsigned int i) +{ + char *c; + switch (i) { + case 9: + c = "BK_SYS"; + break; + case 8: + c = "BK"; + break; + case 7: + c = "BE"; + break; + case 6: + c = "RD"; + break; + case 5: + c = "OAM"; + break; + case 4: + c = "AV"; + break; + case 3: + c = "RV"; + break; + case 2: + c = "VI"; + break; + case 1: + c = "VO"; + break; + case 0: + c = "CTL"; + break; + default: + c = "?"; + break; + } + return (c); +} + +void +rxpollstatpr(void) +{ + struct ifmibdata_supplemental ifmsupp; + size_t miblen = sizeof (ifmsupp); + struct itimerval timer_interval; + struct if_rxpoll_stats *sp; + struct if_netif_stats *np; + sigset_t sigset, oldsigset; + unsigned int ifindex; + int name[6]; + + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + bzero(&ifmsupp, sizeof (struct ifmibdata_supplemental)); + +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + /* Common OID prefix */ + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = ifindex; + name[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(name, 6, &ifmsupp, &miblen, NULL, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + sp = &ifmsupp.ifmd_rxpoll_stats; + + printf("%-4s [ poll on requests: %15u errors: %27u ]\n", + interface, sp->ifi_poll_on_req, sp->ifi_poll_on_err); + printf(" [ poll off requests: %15u errors: %27u ]\n", + sp->ifi_poll_off_req, sp->ifi_poll_off_err); + printf(" [ polled packets: %18llu per poll limit: %19u ]\n", + sp->ifi_poll_packets, sp->ifi_poll_packets_limit); + printf(" [ polled bytes: %20llu ]\n", sp->ifi_poll_bytes); + printf(" [ poll interval: %14llu nsec ]\n", + sp->ifi_poll_interval_time); + printf(" [ sampled packets avg/min/max: %12u / %12u / %12u ]\n", + sp->ifi_poll_packets_avg, sp->ifi_poll_packets_min, + sp->ifi_poll_packets_max); + printf(" [ sampled bytes avg/min/max: %12u / %12u / %12u ]\n", + sp->ifi_poll_bytes_avg, sp->ifi_poll_bytes_min, + sp->ifi_poll_bytes_max); + printf(" [ sampled wakeups avg: %12u ]\n", + sp->ifi_poll_wakeups_avg); + printf(" [ packets lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_packets_lowat, sp->ifi_poll_packets_hiwat); + printf(" [ bytes lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_bytes_lowat, sp->ifi_poll_bytes_hiwat); + printf(" [ wakeups lowat/hiwat threshold: %10u / %10u ]\n", + sp->ifi_poll_wakeups_lowat, sp->ifi_poll_wakeups_hiwat); + + np = &ifmsupp.ifmd_netif_stats; + printf(" [ mit mode: %24U cfg idx: %26u ]\n", + np->ifn_rx_mit_mode, np->ifn_rx_mit_cfg_idx); + printf(" [ cfg packets lo/hi threshold: %12u / %12u ]\n", + np->ifn_rx_mit_cfg_packets_lowat, np->ifn_rx_mit_cfg_packets_hiwat); + printf(" [ cfg bytes lo/hi threshold: %12u / %12u ]\n", + np->ifn_rx_mit_cfg_bytes_lowat, np->ifn_rx_mit_cfg_bytes_hiwat); + printf(" [ cfg interval: %15u nsec ]\n", + np->ifn_rx_mit_cfg_interval); + printf(" [ mit interval: %15llu nsec ]\n", + np->ifn_rx_mit_interval); + printf(" [ mit packets avg/min/max: %12u / %12u / %12u ]\n", + np->ifn_rx_mit_packets_avg, np->ifn_rx_mit_packets_min, + np->ifn_rx_mit_packets_max); + printf(" [ mit bytes avg/min/max: %12u / %12u / %12u ]\n", + np->ifn_rx_mit_bytes_avg, np->ifn_rx_mit_bytes_min, + np->ifn_rx_mit_bytes_max); + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } +} + +static int +create_control_socket(const char *control_name) +{ + struct sockaddr_ctl sc; + struct ctl_info ctl; + int fd; + + fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (fd == -1) { + perror("socket(PF_SYSTEM)"); + return fd; + } + + /* Get the control ID for statistics */ + bzero(&ctl, sizeof(ctl)); + strlcpy(ctl.ctl_name, control_name, sizeof(ctl.ctl_name)); + if (ioctl(fd, CTLIOCGINFO, &ctl) == -1) + { + perror("ioctl(CTLIOCGINFO)"); + close(fd); + return -1; + } + + /* Connect to the statistics control */ + bzero(&sc, sizeof(sc)); + sc.sc_len = sizeof(sc); + sc.sc_family = AF_SYSTEM; + sc.ss_sysaddr = SYSPROTO_CONTROL; + sc.sc_id = ctl.ctl_id; + sc.sc_unit = 0; + if (connect(fd, (struct sockaddr*)&sc, sc.sc_len) != 0) + { + perror("connect(SYSPROTO_CONTROL)"); + close(fd); + return -1; + } + + /* Set socket to non-blocking operation */ + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) { + perror("fcnt(F_SETFL,O_NONBLOCK)"); + close(fd); + return -1; + } + return fd; +} + +static int +add_nstat_src(int fd, const nstat_ifnet_add_param *ifparam, + nstat_src_ref_t *outsrc) +{ + nstat_msg_add_src_req *addreq; + nstat_msg_src_added *addedmsg; + nstat_ifnet_add_param *param; + char buffer[sizeof(*addreq) + sizeof(*param)]; + ssize_t result; + const u_int32_t addreqsize = + offsetof(struct nstat_msg_add_src, param) + sizeof(*param); + + /* Setup the add source request */ + addreq = (nstat_msg_add_src_req *)buffer; + param = (nstat_ifnet_add_param*)addreq->param; + bzero(addreq, addreqsize); + addreq->hdr.context = (uintptr_t)&buffer; + addreq->hdr.type = NSTAT_MSG_TYPE_ADD_SRC; + addreq->provider = NSTAT_PROVIDER_IFNET; + bzero(param, sizeof(*param)); + param->ifindex = ifparam->ifindex; + param->threshold = ifparam->threshold; + + /* Send the add source request */ + result = send(fd, addreq, addreqsize, 0); + if (result != addreqsize) + { + if (result == -1) + perror("send(NSTAT_ADD_SRC_REQ)"); + else + fprintf(stderr, "%s: could only sent %ld out of %d\n", + __func__, result, addreqsize); + return -1; + } + + /* Receive the response */ + addedmsg = (nstat_msg_src_added *)buffer; + result = recv(fd, addedmsg, sizeof(buffer), 0); + if (result < sizeof(*addedmsg)) + { + if (result == -1) + perror("recv(NSTAT_ADD_SRC_RSP)"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %lu\n", __func__, result, + sizeof(*addedmsg)); + return -1; + } + + if (addedmsg->hdr.type != NSTAT_MSG_TYPE_SRC_ADDED) + { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, addedmsg->hdr.type, + NSTAT_MSG_TYPE_SRC_ADDED); + return -1; + } + + if (addedmsg->hdr.context != (uintptr_t)&buffer) + { + fprintf(stderr, "%s: received wrong context, received %llu " + "expected %lu\n", __func__, addedmsg->hdr.context, + (uintptr_t)&buffer); + return -1; + } + *outsrc = addedmsg->srcref; + return 0; +} + +static int +rem_nstat_src(int fd, nstat_src_ref_t sref) +{ + nstat_msg_rem_src_req *remreq; + nstat_msg_src_removed *remrsp; + char buffer[sizeof(*remreq)]; + ssize_t result; + + /* Setup the add source request */ + remreq = (nstat_msg_rem_src_req *)buffer; + bzero(remreq, sizeof(*remreq)); + remreq->hdr.type = NSTAT_MSG_TYPE_REM_SRC; + remreq->srcref = sref; + + /* Send the remove source request */ + result = send(fd, remreq, sizeof(*remreq), 0); + if (result != sizeof(*remreq)) { + if (result == -1) + perror("send(NSTAT_REM_SRC_REQ)"); + else + fprintf(stderr, "%s: could only sent %ld out of %lu\n", + __func__, result, sizeof(*remreq)); + return -1; + } + + /* Receive the response */ + remrsp = (nstat_msg_src_removed *)buffer; + result = recv(fd, remrsp, sizeof(buffer), 0); + if (result < sizeof(*remrsp)) { + if (result == -1) + perror("recv(NSTAT_REM_SRC_RSP)"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %lu\n", __func__, result, + sizeof(*remrsp)); + return -1; + } + + if (remrsp->hdr.type != NSTAT_MSG_TYPE_SRC_REMOVED) { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, remrsp->hdr.type, + NSTAT_MSG_TYPE_SRC_REMOVED); + return -1; + } + + if (remrsp->srcref != sref) { + fprintf(stderr, "%s: received invalid srcref, received %llu " + "expected %llu\n", __func__, remrsp->srcref, sref); + } + return 0; +} + +static int +get_src_decsription(int fd, nstat_src_ref_t srcref, + struct nstat_ifnet_descriptor *ifdesc) +{ + nstat_msg_get_src_description *dreq; + nstat_msg_src_description *drsp; + char buffer[sizeof(*drsp) + sizeof(*ifdesc)]; + ssize_t result; + const u_int32_t descsize = + offsetof(struct nstat_msg_src_description, data) + + sizeof(nstat_ifnet_descriptor); + + dreq = (nstat_msg_get_src_description *)buffer; + bzero(dreq, sizeof(*dreq)); + dreq->hdr.type = NSTAT_MSG_TYPE_GET_SRC_DESC; + dreq->srcref = srcref; + result = send(fd, dreq, sizeof(*dreq), 0); + if (result != sizeof(*dreq)) + { + if (result == -1) + perror("send(NSTAT_GET_SRC_DESC_REQ)"); + else + fprintf(stderr, "%s: sent %ld out of %lu\n", + __func__, result, sizeof(*dreq)); + return -1; + } + + /* Receive the source description response */ + drsp = (nstat_msg_src_description *)buffer; + result = recv(fd, drsp, sizeof(buffer), 0); + if (result < descsize) + { + if (result == -1) + perror("recv(NSTAT_GET_SRC_DESC_RSP"); + else + fprintf(stderr, "%s: recv too small, received %ld, " + "expected %u\n", __func__, result, descsize); + return -1; + } + + if (drsp->hdr.type != NSTAT_MSG_TYPE_SRC_DESC) + { + fprintf(stderr, "%s: received wrong message type, received %u " + "expected %u\n", __func__, drsp->hdr.type, + NSTAT_MSG_TYPE_SRC_DESC); + return -1; + } + + if (drsp->srcref != srcref) + { + fprintf(stderr, "%s: received message for wrong source, " + "received 0x%llx expected 0x%llx\n", + __func__, drsp->srcref, srcref); + return -1; + } + + bcopy(drsp->data, ifdesc, sizeof(*ifdesc)); + return 0; +} + +static void +print_wifi_status(nstat_ifnet_desc_wifi_status *status) +{ + int tmp; +#define val(x, f) \ + ((status->valid_bitmask & NSTAT_IFNET_DESC_WIFI_ ## f ## _VALID) ?\ + status->x : -1) +#define parg(n, un) #n, val(n, un) +#define pretxtl(n, un) \ + (((tmp = val(n, un)) == -1) ? "(not valid)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE) ? "(none)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW) ? "(low)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM) ? "(medium)" : \ + ((tmp == NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH) ? "(high)" : \ + "(?)"))))) + + printf("\nwifi status:\n"); + printf( + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d%s\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d\n", + parg(link_quality_metric, LINK_QUALITY_METRIC), + parg(ul_effective_bandwidth, UL_EFFECTIVE_BANDWIDTH), + parg(ul_max_bandwidth, UL_MAX_BANDWIDTH), + parg(ul_min_latency, UL_MIN_LATENCY), + parg(ul_effective_latency, UL_EFFECTIVE_LATENCY), + parg(ul_max_latency, UL_MAX_LATENCY), + parg(ul_retxt_level, UL_RETXT_LEVEL), + pretxtl(ul_retxt_level, UL_RETXT_LEVEL), + parg(ul_bytes_lost, UL_BYTES_LOST), + parg(ul_error_rate, UL_ERROR_RATE), + parg(dl_effective_bandwidth, DL_EFFECTIVE_BANDWIDTH), + parg(dl_max_bandwidth, DL_MAX_BANDWIDTH), + parg(dl_min_latency, DL_MIN_LATENCY), + parg(dl_effective_latency, DL_EFFECTIVE_LATENCY), + parg(dl_max_latency, DL_MAX_LATENCY), + parg(dl_error_rate, DL_ERROR_RATE), + parg(config_frequency, CONFIG_FREQUENCY), + parg(config_multicast_rate, CONFIG_MULTICAST_RATE), + parg(scan_count, CONFIG_SCAN_COUNT), + parg(scan_duration, CONFIG_SCAN_DURATION) + ); +#undef pretxtl +#undef parg +#undef val +} + +static void +print_cellular_status(nstat_ifnet_desc_cellular_status *status) +{ + int tmp, tmp_mss; +#define val(x, f) \ + ((status->valid_bitmask & NSTAT_IFNET_DESC_CELL_ ## f ## _VALID) ?\ + status->x : -1) +#define parg(n, un) #n, val(n, un) +#define pretxtl(n, un) \ + (((tmp = val(n, un)) == -1) ? "(not valid)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE) ? "(none)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW) ? "(low)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM) ? "(medium)" : \ + ((tmp == NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH) ? "(high)" : \ + "(?)"))))) +#define pretxtm(n, un) \ + (((tmp_mss = val(n,un)) == -1) ? "(not valid)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_NONE) ? "(none)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_MEDIUM) ? "(medium)" : \ + ((tmp_mss == NSTAT_IFNET_DESC_MSS_RECOMMENDED_LOW) ? "(low)" : \ + "(?)")))) + + printf("\ncellular status:\n"); + printf( + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t\t%d\n" + "\t%s:\t\t%d%s\n" + "\t%s:\t\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d\n" + "\t%s:\t%d %s\n", + parg(link_quality_metric, LINK_QUALITY_METRIC), + parg(ul_effective_bandwidth, UL_EFFECTIVE_BANDWIDTH), + parg(ul_max_bandwidth, UL_MAX_BANDWIDTH), + parg(ul_min_latency, UL_MIN_LATENCY), + parg(ul_effective_latency, UL_EFFECTIVE_LATENCY), + parg(ul_max_latency, UL_MAX_LATENCY), + parg(ul_retxt_level, UL_RETXT_LEVEL), + pretxtl(ul_retxt_level, UL_RETXT_LEVEL), + parg(ul_bytes_lost, UL_BYTES_LOST), + parg(ul_min_queue_size, UL_MIN_QUEUE_SIZE), + parg(ul_avg_queue_size, UL_AVG_QUEUE_SIZE), + parg(ul_max_queue_size, UL_MAX_QUEUE_SIZE), + parg(dl_effective_bandwidth, DL_EFFECTIVE_BANDWIDTH), + parg(dl_max_bandwidth, DL_MAX_BANDWIDTH), + parg(config_inactivity_time, CONFIG_INACTIVITY_TIME), + parg(config_backoff_time, CONFIG_BACKOFF_TIME), + parg(mss_recommended, MSS_RECOMMENDED), + pretxtm(mss_recommended, MSS_RECOMMENDED) + ); +#undef pretxtl +#undef parg +#undef val +} + +static int +get_interface_state(int fd, const char *ifname, struct ifreq *ifr) +{ + bzero(ifr, sizeof(*ifr)); + snprintf(ifr->ifr_name, sizeof(ifr->ifr_name), "%s", ifname); + + if (ioctl(fd, SIOCGIFINTERFACESTATE, ifr) == -1) { + perror("ioctl(CTLIOCGINFO)"); + return -1; + } + return 0; +} + +static void +print_interface_state(struct ifreq *ifr) +{ + int lqm, rrc, avail; + + printf("\ninterface state:\n"); + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_LQM_STATE_VALID) { + printf("\tlqm: "); + lqm = ifr->ifr_interface_state.lqm_state; + if (lqm == IFNET_LQM_THRESH_GOOD) + printf("\"good\""); + else if (lqm == IFNET_LQM_THRESH_POOR) + printf("\"poor\""); + else if (lqm == IFNET_LQM_THRESH_BAD) + printf("\"bad\""); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("\"unknown\""); + else if (lqm == IFNET_LQM_THRESH_OFF) + printf("\"off\""); + else + printf("invalid(%d)", lqm); + } + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_RRC_STATE_VALID) { + printf("\trrc: "); + rrc = ifr->ifr_interface_state.rrc_state; + if (rrc == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) + printf("\"connected\""); + else if (rrc == IF_INTERFACE_STATE_RRC_STATE_IDLE) + printf("\"idle\""); + else + printf("\"invalid(%d)\"", rrc); + } + + if (ifr->ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { + printf("\tavailability: "); + avail = ifr->ifr_interface_state.interface_availability; + if (avail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) + printf("\"true\""); + else if (rrc == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) + printf("\"false\""); + else + printf("\"invalid(%d)\"", avail); + } +} + +void +print_link_status(const char *ifname) +{ + unsigned int ifindex; + struct itimerval timer_interval; + sigset_t sigset, oldsigset; + struct nstat_ifnet_descriptor ifdesc; + nstat_ifnet_add_param ifparam; + nstat_src_ref_t sref = 0; + struct ifreq ifr; + int ctl_fd; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + fprintf(stderr, "Invalid interface name\n"); + return; + } + + if ((ctl_fd = create_control_socket(NET_STAT_CONTROL_NAME)) < 0) + return; + + ifparam.ifindex = ifindex; + ifparam.threshold = UINT64_MAX; + if (add_nstat_src(ctl_fd, &ifparam, &sref)) + goto done; +loop: + if (interval > 0) { + /* create a timer that fires repeatedly every interval + * seconds */ + timer_interval.it_value.tv_sec = interval; + timer_interval.it_value.tv_usec = 0; + timer_interval.it_interval.tv_sec = interval; + timer_interval.it_interval.tv_usec = 0; + (void) signal(SIGALRM, catchalarm); + signalled = NO; + (void) setitimer(ITIMER_REAL, &timer_interval, NULL); + } + + /* get interface state */ + if (get_interface_state(ctl_fd, ifname, &ifr)) + goto done; + + /* get ntstat interface description */ + if (get_src_decsription(ctl_fd, sref, &ifdesc)) + goto done; + + /* print time */ + printf("\n%s: ", ifname); + print_time(); + + /* print interface state */ + print_interface_state(&ifr); + + /* print ntsat interface link status */ + if (ifdesc.link_status.link_status_type == + NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR) + print_cellular_status(&ifdesc.link_status.u.cellular); + else if (ifdesc.link_status.link_status_type == + NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI) + print_wifi_status(&ifdesc.link_status.u.wifi); + + fflush(stdout); + + if (interval > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &sigset, &oldsigset); + if (!signalled) { + sigemptyset(&sigset); + sigsuspend(&sigset); + } + (void) sigprocmask(SIG_SETMASK, &oldsigset, NULL); + + signalled = NO; + goto loop; + } +done: + if (sref) + rem_nstat_src(ctl_fd, sref); + close(ctl_fd); +} diff --git a/network_cmds/netstat.tproj/inet.c b/network_cmds/netstat.tproj/inet.c new file mode 100644 index 0000000..68e4ddb --- /dev/null +++ b/network_cmds/netstat.tproj/inet.c @@ -0,0 +1,1361 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + ** @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1983, 1988, 1993, 1995 + * 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. 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. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/if_arp.h> +#include <net/net_perf.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#ifdef INET6 +#include <netinet/ip6.h> +#endif /* INET6 */ +#include <netinet/in_pcb.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +#include <netinet/igmp_var.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <netinet/tcp_seq.h> +#define TCPSTATES +#include <netinet/tcp_fsm.h> +#include <netinet/tcp_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> + +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#define ROUNDUP64(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint64_t) - 1))) : sizeof(uint64_t)) +#define ADVANCE64(x, n) (((char *)x) += ROUNDUP64(n)) + +char *inetname (struct in_addr *); +void inetprint (struct in_addr *, int, char *, int); +#ifdef INET6 +extern void inet6print (struct in6_addr *, int, char *, int); +static int udp_done, tcp_done; +extern int mptcp_done; +#endif /* INET6 */ + +#ifdef SRVCACHE +typedef struct __table_private table_t; + +extern table_t *_nc_table_new(uint32_t n); +extern void _nc_table_free(table_t *tin); + +extern void _nc_table_insert(table_t *t, const char *key, void *datum); +extern void *_nc_table_find(table_t *t, const char *key); +extern void _nc_table_delete(table_t *t, const char *key); + +static table_t *_serv_cache = NULL; + +/* + * Read and cache all known services + */ +static void +_serv_cache_open() +{ + struct servent *s; + char *key, *name, *test; + + if (_serv_cache != NULL) return; + + _serv_cache = _nc_table_new(8192); + setservent(0); + + while (NULL != (s = getservent())) + { + if (s->s_name == NULL) continue; + key = NULL; + asprintf(&key, "%hu/%s", (unsigned short)ntohs(s->s_port), s->s_proto); + name = strdup(s->s_name); + test = _nc_table_find(_serv_cache, key); + if (test == NULL) _nc_table_insert(_serv_cache, key, name); + free(key); + } + + endservent(); +} + +void +_serv_cache_close() +{ + _nc_table_free(_serv_cache); + _serv_cache = NULL; +} + +struct servent * +_serv_cache_getservbyport(int port, char *proto) +{ + static struct servent s; + char *key; + unsigned short p; + + _serv_cache_open(); + + memset(&s, 0, sizeof(struct servent)); + asprintf(&key, "%u/%s", port, (proto == NULL) ? "udp" : proto); + + s.s_name = _nc_table_find(_serv_cache, key); + free(key); + if (s.s_name == NULL) return NULL; + + p = port; + s.s_port = htons(p); + s.s_proto = proto; + return &s; +} + +#endif /* SRVCACHE */ + +/* + * 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. + */ + +struct xgen_n { + u_int32_t xgn_len; /* length of this structure */ + u_int32_t xgn_kind; /* number of PCBs at this time */ +}; + +#define ALL_XGN_KIND_INP (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_INPCB) +#define ALL_XGN_KIND_TCP (ALL_XGN_KIND_INP | XSO_TCPCB) + +void +protopr(uint32_t proto, /* for sysctl version we pass proto # */ + char *name, int af) +{ + int istcp; + static int first = 1; + char *buf, *next; + const char *mibvar; + struct xinpgen *xig, *oxig; + struct xgen_n *xgn; + size_t len; + struct xtcpcb_n *tp = NULL; + struct xinpcb_n *inp = NULL; + struct xsocket_n *so = NULL; + struct xsockbuf_n *so_rcv = NULL; + struct xsockbuf_n *so_snd = NULL; + struct xsockstat_n *so_stat = NULL; + int which = 0; + + istcp = 0; + switch (proto) { + case IPPROTO_TCP: +#ifdef INET6 + if (tcp_done != 0) + return; + else + tcp_done = 1; +#endif + istcp = 1; + mibvar = "net.inet.tcp.pcblist_n"; + break; + case IPPROTO_UDP: +#ifdef INET6 + if (udp_done != 0) + return; + else + udp_done = 1; +#endif + mibvar = "net.inet.udp.pcblist_n"; + break; + case IPPROTO_DIVERT: + mibvar = "net.inet.divert.pcblist_n"; + break; + default: + mibvar = "net.inet.raw.pcblist_n"; + break; + } + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= sizeof(struct xinpgen)) { + free(buf); + return; + } + + oxig = xig = (struct xinpgen *)buf; + for (next = buf + ROUNDUP64(xig->xig_len); next < buf + len; next += ROUNDUP64(xgn->xgn_len)) { + + xgn = (struct xgen_n*)next; + if (xgn->xgn_len <= sizeof(struct xinpgen)) + break; + + if ((which & xgn->xgn_kind) == 0) { + which |= xgn->xgn_kind; + switch (xgn->xgn_kind) { + case XSO_SOCKET: + so = (struct xsocket_n *)xgn; + break; + case XSO_RCVBUF: + so_rcv = (struct xsockbuf_n *)xgn; + break; + case XSO_SNDBUF: + so_snd = (struct xsockbuf_n *)xgn; + break; + case XSO_STATS: + so_stat = (struct xsockstat_n *)xgn; + break; + case XSO_INPCB: + inp = (struct xinpcb_n *)xgn; + break; + case XSO_TCPCB: + tp = (struct xtcpcb_n *)xgn; + break; + default: + printf("unexpected kind %d\n", xgn->xgn_kind); + break; + } + } else { + if (vflag) + printf("got %d twice\n", xgn->xgn_kind); + } + + if ((istcp && which != ALL_XGN_KIND_TCP) || (!istcp && which != ALL_XGN_KIND_INP)) + continue; + which = 0; + + /* Ignore sockets for protocols other than the desired one. */ + if (so->xso_protocol != (int)proto) + continue; + + /* Ignore PCBs which were freed during copyout. */ + if (inp->inp_gencnt > oxig->xig_gen) + continue; + + if ((af == AF_INET && (inp->inp_vflag & INP_IPV4) == 0) +#ifdef INET6 + || (af == AF_INET6 && (inp->inp_vflag & INP_IPV6) == 0) +#endif /* INET6 */ + || (af == AF_UNSPEC && ((inp->inp_vflag & INP_IPV4) == 0 +#ifdef INET6 + && (inp->inp_vflag & + INP_IPV6) == 0 +#endif /* INET6 */ + )) + ) + continue; + + /* + * Local address is not an indication of listening socket or + * server sockey but just rather the socket has been bound. + * That why many UDP sockets were not displayed in the original code. + */ + if (!aflag && istcp && tp->t_state <= TCPS_LISTEN) + continue; + + if (Lflag && !so->so_qlimit) + continue; + + if (first) { + if (!Lflag) { + printf("Active Internet connections"); + if (aflag) + printf(" (including servers)"); + } else + printf( + "Current listen queue sizes (qlen/incqlen/maxqlen)"); + putchar('\n'); + if (Aflag) { + printf("%-16.16s ", "Socket"); + printf("%-9.9s", "Flowhash"); + } + if (Lflag) + printf("%-14.14s %-22.22s\n", + "Listen", "Local Address"); + else { + printf((Aflag && !Wflag) ? + "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %-11.11s" : + "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %-11.11s", + "Proto", "Recv-Q", "Send-Q", + "Local Address", "Foreign Address", + "(state)"); + if (bflag > 0) + printf(" %10.10s %10.10s", "rxbytes", "txbytes"); + if (prioflag >= 0) + printf(" %7.7s[%1d] %7.7s[%1d]", "rxbytes", prioflag, "txbytes", prioflag); + if (vflag > 0) + printf(" %6.6s %6.6s %6.6s %6.6s %6s %10s", + "rhiwat", "shiwat", "pid", "epid", "state", "options"); + printf("\n"); + } + first = 0; + } + if (Aflag) { + if (istcp) + printf("%16lx ", (u_long)inp->inp_ppcb); + else + printf("%16lx ", (u_long)so->so_pcb); + printf("%8x ", inp->inp_flowhash); + } + if (Lflag) { + char buf[15]; + + snprintf(buf, 15, "%d/%d/%d", so->so_qlen, + so->so_incqlen, so->so_qlimit); + printf("%-14.14s ", buf); + } + else { + const char *vchar; + +#ifdef INET6 + if ((inp->inp_vflag & INP_IPV6) != 0) + vchar = ((inp->inp_vflag & INP_IPV4) != 0) + ? "46" : "6 "; + else +#endif + vchar = ((inp->inp_vflag & INP_IPV4) != 0) + ? "4 " : " "; + + printf("%-3.3s%-2.2s %6u %6u ", name, vchar, + so_rcv->sb_cc, + so_snd->sb_cc); + } + if (nflag) { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 1); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, 1); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 1); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, 1); + } /* else nothing printed now */ +#endif /* INET6 */ + } else if (inp->inp_flags & INP_ANONPORT) { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 1); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, 0); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 1); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, 0); + } /* else nothing printed now */ +#endif /* INET6 */ + } else { + if (inp->inp_vflag & INP_IPV4) { + inetprint(&inp->inp_laddr, (int)inp->inp_lport, + name, 0); + if (!Lflag) + inetprint(&inp->inp_faddr, + (int)inp->inp_fport, name, + inp->inp_lport != + inp->inp_fport); + } +#ifdef INET6 + else if (inp->inp_vflag & INP_IPV6) { + inet6print(&inp->in6p_laddr, + (int)inp->inp_lport, name, 0); + if (!Lflag) + inet6print(&inp->in6p_faddr, + (int)inp->inp_fport, name, + inp->inp_lport != + inp->inp_fport); + } /* else nothing printed now */ +#endif /* INET6 */ + } + if (istcp && !Lflag) { + if (tp->t_state < 0 || tp->t_state >= TCP_NSTATES) + printf("%-11d", tp->t_state); + else { + printf("%-11s", tcpstates[tp->t_state]); + } + } + if (!istcp) + printf("%-11s", " "); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + + printf(" %10llu %10llu", rxbytes, txbytes); + } + if (prioflag >= 0) { + printf(" %10llu %10llu", + prioflag < SO_TC_STATS_MAX ? so_stat->xst_tc_stats[prioflag].rxbytes : 0, + prioflag < SO_TC_STATS_MAX ? so_stat->xst_tc_stats[prioflag].txbytes : 0); + } + if (vflag > 0) { + printf(" %6u %6u %6u %6u 0x%04x 0x%08x", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid, + so->so_state, + so->so_options); + } + putchar('\n'); + } + if (xig != oxig && xig->xig_gen != oxig->xig_gen) { + if (oxig->xig_count > xig->xig_count) { + printf("Some %s sockets may have been deleted.\n", + name); + } else if (oxig->xig_count < xig->xig_count) { + printf("Some %s sockets may have been created.\n", + name); + } else { + printf("Some %s sockets may have been created or deleted", + name); + } + } + free(buf); +} + +/* + * Dump TCP statistics structure. + */ +void +tcp_stats(uint32_t off , char *name, int af) +{ + static struct tcpstat ptcpstat; + struct tcpstat tcpstat; + size_t len = sizeof tcpstat; + static uint32_t r_swcsum, pr_swcsum; + static uint32_t t_swcsum, pt_swcsum; + + if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.tcp.stats"); + return; + } + +#ifdef INET6 + if (tcp_done != 0 && interval == 0) + return; + else + tcp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define TCPDIFF(f) (tcpstat.f - ptcpstat.f) +#define p(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f), plural(TCPDIFF(f))) +#define p1a(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f)) +#define p2(f1, f2, m) if (TCPDIFF(f1) || TCPDIFF(f2) || sflag <= 1) \ + printf(m, TCPDIFF(f1), plural(TCPDIFF(f1)), TCPDIFF(f2), plural(TCPDIFF(f2))) +#define p2a(f1, f2, m) if (TCPDIFF(f1) || TCPDIFF(f2) || sflag <= 1) \ + printf(m, TCPDIFF(f1), plural(TCPDIFF(f1)), TCPDIFF(f2)) +#define p3(f, m) if (TCPDIFF(f) || sflag <= 1) \ + printf(m, TCPDIFF(f), plurales(TCPDIFF(f))) + + p(tcps_sndtotal, "\t%u packet%s sent\n"); + p2(tcps_sndpack,tcps_sndbyte, + "\t\t%u data packet%s (%u byte%s)\n"); + p2(tcps_sndrexmitpack, tcps_sndrexmitbyte, + "\t\t%u data packet%s (%u byte%s) retransmitted\n"); + p(tcps_mturesent, "\t\t%u resend%s initiated by MTU discovery\n"); + p2a(tcps_sndacks, tcps_delack, + "\t\t%u ack-only packet%s (%u delayed)\n"); + p(tcps_sndurg, "\t\t%u URG only packet%s\n"); + p(tcps_sndprobe, "\t\t%u window probe packet%s\n"); + p(tcps_sndwinup, "\t\t%u window update packet%s\n"); + p(tcps_sndctrl, "\t\t%u control packet%s\n"); + p(tcps_fcholdpacket, "\t\t%u data packet%s sent after flow control\n"); + p(tcps_synchallenge, "\t\t%u challenge ACK%s sent due to unexpected SYN\n"); + p(tcps_rstchallenge, "\t\t%u challenge ACK%s sent due to unexpected RST\n"); + t_swcsum = tcpstat.tcps_snd_swcsum + tcpstat.tcps_snd6_swcsum; + if ((t_swcsum - pt_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (t_swcsum - pt_swcsum)); + p2(tcps_snd_swcsum, tcps_snd_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(tcps_snd6_swcsum, tcps_snd6_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p(tcps_rcvtotal, "\t%u packet%s received\n"); + p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%u ack%s (for %u byte%s)\n"); + p(tcps_rcvdupack, "\t\t%u duplicate ack%s\n"); + p(tcps_rcvacktoomuch, "\t\t%u ack%s for unsent data\n"); + p2(tcps_rcvpack, tcps_rcvbyte, + "\t\t%u packet%s (%u byte%s) received in-sequence\n"); + p2(tcps_rcvduppack, tcps_rcvdupbyte, + "\t\t%u completely duplicate packet%s (%u byte%s)\n"); + p(tcps_pawsdrop, "\t\t%u old duplicate packet%s\n"); + p(tcps_rcvmemdrop, "\t\t%u received packet%s dropped due to low memory\n"); + p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte, + "\t\t%u packet%s with some dup. data (%u byte%s duped)\n"); + p2(tcps_rcvoopack, tcps_rcvoobyte, + "\t\t%u out-of-order packet%s (%u byte%s)\n"); + p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin, + "\t\t%u packet%s (%u byte%s) of data after window\n"); + p(tcps_rcvwinprobe, "\t\t%u window probe%s\n"); + p(tcps_rcvwinupd, "\t\t%u window update packet%s\n"); + p(tcps_recovered_pkts, "\t\t%u packet%s recovered after loss\n"); + p(tcps_rcvafterclose, "\t\t%u packet%s received after close\n"); + p(tcps_badrst, "\t\t%u bad reset%s\n"); + p(tcps_rcvbadsum, "\t\t%u discarded for bad checksum%s\n"); + r_swcsum = tcpstat.tcps_rcv_swcsum + tcpstat.tcps_rcv6_swcsum; + if ((r_swcsum - pr_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", + (r_swcsum - pr_swcsum)); + p2(tcps_rcv_swcsum, tcps_rcv_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(tcps_rcv6_swcsum, tcps_rcv6_swcsum_bytes, + "\t\t\t%u segment%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p(tcps_rcvbadoff, "\t\t%u discarded for bad header offset field%s\n"); + p1a(tcps_rcvshort, "\t\t%u discarded because packet too short\n"); + p(tcps_connattempt, "\t%u connection request%s\n"); + p(tcps_accepts, "\t%u connection accept%s\n"); + p(tcps_badsyn, "\t%u bad connection attempt%s\n"); + p(tcps_listendrop, "\t%u listen queue overflow%s\n"); + p(tcps_connects, "\t%u connection%s established (including accepts)\n"); + p2(tcps_closed, tcps_drops, + "\t%u connection%s closed (including %u drop%s)\n"); + p(tcps_cachedrtt, "\t\t%u connection%s updated cached RTT on close\n"); + p(tcps_cachedrttvar, + "\t\t%u connection%s updated cached RTT variance on close\n"); + p(tcps_cachedssthresh, + "\t\t%u connection%s updated cached ssthresh on close\n"); + p(tcps_usedrtt, "\t\t%u connection%s initialized RTT from route cache\n"); + p(tcps_usedrttvar, + "\t\t%u connection%s initialized RTT variance from route cache\n"); + p(tcps_usedssthresh, + "\t\t%u connection%s initialized ssthresh from route cache\n"); + p(tcps_conndrops, "\t%u embryonic connection%s dropped\n"); + p2(tcps_rttupdated, tcps_segstimed, + "\t%u segment%s updated rtt (of %u attempt%s)\n"); + p(tcps_rexmttimeo, "\t%u retransmit timeout%s\n"); + p(tcps_timeoutdrop, "\t\t%u connection%s dropped by rexmit timeout\n"); + p(tcps_rxtfindrop, "\t\t%u connection%s dropped after retransmitting FIN\n"); + p(tcps_sndrexmitbad, "\t\t%u unnecessary packet retransmissions%s\n"); + p(tcps_persisttimeo, "\t%u persist timeout%s\n"); + p(tcps_persistdrop, "\t\t%u connection%s dropped by persist timeout\n"); + p(tcps_keeptimeo, "\t%u keepalive timeout%s\n"); + p(tcps_keepprobe, "\t\t%u keepalive probe%s sent\n"); + p(tcps_keepdrops, "\t\t%u connection%s dropped by keepalive\n"); + p(tcps_ka_offload_drops, "\t\t%u connection%s dropped by keepalive offload\n"); + p(tcps_predack, "\t%u correct ACK header prediction%s\n"); + p(tcps_preddat, "\t%u correct data packet header prediction%s\n"); +#ifdef TCP_MAX_SACK + /* TCP_MAX_SACK indicates the header has the SACK structures */ + p(tcps_sack_recovery_episode, "\t%u SACK recovery episode%s\n"); + p(tcps_sack_rexmits, + "\t%u segment rexmit%s in SACK recovery episodes\n"); + p(tcps_sack_rexmit_bytes, + "\t%u byte rexmit%s in SACK recovery episodes\n"); + p(tcps_sack_rcv_blocks, + "\t%u SACK option%s (SACK blocks) received\n"); + p(tcps_sack_send_blocks, "\t%u SACK option%s (SACK blocks) sent\n"); + p1a(tcps_sack_sboverflow, "\t%u SACK scoreboard overflow\n"); +#endif /* TCP_MAX_SACK */ + p(tcps_limited_txt, "\t%u limited transmit%s done\n"); + p(tcps_early_rexmt, "\t%u early retransmit%s done\n"); + p(tcps_sack_ackadv, "\t%u time%s cumulative ack advanced along with SACK\n"); + p(tcps_pto, "\t%u probe timeout%s\n"); + p(tcps_rto_after_pto, "\t\t%u time%s retransmit timeout triggered after probe\n"); + p(tcps_probe_if, "\t\t%u time%s probe packets were sent for an interface\n"); + p(tcps_probe_if_conflict, "\t\t%u time%s couldn't send probe packets for an interface\n"); + p(tcps_tlp_recovery, "\t\t%u time%s fast recovery after tail loss\n"); + p(tcps_tlp_recoverlastpkt, "\t\t%u time%s recovered last packet \n"); + p(tcps_pto_in_recovery, "\t\t%u SACK based rescue retransmit%s\n"); + p(tcps_ecn_client_setup, "\t%u client connection%s attempted to negotiate ECN\n"); + p(tcps_ecn_client_success, "\t\t%u client connection%s successfully negotiated ECN\n"); + p(tcps_ecn_not_supported, "\t\t%u time%s graceful fallback to Non-ECN connection\n"); + p(tcps_ecn_lost_syn, "\t\t%u time%s lost ECN negotiating SYN, followed by retransmission\n"); + p(tcps_ecn_server_setup, "\t\t%u server connection%s attempted to negotiate ECN\n"); + p(tcps_ecn_server_success, "\t\t%u server connection%s successfully negotiated ECN\n"); + p(tcps_ecn_lost_synack, "\t\t%u time%s lost ECN negotiating SYN-ACK, followed by retransmission\n"); + p(tcps_ecn_recv_ce, "\t\t%u time%s received congestion experienced (CE) notification\n"); + p(tcps_ecn_recv_ece, "\t\t%u time%s CWR was sent in response to ECE\n"); + p(tcps_ecn_sent_ece, "\t\t%u time%s sent ECE notification\n"); + p(tcps_ecn_conn_recv_ce, "\t\t%u connection%s received CE atleast once\n"); + p(tcps_ecn_conn_recv_ece, "\t\t%u connection%s received ECE atleast once\n"); + p(tcps_ecn_conn_plnoce, "\t\t%u connection%s using ECN have seen packet loss but no CE\n"); + p(tcps_ecn_conn_pl_ce, "\t\t%u connection%s using ECN have seen packet loss and CE\n"); + p(tcps_ecn_conn_nopl_ce, "\t\t%u connection%s using ECN received CE but no packet loss\n"); + p(tcps_ecn_fallback_synloss, "\t\t%u connection%s fell back to non-ECN due to SYN-loss\n"); + p(tcps_ecn_fallback_reorder, "\t\t%u connection%s fell back to non-ECN due to reordering\n"); + p(tcps_ecn_fallback_ce, "\t\t%u connection%s fell back to non-ECN due to excessive CE-markings\n"); + p(tcps_ecn_fallback_droprst, "\t\t%u connection%s fell back caused by connection drop due to RST\n"); + p(tcps_ecn_fallback_droprxmt, "\t\t%u connection%s fell back due to drop after multiple retransmits \n"); + p(tcps_ecn_fallback_synrst, "\t\t%u connection%s fell back due to RST after SYN\n"); + + p(tcps_detect_reordering, "\t%u time%s packet reordering was detected on a connection\n"); + p(tcps_reordered_pkts, "\t\t%u time%s transmitted packets were reordered\n"); + p(tcps_delay_recovery, "\t\t%u time%s fast recovery was delayed to handle reordering\n"); + p(tcps_avoid_rxmt, "\t\t%u time%s retransmission was avoided by delaying recovery\n"); + p(tcps_unnecessary_rxmt, "\t\t%u retransmission%s not needed \n"); + p(tcps_tailloss_rto, "\t%u retransmission%s due to tail loss\n"); + p(tcps_dsack_sent, "\t%u time%s DSACK option was sent\n"); + p(tcps_dsack_recvd, "\t\t%u time%s DSACK option was received\n"); + p(tcps_dsack_disable, "\t\t%u time%s DSACK was disabled on a connection\n"); + p(tcps_dsack_badrexmt, "\t\t%u time%s recovered from bad retransmission using DSACK\n"); + p(tcps_dsack_ackloss,"\t\t%u time%s ignored DSACK due to ack loss\n"); + p(tcps_dsack_recvd_old,"\t\t%u time%s ignored old DSACK options\n"); + p(tcps_pmtudbh_reverted, "\t%u time%s PMTU Blackhole detection, size reverted\n"); + p(tcps_drop_after_sleep, "\t%u connection%s were dropped after long sleep\n"); + p(tcps_nostretchack, "\t%u connection%s had stretch ack algorithm disabled\n"); + + p(tcps_tfo_cookie_sent,"\t%u time%s a TFO-cookie has been announced\n"); + p(tcps_tfo_syn_data_rcv,"\t%u SYN%s with data and a valid TFO-cookie have been received\n"); + p(tcps_tfo_cookie_req_rcv,"\t%u SYN%s with TFO-cookie-request received\n"); + p(tcps_tfo_cookie_invalid,"\t%u time%s an invalid TFO-cookie has been received\n"); + p(tcps_tfo_cookie_req,"\t%u time%s we requested a TFO-cookie\n"); + p(tcps_tfo_cookie_rcv,"\t\t%u time%s the peer announced a TFO-cookie\n"); + p(tcps_tfo_syn_data_sent,"\t%u time%s we combined SYN with data and a TFO-cookie\n"); + p(tcps_tfo_syn_data_acked,"\t\t%u time%s our SYN with data has been acknowledged\n"); + p(tcps_tfo_syn_loss,"\t%u time%s a connection-attempt with TFO fell back to regular TCP\n"); + p(tcps_tfo_blackhole,"\t%u time%s a TFO-connection blackhole'd\n"); + p(tcps_tfo_cookie_wrong,"\t%u time%s a TFO-cookie we sent was wrong\n"); + p(tcps_tfo_no_cookie_rcv,"\t%u time%s did not received a TFO-cookie we asked for\n"); + p(tcps_tfo_heuristics_disable,"\t%u time%s TFO got disabled due to heuristicsn\n"); + p(tcps_tfo_sndblackhole,"\t%u time%s TFO got blackholed in the sending direction\n"); + + p(tcps_mss_to_default,"\t%u time%s maximum segment size was changed to default\n"); + p(tcps_mss_to_medium,"\t%u time%s maximum segment size was changed to medium\n"); + p(tcps_mss_to_low,"\t%u time%s maximum segment size was changed to low\n"); + + p(tcps_timer_drift_le_1_ms,"\t%u timer drift%s less or equal to 1 ms\n"); + p(tcps_timer_drift_le_10_ms,"\t%u timer drift%s less or equal to 10 ms\n"); + p(tcps_timer_drift_le_20_ms,"\t%u timer drift%s less or equal to 20 ms\n"); + p(tcps_timer_drift_le_50_ms,"\t%u timer drift%s less or equal to 50 ms\n"); + p(tcps_timer_drift_le_100_ms,"\t%u timer drift%s less or equal to 100 ms\n"); + p(tcps_timer_drift_le_200_ms,"\t%u timer drift%s less or equal to 200 ms\n"); + p(tcps_timer_drift_le_500_ms,"\t%u timer drift%s less or equal to 500 ms\n"); + p(tcps_timer_drift_le_1000_ms,"\t%u timer drift%s less or equal to 1000 ms\n"); + p(tcps_timer_drift_gt_1000_ms,"\t%u timer drift%s greater than to 1000 ms\n"); + + if (interval > 0) { + bcopy(&tcpstat, &ptcpstat, len); + pr_swcsum = r_swcsum; + pt_swcsum = t_swcsum; + } + +#undef TCPDIFF +#undef p +#undef p1a +#undef p2 +#undef p2a +#undef p3 +} + +/* + * Dump MPTCP statistics + */ +void +mptcp_stats(uint32_t off , char *name, int af) +{ + static struct tcpstat ptcpstat; + struct tcpstat tcpstat; + size_t len = sizeof tcpstat; + + if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.tcp.stats"); + return; + } + +#ifdef INET6 + if (mptcp_done != 0 && interval == 0) + return; + else + mptcp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define MPTCPDIFF(f) (tcpstat.f - ptcpstat.f) +#define p(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f), plural(MPTCPDIFF(f))) +#define p1a(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f)) +#define p2(f1, f2, m) if (MPTCPDIFF(f1) || MPTCPDIFF(f2) || sflag <= 1) \ + printf(m, MPTCPDIFF(f1), plural(MPTCPDIFF(f1)), \ + MPTCPDIFF(f2), plural(MPTCPDIFF(f2))) +#define p2a(f1, f2, m) if (MPTCPDIFF(f1) || MPTCPDIFF(f2) || sflag <= 1) \ + printf(m, MPTCPDIFF(f1), plural(MPTCPDIFF(f1)), MPTCPDIFF(f2)) +#define p3(f, m) if (MPTCPDIFF(f) || sflag <= 1) \ + printf(m, MPTCPDIFF(f), plurales(MPTCPDIFF(f))) + + p(tcps_mp_sndpacks, "\t%u data packet%s sent\n"); + p(tcps_mp_sndbytes, "\t%u data byte%s sent\n"); + p(tcps_mp_rcvtotal, "\t%u data packet%s received\n"); + p(tcps_mp_rcvbytes, "\t%u data byte%s received\n"); + p(tcps_invalid_mpcap, "\t%u packet%s with an invalid MPCAP option\n"); + p(tcps_invalid_joins, "\t%u packet%s with an invalid MPJOIN option\n"); + p(tcps_mpcap_fallback, "\t%u time%s primary subflow fell back to " + "TCP\n"); + p(tcps_join_fallback, "\t%u time%s secondary subflow fell back to " + "TCP\n"); + p(tcps_estab_fallback, "\t%u DSS option drop%s\n"); + p(tcps_invalid_opt, "\t%u other invalid MPTCP option%s\n"); + p(tcps_mp_reducedwin, "\t%u time%s the MPTCP subflow window was reduced\n"); + p(tcps_mp_badcsum, "\t%u bad DSS checksum%s\n"); + p(tcps_mp_oodata, "\t%u time%s received out of order data \n"); + p3(tcps_mp_switches, "\t%u subflow switch%s\n"); + p3(tcps_mp_sel_symtomsd, "\t%u subflow switch%s due to advisory\n"); + p3(tcps_mp_sel_rtt, "\t%u subflow switch%s due to rtt\n"); + p3(tcps_mp_sel_rto, "\t%u subflow switch%s due to rto\n"); + p3(tcps_mp_sel_peer, "\t%u subflow switch%s due to peer\n"); + p3(tcps_mp_num_probes, "\t%u number of subflow probe%s\n"); + + if (interval > 0) { + bcopy(&tcpstat, &ptcpstat, len); + } + +#undef MPTCPDIFF +#undef p +#undef p1a +#undef p2 +#undef p2a +#undef p3 +} + +/* + * Dump UDP statistics structure. + */ +void +udp_stats(uint32_t off , char *name, int af ) +{ + static struct udpstat pudpstat; + struct udpstat udpstat; + size_t len = sizeof udpstat; + uint32_t delivered; + static uint32_t r_swcsum, pr_swcsum; + static uint32_t t_swcsum, pt_swcsum; + + if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.udp.stats"); + return; + } + +#ifdef INET6 + if (udp_done != 0 && interval == 0) + return; + else + udp_done = 1; +#endif + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define UDPDIFF(f) (udpstat.f - pudpstat.f) +#define p(f, m) if (UDPDIFF(f) || sflag <= 1) \ + printf(m, UDPDIFF(f), plural(UDPDIFF(f))) +#define p1a(f, m) if (UDPDIFF(f) || sflag <= 1) \ + printf(m, UDPDIFF(f)) +#define p2(f1, f2, m) if (UDPDIFF(f1) || UDPDIFF(f2) || sflag <= 1) \ + printf(m, UDPDIFF(f1), plural(UDPDIFF(f1)), UDPDIFF(f2), plural(UDPDIFF(f2))) + p(udps_ipackets, "\t%u datagram%s received\n"); + p1a(udps_hdrops, "\t\t%u with incomplete header\n"); + p1a(udps_badlen, "\t\t%u with bad data length field\n"); + p1a(udps_badsum, "\t\t%u with bad checksum\n"); + p1a(udps_nosum, "\t\t%u with no checksum\n"); + r_swcsum = udpstat.udps_rcv_swcsum + udpstat.udps_rcv6_swcsum; + if ((r_swcsum - pr_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (r_swcsum - pr_swcsum)); + p2(udps_rcv_swcsum, udps_rcv_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(udps_rcv6_swcsum, udps_rcv6_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + p1a(udps_noport, "\t\t%u dropped due to no socket\n"); + p(udps_noportbcast, + "\t\t%u broadcast/multicast datagram%s undelivered\n"); + /* the next statistic is cumulative in udps_noportbcast */ + p(udps_filtermcast, + "\t\t%u time%s multicast source filter matched\n"); + p1a(udps_fullsock, "\t\t%u dropped due to full socket buffers\n"); + p1a(udpps_pcbhashmiss, "\t\t%u not for hashed pcb\n"); + delivered = UDPDIFF(udps_ipackets) - + UDPDIFF(udps_hdrops) - + UDPDIFF(udps_badlen) - + UDPDIFF(udps_badsum) - + UDPDIFF(udps_noport) - + UDPDIFF(udps_noportbcast) - + UDPDIFF(udps_fullsock); + if (delivered || sflag <= 1) + printf("\t\t%u delivered\n", delivered); + p(udps_opackets, "\t%u datagram%s output\n"); + t_swcsum = udpstat.udps_snd_swcsum + udpstat.udps_snd6_swcsum; + if ((t_swcsum - pt_swcsum) || sflag <= 1) + printf("\t\t%u checksummed in software\n", (t_swcsum - pt_swcsum)); + p2(udps_snd_swcsum, udps_snd_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv4\n"); +#if INET6 + p2(udps_snd6_swcsum, udps_snd6_swcsum_bytes, + "\t\t\t%u datagram%s (%u byte%s) over IPv6\n"); +#endif /* INET6 */ + + if (interval > 0) { + bcopy(&udpstat, &pudpstat, len); + pr_swcsum = r_swcsum; + pt_swcsum = t_swcsum; + } + +#undef UDPDIFF +#undef p +#undef p1a +#undef p2 +} + +/* + * Dump IP statistics structure. + */ +void +ip_stats(uint32_t off , char *name, int af ) +{ + static struct ipstat pipstat; + struct ipstat ipstat; + size_t ipstat_len = sizeof ipstat; + + static net_perf_t pout_net_perf, pin_net_perf; + net_perf_t out_net_perf, in_net_perf; + size_t out_net_perf_len = sizeof (out_net_perf); + size_t in_net_perf_len = sizeof (in_net_perf); + + if (sysctlbyname("net.inet.ip.stats", &ipstat, &ipstat_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.stats"); + return; + } + + if (sysctlbyname("net.inet.ip.output_perf_data", &out_net_perf, &out_net_perf_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.output_perf_data"); + bzero(&out_net_perf, out_net_perf_len); + } + + if (sysctlbyname("net.inet.ip.input_perf_data", &in_net_perf, &in_net_perf_len, 0, 0) < 0) { + warn("sysctl: net.inet.ip.input_perf_data"); + bzero(&in_net_perf, in_net_perf_len); + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IPDIFF(f) (ipstat.f - pipstat.f) +#define p(f, m) if (IPDIFF(f) || sflag <= 1) \ + printf(m, IPDIFF(f), plural(IPDIFF(f))) +#define p1a(f, m) if (IPDIFF(f) || sflag <= 1) \ + printf(m, IPDIFF(f)) +#define p2(f1, f2, m) if (IPDIFF(f1) || IPDIFF(f2) || sflag <= 1) \ + printf(m, IPDIFF(f1), plural(IPDIFF(f1)), IPDIFF(f2), plural(IPDIFF(f2))) + + p(ips_total, "\t%u total packet%s received\n"); + p(ips_badsum, "\t\t%u bad header checksum%s\n"); + p2(ips_rcv_swcsum, ips_rcv_swcsum_bytes, + "\t\t%u header%s (%u byte%s) checksummed in software\n"); + p1a(ips_toosmall, "\t\t%u with size smaller than minimum\n"); + p1a(ips_tooshort, "\t\t%u with data size < data length\n"); + p1a(ips_adj, "\t\t%u with data size > data length\n"); + p(ips_adj_hwcsum_clr, + "\t\t\t%u packet%s forced to software checksum\n"); + p1a(ips_toolong, "\t\t%u with ip length > max ip packet size\n"); + p1a(ips_badhlen, "\t\t%u with header length < data size\n"); + p1a(ips_badlen, "\t\t%u with data length < header length\n"); + p1a(ips_badoptions, "\t\t%u with bad options\n"); + p1a(ips_badvers, "\t\t%u with incorrect version number\n"); + p(ips_fragments, "\t\t%u fragment%s received\n"); + p1a(ips_fragdropped, "\t\t\t%u dropped (dup or out of space)\n"); + p1a(ips_fragtimeout, "\t\t\t%u dropped after timeout\n"); + p1a(ips_reassembled, "\t\t\t%u reassembled ok\n"); + p(ips_delivered, "\t\t%u packet%s for this host\n"); + p(ips_noproto, "\t\t%u packet%s for unknown/unsupported protocol\n"); + p(ips_forward, "\t\t%u packet%s forwarded"); + p(ips_fastforward, " (%u packet%s fast forwarded)"); + if (IPDIFF(ips_forward) || sflag <= 1) + putchar('\n'); + p(ips_cantforward, "\t\t%u packet%s not forwardable\n"); + p(ips_notmember, + "\t\t%u packet%s received for unknown multicast group\n"); + p(ips_redirectsent, "\t\t%u redirect%s sent\n"); + p(ips_rxc_collisions, "\t\t%u input packet%s not chained due to collision\n"); + p(ips_rxc_chained, "\t\t%u input packet%s processed in a chain\n"); + p(ips_rxc_notchain, "\t\t%u input packet%s unable to chain\n"); + p(ips_rxc_chainsz_gt2, + "\t\t%u input packet chain%s processed with length greater than 2\n"); + p(ips_rxc_chainsz_gt4, + "\t\t%u input packet chain%s processed with length greater than 4\n"); + p(ips_rxc_notlist, + "\t\t%u input packet%s did not go through list processing path\n"); + + p(ips_rcv_if_weak_match, + "\t\t%u input packet%s that passed the weak ES interface address match\n"); + p(ips_rcv_if_no_match, + "\t\t%u input packet%s with no interface address match\n"); + +#define INPERFDIFF(f) (in_net_perf.f - pin_net_perf.f) + if (INPERFDIFF(np_total_pkts) > 0 && in_net_perf.np_total_usecs > 0) { + printf("\tInput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", INPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", INPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)in_net_perf.np_total_usecs/(double)in_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", in_net_perf.np_hist_bars[0], + INPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[0], in_net_perf.np_hist_bars[1], + INPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[1], in_net_perf.np_hist_bars[2], + INPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[2], in_net_perf.np_hist_bars[3], + INPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + in_net_perf.np_hist_bars[3], INPERFDIFF(np_hist5)); + } +#undef INPERFDIFF + + p(ips_localout, "\t%u packet%s sent from this host\n"); + p(ips_rawout, "\t\t%u packet%s sent with fabricated ip header\n"); + p(ips_odropped, + "\t\t%u output packet%s dropped due to no bufs, etc.\n"); + p(ips_noroute, "\t\t%u output packet%s discarded due to no route\n"); + p(ips_fragmented, "\t\t%u output datagram%s fragmented\n"); + p(ips_ofragments, "\t\t%u fragment%s created\n"); + p(ips_cantfrag, "\t\t%u datagram%s that can't be fragmented\n"); + p(ips_nogif, "\t\t%u tunneling packet%s that can't find gif\n"); + p(ips_badaddr, "\t\t%u datagram%s with bad address in header\n"); + p(ips_pktdropcntrl, + "\t\t%u packet%s dropped due to no bufs for control data\n"); + p(ips_necp_policy_drop, "\t\t%u packet%s dropped due to NECP policy\n"); + p2(ips_snd_swcsum, ips_snd_swcsum_bytes, + "\t\t%u header%s (%u byte%s) checksummed in software\n"); + +#define OUTPERFDIFF(f) (out_net_perf.f - pout_net_perf.f) + if (OUTPERFDIFF(np_total_pkts) > 0 && out_net_perf.np_total_usecs > 0) { + printf("\tOutput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", OUTPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", OUTPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)out_net_perf.np_total_usecs/(double)out_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", out_net_perf.np_hist_bars[0], + OUTPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[0], out_net_perf.np_hist_bars[1], + OUTPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[1], out_net_perf.np_hist_bars[2], + OUTPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[2], out_net_perf.np_hist_bars[3], + OUTPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + out_net_perf.np_hist_bars[3], OUTPERFDIFF(np_hist5)); + } +#undef OUTPERFDIFF + + if (interval > 0) { + bcopy(&ipstat, &pipstat, ipstat_len); + bcopy(&in_net_perf, &pin_net_perf, in_net_perf_len); + bcopy(&out_net_perf, &pout_net_perf, out_net_perf_len); + } + +#undef IPDIFF +#undef p +#undef p1a +#undef p2 +} + +/* + * Dump ARP statistics structure. + */ +void +arp_stats(uint32_t off, char *name, int af) +{ + static struct arpstat parpstat; + struct arpstat arpstat; + size_t len = sizeof (arpstat); + + if (sysctlbyname("net.link.ether.inet.stats", &arpstat, + &len, 0, 0) < 0) { + warn("sysctl: net.link.ether.inet.stats"); + return; + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ARPDIFF(f) (arpstat.f - parpstat.f) +#define p(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), plural(ARPDIFF(f))) +#define p2(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), pluralies(ARPDIFF(f))) +#define p3(f, m) if (ARPDIFF(f) || sflag <= 1) \ + printf(m, ARPDIFF(f), plural(ARPDIFF(f)), pluralies(ARPDIFF(f))) + + p(txrequests, "\t%u broadast ARP request%s sent\n"); + p(txurequests, "\t%u unicast ARP request%s sent\n"); + p2(txreplies, "\t%u ARP repl%s sent\n"); + p(txannounces, "\t%u ARP announcement%s sent\n"); + p(rxrequests, "\t%u ARP request%s received\n"); + p2(rxreplies, "\t%u ARP repl%s received\n"); + p(received, "\t%u total ARP packet%s received\n"); + p(txconflicts, "\t%u ARP conflict probe%s sent\n"); + p(invalidreqs, "\t%u invalid ARP resolve request%s\n"); + p(reqnobufs, "\t%u total packet%s dropped due to lack of memory\n"); + p3(held, "\t%u total packet%s held awaiting ARP repl%s\n"); + p(dropped, "\t%u total packet%s dropped due to no ARP entry\n"); + p(purged, "\t%u total packet%s dropped during ARP entry removal\n"); + p2(timeouts, "\t%u ARP entr%s timed out\n"); + p(dupips, "\t%u Duplicate IP%s seen\n"); + + if (interval > 0) + bcopy(&arpstat, &parpstat, len); + +#undef ARPDIFF +#undef p +#undef p2 +} + +static char *icmpnames[] = { + "echo reply", + "#1", + "#2", + "destination unreachable", + "source quench", + "routing redirect", + "#6", + "#7", + "echo", + "router advertisement", + "router solicitation", + "time exceeded", + "parameter problem", + "time stamp", + "time stamp reply", + "information request", + "information request reply", + "address mask request", + "address mask reply", +}; + +/* + * Dump ICMP statistics. + */ +void +icmp_stats(uint32_t off , char *name, int af ) +{ + static struct icmpstat picmpstat; + struct icmpstat icmpstat; + int i, first; + int mib[4]; /* CTL_NET + PF_INET + IPPROTO_ICMP + req */ + size_t len; + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_ICMP; + mib[3] = ICMPCTL_STATS; + + len = sizeof icmpstat; + memset(&icmpstat, 0, len); + if (sysctl(mib, 4, &icmpstat, &len, (void *)0, 0) < 0) + return; /* XXX should complain, but not traditional */ + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ICMPDIFF(f) (icmpstat.f - picmpstat.f) +#define p(f, m) if (ICMPDIFF(f) || sflag <= 1) \ + printf(m, ICMPDIFF(f), plural(ICMPDIFF(f))) +#define p1a(f, m) if (ICMPDIFF(f) || sflag <= 1) \ + printf(m, ICMPDIFF(f)) + + p(icps_error, "\t%u call%s to icmp_error\n"); + p(icps_oldicmp, + "\t%u error%s not generated 'cuz old message was icmp\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (ICMPDIFF(icps_outhist[i]) != 0) { + if (first) { + printf("\tOutput histogram:\n"); + first = 0; + } + printf("\t\t%s: %u\n", icmpnames[i], + ICMPDIFF(icps_outhist[i])); + } + p(icps_badcode, "\t%u message%s with bad code fields\n"); + p(icps_tooshort, "\t%u message%s < minimum length\n"); + p(icps_checksum, "\t%u bad checksum%s\n"); + p(icps_badlen, "\t%u message%s with bad length\n"); + p1a(icps_bmcastecho, "\t%u multicast echo requests ignored\n"); + p1a(icps_bmcasttstamp, "\t%u multicast timestamp requests ignored\n"); + for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) + if (ICMPDIFF(icps_inhist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %u\n", icmpnames[i], + ICMPDIFF(icps_inhist[i])); + } + p(icps_reflect, "\t%u message response%s generated\n"); + +#undef ICMPDIFF +#undef p +#undef p1a + mib[3] = ICMPCTL_MASKREPL; + len = sizeof i; + if (sysctl(mib, 4, &i, &len, (void *)0, 0) < 0) + return; + printf("\tICMP address mask responses are %sabled\n", + i ? "en" : "dis"); + + if (interval > 0) + bcopy(&icmpstat, &picmpstat, sizeof (icmpstat)); +} + +/* + * Dump IGMP statistics structure. + */ +void +igmp_stats(uint32_t off , char *name, int af ) +{ + static struct igmpstat_v3 pigmpstat; + struct igmpstat_v3 igmpstat; + size_t len = sizeof igmpstat; + + if (sysctlbyname("net.inet.igmp.v3stats", &igmpstat, &len, 0, 0) < 0) { + warn("sysctl: net.inet.igmp.v3stats"); + return; + } + + if (igmpstat.igps_version != IGPS_VERSION_3) { + warnx("%s: version mismatch (%d != %d)", __func__, + igmpstat.igps_version, IGPS_VERSION_3); + } + if (igmpstat.igps_len != IGPS_VERSION3_LEN) { + warnx("%s: size mismatch (%d != %d)", __func__, + igmpstat.igps_len, IGPS_VERSION3_LEN); + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IGMPDIFF(f) ((uintmax_t)(igmpstat.f - pigmpstat.f)) +#define p64(f, m) if (IGMPDIFF(f) || sflag <= 1) \ + printf(m, IGMPDIFF(f), plural(IGMPDIFF(f))) +#define py64(f, m) if (IGMPDIFF(f) || sflag <= 1) \ + printf(m, IGMPDIFF(f), IGMPDIFF(f) != 1 ? "ies" : "y") + + p64(igps_rcv_total, "\t%ju message%s received\n"); + p64(igps_rcv_tooshort, "\t%ju message%s received with too few bytes\n"); + p64(igps_rcv_badttl, "\t%ju message%s received with wrong TTL\n"); + p64(igps_rcv_badsum, "\t%ju message%s received with bad checksum\n"); + py64(igps_rcv_v1v2_queries, "\t%ju V1/V2 membership quer%s received\n"); + py64(igps_rcv_v3_queries, "\t%ju V3 membership quer%s received\n"); + py64(igps_rcv_badqueries, + "\t%ju membership quer%s received with invalid field(s)\n"); + py64(igps_rcv_gen_queries, "\t%ju general quer%s received\n"); + py64(igps_rcv_group_queries, "\t%ju group quer%s received\n"); + py64(igps_rcv_gsr_queries, "\t%ju group-source quer%s received\n"); + py64(igps_drop_gsr_queries, "\t%ju group-source quer%s dropped\n"); + p64(igps_rcv_reports, "\t%ju membership report%s received\n"); + p64(igps_rcv_badreports, + "\t%ju membership report%s received with invalid field(s)\n"); + p64(igps_rcv_ourreports, +"\t%ju membership report%s received for groups to which we belong\n"); + p64(igps_rcv_nora, "\t%ju V3 report%s received without Router Alert\n"); + p64(igps_snd_reports, "\t%ju membership report%s sent\n"); + + if (interval > 0) + bcopy(&igmpstat, &pigmpstat, len); + +#undef IGMPDIFF +#undef p64 +#undef py64 +} + +/* + * Pretty print an Internet address (net address + port). + */ +void +inetprint(struct in_addr *in, int port, char *proto, int numeric_port) +{ + struct servent *sp = 0; + char line[80], *cp; + int width; + + if (Wflag) + snprintf(line, sizeof(line), "%s.", inetname(in)); + else + snprintf(line, sizeof(line), "%.*s.", (Aflag && !numeric_port) ? 12 : 16, inetname(in)); + cp = index(line, '\0'); + if (!numeric_port && port) +#ifdef _SERVICE_CACHE_ + sp = _serv_cache_getservbyport(port, proto); +#else + sp = getservbyport((int)port, proto); +#endif + if (sp || port == 0) + snprintf(cp, sizeof(line) - (cp - line), "%.15s ", sp ? sp->s_name : "*"); + else + snprintf(cp, sizeof(line) - (cp - line), "%d ", ntohs((u_short)port)); + width = (Aflag && !Wflag) ? 18 : 22; + if (Wflag) + printf("%-*s ", width, line); + else + printf("%-*.*s ", width, width, line); +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +char * +inetname(struct in_addr *inp) +{ + register char *cp; + static char line[MAXHOSTNAMELEN]; + struct hostent *hp; + struct netent *np; + + cp = 0; + if (!nflag && 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) { + cp = hp->h_name; + //### trimdomain(cp, strlen(cp)); + } + } + } + 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) ((u_int)((x) & 0xff)) + 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)); + } + return (line); +} diff --git a/network_cmds/netstat.tproj/inet6.c b/network_cmds/netstat.tproj/inet6.c new file mode 100644 index 0000000..4bc757c --- /dev/null +++ b/network_cmds/netstat.tproj/inet6.c @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 2008-2018 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* BSDI inet.c,v 2.3 1995/10/24 02:19:29 prb 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. 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. + * + * $FreeBSD: src/usr.bin/netstat/inet6.c,v 1.3.2.9 2001/08/10 09:07:09 ru Exp $ + */ + +#ifdef INET6 +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/net_perf.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/in_systm.h> +#include <netinet6/in6_pcb.h> +#include <netinet6/in6_var.h> +#include <netinet6/ip6_var.h> +#include <netinet6/raw_ip6.h> + +#include <arpa/inet.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#if defined(__APPLE__) && !defined(__unused) +#define __unused +#endif + +char *inet6name (struct in6_addr *); +void inet6print (struct in6_addr *, int, char *, int); + +static char *ip6nh[] = { + "hop by hop", + "ICMP", + "IGMP", + "#3", + "IP", + "#5", + "TCP", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "UDP", + "#18", + "#19", + "#20", + "#21", + "IDP", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "TP", + "#30", + "#31", + "#32", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "IP6", + "#42", + "routing", + "fragment", + "#45", + "#46", + "#47", + "#48", + "#49", + "ESP", + "AH", + "#52", + "#53", + "#54", + "#55", + "#56", + "#57", + "ICMP6", + "no next header", + "destination option", + "#61", + "mobility", + "#63", + "#64", + "#65", + "#66", + "#67", + "#68", + "#69", + "#70", + "#71", + "#72", + "#73", + "#74", + "#75", + "#76", + "#77", + "#78", + "#79", + "ISOIP", + "#81", + "#82", + "#83", + "#84", + "#85", + "#86", + "#87", + "#88", + "OSPF", + "#80", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "Ethernet", + "#98", + "#99", + "#100", + "#101", + "#102", + "PIM", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "#128", + "#129", + "#130", + "#131", + "#132", + "#133", + "#134", + "#135", + "#136", + "#137", + "#138", + "#139", + "#140", + "#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", +}; + + +static const char *srcrulenames[IP6S_SRCRULE_COUNT] = { + "default", // IP6S_SRCRULE_0 + "prefer same address", // IP6S_SRCRULE_1 + "prefer appropriate scope", // IP6S_SRCRULE_2 + "avoid deprecated addresses", // IP6S_SRCRULE_3 + "prefer home addresses", // IP6S_SRCRULE_4 + "prefer outgoing interface", // IP6S_SRCRULE_5 + "prefer matching label", // IP6S_SRCRULE_6 + "prefer temporary addresses", // IP6S_SRCRULE_7 + "prefer addresses on alive interfaces", // IP6S_SRCRULE_7x + "use longest matching prefix", // IP6S_SRCRULE_8 + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* + * Dump IP6 statistics structure. + */ +void +ip6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct ip6stat pip6stat; + struct ip6stat ip6stat; + int first, i; + int mib[4]; + size_t len; + static net_perf_t pout_net_perf, pin_net_perf; + net_perf_t out_net_perf, in_net_perf; + size_t out_net_perf_len = sizeof (out_net_perf); + size_t in_net_perf_len = sizeof (in_net_perf); + + if (sysctlbyname("net.inet6.ip6.output_perf_data", &out_net_perf, &out_net_perf_len, 0, 0) < 0) { + perror("sysctl: net.inet6.ip6.output_perf_data"); + return; + } + + if (sysctlbyname("net.inet6.ip6.input_perf_data", &in_net_perf, &in_net_perf_len, 0, 0) < 0) { + perror("sysctl: net.inet6.ip6.input_perf_data"); + return; + } + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_STATS; + + len = sizeof ip6stat; + memset(&ip6stat, 0, len); + if (sysctl(mib, 4, &ip6stat, &len, (void *)0, 0) < 0) + return; + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define IP6DIFF(f) (ip6stat.f - pip6stat.f) +#define p(f, m) if (IP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)IP6DIFF(f), plural(IP6DIFF(f))) +#define p1a(f, m) if (IP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)IP6DIFF(f)) + + p(ip6s_total, "\t%llu total packet%s received\n"); + p1a(ip6s_toosmall, "\t\t%llu with size smaller than minimum\n"); + p1a(ip6s_tooshort, "\t\t%llu with data size < data length\n"); + p1a(ip6s_adj, "\t\t%llu with data size > data length\n"); + p(ip6s_adj_hwcsum_clr, + "\t\t\t%llu packet%s forced to software checksum\n"); + p1a(ip6s_badoptions, "\t\t%llu with bad options\n"); + p1a(ip6s_badvers, "\t\t%llu with incorrect version number\n"); + p(ip6s_fragments, "\t\t%llu fragment%s received\n"); + p1a(ip6s_fragdropped, + "\t\t\t%llu dropped (dup or out of space)\n"); + p1a(ip6s_fragtimeout, "\t\t\t%llu dropped after timeout\n"); + p1a(ip6s_fragoverflow, "\t\t\t%llu exceeded limit\n"); + p1a(ip6s_reassembled, "\t\t\t%llu reassembled ok\n"); + p1a(ip6s_atmfrag_rcvd, "\t\t\t%llu atomic fragments received\n"); + p(ip6s_delivered, "\t\t%llu packet%s for this host\n"); + p(ip6s_forward, "\t\t%llu packet%s forwarded\n"); + p(ip6s_cantforward, "\t\t%llu packet%s not forwardable\n"); + p(ip6s_redirectsent, "\t\t%llu redirect%s sent\n"); + p(ip6s_notmember, "\t\t%llu multicast packet%s which we don't join\n"); + p(ip6s_exthdrtoolong, + "\t\t%llu packet%s whose headers are not continuous\n"); + p(ip6s_nogif, "\t\t%llu tunneling packet%s that can't find gif\n"); + p(ip6s_toomanyhdr, + "\t\t%llu packet%s discarded due to too may headers\n"); + p1a(ip6s_forward_cachehit, "\t\t%llu forward cache hit\n"); + p1a(ip6s_forward_cachemiss, "\t\t%llu forward cache miss\n"); + p(ip6s_pktdropcntrl, + "\t\t%llu packet%s dropped due to no bufs for control data\n"); + /* CLAT46 input stats */ + p(ip6s_clat464_in_tooshort_drop, + "\t\t%llu input packet%s dropped due to too short length \n"); + p(ip6s_clat464_in_nov6addr_drop, + "\t\t%llu input packet%s dropped due to missing CLAT46 IPv6 address\n"); + p(ip6s_clat464_in_nov4addr_drop, + "\t\t%llu input packet%s dropped due to missing CLAT46 IPv4 address\n"); + p(ip6s_clat464_in_v4synthfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 IPv4 address derivation failure\n"); + p(ip6s_clat464_in_64transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 IP header translation failure\n"); + p(ip6s_clat464_in_64proto_transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 protocol translation failure\n"); + p(ip6s_clat464_in_64frag_transfail_drop, + "\t\t%llu input packet%s dropped due to CLAT46 fragment translation failure\n"); + p(ip6s_clat464_in_invalpbuf_drop, + "\t\t%llu input packet%s dropped due to invalid pbuf\n"); + p(ip6s_clat464_in_v4_drop, + "\t\t%llu input IPv4 packet%s dropped on CLAT46 enabled interface\n"); + p(ip6s_clat464_in_drop, + "\t\t%llu input packet%s dropped due to CLAT46 failures\n"); + p(ip6s_clat464_in_success, + "\t\t%llu input packet%s successfully translated from IPv6 to IPv4\n"); + +#define INPERFDIFF(f) (in_net_perf.f - pin_net_perf.f) + if (INPERFDIFF(np_total_pkts) > 0 && in_net_perf.np_total_usecs > 0) { + printf("\tInput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", INPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", INPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)in_net_perf.np_total_usecs/(double)in_net_perf.np_total_pkts); + printf("\t\tPerformance Histogram:\n"); + printf("\t\t\t x <= %u: %llu\n", in_net_perf.np_hist_bars[0], + INPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[0], in_net_perf.np_hist_bars[1], + INPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[1], in_net_perf.np_hist_bars[2], + INPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + in_net_perf.np_hist_bars[2], in_net_perf.np_hist_bars[3], + INPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + in_net_perf.np_hist_bars[3], INPERFDIFF(np_hist5)); + } +#undef INPERFDIFF + + p(ip6s_localout, "\t%llu packet%s sent from this host\n"); + p(ip6s_rawout, "\t\t%llu packet%s sent with fabricated ip header\n"); + p(ip6s_odropped, + "\t\t%llu output packet%s dropped due to no bufs, etc.\n"); + p(ip6s_noroute, "\t\t%llu output packet%s discarded due to no route\n"); + p(ip6s_fragmented, "\t\t%llu output datagram%s fragmented\n"); + p(ip6s_ofragments, "\t\t%llu fragment%s created\n"); + p(ip6s_cantfrag, "\t\t%llu datagram%s that can't be fragmented\n"); + p(ip6s_badscope, "\t\t%llu packet%s that violated scope rules\n"); + p(ip6s_necp_policy_drop, "\t\t%llu packet%s dropped due to NECP policy\n"); + /* CLAT46 output stats */ + p(ip6s_clat464_out_nov6addr_drop, + "\t\t%llu output packet%s dropped due to missing CLAT46 IPv6 address\n"); + p(ip6s_clat464_out_v6synthfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 IPv6 address synthesis failure\n"); + p(ip6s_clat464_out_46transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 IP header translation failure\n"); + p(ip6s_clat464_out_46proto_transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 protocol translation failure\n"); + p(ip6s_clat464_out_46frag_transfail_drop, + "\t\t%llu output packet%s dropped due to CLAT46 fragment translation failure\n"); + p(ip6s_clat464_out_invalpbuf_drop, + "\t\t%llu output packet%s dropped due to invalid pbuf\n"); + p(ip6s_clat464_out_drop, + "\t\t%llu output packet%s dropped due to CLAT46 failures\n"); + p(ip6s_clat464_out_success, + "\t\t%llu output packet%s successfully translated from IPv4 to IPv6\n"); + p(ip6s_rcv_if_weak_match, + "\t\t%llu input packet%s that passed the weak ES interface address match\n"); + p(ip6s_rcv_if_no_match, + "\t\t%llu input packet%s with no interface address match\n"); + +#define OUTPERFDIFF(f) (out_net_perf.f - pout_net_perf.f) + if (OUTPERFDIFF(np_total_pkts) > 0 && out_net_perf.np_total_usecs > 0) { + printf("\tOutput Performance Stats:\n"); + printf("\t\t%llu total packets measured\n", OUTPERFDIFF(np_total_pkts)); + printf("\t\t%llu total usec elapsed\n", OUTPERFDIFF(np_total_usecs)); + printf("\t\t%f usec per packet\n", + (double)out_net_perf.np_total_usecs/(double)out_net_perf.np_total_pkts); + printf("\t\tHistogram:\n"); + printf("\t\t\t x <= %u: %llu\n", out_net_perf.np_hist_bars[0], + OUTPERFDIFF(np_hist1)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[0], out_net_perf.np_hist_bars[1], + OUTPERFDIFF(np_hist2)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[1], out_net_perf.np_hist_bars[2], + OUTPERFDIFF(np_hist3)); + printf("\t\t\t %u < x <= %u: %llu\n", + out_net_perf.np_hist_bars[2], out_net_perf.np_hist_bars[3], + OUTPERFDIFF(np_hist4)); + printf("\t\t\t %u < x: %llu\n", + out_net_perf.np_hist_bars[3], OUTPERFDIFF(np_hist5)); + } +#undef OUTPERFDIFF + + for (first = 1, i = 0; i < 256; i++) + if (IP6DIFF(ip6s_nxthist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", ip6nh[i], + (unsigned long long)IP6DIFF(ip6s_nxthist[i])); + } + printf("\tMbuf statistics:\n"); + printf("\t\t%llu one mbuf\n", (unsigned long long)IP6DIFF(ip6s_m1)); + for (first = 1, i = 0; i < 32; i++) { + char ifbuf[IFNAMSIZ]; + if (IP6DIFF(ip6s_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)IP6DIFF(ip6s_m2m[i])); + } + } + printf("\t\t%llu one ext mbuf\n", + (unsigned long long)IP6DIFF(ip6s_mext1)); + printf("\t\t%llu two or more ext mbuf\n", + (unsigned long long)IP6DIFF(ip6s_mext2m)); + + /* for debugging source address selection */ +#define PRINT_SCOPESTAT(s,i) do {\ + switch(i) { /* XXX hardcoding in each case */\ + case 1:\ + p(s, "\t\t\t%llu node-local%s\n");\ + break;\ + case 2:\ + p(s,"\t\t\t%llu link-local%s\n");\ + break;\ + case 5:\ + p(s,"\t\t\t%llu site-local%s\n");\ + break;\ + case 14:\ + p(s,"\t\t\t%llu global%s\n");\ + break;\ + default:\ + printf("\t\t\t%llu addresses scope=%x\n",\ + (unsigned long long)IP6DIFF(s), i);\ + }\ + } while (0); + + p(ip6s_sources_none, + "\t\t%llu failure%s of source address selection\n"); + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_sameif[i]) || 1) { + if (first) { + printf("\t\tsource addresses on an outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_sameif[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_otherif[i]) || 1) { + if (first) { + printf("\t\tsource addresses on a non-outgoing I/F\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_otherif[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_samescope[i]) || 1) { + if (first) { + printf("\t\tsource addresses of same scope\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_samescope[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_otherscope[i]) || 1) { + if (first) { + printf("\t\tsource addresses of a different scope\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_otherscope[i], i); + } + } + for (first = 1, i = 0; i < SCOPE6_ID_MAX; i++) { + if (IP6DIFF(ip6s_sources_deprecated[i]) || 1) { + if (first) { + printf("\t\tdeprecated source addresses\n"); + first = 0; + } + PRINT_SCOPESTAT(ip6s_sources_deprecated[i], i); + } + } +#define PRINT_SRCRULESTAT(s,i) do {\ + if (srcrulenames[i] != NULL) \ + printf("\t\t\t%llu rule%s %s\n", \ + (unsigned long long)IP6DIFF(s), \ + plural(IP6DIFF(s)), \ + srcrulenames[i]); \ +} while (0); + + for (first = 1, i = 0; i < IP6S_SRCRULE_COUNT; i++) { + if (IP6DIFF(ip6s_sources_rule[i]) || 1) { + if (first) { + printf("\t\tsource address selection\n"); + first = 0; + } + PRINT_SRCRULESTAT(ip6s_sources_rule[i], i); + } + } + + p(ip6s_dad_collide, "\t\t%llu duplicate address detection collision%s\n"); + + p(ip6s_dad_loopcount, "\t\t%llu duplicate address detection NS loop%s\n"); + + p(ip6s_sources_skip_expensive_secondary_if, "\t\t%llu time%s ignored source on secondary expensive I/F\n"); + + if (interval > 0) { + bcopy(&ip6stat, &pip6stat, len); + bcopy(&in_net_perf, &pin_net_perf, in_net_perf_len); + bcopy(&out_net_perf, &pout_net_perf, out_net_perf_len); + } +#undef IP6DIFF +#undef p +#undef p1a +} + +/* + * Dump IPv6 per-interface statistics based on RFC 2465. + */ +void +ip6_ifstats(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; + } + + if (interval && vflag > 0) + print_time(); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("ip6 on %s:\n", ifr.ifr_name); + + 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_protounknown, "\t%llu datagram%s with unknown proto received\n"); + p(ifs6_in_truncated, "\t%llu truncated datagram%s 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_atmfrag_rcvd, "\t%llu atomic fragments%s received\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"); + + p(ifs6_cantfoward_icmp6, "\t%llu ICMPv6 packet%s received for unreachable destination\n"); + p(ifs6_addr_expiry_cnt, "\t%llu address expiry event%s reported\n"); + p(ifs6_pfx_expiry_cnt, "\t%llu prefix expiry event%s reported\n"); + p(ifs6_defrtr_expiry_cnt, "\t%llu default router expiry event%s reported\n"); + end: + close(s); + +#undef p +#undef p_5 +} + +static 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", + "MLDv1 listener report", + "MLDv1 listener done", + "router solicitation", + "router advertisement", + "neighbor solicitation", + "neighbor advertisement", + "redirect", + "router renumbering", + "node information request", + "node information reply", + "inverse neighbor solicitation", + "inverse neighbor advertisement", + "MLDv2 listener report", + "#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 ICMP6 statistics. + */ +void +icmp6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct icmp6stat picmp6stat; + struct icmp6stat icmp6stat; + register int i, first; + int mib[4]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_ICMPV6; + mib[3] = ICMPV6CTL_STATS; + + len = sizeof icmp6stat; + memset(&icmp6stat, 0, len); + if (sysctl(mib, 4, &icmp6stat, &len, (void *)0, 0) < 0) + return; + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define ICMP6DIFF(f) (icmp6stat.f - picmp6stat.f) +#define p(f, m) if (ICMP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)ICMP6DIFF(f), plural(ICMP6DIFF(f))) +#define p_5(f, m) printf(m, (unsigned long long)ICMP6DIFF(f)) + + p(icp6s_error, "\t%llu call%s to icmp_error\n"); + p(icp6s_canterror, + "\t%llu error%s not generated because old message was icmp error or so\n"); + p(icp6s_toofreq, + "\t%llu error%s not generated because rate limitation\n"); +#define NELEM (sizeof(icmp6stat.icp6s_outhist)/sizeof(icmp6stat.icp6s_outhist[0])) + for (first = 1, i = 0; i < NELEM; i++) + if (ICMP6DIFF(icp6s_outhist[i]) != 0) { + if (first) { + printf("\tOutput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)ICMP6DIFF(icp6s_outhist[i])); + } +#undef NELEM + p(icp6s_badcode, "\t%llu message%s with bad code fields\n"); + p(icp6s_tooshort, "\t%llu message%s < minimum length\n"); + p(icp6s_checksum, "\t%llu bad checksum%s\n"); + p(icp6s_badlen, "\t%llu message%s with bad length\n"); +#define NELEM (sizeof(icmp6stat.icp6s_inhist)/sizeof(icmp6stat.icp6s_inhist[0])) + for (first = 1, i = 0; i < NELEM; i++) + if (ICMP6DIFF(icp6s_inhist[i]) != 0) { + if (first) { + printf("\tInput histogram:\n"); + first = 0; + } + printf("\t\t%s: %llu\n", icmp6names[i], + (unsigned long long)ICMP6DIFF(icp6s_inhist[i])); + } +#undef NELEM + printf("\tHistogram of error messages to be generated:\n"); + p_5(icp6s_odst_unreach_noroute, "\t\t%llu no route\n"); + p_5(icp6s_odst_unreach_admin, "\t\t%llu administratively prohibited\n"); + p_5(icp6s_odst_unreach_beyondscope, "\t\t%llu beyond scope\n"); + p_5(icp6s_odst_unreach_addr, "\t\t%llu address unreachable\n"); + p_5(icp6s_odst_unreach_noport, "\t\t%llu port unreachable\n"); + p_5(icp6s_opacket_too_big, "\t\t%llu packet too big\n"); + p_5(icp6s_otime_exceed_transit, "\t\t%llu time exceed transit\n"); + p_5(icp6s_otime_exceed_reassembly, "\t\t%llu time exceed reassembly\n"); + p_5(icp6s_oparamprob_header, "\t\t%llu erroneous header field\n"); + p_5(icp6s_oparamprob_nextheader, "\t\t%llu unrecognized next header\n"); + p_5(icp6s_oparamprob_option, "\t\t%llu unrecognized option\n"); + p_5(icp6s_oredirect, "\t\t%llu redirect\n"); + p_5(icp6s_ounknown, "\t\t%llu unknown\n"); + + p(icp6s_reflect, "\t%llu message response%s generated\n"); + p(icp6s_nd_toomanyopt, "\t%llu message%s with too many ND options\n"); + p(icp6s_nd_badopt, "\t%qu message%s with bad ND options\n"); + p(icp6s_badns, "\t%qu bad neighbor solicitation message%s\n"); + p(icp6s_badna, "\t%qu bad neighbor advertisement message%s\n"); + p(icp6s_badrs, "\t%qu bad router solicitation message%s\n"); + p(icp6s_badra, "\t%qu bad router advertisement message%s\n"); + p(icp6s_badredirect, "\t%qu bad redirect message%s\n"); + p(icp6s_pmtuchg, "\t%llu path MTU change%s\n"); + p(icp6s_rfc6980_drop, "\t%qu dropped fragmented NDP message%s\n"); + + if (interval > 0) + bcopy(&icmp6stat, &picmp6stat, len); + +#undef ICMP6DIFF +#undef p +#undef p_5 +} + +/* + * Dump ICMPv6 per-interface statistics based on RFC 2466. + */ +void +icmp6_ifstats(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; + } + + if (interval && vflag > 0) + print_time(); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + printf("icmp6 on %s:\n", ifr.ifr_name); + + 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 raw ip6 statistics structure. + */ +void +rip6_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct rip6stat prip6stat; + struct rip6stat rip6stat; + u_quad_t delivered; + int mib[4]; + size_t l; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_RIP6STATS; + l = sizeof(rip6stat); + if (sysctl(mib, 4, &rip6stat, &l, NULL, 0) < 0) { + perror("Warning: sysctl(net.inet6.ip6.rip6stats)"); + return; + } + + if (interval && vflag > 0) + print_time(); + printf("%s:\n", name); + +#define RIP6DIFF(f) (rip6stat.f - prip6stat.f) +#define p(f, m) if (RIP6DIFF(f) || sflag <= 1) \ + printf(m, (unsigned long long)RIP6DIFF(f), plural(RIP6DIFF(f))) + p(rip6s_ipackets, "\t%llu message%s received\n"); + p(rip6s_isum, "\t%llu checksum calculation%s on inbound\n"); + p(rip6s_badsum, "\t%llu message%s with bad checksum\n"); + p(rip6s_nosock, "\t%llu message%s dropped due to no socket\n"); + p(rip6s_nosockmcast, + "\t%llu multicast message%s dropped due to no socket\n"); + p(rip6s_fullsock, + "\t%llu message%s dropped due to full socket buffers\n"); + delivered = RIP6DIFF(rip6s_ipackets) - + RIP6DIFF(rip6s_badsum) - + RIP6DIFF(rip6s_nosock) - + RIP6DIFF(rip6s_nosockmcast) - + RIP6DIFF(rip6s_fullsock); + if (delivered || sflag <= 1) + printf("\t%llu delivered\n", (unsigned long long)delivered); + p(rip6s_opackets, "\t%llu datagram%s output\n"); + + if (interval > 0) + bcopy(&rip6stat, &prip6stat, l); + +#undef RIP6DIFF +#undef p +} + +/* + * Pretty print an Internet address (net address + port). + * If the nflag was specified, use numbers instead of names. + */ +#ifdef SRVCACHE +extern struct servent * _serv_cache_getservbyport(int port, char *proto); + +#define GETSERVBYPORT6(port, proto, ret)\ +{\ + if (strcmp((proto), "tcp6") == 0)\ + (ret) = _serv_cache_getservbyport((int)(port), "tcp");\ + else if (strcmp((proto), "udp6") == 0)\ + (ret) = _serv_cache_getservbyport((int)(port), "udp");\ + else\ + (ret) = _serv_cache_getservbyport((int)(port), (proto));\ +}; +#else +#define GETSERVBYPORT6(port, proto, ret)\ +{\ + 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));\ +}; +#endif +void +inet6print(struct in6_addr *in6, int port, char *proto, int numeric) +{ + struct servent *sp = 0; + char line[80], *cp; + int width; + + snprintf(line, sizeof(line), "%.*s.", lflag ? 39 : + (Aflag && !numeric) ? 12 : 16, inet6name(in6)); + cp = index(line, '\0'); + if (!numeric && port) + GETSERVBYPORT6(port, proto, sp); + if (sp || port == 0) + snprintf(cp, sizeof(line) - (cp - line), "%.15s", sp ? sp->s_name : "*"); + else + snprintf(cp, sizeof(line) - (cp - line), "%d", ntohs((u_short)port)); + width = lflag ? 45 : Aflag ? 18 : 22; + printf("%-*.*s ", width, width, line); +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ + +char * +inet6name(struct in6_addr *in6p) +{ + register char *cp; + static char line[50]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN]; + static int first = 1; + char hbuf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + const int niflag = NI_NUMERICHOST; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = index(domain, '.'))) + (void) memmove(domain, cp + 1, strlen(cp + 1) + 1); + else + domain[0] = 0; + } + cp = 0; + if (!nflag && !IN6_IS_ADDR_UNSPECIFIED(in6p)) { + hp = gethostbyaddr((char *)in6p, sizeof(*in6p), AF_INET6); + if (hp) { + if ((cp = index(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; + + if (IN6_IS_ADDR_LINKLOCAL(in6p) || + IN6_IS_ADDR_MC_NODELOCAL(in6p) || + IN6_IS_ADDR_MC_LINKLOCAL(in6p)) { + sin6.sin6_scope_id = + ntohs(*(u_int16_t *)&in6p->s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + + 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); +} +#endif /*INET6*/ diff --git a/network_cmds/netstat.tproj/ipsec.c b/network_cmds/netstat.tproj/ipsec.c new file mode 100644 index 0000000..0eab162 --- /dev/null +++ b/network_cmds/netstat.tproj/ipsec.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2008-2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* $FreeBSD: src/usr.bin/netstat/ipsec.c,v 1.1.2.3 2001/08/10 09:07:09 ru Exp $ */ +/* $NetBSD: inet.c,v 1.35.2.1 1999/04/29 14:57:08 perry Exp $ */ +/* $KAME: ipsec.c,v 1.25 2001/03/12 09:04:39 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. 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. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#ifdef IPSEC +#include <netinet6/ipsec.h> +#include <netkey/keysock.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" + +#if defined(__APPLE__) && !defined(__unused) +#define __unused +#endif +/* + * portability issues: + * - bsdi[34] uses PLURAL(), not plural(). + * - freebsd2 can't print "unsigned long long" properly. + */ +/* + * XXX see PORTABILITY for the twist + */ +#define LLU "%llu" +#define CAST unsigned long long + +#ifdef IPSEC +struct val2str { + int val; + const char *str; +}; + +static struct val2str ipsec_ahnames[] = { + { 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", }, +#ifdef SADB_X_AALG_SHA2_256 + { SADB_X_AALG_SHA2_256, "hmac-sha2-256", }, +#endif +#ifdef SADB_X_AALG_SHA2_384 + { SADB_X_AALG_SHA2_384, "hmac-sha2-384", }, +#endif +#ifdef SADB_X_AALG_SHA2_512 + { SADB_X_AALG_SHA2_512, "hmac-sha2-512", }, +#endif + { -1, NULL }, +}; + +static struct val2str ipsec_espnames[] = { + { SADB_EALG_NONE, "none", }, + { SADB_EALG_DESCBC, "des-cbc", }, + { SADB_EALG_3DESCBC, "3des-cbc", }, + { SADB_EALG_NULL, "null", }, +#ifdef SADB_X_EALG_RC5CBC + { SADB_X_EALG_RC5CBC, "rc5-cbc", }, +#endif + { SADB_X_EALG_CAST128CBC, "cast128-cbc", }, + { SADB_X_EALG_BLOWFISHCBC, "blowfish-cbc", }, +#ifdef SADB_X_EALG_RIJNDAELCBC + { SADB_X_EALG_RIJNDAELCBC, "rijndael-cbc", }, +#endif + { -1, NULL }, +}; + +static struct val2str ipsec_compnames[] = { + { SADB_X_CALG_NONE, "none", }, + { SADB_X_CALG_OUI, "oui", }, + { SADB_X_CALG_DEFLATE, "deflate", }, + { SADB_X_CALG_LZS, "lzs", }, + { -1, NULL }, +}; + +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 struct ipsecstat pipsecstat; +static struct ipsecstat ipsecstat; + +static void print_ipsecstats (void); +static const char *pfkey_msgtype_names (int); +static void ipsec_hist (const u_quad_t *, const u_quad_t *, size_t, + const struct val2str *, const char *); + +/* + * Dump IPSEC statistics structure. + */ +static void +ipsec_hist(const u_quad_t *hist, + const u_quad_t *phist, + size_t histmax, + const struct val2str *name, + const char *title) +{ + int first; + size_t proto; + const struct val2str *p; + + first = 1; + for (proto = 0; proto < histmax; proto++) { + if ((hist[proto] - phist[proto]) <= 0) + continue; + if (first) { + printf("\t%s histogram:\n", title); + first = 0; + } + for (p = name; p && p->str; p++) { + if (p->val == (int)proto) + break; + } + if (p && p->str) { + printf("\t\t%s: " LLU "\n", p->str, + (CAST)hist[proto] - (CAST)phist[proto]); + } else { + printf("\t\t#%ld: " LLU "\n", (long)proto, + (CAST)hist[proto] - (CAST)phist[proto]); + } + } +} + +static void +print_ipsecstats(void) +{ +#define IPSECDIFF(f) (ipsecstat.f - pipsecstat.f) +#define p(f, m) if (IPSECDIFF(f) || sflag <= 1) \ + printf(m, (CAST)IPSECDIFF(f), plural(IPSECDIFF(f))) +#define hist(f, n, t) \ + ipsec_hist(ipsecstat.f, pipsecstat.f, \ + sizeof(ipsecstat.f)/sizeof(ipsecstat.f[0]), (n), (t)); + + if (interval && vflag > 0) + print_time(); + + p(in_success, "\t" LLU " inbound packet%s processed successfully\n"); + p(in_polvio, "\t" LLU " inbound packet%s violated process security " + "policy\n"); + p(in_nosa, "\t" LLU " inbound packet%s with no SA available\n"); + p(in_inval, "\t" LLU " invalid inbound packet%s\n"); + p(in_nomem, "\t" LLU " inbound packet%s failed due to insufficient memory\n"); + p(in_badspi, "\t" LLU " inbound packet%s failed getting SPI\n"); + p(in_ahreplay, "\t" LLU " inbound packet%s failed on AH replay check\n"); + p(in_espreplay, "\t" LLU " inbound packet%s failed on ESP replay check\n"); + p(in_ahauthsucc, "\t" LLU " inbound packet%s considered authentic\n"); + p(in_ahauthfail, "\t" LLU " inbound packet%s failed on authentication\n"); + hist(in_ahhist, ipsec_ahnames, "AH input"); + hist(in_esphist, ipsec_espnames, "ESP input"); + hist(in_comphist, ipsec_compnames, "IPComp input"); + + p(out_success, "\t" LLU " outbound packet%s processed successfully\n"); + p(out_polvio, "\t" LLU " outbound packet%s violated process security " + "policy\n"); + p(out_nosa, "\t" LLU " outbound packet%s with no SA available\n"); + p(out_inval, "\t" LLU " invalid outbound packet%s\n"); + p(out_nomem, "\t" LLU " outbound packet%s failed due to insufficient memory\n"); + p(out_noroute, "\t" LLU " outbound packet%s with no route\n"); + hist(out_ahhist, ipsec_ahnames, "AH output"); + hist(out_esphist, ipsec_espnames, "ESP output"); + hist(out_comphist, ipsec_compnames, "IPComp output"); +#undef IPSECDIFF +#undef p +#undef hist +} + +void +ipsec_stats(uint32_t off __unused, char *name, int af __unused) +{ + size_t len; + + len = sizeof(struct ipsecstat); + if (strcmp(name, "ipsec") == 0) { + if (sysctlbyname("net.inet.ipsec.stats", &ipsecstat, &len, 0, 0) == -1) + return; + } else if (strcmp(name, "ipsec6") == 0) { + if (sysctlbyname("net.inet6.ipsec6.stats", &ipsecstat, &len, 0, 0) == -1) + return; + } else + return; + printf ("%s:\n", name); + + print_ipsecstats(); + + if (interval > 0) + bcopy(&ipsecstat, &pipsecstat, len); +} + +static const char * +pfkey_msgtype_names(int x) +{ + const int max = + sizeof(pfkey_msgtypenames)/sizeof(pfkey_msgtypenames[0]); + static char buf[10]; + + if (x < max && pfkey_msgtypenames[x]) + return pfkey_msgtypenames[x]; + snprintf(buf, sizeof(buf), "#%d", x); + return buf; +} + +void +pfkey_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct pfkeystat ppfkeystat; + struct pfkeystat pfkeystat; + unsigned first, type; + size_t len; + + len = sizeof(struct pfkeystat); + if (sysctlbyname("net.key.pfkeystat", &pfkeystat, &len, 0, 0) == -1) + return; + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define PFKEYDIFF(f) (pfkeystat.f - ppfkeystat.f) +#define p(f, m) if (PFKEYDIFF(f) || sflag <= 1) \ + printf(m, (CAST)PFKEYDIFF(f), plural(PFKEYDIFF(f))) + + /* kernel -> userland */ + p(out_total, "\t" LLU " request%s sent to userland\n"); + p(out_bytes, "\t" LLU " byte%s sent to userland\n"); + for (first = 1, type = 0; + type < sizeof(pfkeystat.out_msgtype)/sizeof(pfkeystat.out_msgtype[0]); + type++) { + if (PFKEYDIFF(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), + (CAST)PFKEYDIFF(out_msgtype[type])); + } + p(out_invlen, "\t" LLU " message%s with invalid length field\n"); + p(out_invver, "\t" LLU " message%s with invalid version field\n"); + p(out_invmsgtype, "\t" LLU " message%s with invalid message type field\n"); + p(out_tooshort, "\t" LLU " message%s too short\n"); + p(out_nomem, "\t" LLU " message%s with memory allocation failure\n"); + p(out_dupext, "\t" LLU " message%s with duplicate extension\n"); + p(out_invexttype, "\t" LLU " message%s with invalid extension type\n"); + p(out_invsatype, "\t" LLU " message%s with invalid sa type\n"); + p(out_invaddr, "\t" LLU " message%s with invalid address extension\n"); + + /* userland -> kernel */ + p(in_total, "\t" LLU " request%s sent from userland\n"); + p(in_bytes, "\t" LLU " byte%s sent from userland\n"); + for (first = 1, type = 0; + type < sizeof(pfkeystat.in_msgtype)/sizeof(pfkeystat.in_msgtype[0]); + type++) { + if (PFKEYDIFF(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), + (CAST)PFKEYDIFF(in_msgtype[type])); + } + p(in_msgtarget[KEY_SENDUP_ONE], + "\t" LLU " message%s toward single socket\n"); + p(in_msgtarget[KEY_SENDUP_ALL], + "\t" LLU " message%s toward all sockets\n"); + p(in_msgtarget[KEY_SENDUP_REGISTERED], + "\t" LLU " message%s toward registered sockets\n"); + p(in_nomem, "\t" LLU " message%s with memory allocation failure\n"); + + if (interval > 0) + bcopy(&pfkeystat, &ppfkeystat, len); +#undef PFKEYDIFF +#undef p +} +#endif /*IPSEC*/ diff --git a/network_cmds/netstat.tproj/main.c b/network_cmds/netstat.tproj/main.c new file mode 100644 index 0000000..1b8381e --- /dev/null +++ b/network_cmds/netstat.tproj/main.c @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * 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. 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. + */ + +#ifndef lint +char const copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/sys_domain.h> + +#include <netinet/in.h> +#include <net/pfkeyv2.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "netstat.h" +#include <sys/types.h> +#include <sys/sysctl.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $Id: main.c,v 1.8 2004/10/14 22:24:09 lindak Exp $ + * + */ + +struct protox { + void (*pr_cblocks)(uint32_t, char *, int); + /* control blocks printing routine */ + void (*pr_stats)(uint32_t, char *, int); + /* statistics printing routine */ + void (*pr_istats)(char *); /* per/if statistics printing routine */ + char *pr_name; /* well-known name */ + int pr_protocol; +} protox[] = { + { protopr, tcp_stats, NULL, "tcp", IPPROTO_TCP }, + { protopr, udp_stats, NULL, "udp", IPPROTO_UDP }, + { protopr, NULL, NULL, "divert", IPPROTO_DIVERT }, + { protopr, ip_stats, NULL, "ip", IPPROTO_RAW }, + { protopr, icmp_stats, NULL, "icmp", IPPROTO_ICMP }, + { protopr, igmp_stats, NULL, "igmp", IPPROTO_IGMP }, +#ifdef IPSEC + { NULL, ipsec_stats, NULL, "ipsec", IPPROTO_ESP}, +#endif + { NULL, arp_stats, NULL, "arp", 0 }, + { mptcppr, mptcp_stats, NULL, "mptcp", IPPROTO_TCP }, + { NULL, NULL, NULL, NULL, 0 } +}; + +#ifdef INET6 +struct protox ip6protox[] = { + { protopr, tcp_stats, NULL, "tcp", IPPROTO_TCP }, + { protopr, udp_stats, NULL, "udp", IPPROTO_UDP }, + { protopr, ip6_stats, ip6_ifstats, "ip6", IPPROTO_RAW }, + { protopr, icmp6_stats, icmp6_ifstats, "icmp6",IPPROTO_ICMPV6 }, +#ifdef IPSEC + { NULL, ipsec_stats, NULL, "ipsec6", IPPROTO_ESP }, +#endif + { NULL, rip6_stats, NULL, "rip6", IPPROTO_RAW }, + { mptcppr, mptcp_stats, NULL, "mptcp", IPPROTO_TCP }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif /*INET6*/ + +#ifdef IPSEC +struct protox pfkeyprotox[] = { + { NULL, pfkey_stats, NULL, "pfkey", PF_KEY_V2 }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif + + +struct protox systmprotox[] = { + { systmpr, NULL, NULL, "reg", 0 }, + { systmpr, kevt_stats, NULL, "kevt", SYSPROTO_EVENT }, + { systmpr, kctl_stats, NULL, "kctl", SYSPROTO_CONTROL }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox nstatprotox[] = { + { NULL, print_nstat_stats, NULL, "nstat", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox ipcprotox[] = { + { NULL, print_extbkidle_stats, NULL, "xbkidle", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +struct protox kernprotox[] = { + { NULL, print_net_api_stats, NULL, "net_api", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +#ifdef AF_VSOCK +struct protox vsockprotox[] = { + { vsockpr, NULL, NULL, "vsock", 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; +#endif + +struct protox *protoprotox[] = { + protox, +#ifdef INET6 + ip6protox, +#endif +#ifdef IPSEC + pfkeyprotox, +#endif + systmprotox, + nstatprotox, + ipcprotox, + kernprotox, +#ifdef AF_VSOCK + vsockprotox, +#endif + NULL +}; + +static void printproto (struct protox *, char *); +static void usage (void); +static struct protox *name2protox (char *); +static struct protox *knownname (char *); +#ifdef SRVCACHE +extern void _serv_cache_close(); +#endif + +int Aflag; /* show addresses of protocol control block */ +int aflag; /* show all sockets (including servers) */ +int bflag; /* show i/f total bytes in/out */ +int cflag; /* show specific classq */ +int dflag; /* show i/f dropped packets */ +int Fflag; /* show i/f forwarded packets */ +#if defined(__APPLE__) +int gflag; /* show group (multicast) routing or stats */ +#endif +int iflag; /* show interfaces */ +int lflag; /* show routing table with more information */ +int Lflag; /* show size of listen queues */ +int mflag; /* show memory stats */ +int nflag; /* show addresses numerically */ +static int pflag; /* show given protocol */ +int prioflag = -1; /* show packet priority statistics */ +int Rflag; /* show reachability information */ +int rflag; /* show routing tables (or routing stats) */ +int sflag; /* show protocol statistics */ +int Sflag; /* show additional i/f link status */ +int tflag; /* show i/f watchdog timers */ +int vflag; /* more verbose */ +int Wflag; /* wide display */ +int qflag; /* classq stats display */ +int Qflag; /* opportunistic polling stats display */ +int xflag; /* show extended link-layer reachability information */ +int zflag; /* show only entries with non zero rtt metrics */ + +int cq = -1; /* send classq index (-1 for all) */ +int interval; /* repeat interval for i/f stats */ + +char *interface; /* desired i/f for stats, or NULL for all i/fs */ +int unit; /* unit number for above */ + +int af; /* address family */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct protox *tp = NULL; /* for printing cblocks & stats */ + int ch; + + af = AF_UNSPEC; + + while ((ch = getopt(argc, argv, "Aabc:dFf:gI:ikLlmnP:p:qQrRsStuvWw:xz")) != -1) + switch(ch) { + case 'A': + Aflag = 1; + break; + case 'a': + aflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'c': + cflag = 1; + cq = atoi(optarg); + break; + case 'd': + dflag = 1; + break; + case 'F': + Fflag = 1; + break; + case 'f': + if (strcmp(optarg, "ipx") == 0) + af = AF_IPX; + else if (strcmp(optarg, "inet") == 0) + af = AF_INET; +#ifdef INET6 + else if (strcmp(optarg, "inet6") == 0) + af = AF_INET6; +#endif /*INET6*/ +#ifdef INET6 + else if (strcmp(optarg, "pfkey") == 0) + af = PF_KEY; +#endif /*INET6*/ + else if (strcmp(optarg, "unix") == 0) + af = AF_UNIX; + else if (strcmp(optarg, "systm") == 0) + af = AF_SYSTEM; + else { + errx(1, "%s: unknown address family", optarg); + } + break; +#if defined(__APPLE__) + case 'g': + gflag = 1; + break; +#endif + case 'I': { + char *cp; + + iflag = 1; + for (cp = interface = optarg; isalpha(*cp); cp++) + continue; + unit = atoi(cp); + break; + } + case 'i': + iflag = 1; + break; + case 'l': + lflag += 1; + break; + case 'L': + Lflag = 1; + break; + case 'm': + mflag++; + break; + case 'n': + nflag = 1; + break; + case 'P': + prioflag = atoi(optarg); + break; + case 'p': + if ((tp = name2protox(optarg)) == NULL) { + errx(1, + "%s: unknown or uninstrumented protocol", + optarg); + } + pflag = 1; + break; + case 'q': + qflag++; + break; + case 'Q': + Qflag++; + break; + case 'R': + Rflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + ++sflag; + break; + case 'S': + Sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'u': + af = AF_UNIX; + break; + case 'v': + vflag++; + break; + case 'W': + Wflag = 1; + break; + case 'w': + interval = atoi(optarg); + iflag = 1; + break; + case 'x': + xflag = 1; + Rflag = 1; + break; + case 'z': + zflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + if (isdigit(**argv)) { + interval = atoi(*argv); + if (interval <= 0) + usage(); + ++argv; + iflag = 1; + } + } +#endif + + if (mflag) { + mbpr(); + exit(0); + } + if (iflag && !sflag && !Sflag && !gflag && !qflag && !Qflag) { + if (Rflag) + intpr_ri(NULL); + else + intpr(NULL); + exit(0); + } + if (rflag) { + if (sflag) + rt_stats(); + else + routepr(); + exit(0); + } + if (qflag || Qflag) { + if (interface == NULL) { + fprintf(stderr, "%s statistics option " + "requires interface name\n", qflag ? "Queue" : + "Polling"); + } else if (qflag) { + aqstatpr(); + } else { + rxpollstatpr(); + } + exit(0); + } + if (Sflag) { + if (interface == NULL) { + fprintf(stderr, "additional link status option" + " requires interface name\n"); + } else { + print_link_status(interface); + } + exit(0); + } + +#if defined(__APPLE__) + if (gflag) { + ifmalist_dump(); + exit(0); + } +#endif + + if (tp) { + printproto(tp, tp->pr_name); + exit(0); + } + if (af == AF_INET || af == AF_UNSPEC) + for (tp = protox; tp->pr_name; tp++) + 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 /*INET6*/ +#ifdef IPSEC + if (af == PF_KEY || af == AF_UNSPEC) + for (tp = pfkeyprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /*IPSEC*/ + if ((af == AF_UNIX || af == AF_UNSPEC) && !Lflag && !sflag) + unixpr(); + + if ((af == AF_SYSTEM || af == AF_UNSPEC) && !Lflag) + for (tp = systmprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#if TARGET_OS_IPHONE + if (af == AF_UNSPEC && !Lflag) + for (tp = nstatprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /* TARGET_OS_IPHONE */ + + if (af == AF_UNSPEC && !Lflag) + for (tp = ipcprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); + + if (af == AF_UNSPEC && !Lflag) + for (tp = kernprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); + +#ifdef AF_VSOCK + if (af == AF_VSOCK || af == AF_UNSPEC) + for (tp = vsockprotox; tp->pr_name; tp++) + printproto(tp, tp->pr_name); +#endif /*AF_VSOCK*/ + +#ifdef SRVCACHE + _serv_cache_close(); +#endif + 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(tp, name) + register struct protox *tp; + char *name; +{ + void (*pr)(uint32_t, char *, int); + uint32_t off; + + if (sflag) { + if (iflag && !pflag) { + if (tp->pr_istats) + intpr(tp->pr_istats); + else if (vflag) + printf("%s: no per-interface stats routine\n", + tp->pr_name); + return; + } + else { + pr = tp->pr_stats; + if (!pr) { + if (pflag && vflag) + printf("%s: no stats routine\n", + tp->pr_name); + return; + } + off = tp->pr_protocol; + } + } else { + pr = tp->pr_cblocks; + if (!pr) { + if (pflag && vflag) + printf("%s: no PCB routine\n", tp->pr_name); + return; + } + off = tp->pr_protocol; + } + if (pr != NULL) { + if (sflag && iflag && pflag) + intervalpr(pr, off, name, af); + else + (*pr)(off, name, af); + } else { + printf("### no stats for %s\n", name); + } +} + +char * +plural(int n) +{ + return (n > 1 ? "s" : ""); +} + +char * +plurales(int n) +{ + return (n > 1 ? "es" : ""); +} + +char * +pluralies(int n) +{ + return (n > 1 ? "ies" : "y"); +} + +/* + * Find the protox for the given "well-known" name. + */ +static struct protox * +knownname(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(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); +} + +#define NETSTAT_USAGE "\ +Usage: netstat [-AaLlnW] [-f address_family | -p protocol]\n\ + netstat [-gilns] [-f address_family]\n\ + netstat -i | -I interface [-w wait] [-abdgRtS]\n\ + netstat -s [-s] [-f address_family | -p protocol] [-w wait]\n\ + netstat -i | -I interface -s [-f address_family | -p protocol]\n\ + netstat -m [-m]\n\ + netstat -r [-Aaln] [-f address_family]\n\ + netstat -rs [-s]\n\ +" + +static void +usage(void) +{ + (void) fprintf(stderr, "%s\n", NETSTAT_USAGE); + exit(1); +} + +int +print_time(void) +{ + time_t now; + struct tm tm; + int num_written = 0; + + (void) time(&now); + (void) localtime_r(&now, &tm); + + num_written += printf("%02d:%02d:%02d ", tm.tm_hour, tm.tm_min, tm.tm_sec); + + return (num_written); +} + diff --git a/network_cmds/netstat.tproj/mbuf.c b/network_cmds/netstat.tproj/mbuf.c new file mode 100644 index 0000000..38645e9 --- /dev/null +++ b/network_cmds/netstat.tproj/mbuf.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * 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. 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. + */ + + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include "netstat.h" + +#define YES 1 +typedef int bool; + +struct mbstat mbstat; + +static struct mbtypes { + int mt_type; + char *mt_name; +} mbtypes[] = { + { MT_DATA, "data" }, + { MT_OOBDATA, "oob data" }, + { MT_CONTROL, "ancillary data" }, + { MT_HEADER, "packet headers" }, + { MT_SOCKET, "socket structures" }, /* XXX */ + { MT_PCB, "protocol control blocks" }, /* XXX */ + { MT_RTABLE, "routing table entries" }, /* XXX */ + { MT_HTABLE, "IMP host table entries" }, /* XXX */ + { MT_ATABLE, "address resolution tables" }, + { MT_FTABLE, "fragment reassembly queue headers" }, /* XXX */ + { MT_SONAME, "socket names and addresses" }, + { MT_SOOPTS, "socket options" }, + { MT_RIGHTS, "access rights" }, + { MT_IFADDR, "interface addresses" }, /* XXX */ + { MT_TAG, "packet tags" }, /* XXX */ + { 0, 0 } +}; + +int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short); +bool seen[256]; /* "have we seen this type yet?" */ + +mb_stat_t *mb_stat; +unsigned int njcl, njclbytes; +mleak_stat_t *mleak_stat; +struct mleak_table table; + +#define KERN_IPC_MB_STAT "kern.ipc.mb_stat" +#define KERN_IPC_NJCL "kern.ipc.njcl" +#define KERN_IPC_NJCL_BYTES "kern.ipc.njclbytes" +#define KERN_IPC_MLEAK_TABLE "kern.ipc.mleak_table" +#define KERN_IPC_MLEAK_TOP_TRACE "kern.ipc.mleak_top_trace" + +#define MB_STAT_HDR1 "\ +class buf active ctotal total cache cached uncached memory\n\ +name size bufs bufs bufs state bufs bufs usage\n\ +---------- ----- -------- -------- -------- ----- -------- -------- ---------\n\ +" + +#define MB_STAT_HDR2 "\n\ +class waiter notify purge wretry nwretry failure\n\ +name count count count count count count\n\ +---------- -------- -------- -------- -------- -------- --------\n\ +" + +#define MB_LEAK_HDR "\n\ + calltrace [1] calltrace [2] calltrace [3] calltrace [4] calltrace [5] \n\ + ------------------ ------------------ ------------------ ------------------ ------------------ \n\ +" + +#define MB_LEAK_SPACING " " +static const char *mbpr_state(int); +static const char *mbpr_mem(u_int32_t); +static int mbpr_getdata(void); + +/* + * Print mbuf statistics. + */ +void +mbpr(void) +{ + unsigned long totmem = 0, totfree = 0, totmbufs, totused, totreturned = 0; + double totpct; + u_int32_t m_msize, m_mbufs = 0, m_clfree = 0, m_bigclfree = 0; + u_int32_t m_mbufclfree = 0, m_mbufbigclfree = 0; + u_int32_t m_16kclusters = 0, m_16kclfree = 0, m_mbuf16kclfree = 0; + int i; + struct mbtypes *mp; + mb_class_stat_t *cp; + + if (mbpr_getdata() != 0) + return; + + m_msize = mbstat.m_msize; + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + if (cp->mbcl_size == m_msize) { + m_mbufs = cp->mbcl_active; + } else if (cp->mbcl_size == mbstat.m_mclbytes) { + m_clfree = cp->mbcl_total - cp->mbcl_active; + } else if (cp->mbcl_size == mbstat.m_bigmclbytes) { + m_bigclfree = cp->mbcl_total - cp->mbcl_active; + } else if (njcl > 0 && cp->mbcl_size == njclbytes) { + m_16kclfree = cp->mbcl_total - cp->mbcl_active; + m_16kclusters = cp->mbcl_total; + } else if (cp->mbcl_size == (m_msize + mbstat.m_mclbytes)) { + m_mbufclfree = cp->mbcl_total - cp->mbcl_active; + } else if (cp->mbcl_size == (m_msize + mbstat.m_bigmclbytes)) { + m_mbufbigclfree = cp->mbcl_total - cp->mbcl_active; + } else if (njcl > 0 && cp->mbcl_size == (m_msize + njclbytes)) { + m_mbuf16kclfree = cp->mbcl_total - cp->mbcl_active; + } + } + + /* adjust free counts to include composite caches */ + m_clfree += m_mbufclfree; + m_bigclfree += m_mbufbigclfree; + m_16kclfree += m_mbuf16kclfree; + + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + u_int32_t mem; + + mem = cp->mbcl_ctotal * cp->mbcl_size; + totmem += mem; + totreturned += cp->mbcl_release_cnt; + totfree += (cp->mbcl_mc_cached + cp->mbcl_infree) * + cp->mbcl_size; + if (mflag > 1) { + if (i == 0) + printf(MB_STAT_HDR1); + + if (njcl == 0 && + cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) + continue; + + printf("%-10s %5u %8u %8u %8u %5s %8u %8u %9s\n", + cp->mbcl_cname, cp->mbcl_size, cp->mbcl_active, + cp->mbcl_ctotal, cp->mbcl_total, + mbpr_state(cp->mbcl_mc_state), cp->mbcl_mc_cached, + cp->mbcl_infree, mbpr_mem(mem)); + } + } + + cp = &mb_stat->mbs_class[0]; + for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { + if (mflag > 2) { + if (i == 0) + printf(MB_STAT_HDR2); + + if (njcl == 0 && + cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) + continue; + + printf("%-10s %8u %8llu %8llu %8u %8u %8llu\n", + cp->mbcl_cname, cp->mbcl_mc_waiter_cnt, + cp->mbcl_notified, cp->mbcl_purge_cnt, + cp->mbcl_mc_wretry_cnt, cp->mbcl_mc_nwretry_cnt, + cp->mbcl_fail_cnt); + } + } + + if (mflag > 1) + printf("\n"); + + totmbufs = 0; + for (mp = mbtypes; mp->mt_name; mp++) + totmbufs += mbstat.m_mtypes[mp->mt_type]; + /* + * These stats are not updated atomically in the kernel; + * adjust the total as neeeded. + */ + if (totmbufs > m_mbufs) + totmbufs = m_mbufs; + printf("%lu/%u mbufs in use:\n", totmbufs, m_mbufs); + 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 <mbuf type %d>\n", + mbstat.m_mtypes[i], i); + } + if ((m_mbufs - totmbufs) > 0) + printf("\t%lu mbufs allocated to caches\n", + m_mbufs - totmbufs); + printf("%u/%u mbuf 2KB clusters in use\n", + (unsigned int)(mbstat.m_clusters - m_clfree), + (unsigned int)mbstat.m_clusters); + printf("%u/%u mbuf 4KB clusters in use\n", + (unsigned int)(mbstat.m_bigclusters - m_bigclfree), + (unsigned int)mbstat.m_bigclusters); + if (njcl > 0) { + printf("%u/%u mbuf %uKB clusters in use\n", + m_16kclusters - m_16kclfree, m_16kclusters, + njclbytes/1024); + } + totused = totmem - totfree; + if (totmem == 0) + totpct = 0; + else if (totused < (ULONG_MAX/100)) + totpct = (totused * 100)/(double)totmem; + else { + u_long totmem1 = totmem/100; + u_long totused1 = totused/100; + totpct = (totused1 * 100)/(double)totmem1; + } + printf("%lu KB allocated to network (%.1f%% in use)\n", + totmem / 1024, totpct); + printf("%lu KB returned to the system\n", totreturned / 1024); + + printf("%u requests for memory denied\n", (unsigned int)mbstat.m_drops); + printf("%u requests for memory delayed\n", (unsigned int)mbstat.m_wait); + printf("%u calls to drain routines\n", (unsigned int)mbstat.m_drain); + + free(mb_stat); + mb_stat = NULL; + + if (mleak_stat != NULL) { + mleak_trace_stat_t *mltr; + + printf("\nmbuf leak detection table:\n"); + printf("\ttotal captured: %u (one per %u)\n" + "\ttotal allocs outstanding: %llu\n" + "\tnew hash recorded: %llu allocs, %llu traces\n" + "\thash collisions: %llu allocs, %llu traces\n" + "\toverwrites: %llu allocs, %llu traces\n" + "\tlock conflicts: %llu\n\n", + table.mleak_capture / table.mleak_sample_factor, + table.mleak_sample_factor, + table.outstanding_allocs, + table.alloc_recorded, table.trace_recorded, + table.alloc_collisions, table.trace_collisions, + table.alloc_overwrites, table.trace_overwrites, + table.total_conflicts); + + printf("top %d outstanding traces:\n", mleak_stat->ml_cnt); + for (i = 0; i < mleak_stat->ml_cnt; i++) { + mltr = &mleak_stat->ml_trace[i]; + printf("[%d] %llu outstanding alloc(s), " + "%llu hit(s), %llu collision(s)\n", (i + 1), + mltr->mltr_allocs, mltr->mltr_hitcount, + mltr->mltr_collisions); + } + + printf(MB_LEAK_HDR); + for (i = 0; i < MLEAK_STACK_DEPTH; i++) { + int j; + + printf("%2d: ", (i + 1)); + for (j = 0; j < mleak_stat->ml_cnt; j++) { + mltr = &mleak_stat->ml_trace[j]; + if (i < mltr->mltr_depth) { + if (mleak_stat->ml_isaddr64) { + printf("0x%0llx ", + mltr->mltr_addr[i]); + } else { + printf("0x%08x ", + (u_int32_t)mltr->mltr_addr[i]); + } + } else { + printf(MB_LEAK_SPACING); + } + } + printf("\n"); + } + free(mleak_stat); + mleak_stat = NULL; + } +} + +static const char * +mbpr_state(int state) +{ + char *msg = "?"; + + switch (state) { + case MCS_DISABLED: + msg = "dis"; + break; + + case MCS_ONLINE: + msg = "on"; + break; + + case MCS_PURGING: + msg = "purge"; + break; + + case MCS_OFFLINE: + msg = "off"; + break; + } + return (msg); +} + +static const char * +mbpr_mem(u_int32_t bytes) +{ + static char buf[33]; + double mem = bytes; + + if (mem < 1024) { + (void) snprintf(buf, sizeof (buf), "%d", (int)mem); + } else if ((mem /= 1024) < 1024) { + (void) snprintf(buf, sizeof (buf), "%.1f KB", mem); + } else { + mem /= 1024; + (void) snprintf(buf, sizeof (buf), "%.1f MB", mem); + } + return (buf); +} + +static int +mbpr_getdata(void) +{ + size_t len; + int error = -1; + + if (nmbtypes != 256) { + (void) fprintf(stderr, + "netstat: unexpected change to mbstat; check source\n"); + goto done; + } + + len = sizeof(mbstat); + if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1) + goto done; + + if (sysctlbyname(KERN_IPC_MB_STAT, NULL, &len, 0, 0) == -1) { + (void) fprintf(stderr, + "Error retrieving length for %s\n", KERN_IPC_MB_STAT); + goto done; + } + + mb_stat = calloc(1, len); + if (mb_stat == NULL) { + (void) fprintf(stderr, + "Error allocating %lu bytes for sysctl data\n", len); + goto done; + } + + if (sysctlbyname(KERN_IPC_MB_STAT, mb_stat, &len, 0, 0) == -1) { + (void) fprintf(stderr, + "Error %d getting %s\n", errno, KERN_IPC_MB_STAT); + goto done; + } + + if (mb_stat->mbs_cnt == 0) { + (void) fprintf(stderr, + "Invalid mbuf class count (%d)\n", mb_stat->mbs_cnt); + goto done; + } + + /* mbuf leak detection! */ + if (mflag > 3) { + errno = 0; + len = sizeof (table); + if (sysctlbyname(KERN_IPC_MLEAK_TABLE, &table, &len, 0, 0) == + -1 && errno != ENXIO) { + (void) fprintf(stderr, "error %d getting %s\n", errno, + KERN_IPC_MLEAK_TABLE); + goto done; + } else if (errno == ENXIO) { + (void) fprintf(stderr, "mbuf leak detection is not " + "enabled in the kernel.\n"); + goto skip; + } + + if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, NULL, &len, + 0, 0) == -1) { + (void) fprintf(stderr, "Error retrieving length for " + "%s: %d\n", KERN_IPC_MB_STAT, errno); + goto done; + } + + mleak_stat = calloc(1, len); + if (mleak_stat == NULL) { + (void) fprintf(stderr, "Error allocating %lu bytes " + "for sysctl data\n", len); + goto done; + } + + if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, mleak_stat, &len, + 0, 0) == -1) { + (void) fprintf(stderr, "error %d getting %s\n", errno, + KERN_IPC_MLEAK_TOP_TRACE); + goto done; + } + } + +skip: + len = sizeof (njcl); + (void) sysctlbyname(KERN_IPC_NJCL, &njcl, &len, 0, 0); + len = sizeof (njclbytes); + (void) sysctlbyname(KERN_IPC_NJCL_BYTES, &njclbytes, &len, 0, 0); + + error = 0; + +done: + if (error != 0 && mb_stat != NULL) { + free(mb_stat); + mb_stat = NULL; + } + + if (error != 0 && mleak_stat != NULL) { + free(mleak_stat); + mleak_stat = NULL; + } + + return (error); +} diff --git a/network_cmds/netstat.tproj/mcast.c b/network_cmds/netstat.tproj/mcast.c new file mode 100644 index 0000000..3637f4d --- /dev/null +++ b/network_cmds/netstat.tproj/mcast.c @@ -0,0 +1,887 @@ +/* + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 2007 Bruce M. Simpson <bms@FreeBSD.org> + * 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. + * + */ + +#include <sys/cdefs.h> + +/* + * Print the running system's current multicast group memberships. + * As this relies on getifmaddrs(), it may not be used with a core file. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netinet/igmp_var.h> +#include <netinet6/mld6_var.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <ctype.h> +#include <err.h> +#include <ifaddrs.h> +#include <sysexits.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <ifaddrs.h> + + +#include "netstat.h" + +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +typedef union sockunion sockunion_t; + +/* + * This may have been defined in <net/if.h>. Note that if <net/if.h> is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +//struct ifmaddrs { +// struct ifmaddrs *ifma_next; +// struct sockaddr *ifma_name; +// struct sockaddr *ifma_addr; +// struct sockaddr *ifma_lladdr; +//}; + +void ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af); +static int ifmalist_dump_mcstat(struct ifmaddrs *); +static void in_ifinfo(struct igmp_ifinfo *); +static const char *inm_mode(u_int); +static void inm_print_sources_sysctl(uint32_t, struct in_addr); +#ifdef INET6 +static void in6_ifinfo(struct mld_ifinfo *); +static void in6m_print_sources_sysctl(uint32_t, struct in6_addr *); +static const char *inet6_n2a(struct in6_addr *); +#endif +static void printb(const char *, unsigned int, const char *); +static const char *sdl_addr_to_hex(const struct sockaddr_dl *, char *, int); + +extern char *routename6(struct sockaddr_in6 *); + +#define sa_equal(a1, a2) \ + (bcmp((a1), (a2), ((a1))->sa_len) == 0) + +#define sa_dl_equal(a1, a2) \ + ((((struct sockaddr_dl *)(a1))->sdl_len == \ + ((struct sockaddr_dl *)(a2))->sdl_len) && \ + (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \ + LLADDR((struct sockaddr_dl *)(a2)), \ + ((struct sockaddr_dl *)(a1))->sdl_alen) == 0)) + +#define SALIGN (sizeof(uint32_t) - 1) +#define SA_RLEN(sa) (sa ? ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ + (SALIGN + 1)) : 0) +#define MAX_SYSCTL_TRY 5 +#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) + +void +ifmalist_dump_af(const struct ifmaddrs * const ifmap, int const af) +{ + const struct ifmaddrs *ifma; + sockunion_t *psa; + char myifname[IFNAMSIZ]; + char *pcolon; + char *pafname, *pifname, *plladdr = NULL, *pgroup = NULL; + + switch (af) { + case AF_INET: + pafname = "IPv4"; + break; +#ifdef INET6 + case AF_INET6: + pafname = "IPv6"; + break; +#endif + case AF_LINK: + pafname = "Link-layer"; + break; + default: + return; /* XXX */ + } + + fprintf(stdout, "%s Multicast Group Memberships\n", pafname); + fprintf(stdout, "%-20s\t%-16s\t%s\n", "Group", "Link-layer Address", + "Netif"); + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + + /* Group address */ + psa = (sockunion_t *)ifma->ifma_addr; + if (psa->sa.sa_family != af) + continue; + + switch (psa->sa.sa_family) { + case AF_INET: + pgroup = inet_ntoa(psa->sin.sin_addr); + break; +#ifdef INET6 + case AF_INET6: + pgroup = routename6(&(psa->sin6)); + break; +#endif + case AF_LINK: + if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || + (psa->sdl.sdl_type == IFT_ETHER)) { + pgroup = +ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); +#ifdef notyet + } else { + pgroup = addr2ascii(AF_LINK, + &psa->sdl, + sizeof(struct sockaddr_dl), + addrbuf); +#endif + } + break; + default: + continue; /* XXX */ + } + + /* Link-layer mapping, if any */ + psa = (sockunion_t *)ifma->ifma_lladdr; + if (psa != NULL) { + if (psa->sa.sa_family == AF_LINK) { + if ((psa->sdl.sdl_alen == ETHER_ADDR_LEN) || + (psa->sdl.sdl_type == IFT_ETHER)) { + /* IEEE 802 */ + plladdr = +ether_ntoa((struct ether_addr *)&psa->sdl.sdl_data); +#ifdef notyet + } else { + /* something more exotic */ + plladdr = addr2ascii(AF_LINK, + &psa->sdl, + sizeof(struct sockaddr_dl), + addrbuf); +#endif + } + } else { + int i; + + /* not a link-layer address */ + plladdr = "<invalid>"; + + for (i = 0; psa->sa.sa_len > 2 && i < psa->sa.sa_len - 2; i++) + printf("0x%x ", psa->sa.sa_data[i]); + printf("\n"); + } + } else { + plladdr = "<none>"; + } + + /* Interface upon which the membership exists */ + psa = (sockunion_t *)ifma->ifma_name; + if (psa != NULL && psa->sa.sa_family == AF_LINK) { + strlcpy(myifname, link_ntoa(&psa->sdl), sizeof(myifname)); + pcolon = strchr(myifname, ':'); + if (pcolon) + *pcolon = '\0'; + pifname = myifname; + } else { + pifname = ""; + } + + fprintf(stdout, "%-20s\t%-16s\t%s\n", pgroup, plladdr, pifname); + } +} + +void +ifmalist_dump(void) +{ + struct ifmaddrs *ifmap; + + if (getifmaddrs(&ifmap)) + err(EX_OSERR, "getifmaddrs"); + + ifmalist_dump_af(ifmap, AF_LINK); + fputs("\n", stdout); + ifmalist_dump_af(ifmap, AF_INET); +#ifdef INET6 + fputs("\n", stdout); + ifmalist_dump_af(ifmap, AF_INET6); +#endif + if (sflag) { + fputs("\n", stdout); + ifmalist_dump_mcstat(ifmap); + } + + freeifmaddrs(ifmap); +} + +static int +ifmalist_dump_mcstat(struct ifmaddrs *ifmap) +{ + char thisifname[IFNAMSIZ]; + char addrbuf[NI_MAXHOST]; + struct ifaddrs *ifap, *ifa; + struct ifmaddrs *ifma; + sockunion_t lastifasa; + sockunion_t *psa, *pgsa, *pllsa, *pifasa; + char *pcolon; + char *pafname; + uint32_t lastifindex, thisifindex; + int error; + uint32_t ifindex = 0; + + if (interface != NULL) + ifindex = if_nametoindex(interface); + + error = 0; + ifap = NULL; + lastifindex = 0; + thisifindex = 0; + lastifasa.ss.ss_family = AF_UNSPEC; + + if (getifaddrs(&ifap) != 0) { + warn("getifmaddrs"); + return (-1); + } + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + error = 0; + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + + psa = (sockunion_t *)ifma->ifma_name; + if (psa->sa.sa_family != AF_LINK) { + fprintf(stderr, + "WARNING: Kernel returned invalid data.\n"); + error = -1; + break; + } + + /* Filter on interface name. */ + thisifindex = psa->sdl.sdl_index; + if (ifindex != 0 && thisifindex != ifindex) + continue; + + /* Filter on address family. */ + pgsa = (sockunion_t *)ifma->ifma_addr; + if (af != 0 && pgsa->sa.sa_family != af) + continue; + + strlcpy(thisifname, link_ntoa(&psa->sdl), sizeof(thisifname)); + pcolon = strchr(thisifname, ':'); + if (pcolon) + *pcolon = '\0'; + + /* Only print the banner for the first ifmaddrs entry. */ + if (lastifindex == 0 || lastifindex != thisifindex) { + lastifindex = thisifindex; + fprintf(stdout, "%s:\n", thisifname); + } + + /* + * Currently, multicast joins only take place on the + * primary IPv4 address, and only on the link-local IPv6 + * address, as per IGMPv2/3 and MLDv1/2 semantics. + * Therefore, we only look up the primary address on + * the first pass. + */ + pifasa = NULL; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if ((strcmp(ifa->ifa_name, thisifname) != 0) || + (ifa->ifa_addr == NULL) || + (ifa->ifa_addr->sa_family != pgsa->sa.sa_family)) + continue; + /* + * For AF_INET6 only the link-local address should + * be returned. If built without IPv6 support, + * skip this address entirely. + */ + pifasa = (sockunion_t *)ifa->ifa_addr; + if (pifasa->sa.sa_family == AF_INET6 +#ifdef INET6 + && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr) +#endif + ) { + pifasa = NULL; + continue; + } + break; + } + if (pifasa == NULL) + continue; /* primary address not found */ + + if (!vflag && pifasa->sa.sa_family == AF_LINK) + continue; + + /* Parse and print primary address, if not already printed. */ + if (lastifasa.ss.ss_family == AF_UNSPEC || + ((lastifasa.ss.ss_family == AF_LINK && + !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) || + !sa_equal(&lastifasa.sa, &pifasa->sa))) { + + switch (pifasa->sa.sa_family) { + case AF_INET: + pafname = "inet"; + break; + case AF_INET6: + pafname = "inet6"; + break; + case AF_LINK: + pafname = "link"; + break; + default: + pafname = "unknown"; + break; + } + + switch (pifasa->sa.sa_family) { + case AF_INET6: +#ifdef INET6 + { + const char *p = + inet6_n2a(&pifasa->sin6.sin6_addr); + strlcpy(addrbuf, p, sizeof(addrbuf)); + break; + } +#else + /* FALLTHROUGH */ +#endif + case AF_INET: + error = getnameinfo(&pifasa->sa, + pifasa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST); + if (error) + printf("getnameinfo: %s\n", + gai_strerror(error)); + break; + case AF_LINK: { + (void) sdl_addr_to_hex(&pifasa->sdl, addrbuf, + sizeof (addrbuf)); + break; + } + default: + addrbuf[0] = '\0'; + break; + } + + fprintf(stdout, "\t%s %s\n", pafname, addrbuf); + /* + * Print per-link IGMP information, if available. + */ + if (pifasa->sa.sa_family == AF_INET) { + struct igmp_ifinfo igi; + size_t mibsize, len; + int mib[5]; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.igmp.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct igmp_ifinfo); + if (sysctl(mib, mibsize + 1, &igi, &len, NULL, + 0) == -1) { + perror("sysctl net.inet.igmp.ifinfo"); + goto next_ifnet; + } + in_ifinfo(&igi); + } +#ifdef INET6 + /* + * Print per-link MLD information, if available. + */ + if (pifasa->sa.sa_family == AF_INET6) { + struct mld_ifinfo mli; + size_t mibsize, len; + int mib[5]; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet6.mld.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct mld_ifinfo); + if (sysctl(mib, mibsize + 1, &mli, &len, NULL, + 0) == -1) { + perror("sysctl net.inet6.mld.ifinfo"); + goto next_ifnet; + } + in6_ifinfo(&mli); + } +#endif /* INET6 */ +#if defined(INET6) +next_ifnet: +#endif + lastifasa = *pifasa; + } + + /* Print this group address. */ +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + const char *p = inet6_n2a(&pgsa->sin6.sin6_addr); + strlcpy(addrbuf, p, sizeof(addrbuf)); + } else +#endif + if (pgsa->sa.sa_family == AF_INET) { + error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + if (error) + printf("getnameinfo: %s\n", + gai_strerror(error)); + } else { + (void) sdl_addr_to_hex(&pgsa->sdl, addrbuf, + sizeof (addrbuf)); + } + + fprintf(stdout, "\t\tgroup %s", addrbuf); + if (pgsa->sa.sa_family == AF_INET) { + inm_print_sources_sysctl(thisifindex, + pgsa->sin.sin_addr); + } +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + in6m_print_sources_sysctl(thisifindex, + &pgsa->sin6.sin6_addr); + } +#endif + fprintf(stdout, "\n"); + + /* Link-layer mapping, if present. */ + pllsa = (sockunion_t *)ifma->ifma_lladdr; + if (pllsa != NULL) { + (void) sdl_addr_to_hex(&pllsa->sdl, addrbuf, + sizeof (addrbuf)); + fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf); + } + } + + if (ifap != NULL) + freeifaddrs(ifap); + + return (error); +} + +static void +in_ifinfo(struct igmp_ifinfo *igi) +{ + + printf("\t"); + switch (igi->igi_version) { + case IGMP_VERSION_1: + case IGMP_VERSION_2: + case IGMP_VERSION_3: + printf("igmpv%d", igi->igi_version); + break; + default: + printf("igmpv?(%d)", igi->igi_version); + break; + } + printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); + if (igi->igi_version == IGMP_VERSION_3) { + printf(" rv %u qi %u qri %u uri %u", + igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u v3timer %u", + igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); + } + printf("\n"); +} + +static const char *inm_modes[] = { + "undefined", + "include", + "exclude", +}; + +static const char * +inm_mode(u_int mode) +{ + + if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) + return (inm_modes[mode]); + return (NULL); +} + +/* + * Retrieve per-group source filter mode and lists via sysctl. + */ +static void +inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) +{ +#define MAX_SYSCTL_TRY 5 + int mib[7]; + int ntry = 0; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + mib[6] = gina.s_addr; /* 32 bits wide */ + mibsize = sizeof(mib) / sizeof(mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet.ip.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in_addr); + pina = (struct in_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), + inet_ntoa(*pina++)); + len -= sizeof(struct in_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet.ip.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +#ifdef INET6 + +static void +in6_ifinfo(struct mld_ifinfo *mli) +{ + + printf("\t"); + switch (mli->mli_version) { + case MLD_VERSION_1: + case MLD_VERSION_2: + printf("mldv%d", mli->mli_version); + break; + default: + printf("mldv?(%d)", mli->mli_version); + break; + } + printb(" flags", mli->mli_flags, "\020\1SILENT"); + if (mli->mli_version == MLD_VERSION_2) { + printf(" rv %u qi %u qri %u uri %u", + mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u", mli->mli_v1_timer, + mli->mli_v2_timer); + } + printf("\n"); +} + +/* + * Retrieve MLD per-group source filter mode and lists via sysctl. + * + * Note: The 128-bit IPv6 group addres needs to be segmented into + * 32-bit pieces for marshaling to sysctl. So the MIB name ends + * up looking like this: + * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3] + * Assumes that pgroup originated from the kernel, so its components + * are already in network-byte order. + */ +static void +in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup) +{ +#define MAX_SYSCTL_TRY 5 + char addrbuf[INET6_ADDRSTRLEN]; + int mib[10]; + int ntry = 0; + int *pi; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in6_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = sizeof(mib) / sizeof(mib[0]); + if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib, + &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + pi = (int *)pgroup; + for (i = 0; i < 4; i++) + mib[6 + i] = *pi++; + + mibsize = sizeof(mib) / sizeof(mib[0]); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet6.ip6.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in6_addr); + pina = (struct in6_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + inet_ntop(AF_INET6, (const char *)pina++, addrbuf, + INET6_ADDRSTRLEN); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf); + len -= sizeof(struct in6_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet6.ip6.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +static const char * +inet6_n2a(struct in6_addr *p) +{ + static char buf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + u_int32_t scopeid; + const int niflags = NI_NUMERICHOST; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *p; + if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) || + IN6_IS_ADDR_MC_NODELOCAL(p)) { + scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); + if (scopeid) { + sin6.sin6_scope_id = scopeid; + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } + } + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + buf, sizeof(buf), NULL, 0, niflags) == 0) { + return (buf); + } else { + return ("(invalid)"); + } +} +#endif /* INET6 */ + +/* + * Print a value a la the %b format of the kernel's printf + */ +void +printb(const char *s, unsigned int v, const char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +/* + * convert hardware address to hex string for logging errors. + */ +static const char * +sdl_addr_to_hex(const struct sockaddr_dl *sdl, char *orig_buf, int buflen) +{ + char *buf = orig_buf; + int i; + const u_char *lladdr; + int maxbytes = buflen / 3; + + lladdr = (u_char *)(size_t)sdl->sdl_data + sdl->sdl_nlen; + + if (maxbytes > sdl->sdl_alen) { + maxbytes = sdl->sdl_alen; + } + *buf = '\0'; + for (i = 0; i < maxbytes; i++) { + snprintf(buf, 3, "%02x", lladdr[i]); + buf += 2; + *buf = (i == maxbytes - 1) ? '\0' : ':'; + buf++; + } + return (orig_buf); +} + diff --git a/network_cmds/netstat.tproj/misc.c b/network_cmds/netstat.tproj/misc.c new file mode 100644 index 0000000..ac2a2ca --- /dev/null +++ b/network_cmds/netstat.tproj/misc.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include <sys/sysctl.h> + +#include <net/net_api_stats.h> +#include <err.h> +#include <stdio.h> + +#include "netstat.h" + +void +print_net_api_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct net_api_stats pnet_api_stats; + struct net_api_stats net_api_stats; + size_t len = sizeof(struct net_api_stats); + const char *mibvar = "net.api_stats"; + + if (sysctlbyname(mibvar, &net_api_stats, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (net_api_stats.f - pnet_api_stats.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p(nas_iflt_attach_count, "\t%lld interface filter%s currently attached\n"); + p(nas_iflt_attach_total, "\t%lld interface filter%s attached since boot\n"); + p(nas_iflt_attach_os_total, "\t%lld interface filter%s attached since boot by OS\n"); + + p(nas_ipf_add_count, "\t%lld IP filter%s currently attached\n"); + p(nas_ipf_add_total, "\t%lld IP filter%s attached since boot\n"); + p(nas_ipf_add_os_total, "\t%lld IP filter%s attached since boot by OS\n"); + + p(nas_sfltr_register_count, "\t%lld socket filter%s currently attached\n"); + p(nas_sfltr_register_total, "\t%lld socket filter%s attached since boot\n"); + p(nas_sfltr_register_os_total, "\t%lld socket filter%s attached since boot by OS\n"); + + p(nas_socket_alloc_total, "\t%lld socket%s allocated since boot\n"); + p(nas_socket_in_kernel_total, "\t%lld socket%s allocated in-kernel since boot\n"); + p(nas_socket_in_kernel_os_total, "\t%lld socket%s allocated in-kernel by OS\n"); + p(nas_socket_necp_clientuuid_total, "\t%lld socket%s with NECP client UUID since boot\n"); + + p(nas_socket_domain_local_total, "\t%lld local domain socket%s allocated since boot\n"); + p(nas_socket_domain_route_total, "\t%lld route domain socket%s allocated since boot\n"); + p(nas_socket_domain_inet_total, "\t%lld inet domain socket%s allocated since boot\n"); + p(nas_socket_domain_inet6_total, "\t%lld inet6 domain socket%s allocated since boot\n"); + p(nas_socket_domain_system_total, "\t%lld system domain socket%s allocated since boot\n"); + p(nas_socket_domain_multipath_total, "\t%lld multipath domain socket%s allocated since boot\n"); + p(nas_socket_domain_key_total, "\t%lld key domain socket%s allocated since boot\n"); + p(nas_socket_domain_ndrv_total, "\t%lld ndrv domain socket%s allocated since boot\n"); + p(nas_socket_domain_other_total, "\t%lld other domains socket%s allocated since boot\n"); + + p(nas_socket_inet_stream_total, "\t%lld IPv4 stream socket%s created since boot\n"); + p(nas_socket_inet_dgram_total, "\t%lld IPv4 datagram socket%s created since boot\n"); + p(nas_socket_inet_dgram_connected, "\t%lld IPv4 datagram socket%s connected\n"); + p(nas_socket_inet_dgram_dns, "\t%lld IPv4 DNS socket%s\n"); + p(nas_socket_inet_dgram_no_data, "\t%lld IPv4 datagram socket%s without data\n"); + + p(nas_socket_inet6_stream_total, "\t%lld IPv6 stream socket%s created since boot\n"); + p(nas_socket_inet6_dgram_total, "\t%lld IPv6 datagram socket%s created since boot\n"); + p(nas_socket_inet6_dgram_connected, "\t%lld IPv6 datagram socket%s connected\n"); + p(nas_socket_inet6_dgram_dns, "\t%lld IPv6 DNS socket%s\n"); + p(nas_socket_inet6_dgram_no_data, "\t%lld IPv6 datagram socket%s without data\n"); + + p(nas_socket_mcast_join_total, "\t%lld socket multicast join%s since boot\n"); + p(nas_socket_mcast_join_os_total, "\t%lld socket multicast join%s since boot by OS\n"); + + p(nas_nx_flow_inet_stream_total, "\t%lld IPv4 stream nexus flow%s added since boot\n"); + p(nas_nx_flow_inet_dgram_total, "\t%lld IPv4 datagram nexus flow%s added since boot\n"); + + p(nas_nx_flow_inet6_stream_total, "\t%lld IPv6 stream nexus flow%s added since boot\n"); + p(nas_nx_flow_inet6_dgram_total, "\t%lld IPv6 datagram nexus flow%s added since boot\n"); + + p(nas_ifnet_alloc_count, "\t%lld interface%s currently allocated\n"); + p(nas_ifnet_alloc_total, "\t%lld interface%s allocated since boot\n"); + p(nas_ifnet_alloc_os_count, "\t%lld interface%s currently allocated by OS\n"); + p(nas_ifnet_alloc_os_total, "\t%lld extended interface%s allocated since boot by OS\n"); + + p(nas_pf_addrule_total, "\t%lld PF addrule operation%s since boot\n"); + p(nas_pf_addrule_os, "\t%lld PF addrule operation%s since boot by OS\n"); + + p(nas_vmnet_total, "\t%lld vmnet start%s since boot\n"); + +#undef STATDIFF +#undef p +#undef p1a +} + diff --git a/network_cmds/netstat.tproj/mptcp.c b/network_cmds/netstat.tproj/mptcp.c new file mode 100644 index 0000000..70be928 --- /dev/null +++ b/network_cmds/netstat.tproj/mptcp.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#include <stdio.h> +#include <err.h> +#include <stdlib.h> +#include <strings.h> +#include <inttypes.h> + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netinet/mptcp_var.h> + +#include <arpa/inet.h> + +#include "netstat.h" + +/* XXX we can't include tcp_fsm.h because inet.c already includes it. */ +static const char *tcpstates[] = { + "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", + "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", + "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT" +}; + +static const char *mptcpstates[] = { + "CLOSED", "LISTEN", "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", + "CLOSING", "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT", "TERMINATE" +}; + +int mptcp_done = 0; +extern void inetprint (struct in_addr *, int, char *, int); +extern void inet6print (struct in6_addr *, int, char *, int); + +static void +printmptcp(int id, conninfo_mptcp_t *mptcp) +{ + int i; + conninfo_tcp_t *tcpci; + struct sockaddr_storage *src, *dst; + mptcp_flow_t *flow; + int af; + + printf("mptcp/%-2.2d %-8.8x/%-8.8x %50s \n" + " [tok(%#"PRIx32") snd(%#"PRIx64") rcv(%#"PRIx64") " + "aid(%d)]\n", id, + mptcp->mptcpci_mpte_flags, mptcp->mptcpci_flags, + mptcpstates[mptcp->mptcpci_state], mptcp->mptcpci_rtoken, + mptcp->mptcpci_sndnxt, mptcp->mptcpci_rcvnxt, + mptcp->mptcpci_mpte_addrid); + + flow = (mptcp_flow_t*)((caddr_t)mptcp + mptcp->mptcpci_flow_offset); + + for (i = 0; i < mptcp->mptcpci_nflows; i++) { + src = &flow->flow_src; + dst = &flow->flow_dst; + af = src->ss_family; + printf(" tcp%d/%-2.2d ", af == AF_INET ? 4 : 6, + flow->flow_cid); + printf("%-8.8x ", flow->flow_flags); +#define SIN(x) ((struct sockaddr_in *)(x)) +#define SIN6(x) ((struct sockaddr_in6 *)(x)) + switch (af) { + case AF_INET: + inetprint(&SIN(src)->sin_addr, SIN(src)->sin_port, + "tcp", nflag); + inetprint(&SIN(dst)->sin_addr, SIN(dst)->sin_port, + "tcp", nflag); + break; +#ifdef INET6 + case AF_INET6: + inet6print(&SIN6(src)->sin6_addr, SIN6(src)->sin6_port, + "tcp", nflag); + inet6print(&SIN6(dst)->sin6_addr, SIN6(dst)->sin6_port, + "tcp", nflag); + break; + } +#endif +#undef SIN +#undef SIN6 + tcpci = (conninfo_tcp_t*)((caddr_t)flow + + flow->flow_tcpci_offset); + printf("%s \n" + " [relseq(%-4.4d), err(%d)]\n", + tcpstates[tcpci->tcpci_tcp_info.tcpi_state], + flow->flow_relseq, + flow->flow_soerror); + + flow = (mptcp_flow_t*)((caddr_t)flow + flow->flow_len); + } +} + +void +mptcppr(uint32_t off, char *name, int af) +{ +#pragma unused(off, name, af) + const char *mibvar = "net.inet.mptcp.pcblist"; + size_t len = 0; + conninfo_mptcp_t *mptcp; + char *buf, *bufp; + int id = 0; + + if (Lflag || Aflag || mptcp_done) + return; + mptcp_done = 1; + + if (sysctlbyname(mibvar, 0, &len, NULL, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == NULL) { + warn("malloc"); + return; + } + if (sysctlbyname(mibvar, buf, &len, NULL, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + printf("Active Multipath Internet connections\n"); + printf("%-8.8s %-9.9s %-22.22s %-22.22s %-11.11s\n", + "Proto/ID", "Flags", + "Local Address", "Foreign Address", + "(state)"); + + bufp = buf; + while (bufp < buf + len) { + /* Sanity check */ + if (buf + len - bufp < sizeof(conninfo_mptcp_t)) + break; + mptcp = (conninfo_mptcp_t *)bufp; + printmptcp(id++, mptcp); + bufp += mptcp->mptcpci_len; + } + free(buf); +} diff --git a/network_cmds/netstat.tproj/netstat.1 b/network_cmds/netstat.tproj/netstat.1 new file mode 100644 index 0000000..61ab843 --- /dev/null +++ b/network_cmds/netstat.tproj/netstat.1 @@ -0,0 +1,445 @@ +.\" Copyright (c) 2015 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" 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. 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. +.\" +.\" @(#)netstat.1 8.8 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/netstat/netstat.1,v 1.22.2.7 2001/08/10 09:07:09 ru Exp $ +.\" +.Dd June 15, 2001 +.Dt NETSTAT 1 +.Os Darwin +.Sh NAME +.Nm netstat +.Nd show network status +.Sh SYNOPSIS +.Nm +.Op Fl AaLlnW +.Op Fl f Ar address_family | Fl p Ar protocol +.Nm +.Op Fl gilns +.Op Fl v +.Op Fl f Ar address_family +.Op Fl I Ar interface +.Nm +.Fl i | I Ar interface +.Op Fl w Ar wait +.Op Fl c Ar queue +.Op Fl abdgqRtS +.Nm +.Fl s Op Fl s +.Op Fl f Ar address_family | Fl p Ar protocol +.Op Fl w Ar wait +.Nm +.Fl i | I Ar interface Fl s +.Op Fl f Ar address_family | Fl p Ar protocol +.Nm +.Fl m +.Op Fl m +.Nm +.Fl r +.Op Fl Aaln +.Op Fl f Ar address_family +.Nm +.Fl rs +.Op Fl s +.\"----------------------------------------------------------------------------------------- +.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 for the specified protocol or address family. If a +.Ar wait +interval is specified, the protocol information over the last interval seconds will be displayed. +The fifth form displays per-interface statistics for the specified protocol or address family. +The sixth form displays +.Xr mbuf 9 +statistics. The seventh form displays routing table for the specified address family. The +eighth form displays routing statistics. +.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 and the flow hash; 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. With the routing table display (option +.Fl r , +as described below), show protocol-cloned routes (routes generated by a +.Dv RTF_PRCLONING +parent route); normally these routes are not shown. +.It Fl b +With the interface display (option +.Fl i , +as described below), show the number of bytes in and out. +.It Fl c Ar queue +With the queue statistics (option +.Fl q , +as described below), show only those for the specified +.Ar queue . +.It Fl d +With either interface display (option +.Fl i +or an interval, as described below), show the number of dropped packets. +.It Fl f Ar address_family +Limit statistics or address control block reports to those of the specified +.Ar address family . +The following address families are recognized: +.Ar inet , +for +.Dv AF_INET , +.Ar inet6 , +for +.Dv AF_INET6 +and +.Ar unix , +for +.Dv AF_UNIX . +.It Fl g +Show information related to multicast (group address) membership. If the +.Fl s +option is also present, show extended interface group management statistics. If the +.Fl v +option is specified, show link-layer memberships; they are suppressed by default. +Source lists for each group will also be printed. Specifiying +.Fl v +twice will print the control plane timers for each interface and the source list counters +for each group. If the +.Fl i +is specified, only that interface will be shown. If the +.Fl f +is specified, only information for the address family will be displayed. +.It Fl I Ar interface +Show information about the specified interface; used with a +.Ar wait +interval as described below. +If the +.Fl s +option is present, show per-interface protocol statistics on the +.Ar interface +for the specified +.Ar address_family +or +.Ar protocol , +or for all protocol families. +.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 s +option is present, show per-interface statistics on all interfaces for the specified +.Ar address_family +or +.Ar protocol , +or for all protocol families. +.It Fl L +Show the size of the various listen queues. The first count shows the number of +unaccepted connections. The second count shows the amount of unaccepted incomplete +connections. The third count is the maximum number of queued connections. +.It Fl l +Print full IPv6 address. +.It Fl m +Show statistics recorded by the memory management routines (the network stack manages a private pool of memory buffers). More detailed information about the buffers, which includes their cache related statistics, can be obtained by using +.Fl mm +or +.Fl m +.Fl m +option. +.It Fl n +Show network addresses as numbers (normally +.Nm +interprets addresses and attempts to display them symbolically). This option may be +used with any of the display formats. +.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 . +The special protocol name +.Dq bdg +is used to show bridging statistics. 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 network interface send queue statistics. By default all queues are displayed, unless +specified with +.Fl c . +This option requires specifying an interface with +.Fl I +option. More detailed information about the queues, which includes their queueing algorithm related statistics, can be obtained by using +.Fl qq +or +.Fl q +.Fl q +option. +.It Fl r +Show the routing tables. Use with +.Fl a +to show protocol-cloned routes. When +.Fl s +is also present, show routing statistics instead. When +.Fl l +is also present, +.Nm +assumes more columns are there and the maximum transmission unit. +More detailed information about the route metrics are displayed with +.Fl ll +for TCP round trip times +.Fl lll +for all metrics. +Use the +.Fl z +flags to display only entries with non-zero RTT values. +.Pq Dq mtu +are also displayed. +.It Fl R +Show reachability information. Use with +.Fl i +to show link-layer reachability information for a given interface. +.It Fl s +Show per-protocol statistics. If this option is repeated, counters with a value of +zero are suppressed. For security reasons, root privileges are required to read TCP statistics and in the absence of such privileges all TCP counters will be reported as zero. +.It Fl S +Show interface link status and interface state information about the specified interface. This option requires specifying an interface with +.Fl I +option. +.It Fl v +Increase verbosity level. +.It Fl W +In certain displays, avoid truncating addresses even if this causes some fields to +overflow. +.It Fl w Ar wait +Show network interface or protocol statistics at intervals of +.Ar wait +seconds. +.It Fl x +Show extended link-layer reachability information in addition to that shown by +the +.Fl R +flag. +.El +.Pp +.\"------------------------------------------------------------------------------- +.Sh OUTPUT +.\"------------------------------------------------------------------------------- +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 +.Dq host.port +or +.Dq network.port +if a socket's address specifies a network but no specific host address. +If known, the host and network addresses are displayed symbolically +according to the databases +.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 +.Dq dot format , +refer to +.Xr inet 3 ) . +Unspecified, +or +.Dq wildcard , +addresses and ports appear as +.Dq * . +.Pp +Internet domain socket states: +.Bl -column X LISTEN +CLOSED: The socket is not in use. +.Pp +LISTEN: The socket is listening for incoming connections. Unconnected +listening sockets like these are only displayed when using the -a option. +.Pp +SYN_SENT: The socket is actively trying to establish a connection to a +remote peer. +.Pp +SYN_RCVD: The socket has passively received a connection request from a +remote peer. +.Pp +ESTABLISHED: The socket has an established connection between a local +application and a remote peer. +.Pp +CLOSE_WAIT: The socket connection has been closed by the remote peer, +and the system is waiting for the local application to close its half of +the connection. +.Pp +LAST_ACK: The socket connection has been closed by the remote peer, the +local application has closed its half of the connection, and the system +is waiting for the remote peer to acknowledge the close. +.Pp +FIN_WAIT_1: The socket connection has been closed by the local +application, the remote peer has not yet acknowledged the close, and the +system is waiting for it to close its half of the connection. +.Pp +FIN_WAIT_2: The socket connection has been closed by the local +application, the remote peer has acknowledged the close, and the system +is waiting for it to close its half of the connection. +.Pp +CLOSING: The socket connection has been closed by the local application +and the remote peer simultaneously, and the remote peer has not yet +acknowledged the close attempt of the local application. +.Pp +TIME_WAIT: The socket connection has been closed by the local +application, the remote peer has closed its half of the connection, and +the system is waiting to be sure that the remote peer received the last +acknowledgement. +.El +.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 +.Pq Dq 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 +1 RTF_PROTO1 Protocol specific routing flag #1 +2 RTF_PROTO2 Protocol specific routing flag #2 +3 RTF_PROTO3 Protocol specific routing flag #3 +B RTF_BLACKHOLE Just discard packets (during updates) +b RTF_BROADCAST The route represents a broadcast address +C RTF_CLONING Generate new routes on use +c RTF_PRCLONING Protocol-specified generate new routes on use +D RTF_DYNAMIC Created dynamically (by redirect) +G RTF_GATEWAY Destination requires forwarding by intermediary +H RTF_HOST Host entry (net otherwise) +I RTF_IFSCOPE Route is associated with an interface scope +i RTF_IFREF Route is holding a reference to the interface +L RTF_LLINFO Valid protocol to link address translation +M RTF_MODIFIED Modified dynamically (by redirect) +m RTF_MULTICAST The route represents a multicast address +R RTF_REJECT Host or net unreachable +r RTF_ROUTER Host is a default router +S RTF_STATIC Manually added +U RTF_UP Route usable +W RTF_WASCLONED Route was generated as a result of cloning +X RTF_XRESOLVE External daemon translates proto to link address +Y RTF_PROXY Proxying; cloned routes will not be scoped +.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 +interface entry indicates the network interface utilized for the route. +A route which is marked with the RTF_IFSCOPE flag is instantiated for +the corresponding interface. A cloning route which is marked with the +RTF_PROXY flag will not generate new routes that are associated +with its interface scope. +.Pp +When +.Nm netstat +is invoked with the +.Fl w +option and a +.Ar wait +interval argument, it displays a running count of statistics related to +network interfaces or protocols. An obsolete version of this option used a numeric +parameter with no option, and is currently supported for backward +compatibility. By default, this display summarizes information for all +interfaces. Information for a specific interface may be displayed with the +.Fl I +option. +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr ps 1 , +.Xr inet 4 , +.Xr unix 4 , +.Xr hosts 5 , +.Xr networks 5 , +.Xr protocols 5 , +.Xr route 8 , +.Xr services 5 , +.Xr iostat 8 , +.Sh HISTORY +The +.Nm netstat +command appeared in +.Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. +.Sh BUGS +The notion of errors is ill-defined. diff --git a/network_cmds/netstat.tproj/netstat.h b/network_cmds/netstat.tproj/netstat.h new file mode 100644 index 0000000..68511da --- /dev/null +++ b/network_cmds/netstat.tproj/netstat.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2008-2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * 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. 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. + * + * @(#)netstat.h 8.2 (Berkeley) 1/4/94 + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <stdint.h> + +#include <TargetConditionals.h> + +extern int Aflag; /* show addresses of protocol control block */ +extern int aflag; /* show all sockets (including servers) */ +extern int bflag; /* show i/f total bytes in/out */ +extern int cflag; /* show specific classq */ +extern int dflag; /* show i/f dropped packets */ +extern int Fflag; /* show i/f forwarded packets */ +extern int gflag; /* show group (multicast) routing or stats */ +extern int iflag; /* show interfaces */ +extern int lflag; /* show routing table with use and ref */ +extern int Lflag; /* show size of listen queues */ +extern int mflag; /* show memory stats */ +extern int nflag; /* show addresses numerically */ +extern int Rflag; /* show reachability information */ +extern int rflag; /* show routing tables (or routing stats) */ +extern int sflag; /* show protocol statistics */ +extern int prioflag; /* show packet priority statistics */ +extern int tflag; /* show i/f watchdog timers */ +extern int vflag; /* more verbose */ +extern int Wflag; /* wide display */ +extern int qflag; /* Display ifclassq stats */ +extern int Qflag; /* Display opportunistic polling stats */ +extern int xflag; /* show extended link-layer reachability information */ +extern int zflag; /* show only entries with non zero rtt metrics */ + +extern int cq; /* send classq index (-1 for all) */ +extern int interval; /* repeat interval for i/f stats */ + +extern char *interface; /* desired i/f for stats, or NULL for all i/fs */ +extern int unit; /* unit number for above */ + +extern int af; /* address family */ + +extern char *plural(int); +extern char *plurales(int); +extern char *pluralies(int); + +extern void protopr(uint32_t, char *, int); +extern void mptcppr(uint32_t, char *, int); +extern void tcp_stats(uint32_t, char *, int); +extern void mptcp_stats(uint32_t, char *, int); +extern void udp_stats(uint32_t, char *, int); +extern void ip_stats(uint32_t, char *, int); +extern void icmp_stats(uint32_t, char *, int); +extern void igmp_stats(uint32_t, char *, int); +extern void arp_stats(uint32_t, char *, int); +#ifdef IPSEC +extern void ipsec_stats(uint32_t, char *, int); +#endif + +#ifdef INET6 +extern void ip6_stats(uint32_t, char *, int); +extern void ip6_ifstats(char *); +extern void icmp6_stats(uint32_t, char *, int); +extern void icmp6_ifstats(char *); +extern void rip6_stats(uint32_t, char *, int); + +/* forward references */ +struct sockaddr_in6; +struct in6_addr; +struct sockaddr; + +extern char *routename6(struct sockaddr_in6 *); +extern char *netname6(struct sockaddr_in6 *, struct sockaddr *); +#endif /*INET6*/ + +#ifdef IPSEC +extern void pfkey_stats(uint32_t, char *, int); +#endif + +extern void systmpr(uint32_t, char *, int); +extern void kctl_stats(uint32_t, char *, int); +extern void kevt_stats(uint32_t, char *, int); + +extern void mbpr(void); + +extern void intpr(void (*)(char *)); +extern void intpr_ri(void (*)(char *)); +extern void intervalpr(void (*)(uint32_t, char *, int), uint32_t, + char *, int); + +extern void pr_rthdr(int); +extern void pr_family(int); +extern void rt_stats(void); +extern void upHex(char *); +extern char *routename(uint32_t); +extern char *netname(uint32_t, uint32_t); +extern void routepr(void); + +extern void unixpr(void); +extern void aqstatpr(void); +extern void rxpollstatpr(void); +extern void vsockpr(uint32_t,char *,int); + +extern void ifmalist_dump(void); + +extern int print_time(void); +extern void print_link_status(const char *); + +extern void print_extbkidle_stats(uint32_t, char *, int); +extern void print_nstat_stats(uint32_t, char *, int); +extern void print_net_api_stats(uint32_t, char *, int); + diff --git a/network_cmds/netstat.tproj/route.c b/network_cmds/netstat.tproj/route.c new file mode 100644 index 0000000..943e3c8 --- /dev/null +++ b/network_cmds/netstat.tproj/route.c @@ -0,0 +1,795 @@ +/* + * Copyright (c) 2008-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * 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. 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. + */ + +#include <stdint.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#include <net/radix.h> + +#include <netinet/in.h> + +#include <sys/sysctl.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <err.h> +#include <time.h> +#include "netstat.h" + +/* alignment constraint for routing socket */ +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +/* + * Definitions for showing gateway flags. + */ +struct bits { + uint32_t b_mask; + char b_val; +} bits[] = { + { RTF_UP, 'U' }, + { RTF_GATEWAY, 'G' }, + { RTF_HOST, 'H' }, + { RTF_REJECT, 'R' }, + { RTF_DYNAMIC, 'D' }, + { RTF_MODIFIED, 'M' }, + { RTF_MULTICAST,'m' }, + { RTF_DONE, 'd' }, /* Completed -- for routing messages only */ + { RTF_CLONING, 'C' }, + { RTF_XRESOLVE, 'X' }, + { RTF_LLINFO, 'L' }, + { RTF_STATIC, 'S' }, + { RTF_PROTO1, '1' }, + { RTF_PROTO2, '2' }, + { RTF_WASCLONED,'W' }, + { RTF_PRCLONING,'c' }, + { RTF_PROTO3, '3' }, + { RTF_BLACKHOLE,'B' }, + { RTF_BROADCAST,'b' }, + { RTF_IFSCOPE, 'I' }, + { RTF_IFREF, 'i' }, + { RTF_PROXY, 'Y' }, + { RTF_ROUTER, 'r' }, + { 0 } +}; + +typedef union { + uint32_t dummy; /* Helps align structure. */ + struct sockaddr u_sa; + u_short u_data[128]; +} sa_u; + +static void np_rtentry __P((struct rt_msghdr2 *)); +static void p_sockaddr __P((struct sockaddr *, struct sockaddr *, int, int)); +static void p_flags __P((int, char *)); +static uint32_t forgemask __P((uint32_t)); +static void domask __P((char *, uint32_t, uint32_t)); + +/* + * Print address family header before a section of the routing table. + */ +void +pr_family(int af) +{ + char *afname; + + switch (af) { + case AF_INET: + afname = "Internet"; + break; +#ifdef INET6 + case AF_INET6: + afname = "Internet6"; + break; +#endif /*INET6*/ + case AF_IPX: + afname = "IPX"; + break; + default: + afname = NULL; + break; + } + if (afname) + printf("\n%s:\n", afname); + else + printf("\nProtocol Family %d:\n", af); +} + +/* column widths; each followed by one space */ +#ifndef INET6 +#define WID_DST(af) 18 /* width of destination column */ +#define WID_GW(af) 18 /* width of gateway column */ +#define WID_RT_IFA(af) 18 /* width of source column */ +#define WID_IF(af) 7 /* width of netif column */ +#else +#define WID_DST(af) \ + ((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 39: 18)) : 18) +#define WID_GW(af) \ + ((af) == AF_INET6 ? (lflag ? 31 : (nflag ? 31 : 18)) : 18) +#define WID_RT_IFA(af) \ + ((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 39 : 18)) : 18) +#define WID_IF(af) ((af) == AF_INET6 ? 8 : 7) +#endif /*INET6*/ + +/* + * Print header for routing table columns. + */ +void +pr_rthdr(int af) +{ + if (lflag) { + if (lflag > 2) + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s " + "%10s %10s %8s %8s %8s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire", + "rtt(ms)", "rttvar(ms)", "recvpipe", "sendpipe", "ssthresh"); + else if (lflag > 1) + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s " + "%10s %10s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire", + "rtt(ms)", "rttvar(ms)"); + else + printf("%-*.*s %-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + WID_RT_IFA(af), WID_RT_IFA(af), "RT_IFA", + "Flags", "Refs", "Use", "Mtu", + WID_IF(af), WID_IF(af), "Netif", "Expire"); + } else { + printf("%-*.*s %-*.*s %-10.10s %*.*s %6s\n", + WID_DST(af), WID_DST(af), "Destination", + WID_GW(af), WID_GW(af), "Gateway", + "Flags", WID_IF(af), WID_IF(af), "Netif", "Expire"); + } +} + +/* + * Print routing tables. + */ +void +routepr(void) +{ + size_t extra_space; + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr2 *rtm; + int try = 1; + + printf("Routing tables\n"); + + again: + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_DUMP2; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { + err(1, "sysctl: net.route.0.0.dump estimate"); + } + /* allocate extra space in case the table grows */ + extra_space = needed / 2; + if (needed <= (SIZE_MAX - extra_space)) { + needed += extra_space; + } + if ((buf = malloc(needed)) == 0) { + err(2, "malloc(%lu)", (unsigned long)needed); + } + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { +#define MAX_TRIES 10 + if (errno == ENOMEM && try < MAX_TRIES) { + /* the buffer we provided was too small, try again */ + free(buf); + try++; + goto again; + } + err(1, "sysctl: net.route.0.0.dump"); + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr2 *)next; + np_rtentry(rtm); + } +} + +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 *)(ROUNDUP(sa->sa_len) + (char *)sa); + } else { + rti_info[i] = NULL; + } +} +} + +static void +np_rtentry(struct rt_msghdr2 *rtm) +{ + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + struct sockaddr *rti_info[RTAX_MAX]; + static int old_fam; + int fam = 0; + u_short lastindex = 0xffff; + static char ifname[IFNAMSIZ + 1]; + sa_u addr, mask; + + /* + * Don't print protocol-cloned routes unless -a. + */ + if ((rtm->rtm_flags & RTF_WASCLONED) && + (rtm->rtm_parentflags & RTF_PRCLONING) && + !aflag) { + return; + } + + if (lflag > 1 && zflag != 0 && rtm->rtm_rmx.rmx_rtt == 0 && rtm->rtm_rmx.rmx_rttvar == 0) + return; + fam = sa->sa_family; + if (af != AF_UNSPEC && af != fam) + return; + if (fam != old_fam) { + pr_family(fam); + pr_rthdr(fam); + old_fam = fam; + } + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + bzero(&addr, sizeof(addr)); + if ((rtm->rtm_addrs & RTA_DST)) + bcopy(rti_info[RTAX_DST], &addr, rti_info[RTAX_DST]->sa_len); + bzero(&mask, sizeof(mask)); + if ((rtm->rtm_addrs & RTA_NETMASK)) + bcopy(rti_info[RTAX_NETMASK], &mask, rti_info[RTAX_NETMASK]->sa_len); + p_sockaddr(&addr.u_sa, &mask.u_sa, rtm->rtm_flags, + WID_DST(addr.u_sa.sa_family)); + + p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST, + WID_GW(addr.u_sa.sa_family)); + + if (lflag && (rtm->rtm_addrs & RTA_IFA)) { + p_sockaddr(rti_info[RTAX_IFA], NULL, RTF_HOST, + WID_RT_IFA(addr.u_sa.sa_family)); + } + + p_flags(rtm->rtm_flags, "%-10.10s "); + + if (lflag) { + printf("%6u %8u ", rtm->rtm_refcnt, (unsigned int)rtm->rtm_use); + if (rtm->rtm_rmx.rmx_mtu != 0) + printf("%6u ", rtm->rtm_rmx.rmx_mtu); + else + printf("%6s ", ""); + } + + if (rtm->rtm_index != lastindex) { + if_indextoname(rtm->rtm_index, ifname); + lastindex = rtm->rtm_index; + } + printf("%*.*s", WID_IF(addr.u_sa.sa_family), + WID_IF(addr.u_sa.sa_family), ifname); + + if (rtm->rtm_rmx.rmx_expire) { + time_t expire_time; + + if ((expire_time = + rtm->rtm_rmx.rmx_expire - time((time_t *)0)) > 0) + printf(" %6d", (int)expire_time); + else + printf(" %6s", "!"); + } else { + printf(" %6s", ""); + } + if (lflag > 1) { + if (rtm->rtm_rmx.rmx_rtt != 0) + printf(" %6u.%03u", rtm->rtm_rmx.rmx_rtt / 1000, + rtm->rtm_rmx.rmx_rtt % 1000); + else + printf(" %10s", ""); + if (rtm->rtm_rmx.rmx_rttvar != 0) + printf(" %6u.%03u", rtm->rtm_rmx.rmx_rttvar / 1000, + rtm->rtm_rmx.rmx_rttvar % 1000); + else + printf(" %10s", ""); + if (lflag > 2) { + if (rtm->rtm_rmx.rmx_recvpipe != 0) + printf(" %8u", rtm->rtm_rmx.rmx_recvpipe); + else + printf(" %8s", ""); + if (rtm->rtm_rmx.rmx_sendpipe != 0) + printf(" %8u", rtm->rtm_rmx.rmx_sendpipe); + else + printf(" %8s", ""); + if (rtm->rtm_rmx.rmx_ssthresh != 0) + printf(" %8u", rtm->rtm_rmx.rmx_ssthresh); + else + printf(" %8s", ""); + } + } + putchar('\n'); +} + +static void +p_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags, int width) +{ + char workbuf[128], *cplim; + char *cp = workbuf; + + switch(sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + if ((sin->sin_addr.s_addr == INADDR_ANY) && + mask && + (ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr) == 0L || mask->sa_len == 0)) + cp = "default" ; + else if (flags & RTF_HOST) + cp = routename(sin->sin_addr.s_addr); + else if (mask) + cp = netname(sin->sin_addr.s_addr, + ntohl(((struct sockaddr_in *)mask)-> + sin_addr.s_addr)); + else + cp = netname(sin->sin_addr.s_addr, 0L); + break; + } + +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + struct in6_addr *in6 = &sa6->sin6_addr; + + /* + * XXX: This is a special workaround for KAME kernels. + * sin6_scope_id field of SA should be set in the future. + */ + if (IN6_IS_ADDR_LINKLOCAL(in6) || + IN6_IS_ADDR_MC_NODELOCAL(in6) || + IN6_IS_ADDR_MC_LINKLOCAL(in6)) { + /* XXX: override is ok? */ + sa6->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)&in6->s6_addr[2]); + *(u_short *)&in6->s6_addr[2] = 0; + } + + if (flags & RTF_HOST) + cp = routename6(sa6); + else if (mask) + cp = netname6(sa6, mask); + else + cp = netname6(sa6, NULL); + break; + } +#endif /*INET6*/ + + case AF_LINK: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + + if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && + sdl->sdl_slen == 0) { + (void) snprintf(workbuf, sizeof(workbuf), "link#%d", sdl->sdl_index); + } else { + switch (sdl->sdl_type) { + + case IFT_ETHER: { + int i; + u_char *lla = (u_char *)sdl->sdl_data + + sdl->sdl_nlen; + + cplim = ""; + for (i = 0; i < sdl->sdl_alen; i++, lla++) { + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "%s%x", cplim, *lla); + cplim = ":"; + } + cp = workbuf; + break; + } + + default: + cp = link_ntoa(sdl); + break; + } + } + break; + } + + default: { + u_char *s = (u_char *)sa->sa_data, *slim; + + slim = sa->sa_len + (u_char *) sa; + cplim = cp + sizeof(workbuf) - 6; + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "(%d)", sa->sa_family); + while (s < slim && cp < cplim) { + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), " %02x", *s++); + if (s < slim) + cp += snprintf(cp, sizeof(workbuf) - (cp - workbuf), "%02x", *s++); + } + cp = workbuf; + } + } + if (width < 0 ) { + printf("%s ", cp); + } else { + if (nflag) + printf("%-*s ", width, cp); + else + printf("%-*.*s ", width, width, cp); + } +} + +static void +p_flags(int f, char *format) +{ + char name[33], *flags; + struct bits *p = bits; + + for (flags = name; p->b_mask; p++) + if (p->b_mask & f) + *flags++ = p->b_val; + *flags = '\0'; + printf(format, name); +} + +char * +routename(uint32_t in) +{ + char *cp; + static char line[MAXHOSTNAMELEN]; + struct hostent *hp; + + cp = 0; + if (!nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + cp = hp->h_name; + //### trimdomain(cp, strlen(cp)); + } + } + if (cp) { + strlcpy(line, cp, sizeof(line)); + } else { +#define C(x) ((x) & 0xff) + in = ntohl(in); + snprintf(line, sizeof(line), "%u.%u.%u.%u", + C(in >> 24), C(in >> 16), C(in >> 8), C(in)); + } + return (line); +} + +static uint32_t +forgemask(uint32_t a) +{ + uint32_t m; + + if (IN_CLASSA(a)) + m = IN_CLASSA_NET; + else if (IN_CLASSB(a)) + m = IN_CLASSB_NET; + else + m = IN_CLASSC_NET; + return (m); +} + +static void +domask(char *dst, uint32_t addr, uint32_t mask) +{ + int b, i; + + if (!mask || (forgemask(addr) == mask)) { + *dst = '\0'; + return; + } + i = 0; + for (b = 0; b < 32; b++) + if (mask & (1 << b)) { + int bb; + + i = b; + for (bb = b+1; bb < 32; bb++) + if (!(mask & (1 << bb))) { + i = -1; /* noncontig */ + break; + } + break; + } + if (i == -1) + snprintf(dst, sizeof(dst), "&0x%x", mask); + else + snprintf(dst, sizeof(dst), "/%d", 32-i); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(uint32_t in, uint32_t mask) +{ + char *cp = 0; + static char line[MAXHOSTNAMELEN]; + struct netent *np = 0; + uint32_t net, omask, dmask; + uint32_t i; + + i = ntohl(in); + dmask = forgemask(i); + omask = mask; + if (!nflag && i) { + net = i & dmask; + if (!(np = getnetbyaddr(i, AF_INET)) && net != i) + np = getnetbyaddr(net, AF_INET); + if (np) { + cp = np->n_name; + //### trimdomain(cp, strlen(cp)); + } + } + if (cp) + strlcpy(line, cp, sizeof(line)); + else { + switch (dmask) { + case IN_CLASSA_NET: + if ((i & IN_CLASSA_HOST) == 0) { + snprintf(line, sizeof(line), "%u", C(i >> 24)); + break; + } + /* FALLTHROUGH */ + case IN_CLASSB_NET: + if ((i & IN_CLASSB_HOST) == 0) { + snprintf(line, sizeof(line), "%u.%u", + C(i >> 24), C(i >> 16)); + break; + } + /* FALLTHROUGH */ + case IN_CLASSC_NET: + if ((i & IN_CLASSC_HOST) == 0) { + snprintf(line, sizeof(line), "%u.%u.%u", + C(i >> 24), C(i >> 16), C(i >> 8)); + break; + } + /* FALLTHROUGH */ + default: + snprintf(line, sizeof(line), "%u.%u.%u.%u", + C(i >> 24), C(i >> 16), C(i >> 8), C(i)); + break; + } + } + domask(line+strlen(line), i, omask); + return (line); +} + +#ifdef INET6 +char * +netname6(struct sockaddr_in6 *sa6, struct sockaddr *sam) +{ + static char line[MAXHOSTNAMELEN]; + u_char *lim; + int masklen, illegal = 0, flag = NI_WITHSCOPEID; + struct in6_addr *mask = sam ? &((struct sockaddr_in6 *)sam)->sin6_addr : 0; + + if (sam && sam->sa_len == 0) { + masklen = 0; + } else if (mask) { + u_char *p = (u_char *)mask; + for (masklen = 0, lim = p + 16; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + default: + illegal ++; + break; + } + } + if (illegal) + fprintf(stderr, "illegal prefixlen\n"); + } else { + masklen = 128; + } + if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr)) + return("default"); + + if (nflag) + flag |= NI_NUMERICHOST; + getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line), + NULL, 0, flag); + + if (nflag) + snprintf(&line[strlen(line)], sizeof(line) - strlen(line), "/%d", masklen); + + return line; +} + +char * +routename6(struct sockaddr_in6 *sa6) +{ + static char line[MAXHOSTNAMELEN]; + int flag = NI_WITHSCOPEID; + /* use local variable for safety */ + struct sockaddr_in6 sa6_local = {sizeof(sa6_local), AF_INET6, }; + + sa6_local.sin6_addr = sa6->sin6_addr; + sa6_local.sin6_scope_id = sa6->sin6_scope_id; + + if (nflag) + flag |= NI_NUMERICHOST; + + getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len, + line, sizeof(line), NULL, 0, flag); + + return line; +} +#endif /*INET6*/ + +/* + * Print routing statistics + */ +void +rt_stats(void) +{ + struct rtstat rtstat; + int rttrash; + int mib[6]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_STAT; + mib[5] = 0; + len = sizeof(struct rtstat); + if (sysctl(mib, 6, &rtstat, &len, 0, 0) == -1) + return; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_TRASH; + mib[5] = 0; + len = sizeof(rttrash); + if (sysctl(mib, 6, &rttrash, &len, 0, 0) == -1) + return; + + printf("routing:\n"); + +#define p(f, m) if (rtstat.f || sflag <= 1) \ + printf(m, rtstat.f, plural(rtstat.f)) + + p(rts_badredirect, "\t%u bad routing redirect%s\n"); + p(rts_dynamic, "\t%u dynamically created route%s\n"); + p(rts_newgateway, "\t%u new gateway%s due to redirects\n"); + p(rts_unreach, "\t%u destination%s found unreachable\n"); + p(rts_wildcard, "\t%u use%s of a wildcard route\n"); + p(rts_badrtgwroute, "\t%u lookup%s returned indirect " + "routes pointing to indirect gateway route\n"); +#undef p + + if (rttrash || sflag <= 1) + printf("\t%u route%s not in table but not freed\n", + rttrash, plural(rttrash)); +} + +void +upHex(char *p0) +{ + char *p = p0; + + for (; *p; p++) + switch (*p) { + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + *p += ('A' - 'a'); + break; + } +} diff --git a/network_cmds/netstat.tproj/systm.c b/network_cmds/netstat.tproj/systm.c new file mode 100644 index 0000000..3e076ca --- /dev/null +++ b/network_cmds/netstat.tproj/systm.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2014-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*- + * 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. 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. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/sys_domain.h> +#include <sys/kern_control.h> +#include <sys/kern_event.h> +#include <net/ntstat.h> + +#include <errno.h> +#include <err.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include "netstat.h" + +#define ROUNDUP64(a) \ +((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint64_t) - 1))) : sizeof(uint64_t)) +#define ADVANCE64(x, n) (((char *)x) += ROUNDUP64(n)) + +struct xgen_n { + u_int32_t xgn_len; /* length of this structure */ + u_int32_t xgn_kind; /* number of PCBs at this time */ +}; + +#define ALL_XGN_KIND_KCREG (XSO_KCREG) +#define ALL_XGN_KIND_EVT (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_EVT) +#define ALL_XGN_KIND_KCB (XSO_SOCKET | XSO_RCVBUF | XSO_SNDBUF | XSO_STATS | XSO_KCB) + +void +systmpr(uint32_t proto, + char *name, int af) +{ + const char *mibvar; + size_t len; + char *buf, *next; + struct xsystmgen *xig, *oxig; + struct xgen_n *xgn; + int which = 0; + struct xsocket_n *so = NULL; + struct xsockbuf_n *so_rcv = NULL; + struct xsockbuf_n *so_snd = NULL; + struct xsockstat_n *so_stat = NULL; + struct xkctl_reg *kctl = NULL; + struct xkctlpcb *kcb = NULL; + struct xkevtpcb *kevb = NULL; + int first = 1; + + switch (proto) { + case SYSPROTO_EVENT: + mibvar = "net.systm.kevt.pcblist"; + break; + case SYSPROTO_CONTROL: + mibvar = "net.systm.kctl.pcblist"; + break; + case 0: + mibvar = "net.systm.kctl.reg_list"; + break; + default: + mibvar = NULL; + break; + } + if (mibvar == NULL) + return; + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= sizeof(struct xsystmgen)) { + free(buf); + return; + } + oxig = xig = (struct xsystmgen *)buf; + for (next = buf + ROUNDUP64(xig->xg_len); next < buf + len; + next += ROUNDUP64(xgn->xgn_len)) { + xgn = (struct xgen_n*)next; + if (xgn->xgn_len <= sizeof(struct xsystmgen)) + break; + + if ((which & xgn->xgn_kind) == 0) { + which |= xgn->xgn_kind; + switch (xgn->xgn_kind) { + case XSO_SOCKET: + so = (struct xsocket_n *)xgn; + break; + case XSO_RCVBUF: + so_rcv = (struct xsockbuf_n *)xgn; + break; + case XSO_SNDBUF: + so_snd = (struct xsockbuf_n *)xgn; + break; + case XSO_STATS: + so_stat = (struct xsockstat_n *)xgn; + break; + case XSO_KCREG: + kctl = (struct xkctl_reg *)xgn; + break; + case XSO_KCB: + kcb = (struct xkctlpcb *)xgn; + break; + case XSO_EVT: + kevb = (struct xkevtpcb *)xgn; + break; + default: + printf("unexpected kind %d\n", xgn->xgn_kind); + break; + } + } else { + if (vflag) + printf("got %d twice\n", xgn->xgn_kind); + } + + if (which == ALL_XGN_KIND_KCREG) { + which = 0; + + if (first) { + printf("Registered kernel control modules\n"); + if (Aflag) + printf("%-16.16s ", "kctlref"); + printf("%-8.8s ", "id"); + if (Aflag) + printf("%-8.8s ", "unit"); + printf("%-8.8s ", "flags"); + printf("%-8.8s ", "pcbcount"); + printf("%-8.8s ", "rcvbuf"); + printf("%-8.8s ", "sndbuf"); + printf("%s ", "name"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kctl->xkr_kctlref); + printf("%8x ", kctl->xkr_id); + if (Aflag) + printf("%8d ", kctl->xkr_reg_unit); + printf("%8x ", kctl->xkr_flags); + printf("%8d ", kctl->xkr_pcbcount); + printf("%8d ", kctl->xkr_recvbufsize); + printf("%8d ", kctl->xkr_sendbufsize); + printf("%s ", kctl->xkr_name); + printf("\n"); + } else if (which == ALL_XGN_KIND_KCB) { + which = 0; + + if (first) { + printf("Active kernel control sockets\n"); + if (Aflag) + printf("%16.16s ", "pcb"); + printf("%-5.5s %-6.6s %-6.6s ", + "Proto", "Recv-Q", "Send-Q"); + if (bflag > 0) + printf("%10.10s %10.10s ", + "rxbytes", "txbytes"); + if (vflag > 0) + printf("%6.6s %6.6s %6.6s %6.6s ", + "rhiwat", "shiwat", "pid", "epid"); + printf("%6.6s ", "unit"); + printf("%6.6s ", "id"); + printf("%s", "name"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kcb->xkp_kctpcb); + printf("%-5.5s %6u %6u ", name, + so_rcv->sb_cc, + so_snd->sb_cc); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + printf("%10llu %10llu ", rxbytes, txbytes); + } + if (vflag > 0) { + printf("%6u %6u %6u %6u ", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid); + } + printf("%6d ", kcb->xkp_unit); + printf("%6d ", kcb->xkp_kctlid); + printf("%s", kcb->xkp_kctlname); + printf("\n"); + + } else if (which == ALL_XGN_KIND_EVT) { + which = 0; + if (first) { + printf("Active kernel event sockets\n"); + if (Aflag) + printf("%16.16s ", "pcb"); + printf("%-5.5s %-6.6s %-6.6s ", + "Proto", "Recv-Q", "Send-Q"); + printf("%6.6s ", "vendor"); + printf("%6.6s ", "class"); + printf("%6.6s", "subclass"); + if (bflag > 0) + printf("%10.10s %10.10s ", + "rxbytes", "txbytes"); + if (vflag > 0) + printf("%6.6s %6.6s %6.6s %6.6s", + "rhiwat", "shiwat", "pid", "epid"); + printf("\n"); + first = 0; + } + if (Aflag) + printf("%16llx ", kevb->kep_evtpcb); + printf("%-5.5s %6u %6u ", name, + so_rcv->sb_cc, + so_snd->sb_cc); + printf("%6d ", kevb->kep_vendor_code_filter); + printf("%6d ", kevb->kep_class_filter); + printf("%6d", kevb->kep_subclass_filter); + if (bflag > 0) { + int i; + u_int64_t rxbytes = 0; + u_int64_t txbytes = 0; + + for (i = 0; i < SO_TC_STATS_MAX; i++) { + rxbytes += so_stat->xst_tc_stats[i].rxbytes; + txbytes += so_stat->xst_tc_stats[i].txbytes; + } + printf("%10llu %10llu ", rxbytes, txbytes); + } + if (vflag > 0) { + printf("%6u %6u %6u %6u", + so_rcv->sb_hiwat, + so_snd->sb_hiwat, + so->so_last_pid, + so->so_e_pid); + } + printf("\n"); + } + + } + if (xig != oxig && xig->xg_gen != oxig->xg_gen) { + if (oxig->xg_count > xig->xg_count) { + printf("Some %s sockets may have been deleted.\n", + name); + } else if (oxig->xg_count < xig->xg_count) { + printf("Some %s sockets may have been created.\n", + name); + } else { + printf("Some %s sockets may have been created or deleted", + name); + } + } + free(buf); +} + +void +kctl_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct kctlstat pkctlstat; + struct kctlstat kctlstat; + size_t len = sizeof(struct kctlstat); + const char *mibvar = "net.systm.kctl.stats"; + + if (sysctlbyname(mibvar, &kctlstat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define STATDIFF(f) (kctlstat.f - pkctlstat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + p(kcs_reg_total, "\t%llu total kernel control module%s registered\n"); + p(kcs_reg_count, "\t%llu current kernel control module%s registered\n"); + p(kcs_pcbcount, "\t%llu current kernel control socket%s\n"); + p1a(kcs_gencnt, "\t%llu kernel control generation count\n"); + p(kcs_connections, "\t%llu connection attempt%s\n"); + p(kcs_conn_fail, "\t%llu connection failure%s\n"); + p(kcs_send_fail, "\t%llu send failure%s\n"); + p(kcs_send_list_fail, "\t%llu send list failure%s\n"); + p(kcs_enqueue_fail, "\t%llu enqueue failure%s\n"); + p(kcs_enqueue_fullsock, "\t%llu packet%s dropped due to full socket buffers\n"); + p(kcs_bad_kctlref, "\t%llu failure%s with bad kern_ctl_ref\n"); + p(kcs_tbl_size_too_big, "\t%llu register failure%s because of too many kern_ctl_ref\n"); + p(kcs_enqdata_mb_alloc_fail, "\t%llu enqueuedata failure%s because could not allocate a packet\n"); + p(kcs_enqdata_sbappend_fail, "\t%llu enqueuedata failure%s due to full socket buffers\n"); + +#undef STATDIFF +#undef p +#undef p1a + + if (interval > 0) + bcopy(&kctlstat, &pkctlstat, len); +} + +void +kevt_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct kevtstat pkevtstat; + struct kevtstat kevtstat; + size_t len = sizeof(struct kevtstat); + const char *mibvar = "net.systm.kevt.stats"; + + if (sysctlbyname(mibvar, &kevtstat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + +#define STATDIFF(f) (kevtstat.f - pkevtstat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + p(kes_pcbcount, "\t%llu current kernel control socket%s\n"); + p1a(kes_gencnt, "\t%llu kernel control generation count\n"); + p(kes_badvendor, "\t%llu bad vendor failure%s\n"); + p(kes_toobig, "\t%llu message too big failure%s\n"); + p(kes_nomem, "\t%llu out of memory failure%s\n"); + p(kes_fullsock, "\t%llu message%s dropped due to full socket buffers\n"); + p(kes_posted, "\t%llu message%s posted\n"); + +#undef STATDIFF +#undef p +#undef p1a + + if (interval > 0) + bcopy(&kevtstat, &pkevtstat, len); +} + +void +print_extbkidle_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct soextbkidlestat psoextbkidlestat; + struct soextbkidlestat soextbkidlestat; + size_t len = sizeof(struct soextbkidlestat); + const char *mibvar = "kern.ipc.extbkidlestat"; + + if (sysctlbyname(mibvar, &soextbkidlestat, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (soextbkidlestat.f - psoextbkidlestat.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ + printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p1a(so_xbkidle_maxperproc, "\t%u max per process\n"); + p1a(so_xbkidle_time, "\t%u maximum time (seconds)\n"); + p1a(so_xbkidle_rcvhiwat, "\t%u high water mark\n"); + p(so_xbkidle_notsupp, "\t%u socket option not supported failure%s\n"); + p(so_xbkidle_toomany, "\t%u too many sockets failure%s\n"); + p(so_xbkidle_wantok, "\t%u total socket%s requested OK\n"); + p(so_xbkidle_active, "\t%u extended bk idle socket%s\n"); + p(so_xbkidle_nocell, "\t%u no cellular failure%s\n"); + p(so_xbkidle_notime, "\t%u no time failures%s\n"); + p(so_xbkidle_forced, "\t%u forced defunct socket%s\n"); + p(so_xbkidle_resumed, "\t%u resumed socket%s\n"); + p(so_xbkidle_expired, "\t%u timeout expired failure%s\n"); + p1a(so_xbkidle_expired, "\t%u timer rescheduled\n"); + p(so_xbkidle_nodlgtd, "\t%u no delegated failure%s\n"); + +#undef STATDIFF +#undef p +#undef p1a +} + +void +print_nstat_stats(uint32_t off __unused, char *name, int af __unused) +{ + static struct nstat_stats pnstat_stats; + struct nstat_stats nstat_stats; + size_t len = sizeof(struct nstat_stats); + const char *mibvar = "net.stats.stats"; + + if (sysctlbyname(mibvar, &nstat_stats, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + return; + } + +#define STATDIFF(f) (nstat_stats.f - pnstat_stats.f) +#define p(f, m) if (STATDIFF(f) || sflag <= 1) \ +printf(m, STATDIFF(f), plural(STATDIFF(f))) +#define p1a(f, m) if (STATDIFF(f) || sflag <= 1) \ +printf(m, STATDIFF(f)) + + if (interval && vflag > 0) + print_time(); + printf ("%s:\n", name); + + p(nstat_successmsgfailures, "\t%u enqueue success message failure%s\n"); + p(nstat_sendcountfailures, "\t%u enqueue source counts message failure%s\n"); + p(nstat_sysinfofailures, "\t%u enqueue sysinfo message failure%s\n"); + p(nstat_srcupatefailures, "\t%u enqueue source udpate message failure%s\n"); + p(nstat_descriptionfailures, "\t%u enqueue description message failure%s\n"); + p(nstat_msgremovedfailures, "\t%u enqueue remove message failure%s\n"); + p(nstat_srcaddedfailures, "\t%u enqueue source added message failure%s\n"); + p(nstat_msgerrorfailures, "\t%u enqueue error message failure%s\n"); + p(nstat_copy_descriptor_failures, "\t%u copy descriptor failure%s\n"); + p(nstat_provider_counts_failures, "\t%u provider counts failure%s\n"); + p(nstat_control_send_description_failures, "\t%u control send description failure%s\n"); + p(nstat_control_send_goodbye_failures, "\t%u control send goodbye failure%s\n"); + p(nstat_flush_accumulated_msgs_failures, "\t%u flush accumulated messages failure%s\n"); + p(nstat_accumulate_msg_failures, "\t%u accumulated message failure%s\n"); + p(nstat_control_cleanup_source_failures, "\t%u control cleanup source failure%s\n"); + p(nstat_handle_msg_failures, "\t%u handle message failure%s\n"); + +#undef STATDIFF +#undef p +#undef p1a +} diff --git a/network_cmds/netstat.tproj/tp_astring.c b/network_cmds/netstat.tproj/tp_astring.c new file mode 100644 index 0000000..af08ceb --- /dev/null +++ b/network_cmds/netstat.tproj/tp_astring.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1991, 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. 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. + * + * @(#)tp_astring.c 8.1 (Berkeley) 6/10/93 + */ + +char *tp_sstring[] = { +"ST_ERROR(0x0)", +"TP_CLOSED(0x1)", +"TP_CRSENT(0x2)", +"TP_AKWAIT(0x3)", +"TP_OPEN(0x4)", +"TP_CLOSING(0x5)", +"TP_REFWAIT(0x6)", +"TP_LISTENING(0x7)", +"TP_CONFIRMING(0x8)", +}; + +char *tp_estring[] = { +"TM_inact(0x0)", +"TM_retrans(0x1)", +"TM_sendack(0x2)", +"TM_notused(0x3)", +"TM_reference(0x4)", +"TM_data_retrans(0x5)", +"ER_TPDU(0x6)", +"CR_TPDU(0x7)", +"DR_TPDU(0x8)", +"DC_TPDU(0x9)", +"CC_TPDU(0xa)", +"AK_TPDU(0xb)", +"DT_TPDU(0xc)", +"XPD_TPDU(0xd)", +"XAK_TPDU(0xe)", +"T_CONN_req(0xf)", +"T_DISC_req(0x10)", +"T_LISTEN_req(0x11)", +"T_DATA_req(0x12)", +"T_XPD_req(0x13)", +"T_USR_rcvd(0x14)", +"T_USR_Xrcvd(0x15)", +"T_DETACH(0x16)", +"T_NETRESET(0x17)", +"T_ACPT_req(0x18)", +}; diff --git a/network_cmds/netstat.tproj/unix.c b/network_cmds/netstat.tproj/unix.c new file mode 100644 index 0000000..eece65a --- /dev/null +++ b/network_cmds/netstat.tproj/unix.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*- + * 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. 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. + */ + +/* + * Display protocol blocks in the unix domain. + */ +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> +#include <sys/un.h> +#include <sys/unpcb.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <err.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include "netstat.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +static void unixdomainpr __P((struct xunpcb64 *, struct xsocket64 *)); +#else +static void unixdomainpr __P((struct xunpcb *, struct xsocket *)); +#endif + +static const char *const socktype[] = + { "#0", "stream", "dgram", "raw" }; + +void +unixpr() +{ + char *buf; + int type; + size_t len; + struct xunpgen *xug, *oxug; +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct xsocket64 *so; + struct xunpcb64 *xunp; + char mibvar[sizeof "net.local.seqpacket.pcblist64"]; +#else + struct xsocket *so; + struct xunpcb *xunp; + char mibvar[sizeof "net.local.seqpacket.pcblist"]; +#endif + + for (type = SOCK_STREAM; type <= SOCK_RAW; type++) { +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist64", socktype[type]); +#else + snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist", socktype[type]); +#endif + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + continue; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + oxug = xug = (struct xunpgen *)buf; + for (xug = (struct xunpgen *)((char *)xug + xug->xug_len); + xug->xug_len > sizeof(struct xunpgen); + xug = (struct xunpgen *)((char *)xug + xug->xug_len)) { +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + xunp = (struct xunpcb64 *)xug; +#else + xunp = (struct xunpcb *)xug; +#endif + so = &xunp->xu_socket; + + /* Ignore PCBs which were freed during copyout. */ +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (xunp->xunp_gencnt > oxug->xug_gen) +#else + if (xunp->xu_unp.unp_gencnt > oxug->xug_gen) +#endif + continue; + unixdomainpr(xunp, so); + } + if (xug != oxug && xug->xug_gen != oxug->xug_gen) { + if (oxug->xug_count > xug->xug_count) { + printf("Some %s sockets may have been deleted.\n", + socktype[type]); + } else if (oxug->xug_count < xug->xug_count) { + printf("Some %s sockets may have been created.\n", + socktype[type]); + } else { + printf("Some %s sockets may have been created or deleted\n", + socktype[type]); + } + } + free(buf); + } +} + +static void +unixdomainpr(xunp, so) +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct xunpcb64 *xunp; + struct xsocket64 *so; +#else + struct xunpcb *xunp; + struct xsocket *so; +#endif +{ +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + struct unpcb *unp; +#endif + struct sockaddr_un *sa; + static int first = 1; + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + sa = &xunp->xu_addr; +#else + unp = &xunp->xu_unp; + if (unp->unp_addr) + sa = &xunp->xu_addr; + else + sa = (struct sockaddr_un *)0; +#endif + + if (first) { + printf("Active LOCAL (UNIX) domain sockets\n"); + printf( +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +"%-16.16s %-6.6s %-6.6s %-6.6s %16.16s %16.16s %16.16s %16.16s Addr\n", +#else +"%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n", +#endif + "Address", "Type", "Recv-Q", "Send-Q", + "Inode", "Conn", "Refs", "Nextref"); + first = 0; + } +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + printf("%16lx %-6.6s %6u %6u %16lx %16lx %16lx %16lx", + (long)xunp->xu_unpp, socktype[so->so_type], so->so_rcv.sb_cc, + so->so_snd.sb_cc, + (long)xunp->xunp_vnode, (long)xunp->xunp_conn, + (long)xunp->xunp_refs, (long)xunp->xunp_reflink.le_next); +#else + printf("%8lx %-6.6s %6u %6u %8lx %8lx %8lx %8lx", + (long)so->so_pcb, socktype[so->so_type], so->so_rcv.sb_cc, + so->so_snd.sb_cc, + (long)unp->unp_vnode, (long)unp->unp_conn, + (long)unp->unp_refs.lh_first, (long)unp->unp_reflink.le_next); +#endif + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (sa->sun_len) +#else + if (sa) +#endif + printf(" %.*s", + (int)(sa->sun_len - offsetof(struct sockaddr_un, sun_path)), + sa->sun_path); + putchar('\n'); +} diff --git a/network_cmds/netstat.tproj/vsock.c b/network_cmds/netstat.tproj/vsock.c new file mode 100644 index 0000000..b52dc5e --- /dev/null +++ b/network_cmds/netstat.tproj/vsock.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Display protocol blocks in the vsock domain. + */ +#include <sys/proc_info.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/vsock.h> + +#include <errno.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include "netstat.h" + +#ifdef AF_VSOCK + +static void vsockdomainpr __P((struct xvsockpcb *)); + +void +vsockpr(uint32_t proto, +char *name, int af) +{ + char *buf, *next; + size_t len; + struct xvsockpgen *xvg, *oxvg; + struct xvsockpcb *xpcb; + + const char* mibvar = "net.vsock.pcblist"; + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return; + } + if ((buf = malloc(len)) == 0) { + warn("malloc %lu bytes", (u_long)len); + return; + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return; + } + + /* + * Bail-out to avoid logic error in the loop below when + * there is in fact no more control block to process + */ + if (len <= 2 * sizeof(struct xvsockpgen)) { + free(buf); + return; + } + + oxvg = (struct xvsockpgen *)buf; + + // Save room for the last xvsockpgen. + len -= oxvg->xvg_len; + + for (next = buf + oxvg->xvg_len; next < buf + len; next += xpcb->xv_len) { + xpcb = (struct xvsockpcb *)next; + + /* Ignore PCBs which were freed during copyout. */ + if (xpcb->xvp_gencnt > oxvg->xvg_gen) + continue; + vsockdomainpr(xpcb); + } + xvg = (struct xvsockpgen *)next; + if (xvg != oxvg && xvg->xvg_gen != oxvg->xvg_gen) { + if (oxvg->xvg_count > xvg->xvg_count) { + printf("Some vsock sockets may have been deleted.\n"); + } else if (oxvg->xvg_count < xvg->xvg_count) { + printf("Some vsock sockets may have been created.\n"); + } else { + printf("Some vsock sockets may have been created or deleted.\n"); + } + } + free(buf); +} + +static void +vsock_print_addr(buf, cid, port) + char *buf; + uint32_t cid; + uint32_t port; +{ + if (cid == VMADDR_CID_ANY && port == VMADDR_PORT_ANY) { + (void) sprintf(buf, "*:*"); + } else if (cid == VMADDR_CID_ANY) { + (void) sprintf(buf, "*:%u", port); + } else if (port == VMADDR_PORT_ANY) { + (void) sprintf(buf, "%u:*", cid); + } else { + (void) sprintf(buf, "%u:%u", cid, port); + } +} + +static void +vsockdomainpr(xpcb) + struct xvsockpcb *xpcb; +{ + static int first = 1; + + if (first) { + printf("Active VSock sockets\n"); + printf("%-5.5s %-6.6s %-6.6s %-6.6s %-18.18s %-18.18s %-11.11s", + "Proto", "Type", + "Recv-Q", "Send-Q", + "Local Address", "Foreign Address", + "State"); + if (vflag > 0) + printf(" %10.10s %10.10s %10.10s %10.10s %6.6s %6.6s %6.6s %6s %10s", + "rxcnt", "txcnt", "peer_rxcnt", "peer_rxhiwat", + "rxhiwat", "txhiwat", "pid", "state", "options"); + printf("\n"); + first = 0; + } + + struct xsocket *so = &xpcb->xv_socket; + + char srcAddr[50]; + char dstAddr[50]; + + vsock_print_addr(srcAddr, xpcb->xvp_local_cid, xpcb->xvp_local_port); + vsock_print_addr(dstAddr, xpcb->xvp_remote_cid, xpcb->xvp_remote_port); + + // Determine the vsock socket state. + char *state; + if (so->so_state & SOI_S_ISCONNECTING) { + state = "CONNECTING"; + } else if (so->so_state & SOI_S_ISCONNECTED) { + state = "ESTABLISHED"; + } else if (so->so_state & SOI_S_ISDISCONNECTING) { + state = "CLOSING"; + } else if (so->so_options & SO_ACCEPTCONN) { + state = "LISTEN"; + } else { + state = "CLOSED"; + } + + printf("%-5.5s %-6.6s %6u %6u %-18s %-18s %-11s", + "vsock", "stream", + so->so_rcv.sb_cc, so->so_snd.sb_cc, + srcAddr, dstAddr, + state); + if (vflag > 0) + printf(" %10u %10u %10u %10u %6u %6u %6u 0x%04x 0x%08x", + xpcb->xvp_rxcnt, + xpcb->xvp_txcnt, + xpcb->xvp_peer_rxcnt, + xpcb->xvp_peer_rxhiwat, + so->so_rcv.sb_hiwat, + so->so_snd.sb_hiwat, + xpcb->xvp_last_pid, + so->so_state, + so->so_options); + printf("\n"); +} + +#endif /* AF_VSOCK */ |