diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c | 5950 |
1 files changed, 5950 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c new file mode 100644 index 0000000000..f978ae489e --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c @@ -0,0 +1,5950 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 1990 Mentat Inc. + * netstat.c 2.2, last change 9/9/91 + * MROUTING Revision 3.5 + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * simple netstat based on snmp/mib-2 interface to the TCP/IP stack + * + * NOTES: + * 1. A comment "LINTED: (note 1)" appears before certain lines where + * lint would have complained, "pointer cast may result in improper + * alignment". These are lines where lint had suspected potential + * improper alignment of a data structure; in each such situation + * we have relied on the kernel guaranteeing proper alignment. + * 2. Some 'for' loops have been commented as "'for' loop 1", etc + * because they have 'continue' or 'break' statements in their + * bodies. 'continue' statements have been used inside some loops + * where avoiding them would have led to deep levels of indentation. + * + * TODO: + * Add ability to request subsets from kernel (with level = MIB2_IP; + * name = 0 meaning everything for compatibility) + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <kstat.h> +#include <assert.h> + +#include <sys/types.h> +#include <sys/stream.h> +#include <stropts.h> +#include <sys/strstat.h> +#include <sys/sysmacros.h> +#include <sys/tihdr.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <net/if.h> +#include <net/route.h> + +#include <inet/common.h> +#include <inet/mib2.h> +#include <inet/ip.h> +#include <inet/arp.h> +#include <inet/tcp.h> +#include <netinet/igmp_var.h> +#include <netinet/ip_mroute.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <sys/systeminfo.h> +#include <arpa/inet.h> + +#include <netinet/dhcp.h> +#include <dhcpagent_ipc.h> +#include <dhcpagent_util.h> +#include <compat.h> + +extern void unixpr(kstat_ctl_t *kc); +extern void setifdhcp(const char *caller, const char *ifname, + int argc, char *argv[]); + +#define STR_EXPAND 4 + +#define V4MASK_TO_V6(v4, v6) ((v6)._S6_un._S6_u32[0] = 0xfffffffful, \ + (v6)._S6_un._S6_u32[1] = 0xfffffffful, \ + (v6)._S6_un._S6_u32[2] = 0xfffffffful, \ + (v6)._S6_un._S6_u32[3] = (v4)) + +#define IN6_IS_V4MASK(v6) ((v6)._S6_un._S6_u32[0] == 0xfffffffful && \ + (v6)._S6_un._S6_u32[1] == 0xfffffffful && \ + (v6)._S6_un._S6_u32[2] == 0xfffffffful) + +typedef struct mib_item_s { + struct mib_item_s *next_item; + int group; + int mib_id; + int length; + void *valp; +} mib_item_t; + +struct ifstat { + uint64_t ipackets; + uint64_t ierrors; + uint64_t opackets; + uint64_t oerrors; + uint64_t collisions; +}; + +struct iflist { + struct iflist *next_if; + char ifname[LIFNAMSIZ]; + struct ifstat tot; +}; + +static mib_item_t *mibget(int sd); +static void mibfree(mib_item_t *firstitem); +static int mibopen(void); +static void mib_get_constants(mib_item_t *item); +static mib_item_t *mib_item_dup(mib_item_t *item); +static mib_item_t *mib_item_diff(mib_item_t *item1, + mib_item_t *item2); +static void mib_item_destroy(mib_item_t **item); + +static boolean_t octetstrmatch(const Octet_t *a, const Octet_t *b); +static char *octetstr(Octet_t *op, int code, + char *dst, uint_t dstlen); +static char *pr_addr(uint_t addr, + char *dst, uint_t dstlen); +static char *pr_addrnz(ipaddr_t addr, char *dst, uint_t dstlen); +static char *pr_addr6(const in6_addr_t *addr, + char *dst, uint_t dstlen); +static char *pr_mask(uint_t addr, + char *dst, uint_t dstlen); +static char *pr_prefix6(struct in6_addr *addr, uint_t prefixlen, + char *dst, uint_t dstlen); +static char *pr_ap(uint_t addr, uint_t port, + char *proto, char *dst, uint_t dstlen); +static char *pr_ap6(const in6_addr_t *addr, uint_t port, + char *proto, char *dst, uint_t dstlen); +static char *pr_net(uint_t addr, uint_t mask, + char *dst, uint_t dstlen); +static char *pr_netaddr(uint_t addr, uint_t mask, + char *dst, uint_t dstlen); +static char *pr_netclassless(ipaddr_t addr, ipaddr_t mask, + char *dst, size_t dstlen); +static char *fmodestr(uint_t fmode); +static char *portname(uint_t port, char *proto, + char *dst, uint_t dstlen); + +static char *mitcp_state(int code); + +static void stat_report(mib_item_t *item); +static void mrt_stat_report(mib_item_t *item); +static void arp_report(mib_item_t *item); +static void ndp_report(mib_item_t *item); +static void mrt_report(mib_item_t *item); +static void if_stat_total(struct ifstat *oldstats, + struct ifstat *newstats, struct ifstat *sumstats); +static void if_report(mib_item_t *item, char *ifname, + int Iflag_only, boolean_t once_only); +static void if_report_ip4(mib2_ipAddrEntry_t *ap, + char ifname[], char logintname[], + struct ifstat *statptr, boolean_t ksp_not_null); +static void if_report_ip6(mib2_ipv6AddrEntry_t *ap6, + char ifname[], char logintname[], + struct ifstat *statptr, boolean_t ksp_not_null); +static void ire_report(mib_item_t *item); +static void tcp_report(mib_item_t *item); +static void udp_report(mib_item_t *item); +static void group_report(mib_item_t *item); +static void print_ip_stats(mib2_ip_t *ip); +static void print_icmp_stats(mib2_icmp_t *icmp); +static void print_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6); +static void print_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6); +static void print_sctp_stats(mib2_sctp_t *tcp); +static void print_tcp_stats(mib2_tcp_t *tcp); +static void print_udp_stats(mib2_udp_t *udp); +static void print_rawip_stats(mib2_rawip_t *rawip); +static void print_igmp_stats(struct igmpstat *igps); +static void print_mrt_stats(struct mrtstat *mrts); +static void sctp_report(mib_item_t *item); +static void sum_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6, + mib2_ipv6IfStatsEntry_t *sum6); +static void sum_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6, + mib2_ipv6IfIcmpEntry_t *sum6); +static void m_report(void); +static void dhcp_report(char *); + + void fail(int, char *, ...); +static uint64_t kstat_named_value(kstat_t *, char *); +static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *); +static int isnum(char *); +static char *plural(int n); +static char *pluraly(int n); +static char *plurales(int n); +static void process_filter(char *arg); +static boolean_t family_selected(int family); + +static void usage(char *); +static void fatal(int errcode, char *str1, ...); + +#define PLURAL(n) plural((int)n) +#define PLURALY(n) pluraly((int)n) +#define PLURALES(n) plurales((int)n) +#define IFLAGMOD(flg, val1, val2) if (flg == val1) flg = val2 +#define MDIFF(diff, elem2, elem1, member) (diff)->member = \ + (elem2)->member - (elem1)->member + + +static boolean_t Aflag = B_FALSE; /* All sockets/ifs/rtng-tbls */ +static boolean_t Dflag = B_FALSE; /* Debug Info */ +static boolean_t Iflag = B_FALSE; /* IP Traffic Interfaces */ +static boolean_t Mflag = B_FALSE; /* STREAMS Memory Statistics */ +static boolean_t Nflag = B_FALSE; /* Numeric Network Addresses */ +static boolean_t Rflag = B_FALSE; /* Routing Tables */ +static boolean_t Sflag = B_FALSE; /* Per-protocol Statistics */ +static boolean_t Vflag = B_FALSE; /* Verbose */ +static boolean_t Pflag = B_FALSE; /* Net to Media Tables */ +static boolean_t Gflag = B_FALSE; /* Multicast group membership */ +static boolean_t MMflag = B_FALSE; /* Multicast routing table */ +static boolean_t DHCPflag = B_FALSE; /* DHCP statistics */ + +static int v4compat = 0; /* Compatible printing format for status */ + +static int proto = IPPROTO_MAX; /* all protocols */ +kstat_ctl_t *kc = NULL; + +/* + * Sizes of data structures extracted from the base mib. + * This allows the size of the tables entries to grow while preserving + * binary compatibility. + */ +static int ipAddrEntrySize; +static int ipRouteEntrySize; +static int ipNetToMediaEntrySize; +static int ipMemberEntrySize; +static int ipGroupSourceEntrySize; +static int vifctlSize; +static int mfcctlSize; + +static int ipv6IfStatsEntrySize; +static int ipv6IfIcmpEntrySize; +static int ipv6AddrEntrySize; +static int ipv6RouteEntrySize; +static int ipv6NetToMediaEntrySize; +static int ipv6MemberEntrySize; +static int ipv6GroupSourceEntrySize; + +static int tcpConnEntrySize; +static int tcp6ConnEntrySize; +static int udpEntrySize; +static int udp6EntrySize; +static int sctpEntrySize; +static int sctpLocalEntrySize; +static int sctpRemoteEntrySize; + +#define protocol_selected(p) (proto == IPPROTO_MAX || proto == (p)) + +/* Machinery used for -f (filter) option */ +#define FK_AF 0 +#define FK_INIF 1 +#define FK_OUTIF 2 +#define FK_SRC 3 +#define FK_DST 4 +#define FK_FLAGS 5 +#define NFILTERKEYS 6 + +static const char *filter_keys[NFILTERKEYS] = { + "af", "inif", "outif", "src", "dst", "flags" +}; + +/* Flags on routes */ +#define FLF_A 0x00000001 +#define FLF_B 0x00000002 +#define FLF_D 0x00000004 +#define FLF_G 0x00000008 +#define FLF_H 0x00000010 +#define FLF_L 0x00000020 +#define FLF_U 0x00000040 +#define FLF_M 0x00000080 +#define FLF_S 0x00000100 +static const char flag_list[] = "ABDGHLUMS"; + +typedef struct filter_rule filter_t; + +struct filter_rule { + filter_t *f_next; + union { + int f_family; + const char *f_ifname; + struct { + struct hostent *f_address; + in6_addr_t f_mask; + } a; + struct { + uint_t f_flagset; + uint_t f_flagclear; + } f; + } u; +}; + +/* + * The user-specified filters are linked into lists separated by + * keyword (type of filter). Thus, the matching algorithm is: + * For each non-empty filter list + * If no filters in the list match + * then stop here; route doesn't match + * If loop above completes, then route does match and will be + * displayed. + */ +static filter_t *filters[NFILTERKEYS]; + +int +main(int argc, char **argv) +{ + char *name; + mib_item_t *item = NULL; + mib_item_t *previtem = NULL; + int sd = -1; + char *ifname = NULL; + int interval = 0; /* Single time by default */ + int count = -1; /* Forever */ + int c; + int d; + /* + * Possible values of 'Iflag_only': + * -1, no feature-flags; + * 0, IFlag and other feature-flags enabled + * 1, IFlag is the only feature-flag enabled + * : trinary variable, modified using IFLAGMOD() + */ + int Iflag_only = -1; + boolean_t once_only = B_FALSE; /* '-i' with count > 1 */ + extern char *optarg; + extern int optind; + char *default_ip_str = NULL; + + name = argv[0]; + + v4compat = get_compat_flag(&default_ip_str); + if (v4compat == DEFAULT_PROT_BAD_VALUE) + fatal(2, "%s: %s: Bad value for %s in %s\n", name, + default_ip_str, DEFAULT_IP, INET_DEFAULT_FILE); + free(default_ip_str); + + while ((c = getopt(argc, argv, "adimnrspMgvf:P:I:D")) != -1) { + switch ((char)c) { + case 'a': /* all connections */ + Aflag = B_TRUE; + break; + + case 'd': /* turn on debugging */ + Dflag = B_TRUE; + break; + + case 'i': /* interface (ill/ipif report) */ + Iflag = B_TRUE; + IFLAGMOD(Iflag_only, -1, 1); /* '-i' exists */ + break; + + case 'm': /* streams msg report */ + Mflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'n': /* numeric format */ + Nflag = B_TRUE; + break; + + case 'r': /* route tables */ + Rflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 's': /* per-protocol statistics */ + Sflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'p': /* arp/ndp table */ + Pflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'M': /* multicast routing tables */ + MMflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'g': /* multicast group membership */ + Gflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'v': /* verbose output format */ + Vflag = B_TRUE; + IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */ + break; + + case 'f': + process_filter(optarg); + break; + + case 'P': + if (strcmp(optarg, "ip") == 0) { + proto = IPPROTO_IP; + } else if (strcmp(optarg, "ipv6") == 0 || + strcmp(optarg, "ip6") == 0) { + v4compat = 0; /* Overridden */ + proto = IPPROTO_IPV6; + } else if (strcmp(optarg, "icmp") == 0) { + proto = IPPROTO_ICMP; + } else if (strcmp(optarg, "icmpv6") == 0 || + strcmp(optarg, "icmp6") == 0) { + v4compat = 0; /* Overridden */ + proto = IPPROTO_ICMPV6; + } else if (strcmp(optarg, "igmp") == 0) { + proto = IPPROTO_IGMP; + } else if (strcmp(optarg, "udp") == 0) { + proto = IPPROTO_UDP; + } else if (strcmp(optarg, "tcp") == 0) { + proto = IPPROTO_TCP; + } else if (strcmp(optarg, "sctp") == 0) { + proto = IPPROTO_SCTP; + } else if (strcmp(optarg, "raw") == 0 || + strcmp(optarg, "rawip") == 0) { + proto = IPPROTO_RAW; + } else { + fatal(1, "%s: unknown protocol.\n", optarg); + } + break; + + case 'I': + ifname = optarg; + Iflag = B_TRUE; + IFLAGMOD(Iflag_only, -1, 1); /* see macro def'n */ + break; + + case 'D': + DHCPflag = B_TRUE; + Iflag_only = 0; + break; + + case '?': + default: + usage(name); + } + } + + /* + * Handle other arguments: find interval, count; the + * flags that accept 'interval' and 'count' are OR'd + * in the outermost 'if'; more flags may be added as + * required + */ + if (Iflag || Sflag || Mflag) { + for (d = optind; d < argc; d++) { + if (isnum(argv[d])) { + interval = atoi(argv[d]); + if (d + 1 < argc && + isnum(argv[d + 1])) { + count = atoi(argv[d + 1]); + optind++; + } + optind++; + if (interval == 0 || count == 0) + usage(name); + break; + } + } + } + if (optind < argc) { + if (Iflag && isnum(argv[optind])) { + count = atoi(argv[optind]); + if (count == 0) + usage(name); + optind++; + } + } + if (optind < argc) { + (void) fprintf(stderr, + "%s: extra arguments\n", name); + usage(name); + } + if (interval) + setbuf(stdout, NULL); + + if (DHCPflag) { + dhcp_report(Iflag ? ifname : NULL); + exit(0); + } + + /* Get data structures: priming before iteration */ + if (family_selected(AF_INET) || family_selected(AF_INET6)) { + sd = mibopen(); + if (sd == -1) + fatal(1, "can't open mib stream\n"); + if ((item = mibget(sd)) == NULL) { + (void) close(sd); + fatal(1, "mibget() failed\n"); + } + /* Extract constant sizes - need do once only */ + mib_get_constants(item); + } + if ((kc = kstat_open()) == NULL) { + mibfree(item); + (void) close(sd); + fail(1, "kstat_open(): can't open /dev/kstat"); + } + + if (interval <= 0) { + count = 1; + once_only = B_TRUE; + } + /* 'for' loop 1: */ + for (;;) { + mib_item_t *curritem = NULL; /* only for -[M]s */ + + /* netstat: AF_INET[6] behaviour */ + if (family_selected(AF_INET) || family_selected(AF_INET6)) { + if (Sflag) { + curritem = mib_item_diff(previtem, item); + if (curritem == NULL) + fatal(1, "can't process mib data, " + "out of memory\n"); + mib_item_destroy(&previtem); + } + + if (!(Iflag || Rflag || Sflag || Mflag || + MMflag || Pflag || Gflag || DHCPflag)) { + if (protocol_selected(IPPROTO_UDP)) + udp_report(item); + if (protocol_selected(IPPROTO_TCP)) + tcp_report(item); + if (protocol_selected(IPPROTO_SCTP)) + sctp_report(item); + } + if (Iflag) + if_report(item, ifname, Iflag_only, once_only); + if (Mflag) + m_report(); + if (Rflag) + ire_report(item); + if (Sflag && MMflag) { + mrt_stat_report(curritem); + } else { + if (Sflag) + stat_report(curritem); + if (MMflag) + mrt_report(item); + } + if (Gflag) + group_report(item); + if (Pflag) { + if (family_selected(AF_INET)) + arp_report(item); + if (family_selected(AF_INET6)) + ndp_report(item); + } + mib_item_destroy(&curritem); + } + + /* netstat: AF_UNIX behaviour */ + if (family_selected(AF_UNIX) && + (!(Iflag || Rflag || Sflag || Mflag || + MMflag || Pflag || Gflag))) + unixpr(kc); + (void) kstat_close(kc); + + /* iteration handling code */ + if (count > 0 && --count == 0) + break; + (void) sleep(interval); + + /* re-populating of data structures */ + if (family_selected(AF_INET) || family_selected(AF_INET6)) { + if (Sflag) { + /* previtem is a cut-down list */ + previtem = mib_item_dup(item); + if (previtem == NULL) + fatal(1, "can't process mib data, " + "out of memory\n"); + } + mibfree(item); + (void) close(sd); + if ((sd = mibopen()) == -1) + fatal(1, "can't open mib stream anymore\n"); + if ((item = mibget(sd)) == NULL) { + (void) close(sd); + fatal(1, "mibget() failed\n"); + } + } + if ((kc = kstat_open()) == NULL) + fail(1, "kstat_open(): can't open /dev/kstat"); + + } /* 'for' loop 1 ends */ + mibfree(item); + (void) close(sd); + + return (0); +} + + +static int +isnum(char *p) +{ + int len; + int i; + + len = strlen(p); + for (i = 0; i < len; i++) + if (!isdigit(p[i])) + return (0); + return (1); +} + + +/* --------------------------------- MIBGET -------------------------------- */ + +static mib_item_t * +mibget(int sd) +{ + /* + * buf is an automatic for this function, so the + * compiler has complete control over its alignment; + * it is assumed this alignment is satisfactory for + * it to be casted to certain other struct pointers + * here, such as struct T_optmgmt_ack * . + */ + uintptr_t buf[512 / sizeof (uintptr_t)]; + int flags; + int i, j, getcode; + struct strbuf ctlbuf, databuf; + struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; + struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; + struct T_error_ack *tea = (struct T_error_ack *)buf; + struct opthdr *req; + mib_item_t *first_item = NULL; + mib_item_t *last_item = NULL; + mib_item_t *temp; + + tor->PRIM_type = T_SVR4_OPTMGMT_REQ; + tor->OPT_offset = sizeof (struct T_optmgmt_req); + tor->OPT_length = sizeof (struct opthdr); + tor->MGMT_flags = T_CURRENT; + req = (struct opthdr *)&tor[1]; + req->level = MIB2_IP; /* any MIB2_xxx value ok here */ + req->name = 0; + req->len = 0; + + ctlbuf.buf = (char *)buf; + ctlbuf.len = tor->OPT_length + tor->OPT_offset; + flags = 0; + if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) { + perror("mibget: putmsg(ctl) failed"); + goto error_exit; + } + + /* + * Each reply consists of a ctl part for one fixed structure + * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, + * containing an opthdr structure. level/name identify the entry, + * len is the size of the data part of the message. + */ + req = (struct opthdr *)&toa[1]; + ctlbuf.maxlen = sizeof (buf); + j = 1; + for (;;) { + flags = 0; + getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags); + if (getcode == -1) { + perror("mibget getmsg(ctl) failed"); + if (Dflag) { + (void) fputs("# level name len\n", + stderr); + i = 0; + for (last_item = first_item; last_item; + last_item = last_item->next_item) + (void) printf("%d %4d %5d %d\n", + ++i, + last_item->group, + last_item->mib_id, + last_item->length); + } + goto error_exit; + } + if (getcode == 0 && + ctlbuf.len >= sizeof (struct T_optmgmt_ack) && + toa->PRIM_type == T_OPTMGMT_ACK && + toa->MGMT_flags == T_SUCCESS && + req->len == 0) { + if (Dflag) + (void) printf("mibget getmsg() %d returned " + "EOD (level %ld, name %ld)\n", + j, req->level, req->name); + return (first_item); /* this is EOD msg */ + } + + if (ctlbuf.len >= sizeof (struct T_error_ack) && + tea->PRIM_type == T_ERROR_ACK) { + (void) fprintf(stderr, + "mibget %d gives T_ERROR_ACK: TLI_error = 0x%lx, " + "UNIX_error = 0x%lx\n", + j, tea->TLI_error, tea->UNIX_error); + + errno = (tea->TLI_error == TSYSERR) ? + tea->UNIX_error : EPROTO; + goto error_exit; + } + + if (getcode != MOREDATA || + ctlbuf.len < sizeof (struct T_optmgmt_ack) || + toa->PRIM_type != T_OPTMGMT_ACK || + toa->MGMT_flags != T_SUCCESS) { + (void) printf("mibget getmsg(ctl) %d returned %d, " + "ctlbuf.len = %d, PRIM_type = %ld\n", + j, getcode, ctlbuf.len, toa->PRIM_type); + + if (toa->PRIM_type == T_OPTMGMT_ACK) + (void) printf("T_OPTMGMT_ACK: " + "MGMT_flags = 0x%lx, req->len = %ld\n", + toa->MGMT_flags, req->len); + errno = ENOMSG; + goto error_exit; + } + + temp = (mib_item_t *)malloc(sizeof (mib_item_t)); + if (temp == NULL) { + perror("mibget malloc failed"); + goto error_exit; + } + if (last_item != NULL) + last_item->next_item = temp; + else + first_item = temp; + last_item = temp; + last_item->next_item = NULL; + last_item->group = req->level; + last_item->mib_id = req->name; + last_item->length = req->len; + last_item->valp = malloc((int)req->len); + if (last_item->valp == NULL) + goto error_exit; + if (Dflag) + (void) printf("msg %d: group = %4d mib_id = %5d" + "length = %d\n", + j, last_item->group, last_item->mib_id, + last_item->length); + + databuf.maxlen = last_item->length; + databuf.buf = (char *)last_item->valp; + databuf.len = 0; + flags = 0; + getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags); + if (getcode == -1) { + perror("mibget getmsg(data) failed"); + goto error_exit; + } else if (getcode != 0) { + (void) printf("mibget getmsg(data) returned %d, " + "databuf.maxlen = %d, databuf.len = %d\n", + getcode, databuf.maxlen, databuf.len); + goto error_exit; + } + j++; + } + /* NOTREACHED */ + +error_exit:; + mibfree(first_item); + return (NULL); +} + +/* + * mibfree: frees a linked list of type (mib_item_t *) + * returned by mibget(); this is NOT THE SAME AS + * mib_item_destroy(), so should be used for objects + * returned by mibget() only + */ +static void +mibfree(mib_item_t *firstitem) +{ + mib_item_t *lastitem; + + while (firstitem != NULL) { + lastitem = firstitem; + firstitem = firstitem->next_item; + if (lastitem->valp != NULL) + free(lastitem->valp); + free(lastitem); + } +} + +static int +mibopen(void) +{ + int sd; + + sd = open("/dev/arp", O_RDWR); + if (sd == -1) { + perror("arp open"); + return (-1); + } + if (ioctl(sd, I_PUSH, "tcp") == -1) { + perror("tcp I_PUSH"); + (void) close(sd); + return (-1); + } + if (ioctl(sd, I_PUSH, "udp") == -1) { + perror("udp I_PUSH"); + (void) close(sd); + return (-1); + } + if (ioctl(sd, I_PUSH, "icmp") == -1) { + perror("icmp I_PUSH"); + (void) close(sd); + return (-1); + } + return (sd); +} + +/* + * mib_item_dup: returns a clean mib_item_t * linked + * list, so that for every element item->mib_id is 0; + * to deallocate this linked list, use mib_item_destroy + */ +static mib_item_t * +mib_item_dup(mib_item_t *item) +{ + int c = 0; + mib_item_t *localp; + mib_item_t *tempp; + + for (tempp = item; tempp; tempp = tempp->next_item) + if (tempp->mib_id == 0) + c++; + tempp = NULL; + + localp = (mib_item_t *)malloc(c * sizeof (mib_item_t)); + if (localp == NULL) + return (NULL); + c = 0; + for (; item; item = item->next_item) { + if (item->mib_id == 0) { + /* Replicate item in localp */ + (localp[c]).next_item = NULL; + (localp[c]).group = item->group; + (localp[c]).mib_id = item->mib_id; + (localp[c]).length = item->length; + (localp[c]).valp = (uintptr_t *)malloc( + item->length); + if ((localp[c]).valp == NULL) { + mib_item_destroy(&localp); + return (NULL); + } + (void *) memcpy((localp[c]).valp, + item->valp, + item->length); + tempp = &(localp[c]); + if (c > 0) + (localp[c - 1]).next_item = tempp; + c++; + } + } + return (localp); +} + +/* + * mib_item_diff: takes two (mib_item_t *) linked lists + * item1 and item2 and computes the difference between + * differentiable values in item2 against item1 for every + * given member of item2; returns an mib_item_t * linked + * list of diff's, or a copy of item2 if item1 is NULL; + * will return NULL if system out of memory; works only + * for item->mib_id == 0 + */ +static mib_item_t * +mib_item_diff(mib_item_t *item1, mib_item_t *item2) { + int nitems = 0; /* no. of items in item2 */ + mib_item_t *tempp2; /* walking copy of item2 */ + mib_item_t *tempp1; /* walking copy of item1 */ + mib_item_t *diffp; + mib_item_t *diffptr; /* walking copy of diffp */ + mib_item_t *prevp = NULL; + + if (item1 == NULL) { + diffp = mib_item_dup(item2); + return (diffp); + } + + for (tempp2 = item2; + tempp2; + tempp2 = tempp2->next_item) { + if (tempp2->mib_id == 0) + switch (tempp2->group) { + /* + * upon adding a case here, the same + * must also be added in the next + * switch statement, alongwith + * appropriate code + */ + case MIB2_IP: + case MIB2_IP6: + case EXPER_DVMRP: + case EXPER_IGMP: + case MIB2_ICMP: + case MIB2_ICMP6: + case MIB2_TCP: + case MIB2_UDP: + case MIB2_SCTP: + case EXPER_RAWIP: + nitems++; + } + } + tempp2 = NULL; + if (nitems == 0) { + diffp = mib_item_dup(item2); + return (diffp); + } + + diffp = (mib_item_t *)calloc(nitems, sizeof (mib_item_t)); + if (diffp == NULL) + return (NULL); + diffptr = diffp; + /* 'for' loop 1: */ + for (tempp2 = item2; tempp2 != NULL; tempp2 = tempp2->next_item) { + if (tempp2->mib_id != 0) + continue; /* 'for' loop 1 */ + /* 'for' loop 2: */ + for (tempp1 = item1; tempp1 != NULL; + tempp1 = tempp1->next_item) { + if (!(tempp1->mib_id == 0 && + tempp1->group == tempp2->group && + tempp1->mib_id == tempp2->mib_id)) + continue; /* 'for' loop 2 */ + /* found comparable data sets */ + if (prevp != NULL) + prevp->next_item = diffptr; + switch (tempp2->group) { + /* + * Indenting note: Because of long variable names + * in cases MIB2_IP6 and MIB2_ICMP6, their contents + * have been indented by one tab space only + */ + case MIB2_IP: { + mib2_ip_t *i2 = (mib2_ip_t *)tempp2->valp; + mib2_ip_t *i1 = (mib2_ip_t *)tempp1->valp; + mib2_ip_t *d; + + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_ip_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + d->ipForwarding = i2->ipForwarding; + d->ipDefaultTTL = i2->ipDefaultTTL; + MDIFF(d, i2, i1, ipInReceives); + MDIFF(d, i2, i1, ipInHdrErrors); + MDIFF(d, i2, i1, ipInAddrErrors); + MDIFF(d, i2, i1, ipInCksumErrs); + MDIFF(d, i2, i1, ipForwDatagrams); + MDIFF(d, i2, i1, ipForwProhibits); + MDIFF(d, i2, i1, ipInUnknownProtos); + MDIFF(d, i2, i1, ipInDiscards); + MDIFF(d, i2, i1, ipInDelivers); + MDIFF(d, i2, i1, ipOutRequests); + MDIFF(d, i2, i1, ipOutDiscards); + MDIFF(d, i2, i1, ipOutNoRoutes); + MDIFF(d, i2, i1, ipReasmTimeout); + MDIFF(d, i2, i1, ipReasmReqds); + MDIFF(d, i2, i1, ipReasmOKs); + MDIFF(d, i2, i1, ipReasmFails); + MDIFF(d, i2, i1, ipReasmDuplicates); + MDIFF(d, i2, i1, ipReasmPartDups); + MDIFF(d, i2, i1, ipFragOKs); + MDIFF(d, i2, i1, ipFragFails); + MDIFF(d, i2, i1, ipFragCreates); + MDIFF(d, i2, i1, ipRoutingDiscards); + MDIFF(d, i2, i1, tcpInErrs); + MDIFF(d, i2, i1, udpNoPorts); + MDIFF(d, i2, i1, udpInCksumErrs); + MDIFF(d, i2, i1, udpInOverflows); + MDIFF(d, i2, i1, rawipInOverflows); + MDIFF(d, i2, i1, ipsecInSucceeded); + MDIFF(d, i2, i1, ipsecInFailed); + MDIFF(d, i2, i1, ipInIPv6); + MDIFF(d, i2, i1, ipOutIPv6); + MDIFF(d, i2, i1, ipOutSwitchIPv6); + prevp = diffptr++; + break; + } + case MIB2_IP6: { + mib2_ipv6IfStatsEntry_t *i2; + mib2_ipv6IfStatsEntry_t *i1; + mib2_ipv6IfStatsEntry_t *d; + + i2 = (mib2_ipv6IfStatsEntry_t *)tempp2->valp; + i1 = (mib2_ipv6IfStatsEntry_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_ipv6IfStatsEntry_t *)calloc( + tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + d->ipv6Forwarding = i2->ipv6Forwarding; + d->ipv6DefaultHopLimit = + i2->ipv6DefaultHopLimit; + + MDIFF(d, i2, i1, ipv6InReceives); + MDIFF(d, i2, i1, ipv6InHdrErrors); + MDIFF(d, i2, i1, ipv6InTooBigErrors); + MDIFF(d, i2, i1, ipv6InNoRoutes); + MDIFF(d, i2, i1, ipv6InAddrErrors); + MDIFF(d, i2, i1, ipv6InUnknownProtos); + MDIFF(d, i2, i1, ipv6InTruncatedPkts); + MDIFF(d, i2, i1, ipv6InDiscards); + MDIFF(d, i2, i1, ipv6InDelivers); + MDIFF(d, i2, i1, ipv6OutForwDatagrams); + MDIFF(d, i2, i1, ipv6OutRequests); + MDIFF(d, i2, i1, ipv6OutDiscards); + MDIFF(d, i2, i1, ipv6OutNoRoutes); + MDIFF(d, i2, i1, ipv6OutFragOKs); + MDIFF(d, i2, i1, ipv6OutFragFails); + MDIFF(d, i2, i1, ipv6OutFragCreates); + MDIFF(d, i2, i1, ipv6ReasmReqds); + MDIFF(d, i2, i1, ipv6ReasmOKs); + MDIFF(d, i2, i1, ipv6ReasmFails); + MDIFF(d, i2, i1, ipv6InMcastPkts); + MDIFF(d, i2, i1, ipv6OutMcastPkts); + MDIFF(d, i2, i1, ipv6ReasmDuplicates); + MDIFF(d, i2, i1, ipv6ReasmPartDups); + MDIFF(d, i2, i1, ipv6ForwProhibits); + MDIFF(d, i2, i1, udpInCksumErrs); + MDIFF(d, i2, i1, udpInOverflows); + MDIFF(d, i2, i1, rawipInOverflows); + MDIFF(d, i2, i1, ipv6InIPv4); + MDIFF(d, i2, i1, ipv6OutIPv4); + MDIFF(d, i2, i1, ipv6OutSwitchIPv4); + prevp = diffptr++; + break; + } + case EXPER_DVMRP: { + struct mrtstat *m2; + struct mrtstat *m1; + struct mrtstat *d; + + m2 = (struct mrtstat *)tempp2->valp; + m1 = (struct mrtstat *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (struct mrtstat *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, m2, m1, mrts_mfc_hits); + MDIFF(d, m2, m1, mrts_mfc_misses); + MDIFF(d, m2, m1, mrts_fwd_in); + MDIFF(d, m2, m1, mrts_fwd_out); + d->mrts_upcalls = m2->mrts_upcalls; + MDIFF(d, m2, m1, mrts_fwd_drop); + MDIFF(d, m2, m1, mrts_bad_tunnel); + MDIFF(d, m2, m1, mrts_cant_tunnel); + MDIFF(d, m2, m1, mrts_wrong_if); + MDIFF(d, m2, m1, mrts_upq_ovflw); + MDIFF(d, m2, m1, mrts_cache_cleanups); + MDIFF(d, m2, m1, mrts_drop_sel); + MDIFF(d, m2, m1, mrts_q_overflow); + MDIFF(d, m2, m1, mrts_pkt2large); + MDIFF(d, m2, m1, mrts_pim_badversion); + MDIFF(d, m2, m1, mrts_pim_rcv_badcsum); + MDIFF(d, m2, m1, mrts_pim_badregisters); + MDIFF(d, m2, m1, mrts_pim_regforwards); + MDIFF(d, m2, m1, mrts_pim_regsend_drops); + MDIFF(d, m2, m1, mrts_pim_malformed); + MDIFF(d, m2, m1, mrts_pim_nomemory); + prevp = diffptr++; + break; + } + case EXPER_IGMP: { + struct igmpstat *i2; + struct igmpstat *i1; + struct igmpstat *d; + + i2 = (struct igmpstat *)tempp2->valp; + i1 = (struct igmpstat *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (struct igmpstat *)calloc( + tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, i2, i1, igps_rcv_total); + MDIFF(d, i2, i1, igps_rcv_tooshort); + MDIFF(d, i2, i1, igps_rcv_badsum); + MDIFF(d, i2, i1, igps_rcv_queries); + MDIFF(d, i2, i1, igps_rcv_badqueries); + MDIFF(d, i2, i1, igps_rcv_reports); + MDIFF(d, i2, i1, igps_rcv_badreports); + MDIFF(d, i2, i1, igps_rcv_ourreports); + MDIFF(d, i2, i1, igps_snd_reports); + prevp = diffptr++; + break; + } + case MIB2_ICMP: { + mib2_icmp_t *i2; + mib2_icmp_t *i1; + mib2_icmp_t *d; + + i2 = (mib2_icmp_t *)tempp2->valp; + i1 = (mib2_icmp_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_icmp_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, i2, i1, icmpInMsgs); + MDIFF(d, i2, i1, icmpInErrors); + MDIFF(d, i2, i1, icmpInCksumErrs); + MDIFF(d, i2, i1, icmpInUnknowns); + MDIFF(d, i2, i1, icmpInDestUnreachs); + MDIFF(d, i2, i1, icmpInTimeExcds); + MDIFF(d, i2, i1, icmpInParmProbs); + MDIFF(d, i2, i1, icmpInSrcQuenchs); + MDIFF(d, i2, i1, icmpInRedirects); + MDIFF(d, i2, i1, icmpInBadRedirects); + MDIFF(d, i2, i1, icmpInEchos); + MDIFF(d, i2, i1, icmpInEchoReps); + MDIFF(d, i2, i1, icmpInTimestamps); + MDIFF(d, i2, i1, icmpInAddrMasks); + MDIFF(d, i2, i1, icmpInAddrMaskReps); + MDIFF(d, i2, i1, icmpInFragNeeded); + MDIFF(d, i2, i1, icmpOutMsgs); + MDIFF(d, i2, i1, icmpOutDrops); + MDIFF(d, i2, i1, icmpOutErrors); + MDIFF(d, i2, i1, icmpOutDestUnreachs); + MDIFF(d, i2, i1, icmpOutTimeExcds); + MDIFF(d, i2, i1, icmpOutParmProbs); + MDIFF(d, i2, i1, icmpOutSrcQuenchs); + MDIFF(d, i2, i1, icmpOutRedirects); + MDIFF(d, i2, i1, icmpOutEchos); + MDIFF(d, i2, i1, icmpOutEchoReps); + MDIFF(d, i2, i1, icmpOutTimestamps); + MDIFF(d, i2, i1, icmpOutTimestampReps); + MDIFF(d, i2, i1, icmpOutAddrMasks); + MDIFF(d, i2, i1, icmpOutAddrMaskReps); + MDIFF(d, i2, i1, icmpOutFragNeeded); + MDIFF(d, i2, i1, icmpInOverflows); + prevp = diffptr++; + break; + } + case MIB2_ICMP6: { + mib2_ipv6IfIcmpEntry_t *i2; + mib2_ipv6IfIcmpEntry_t *i1; + mib2_ipv6IfIcmpEntry_t *d; + + i2 = (mib2_ipv6IfIcmpEntry_t *)tempp2->valp; + i1 = (mib2_ipv6IfIcmpEntry_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_ipv6IfIcmpEntry_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, i2, i1, ipv6IfIcmpInMsgs); + MDIFF(d, i2, i1, ipv6IfIcmpInErrors); + MDIFF(d, i2, i1, ipv6IfIcmpInDestUnreachs); + MDIFF(d, i2, i1, ipv6IfIcmpInAdminProhibs); + MDIFF(d, i2, i1, ipv6IfIcmpInTimeExcds); + MDIFF(d, i2, i1, ipv6IfIcmpInParmProblems); + MDIFF(d, i2, i1, ipv6IfIcmpInPktTooBigs); + MDIFF(d, i2, i1, ipv6IfIcmpInEchos); + MDIFF(d, i2, i1, ipv6IfIcmpInEchoReplies); + MDIFF(d, i2, i1, ipv6IfIcmpInRouterSolicits); + MDIFF(d, i2, i1, ipv6IfIcmpInRouterAdvertisements); + MDIFF(d, i2, i1, ipv6IfIcmpInNeighborSolicits); + MDIFF(d, i2, i1, ipv6IfIcmpInNeighborAdvertisements); + MDIFF(d, i2, i1, ipv6IfIcmpInRedirects); + MDIFF(d, i2, i1, ipv6IfIcmpInBadRedirects); + MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembQueries); + MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembResponses); + MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembReductions); + MDIFF(d, i2, i1, ipv6IfIcmpInOverflows); + MDIFF(d, i2, i1, ipv6IfIcmpOutMsgs); + MDIFF(d, i2, i1, ipv6IfIcmpOutErrors); + MDIFF(d, i2, i1, ipv6IfIcmpOutDestUnreachs); + MDIFF(d, i2, i1, ipv6IfIcmpOutAdminProhibs); + MDIFF(d, i2, i1, ipv6IfIcmpOutTimeExcds); + MDIFF(d, i2, i1, ipv6IfIcmpOutParmProblems); + MDIFF(d, i2, i1, ipv6IfIcmpOutPktTooBigs); + MDIFF(d, i2, i1, ipv6IfIcmpOutEchos); + MDIFF(d, i2, i1, ipv6IfIcmpOutEchoReplies); + MDIFF(d, i2, i1, ipv6IfIcmpOutRouterSolicits); + MDIFF(d, i2, i1, ipv6IfIcmpOutRouterAdvertisements); + MDIFF(d, i2, i1, ipv6IfIcmpOutNeighborSolicits); + MDIFF(d, i2, i1, ipv6IfIcmpOutNeighborAdvertisements); + MDIFF(d, i2, i1, ipv6IfIcmpOutRedirects); + MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembQueries); + MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembResponses); + MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembReductions); + prevp = diffptr++; + break; + } + case MIB2_TCP: { + mib2_tcp_t *t2; + mib2_tcp_t *t1; + mib2_tcp_t *d; + + t2 = (mib2_tcp_t *)tempp2->valp; + t1 = (mib2_tcp_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_tcp_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + d->tcpRtoMin = t2->tcpRtoMin; + d->tcpRtoMax = t2->tcpRtoMax; + d->tcpMaxConn = t2->tcpMaxConn; + MDIFF(d, t2, t1, tcpActiveOpens); + MDIFF(d, t2, t1, tcpPassiveOpens); + MDIFF(d, t2, t1, tcpAttemptFails); + MDIFF(d, t2, t1, tcpEstabResets); + d->tcpCurrEstab = t2->tcpCurrEstab; + MDIFF(d, t2, t1, tcpOutSegs); + MDIFF(d, t2, t1, tcpOutDataSegs); + MDIFF(d, t2, t1, tcpOutDataBytes); + MDIFF(d, t2, t1, tcpRetransSegs); + MDIFF(d, t2, t1, tcpRetransBytes); + MDIFF(d, t2, t1, tcpOutAck); + MDIFF(d, t2, t1, tcpOutAckDelayed); + MDIFF(d, t2, t1, tcpOutUrg); + MDIFF(d, t2, t1, tcpOutWinUpdate); + MDIFF(d, t2, t1, tcpOutWinProbe); + MDIFF(d, t2, t1, tcpOutControl); + MDIFF(d, t2, t1, tcpOutRsts); + MDIFF(d, t2, t1, tcpOutFastRetrans); + MDIFF(d, t2, t1, tcpInSegs); + MDIFF(d, t2, t1, tcpInAckSegs); + MDIFF(d, t2, t1, tcpInAckBytes); + MDIFF(d, t2, t1, tcpInDupAck); + MDIFF(d, t2, t1, tcpInAckUnsent); + MDIFF(d, t2, t1, tcpInDataInorderSegs); + MDIFF(d, t2, t1, tcpInDataInorderBytes); + MDIFF(d, t2, t1, tcpInDataUnorderSegs); + MDIFF(d, t2, t1, tcpInDataUnorderBytes); + MDIFF(d, t2, t1, tcpInDataDupSegs); + MDIFF(d, t2, t1, tcpInDataDupBytes); + MDIFF(d, t2, t1, tcpInDataPartDupSegs); + MDIFF(d, t2, t1, tcpInDataPartDupBytes); + MDIFF(d, t2, t1, tcpInDataPastWinSegs); + MDIFF(d, t2, t1, tcpInDataPastWinBytes); + MDIFF(d, t2, t1, tcpInWinProbe); + MDIFF(d, t2, t1, tcpInWinUpdate); + MDIFF(d, t2, t1, tcpInClosed); + MDIFF(d, t2, t1, tcpRttNoUpdate); + MDIFF(d, t2, t1, tcpRttUpdate); + MDIFF(d, t2, t1, tcpTimRetrans); + MDIFF(d, t2, t1, tcpTimRetransDrop); + MDIFF(d, t2, t1, tcpTimKeepalive); + MDIFF(d, t2, t1, tcpTimKeepaliveProbe); + MDIFF(d, t2, t1, tcpTimKeepaliveDrop); + MDIFF(d, t2, t1, tcpListenDrop); + MDIFF(d, t2, t1, tcpListenDropQ0); + MDIFF(d, t2, t1, tcpHalfOpenDrop); + MDIFF(d, t2, t1, tcpOutSackRetransSegs); + prevp = diffptr++; + break; + } + case MIB2_UDP: { + mib2_udp_t *u2; + mib2_udp_t *u1; + mib2_udp_t *d; + + u2 = (mib2_udp_t *)tempp2->valp; + u1 = (mib2_udp_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_udp_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, u2, u1, udpInDatagrams); + MDIFF(d, u2, u1, udpInErrors); + MDIFF(d, u2, u1, udpOutDatagrams); + MDIFF(d, u2, u1, udpOutErrors); + prevp = diffptr++; + break; + } + case MIB2_SCTP: { + mib2_sctp_t *s2; + mib2_sctp_t *s1; + mib2_sctp_t *d; + + s2 = (mib2_sctp_t *)tempp2->valp; + s1 = (mib2_sctp_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_sctp_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + d->sctpRtoAlgorithm = s2->sctpRtoAlgorithm; + d->sctpRtoMin = s2->sctpRtoMin; + d->sctpRtoMax = s2->sctpRtoMax; + d->sctpRtoInitial = s2->sctpRtoInitial; + d->sctpMaxAssocs = s2->sctpMaxAssocs; + d->sctpValCookieLife = s2->sctpValCookieLife; + d->sctpMaxInitRetr = s2->sctpMaxInitRetr; + d->sctpCurrEstab = s2->sctpCurrEstab; + MDIFF(d, s2, s1, sctpActiveEstab); + MDIFF(d, s2, s1, sctpPassiveEstab); + MDIFF(d, s2, s1, sctpAborted); + MDIFF(d, s2, s1, sctpShutdowns); + MDIFF(d, s2, s1, sctpOutOfBlue); + MDIFF(d, s2, s1, sctpChecksumError); + MDIFF(d, s2, s1, sctpOutCtrlChunks); + MDIFF(d, s2, s1, sctpOutOrderChunks); + MDIFF(d, s2, s1, sctpOutUnorderChunks); + MDIFF(d, s2, s1, sctpRetransChunks); + MDIFF(d, s2, s1, sctpOutAck); + MDIFF(d, s2, s1, sctpOutAckDelayed); + MDIFF(d, s2, s1, sctpOutWinUpdate); + MDIFF(d, s2, s1, sctpOutFastRetrans); + MDIFF(d, s2, s1, sctpOutWinProbe); + MDIFF(d, s2, s1, sctpInCtrlChunks); + MDIFF(d, s2, s1, sctpInOrderChunks); + MDIFF(d, s2, s1, sctpInUnorderChunks); + MDIFF(d, s2, s1, sctpInAck); + MDIFF(d, s2, s1, sctpInDupAck); + MDIFF(d, s2, s1, sctpInAckUnsent); + MDIFF(d, s2, s1, sctpFragUsrMsgs); + MDIFF(d, s2, s1, sctpReasmUsrMsgs); + MDIFF(d, s2, s1, sctpOutSCTPPkts); + MDIFF(d, s2, s1, sctpInSCTPPkts); + MDIFF(d, s2, s1, sctpInInvalidCookie); + MDIFF(d, s2, s1, sctpTimRetrans); + MDIFF(d, s2, s1, sctpTimRetransDrop); + MDIFF(d, s2, s1, sctpTimHeartBeatProbe); + MDIFF(d, s2, s1, sctpTimHeartBeatDrop); + MDIFF(d, s2, s1, sctpListenDrop); + MDIFF(d, s2, s1, sctpInClosed); + prevp = diffptr++; + break; + } + case EXPER_RAWIP: { + mib2_rawip_t *r2; + mib2_rawip_t *r1; + mib2_rawip_t *d; + + r2 = (mib2_rawip_t *)tempp2->valp; + r1 = (mib2_rawip_t *)tempp1->valp; + diffptr->group = tempp2->group; + diffptr->mib_id = tempp2->mib_id; + diffptr->length = tempp2->length; + d = (mib2_rawip_t *)calloc(tempp2->length, 1); + if (d == NULL) + goto mibdiff_out_of_memory; + diffptr->valp = d; + MDIFF(d, r2, r1, rawipInDatagrams); + MDIFF(d, r2, r1, rawipInErrors); + MDIFF(d, r2, r1, rawipInCksumErrs); + MDIFF(d, r2, r1, rawipOutDatagrams); + MDIFF(d, r2, r1, rawipOutErrors); + prevp = diffptr++; + break; + } + /* + * there are more "group" types but they aren't + * required for the -s and -Ms options + */ + } + } /* 'for' loop 2 ends */ + tempp1 = NULL; + } /* 'for' loop 1 ends */ + tempp2 = NULL; + diffptr--; + diffptr->next_item = NULL; + return (diffp); + +mibdiff_out_of_memory:; + mib_item_destroy(&diffp); + return (NULL); +} + +/* + * mib_item_destroy: cleans up a mib_item_t * + * that was created by calling mib_item_dup or + * mib_item_diff + */ +static void +mib_item_destroy(mib_item_t **itemp) { + int nitems = 0; + int c = 0; + mib_item_t *tempp; + + if (itemp == NULL || *itemp == NULL) + return; + + for (tempp = *itemp; tempp != NULL; tempp = tempp->next_item) + if (tempp->mib_id == 0) + nitems++; + else + return; /* cannot destroy! */ + + if (nitems == 0) + return; /* cannot destroy! */ + + for (c = nitems - 1; c >= 0; c--) { + if ((itemp[0][c]).valp != NULL) + free((itemp[0][c]).valp); + } + free(*itemp); + + *itemp = NULL; +} + +/* Compare two Octet_ts. Return B_TRUE if they match, B_FALSE if not. */ +static boolean_t +octetstrmatch(const Octet_t *a, const Octet_t *b) +{ + if (a == NULL || b == NULL) + return (B_FALSE); + + if (a->o_length != b->o_length) + return (B_FALSE); + + return (memcmp(a->o_bytes, b->o_bytes, a->o_length) == 0); +} + +/* If octetstr() changes make an appropriate change to STR_EXPAND */ +static char * +octetstr(Octet_t *op, int code, char *dst, uint_t dstlen) +{ + int i; + char *cp; + + cp = dst; + if (op) { + for (i = 0; i < op->o_length; i++) { + switch (code) { + case 'd': + if (cp - dst + 4 > dstlen) { + *cp = '\0'; + return (dst); + } + (void) snprintf(cp, 5, "%d.", + 0xff & op->o_bytes[i]); + cp = strchr(cp, '\0'); + break; + case 'a': + if (cp - dst + 1 > dstlen) { + *cp = '\0'; + return (dst); + } + *cp++ = op->o_bytes[i]; + break; + case 'h': + default: + if (cp - dst + 3 > dstlen) { + *cp = '\0'; + return (dst); + } + (void) snprintf(cp, 4, "%02x:", + 0xff & op->o_bytes[i]); + cp += 3; + break; + } + } + } + if (code != 'a' && cp != dst) + cp--; + *cp = '\0'; + return (dst); +} + +static char * +mitcp_state(int state) +{ +static char tcpsbuf[50]; + char *cp; + + switch (state) { + case TCPS_CLOSED: + cp = "CLOSED"; + break; + case TCPS_IDLE: + cp = "IDLE"; + break; + case TCPS_BOUND: + cp = "BOUND"; + break; + case TCPS_LISTEN: + cp = "LISTEN"; + break; + case TCPS_SYN_SENT: + cp = "SYN_SENT"; + break; + case TCPS_SYN_RCVD: + cp = "SYN_RCVD"; + break; + case TCPS_ESTABLISHED: + cp = "ESTABLISHED"; + break; + case TCPS_CLOSE_WAIT: + cp = "CLOSE_WAIT"; + break; + case TCPS_FIN_WAIT_1: + cp = "FIN_WAIT_1"; + break; + case TCPS_CLOSING: + cp = "CLOSING"; + break; + case TCPS_LAST_ACK: + cp = "LAST_ACK"; + break; + case TCPS_FIN_WAIT_2: + cp = "FIN_WAIT_2"; + break; + case TCPS_TIME_WAIT: + cp = "TIME_WAIT"; + break; + default: + (void) snprintf(tcpsbuf, sizeof (tcpsbuf), + "UnknownState(%d)", state); + cp = tcpsbuf; + break; + } + return (cp); +} + +static int odd; + +static void +prval_init(void) +{ + odd = 0; +} + +static void +prval(char *str, Counter val) +{ + (void) printf("\t%-20s=%6u", str, val); + if (odd++ & 1) + (void) putchar('\n'); +} + +static void +prval64(char *str, Counter64 val) +{ + (void) printf("\t%-20s=%6llu", str, val); + if (odd++ & 1) + (void) putchar('\n'); +} + +static void +pr_int_val(char *str, int val) +{ + (void) printf("\t%-20s=%6d", str, val); + if (odd++ & 1) + (void) putchar('\n'); +} + +static void +pr_sctp_rtoalgo(char *str, int val) +{ + (void) printf("\t%-20s=", str); + switch (val) { + case MIB2_SCTP_RTOALGO_OTHER: + (void) printf("%6.6s", "other"); + break; + + case MIB2_SCTP_RTOALGO_VANJ: + (void) printf("%6.6s", "vanj"); + break; + + default: + (void) printf("%6d", val); + break; + } + if (odd++ & 1) + (void) putchar('\n'); +} + +static void +prval_end(void) +{ + if (odd++ & 1) + (void) putchar('\n'); +} + +/* Extract constant sizes */ +static void +mib_get_constants(mib_item_t *item) +{ + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (item->mib_id != 0) + continue; /* 'for' loop 1 */ + + switch (item->group) { + case MIB2_IP: { + mib2_ip_t *ip = (mib2_ip_t *)item->valp; + + ipAddrEntrySize = ip->ipAddrEntrySize; + ipRouteEntrySize = ip->ipRouteEntrySize; + ipNetToMediaEntrySize = ip->ipNetToMediaEntrySize; + ipMemberEntrySize = ip->ipMemberEntrySize; + ipGroupSourceEntrySize = ip->ipGroupSourceEntrySize; + assert(IS_P2ALIGNED(ipAddrEntrySize, + sizeof (mib2_ipAddrEntry_t *)) && + IS_P2ALIGNED(ipRouteEntrySize, + sizeof (mib2_ipRouteEntry_t *)) && + IS_P2ALIGNED(ipNetToMediaEntrySize, + sizeof (mib2_ipNetToMediaEntry_t *)) && + IS_P2ALIGNED(ipMemberEntrySize, + sizeof (ip_member_t *)) && + IS_P2ALIGNED(ipGroupSourceEntrySize, + sizeof (ip_grpsrc_t *))); + break; + } + case EXPER_DVMRP: { + struct mrtstat *mrts = (struct mrtstat *)item->valp; + + vifctlSize = mrts->mrts_vifctlSize; + mfcctlSize = mrts->mrts_mfcctlSize; + assert(IS_P2ALIGNED(vifctlSize, + sizeof (struct vifclt *)) && + IS_P2ALIGNED(mfcctlSize, sizeof (struct mfcctl *))); + break; + } + case MIB2_IP6: { + mib2_ipv6IfStatsEntry_t *ip6; + /* Just use the first entry */ + + ip6 = (mib2_ipv6IfStatsEntry_t *)item->valp; + ipv6IfStatsEntrySize = ip6->ipv6IfStatsEntrySize; + ipv6AddrEntrySize = ip6->ipv6AddrEntrySize; + ipv6RouteEntrySize = ip6->ipv6RouteEntrySize; + ipv6NetToMediaEntrySize = ip6->ipv6NetToMediaEntrySize; + ipv6MemberEntrySize = ip6->ipv6MemberEntrySize; + ipv6GroupSourceEntrySize = + ip6->ipv6GroupSourceEntrySize; + assert(IS_P2ALIGNED(ipv6IfStatsEntrySize, + sizeof (mib2_ipv6IfStatsEntry_t *)) && + IS_P2ALIGNED(ipv6AddrEntrySize, + sizeof (mib2_ipv6AddrEntry_t *)) && + IS_P2ALIGNED(ipv6RouteEntrySize, + sizeof (mib2_ipv6RouteEntry_t *)) && + IS_P2ALIGNED(ipv6NetToMediaEntrySize, + sizeof (mib2_ipv6NetToMediaEntry_t *)) && + IS_P2ALIGNED(ipv6MemberEntrySize, + sizeof (ipv6_member_t *)) && + IS_P2ALIGNED(ipv6GroupSourceEntrySize, + sizeof (ipv6_grpsrc_t *))); + break; + } + case MIB2_ICMP6: { + mib2_ipv6IfIcmpEntry_t *icmp6; + /* Just use the first entry */ + + icmp6 = (mib2_ipv6IfIcmpEntry_t *)item->valp; + ipv6IfIcmpEntrySize = icmp6->ipv6IfIcmpEntrySize; + assert(IS_P2ALIGNED(ipv6IfIcmpEntrySize, + sizeof (mib2_ipv6IfIcmpEntry_t *))); + break; + } + case MIB2_TCP: { + mib2_tcp_t *tcp = (mib2_tcp_t *)item->valp; + + tcpConnEntrySize = tcp->tcpConnTableSize; + tcp6ConnEntrySize = tcp->tcp6ConnTableSize; + assert(IS_P2ALIGNED(tcpConnEntrySize, + sizeof (mib2_tcpConnEntry_t *)) && + IS_P2ALIGNED(tcp6ConnEntrySize, + sizeof (mib2_tcp6ConnEntry_t *))); + break; + } + case MIB2_UDP: { + mib2_udp_t *udp = (mib2_udp_t *)item->valp; + + udpEntrySize = udp->udpEntrySize; + udp6EntrySize = udp->udp6EntrySize; + assert(IS_P2ALIGNED(udpEntrySize, + sizeof (mib2_udpEntry_t *)) && + IS_P2ALIGNED(udp6EntrySize, + sizeof (mib2_udp6Entry_t *))); + break; + } + case MIB2_SCTP: { + mib2_sctp_t *sctp = (mib2_sctp_t *)item->valp; + + sctpEntrySize = sctp->sctpEntrySize; + sctpLocalEntrySize = sctp->sctpLocalEntrySize; + sctpRemoteEntrySize = sctp->sctpRemoteEntrySize; + break; + } + } + } /* 'for' loop 1 ends */ + + if (Dflag) { + (void) puts("mib_get_constants:"); + (void) printf("\tipv6IfStatsEntrySize %d\n", + ipv6IfStatsEntrySize); + (void) printf("\tipAddrEntrySize %d\n", ipAddrEntrySize); + (void) printf("\tipRouteEntrySize %d\n", ipRouteEntrySize); + (void) printf("\tipNetToMediaEntrySize %d\n", + ipNetToMediaEntrySize); + (void) printf("\tipMemberEntrySize %d\n", ipMemberEntrySize); + (void) printf("\tvifctlSize %d\n", vifctlSize); + (void) printf("\tmfcctlSize %d\n", mfcctlSize); + + (void) printf("\tipv6AddrEntrySize %d\n", ipv6AddrEntrySize); + (void) printf("\tipv6RouteEntrySize %d\n", ipv6RouteEntrySize); + (void) printf("\tipv6NetToMediaEntrySize %d\n", + ipv6NetToMediaEntrySize); + (void) printf("\tipv6MemberEntrySize %d\n", + ipv6MemberEntrySize); + (void) printf("\tipv6IfIcmpEntrySize %d\n", + ipv6IfIcmpEntrySize); + (void) printf("\ttcpConnEntrySize %d\n", tcpConnEntrySize); + (void) printf("\ttcp6ConnEntrySize %d\n", tcp6ConnEntrySize); + (void) printf("\tudpEntrySize %d\n", udpEntrySize); + (void) printf("\tudp6EntrySize %d\n", udp6EntrySize); + (void) printf("\tsctpEntrySize %d\n", sctpEntrySize); + (void) printf("\tsctpLocalEntrySize %d\n", sctpLocalEntrySize); + (void) printf("\tsctpRemoteEntrySize %d\n", + sctpRemoteEntrySize); + } +} + + +/* ----------------------------- STAT_REPORT ------------------------------- */ + +static void +stat_report(mib_item_t *item) +{ + int jtemp = 0; + char ifname[LIFNAMSIZ + 1]; + char *ifnamep; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, + item->length, item->valp); + } + if (item->mib_id != 0) + continue; /* 'for' loop 1 */ + + switch (item->group) { + case MIB2_IP: { + mib2_ip_t *ip = (mib2_ip_t *)item->valp; + + if (protocol_selected(IPPROTO_IP) && + family_selected(AF_INET)) { + (void) fputs(v4compat ? "\nIP" : "\nIPv4", + stdout); + print_ip_stats(ip); + } + break; + } + case MIB2_ICMP: { + mib2_icmp_t *icmp = + (mib2_icmp_t *)item->valp; + + if (protocol_selected(IPPROTO_ICMP) && + family_selected(AF_INET)) { + (void) fputs(v4compat ? "\nICMP" : "\nICMPv4", + stdout); + print_icmp_stats(icmp); + } + break; + } + case MIB2_IP6: { + mib2_ipv6IfStatsEntry_t *ip6; + mib2_ipv6IfStatsEntry_t sum6; + + if (!(protocol_selected(IPPROTO_IPV6)) || + !(family_selected(AF_INET6))) + break; + bzero(&sum6, sizeof (sum6)); + /* 'for' loop 2a: */ + for (ip6 = (mib2_ipv6IfStatsEntry_t *)item->valp; + (char *)ip6 < (char *)item->valp + + item->length; + /* LINTED: (note 1) */ + ip6 = (mib2_ipv6IfStatsEntry_t *)((char *)ip6 + + ipv6IfStatsEntrySize)) { + + if (ip6->ipv6IfIndex == 0) { + /* + * The "unknown interface" ip6 + * mib. Just add to the sum. + */ + sum_ip6_stats(ip6, &sum6); + continue; /* 'for' loop 2a */ + } + ifnamep = if_indextoname( + ip6->ipv6IfIndex, + ifname); + if (ifnamep == NULL) { + (void) printf( + "Invalid ifindex %d\n", + ip6->ipv6IfIndex); + continue; /* 'for' loop 2a */ + } + + if (Aflag) { + (void) printf("\nIPv6 for %s\n", + ifnamep); + print_ip6_stats(ip6); + } + sum_ip6_stats(ip6, &sum6); + } /* 'for' loop 2a ends */ + (void) fputs("\nIPv6", stdout); + print_ip6_stats(&sum6); + break; + } + case MIB2_ICMP6: { + mib2_ipv6IfIcmpEntry_t *icmp6; + mib2_ipv6IfIcmpEntry_t sum6; + + if (!(protocol_selected(IPPROTO_ICMPV6)) || + !(family_selected(AF_INET6))) + break; + bzero(&sum6, sizeof (sum6)); + /* 'for' loop 2b: */ + for (icmp6 = + (mib2_ipv6IfIcmpEntry_t *)item->valp; + (char *)icmp6 < (char *)item->valp + + item->length; + icmp6 = + /* LINTED: (note 1) */ + (mib2_ipv6IfIcmpEntry_t *)((char *)icmp6 + + ipv6IfIcmpEntrySize)) { + + if (icmp6->ipv6IfIcmpIfIndex == 0) { + /* + * The "unknown interface" icmp6 + * mib. Just add to the sum. + */ + sum_icmp6_stats(icmp6, &sum6); + continue; /* 'for' loop 2b: */ + } + ifnamep = if_indextoname( + icmp6->ipv6IfIcmpIfIndex, ifname); + if (ifnamep == NULL) { + (void) printf( + "Invalid ifindex %d\n", + icmp6->ipv6IfIcmpIfIndex); + continue; /* 'for' loop 2b: */ + } + + if (Aflag) { + (void) printf( + "\nICMPv6 for %s\n", + ifnamep); + print_icmp6_stats(icmp6); + } + sum_icmp6_stats(icmp6, &sum6); + } /* 'for' loop 2b ends */ + (void) fputs("\nICMPv6", stdout); + print_icmp6_stats(&sum6); + break; + } + case MIB2_TCP: { + mib2_tcp_t *tcp = (mib2_tcp_t *)item->valp; + + if (protocol_selected(IPPROTO_TCP) && + (family_selected(AF_INET) || + family_selected(AF_INET6))) { + (void) fputs("\nTCP", stdout); + print_tcp_stats(tcp); + } + break; + } + case MIB2_UDP: { + mib2_udp_t *udp = (mib2_udp_t *)item->valp; + + if (protocol_selected(IPPROTO_UDP) && + (family_selected(AF_INET) || + family_selected(AF_INET6))) { + (void) fputs("\nUDP", stdout); + print_udp_stats(udp); + } + break; + } + case MIB2_SCTP: { + mib2_sctp_t *sctp = (mib2_sctp_t *)item->valp; + + if (protocol_selected(IPPROTO_SCTP) && + (family_selected(AF_INET) || + family_selected(AF_INET6))) { + (void) fputs("\nSCTP", stdout); + print_sctp_stats(sctp); + } + break; + } + case EXPER_RAWIP: { + mib2_rawip_t *rawip = + (mib2_rawip_t *)item->valp; + + if (protocol_selected(IPPROTO_RAW) && + (family_selected(AF_INET) || + family_selected(AF_INET6))) { + (void) fputs("\nRAWIP", stdout); + print_rawip_stats(rawip); + } + break; + } + case EXPER_IGMP: { + struct igmpstat *igps = + (struct igmpstat *)item->valp; + + if (protocol_selected(IPPROTO_IGMP) && + (family_selected(AF_INET))) { + (void) fputs("\nIGMP:\n", stdout); + print_igmp_stats(igps); + } + break; + } + } + } /* 'for' loop 1 ends */ + (void) putchar('\n'); + (void) fflush(stdout); +} + +static void +print_ip_stats(mib2_ip_t *ip) +{ + prval_init(); + pr_int_val("ipForwarding", ip->ipForwarding); + pr_int_val("ipDefaultTTL", ip->ipDefaultTTL); + prval("ipInReceives", ip->ipInReceives); + prval("ipInHdrErrors", ip->ipInHdrErrors); + prval("ipInAddrErrors", ip->ipInAddrErrors); + prval("ipInCksumErrs", ip->ipInCksumErrs); + prval("ipForwDatagrams", ip->ipForwDatagrams); + prval("ipForwProhibits", ip->ipForwProhibits); + prval("ipInUnknownProtos", ip->ipInUnknownProtos); + prval("ipInDiscards", ip->ipInDiscards); + prval("ipInDelivers", ip->ipInDelivers); + prval("ipOutRequests", ip->ipOutRequests); + prval("ipOutDiscards", ip->ipOutDiscards); + prval("ipOutNoRoutes", ip->ipOutNoRoutes); + pr_int_val("ipReasmTimeout", ip->ipReasmTimeout); + prval("ipReasmReqds", ip->ipReasmReqds); + prval("ipReasmOKs", ip->ipReasmOKs); + prval("ipReasmFails", ip->ipReasmFails); + prval("ipReasmDuplicates", ip->ipReasmDuplicates); + prval("ipReasmPartDups", ip->ipReasmPartDups); + prval("ipFragOKs", ip->ipFragOKs); + prval("ipFragFails", ip->ipFragFails); + prval("ipFragCreates", ip->ipFragCreates); + prval("ipRoutingDiscards", ip->ipRoutingDiscards); + + prval("tcpInErrs", ip->tcpInErrs); + prval("udpNoPorts", ip->udpNoPorts); + prval("udpInCksumErrs", ip->udpInCksumErrs); + prval("udpInOverflows", ip->udpInOverflows); + prval("rawipInOverflows", ip->rawipInOverflows); + prval("ipsecInSucceeded", ip->ipsecInSucceeded); + prval("ipsecInFailed", ip->ipsecInFailed); + prval("ipInIPv6", ip->ipInIPv6); + prval("ipOutIPv6", ip->ipOutIPv6); + prval("ipOutSwitchIPv6", ip->ipOutSwitchIPv6); + prval_end(); +} + +static void +print_icmp_stats(mib2_icmp_t *icmp) +{ + prval_init(); + prval("icmpInMsgs", icmp->icmpInMsgs); + prval("icmpInErrors", icmp->icmpInErrors); + prval("icmpInCksumErrs", icmp->icmpInCksumErrs); + prval("icmpInUnknowns", icmp->icmpInUnknowns); + prval("icmpInDestUnreachs", icmp->icmpInDestUnreachs); + prval("icmpInTimeExcds", icmp->icmpInTimeExcds); + prval("icmpInParmProbs", icmp->icmpInParmProbs); + prval("icmpInSrcQuenchs", icmp->icmpInSrcQuenchs); + prval("icmpInRedirects", icmp->icmpInRedirects); + prval("icmpInBadRedirects", icmp->icmpInBadRedirects); + prval("icmpInEchos", icmp->icmpInEchos); + prval("icmpInEchoReps", icmp->icmpInEchoReps); + prval("icmpInTimestamps", icmp->icmpInTimestamps); + prval("icmpInTimestampReps", icmp->icmpInTimestampReps); + prval("icmpInAddrMasks", icmp->icmpInAddrMasks); + prval("icmpInAddrMaskReps", icmp->icmpInAddrMaskReps); + prval("icmpInFragNeeded", icmp->icmpInFragNeeded); + prval("icmpOutMsgs", icmp->icmpOutMsgs); + prval("icmpOutDrops", icmp->icmpOutDrops); + prval("icmpOutErrors", icmp->icmpOutErrors); + prval("icmpOutDestUnreachs", icmp->icmpOutDestUnreachs); + prval("icmpOutTimeExcds", icmp->icmpOutTimeExcds); + prval("icmpOutParmProbs", icmp->icmpOutParmProbs); + prval("icmpOutSrcQuenchs", icmp->icmpOutSrcQuenchs); + prval("icmpOutRedirects", icmp->icmpOutRedirects); + prval("icmpOutEchos", icmp->icmpOutEchos); + prval("icmpOutEchoReps", icmp->icmpOutEchoReps); + prval("icmpOutTimestamps", icmp->icmpOutTimestamps); + prval("icmpOutTimestampReps", icmp->icmpOutTimestampReps); + prval("icmpOutAddrMasks", icmp->icmpOutAddrMasks); + prval("icmpOutAddrMaskReps", icmp->icmpOutAddrMaskReps); + prval("icmpOutFragNeeded", icmp->icmpOutFragNeeded); + prval("icmpInOverflows", icmp->icmpInOverflows); + prval_end(); +} + +static void +print_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6) +{ + prval_init(); + prval("ipv6Forwarding", ip6->ipv6Forwarding); + prval("ipv6DefaultHopLimit", ip6->ipv6DefaultHopLimit); + + prval("ipv6InReceives", ip6->ipv6InReceives); + prval("ipv6InHdrErrors", ip6->ipv6InHdrErrors); + prval("ipv6InTooBigErrors", ip6->ipv6InTooBigErrors); + prval("ipv6InNoRoutes", ip6->ipv6InNoRoutes); + prval("ipv6InAddrErrors", ip6->ipv6InAddrErrors); + prval("ipv6InUnknownProtos", ip6->ipv6InUnknownProtos); + prval("ipv6InTruncatedPkts", ip6->ipv6InTruncatedPkts); + prval("ipv6InDiscards", ip6->ipv6InDiscards); + prval("ipv6InDelivers", ip6->ipv6InDelivers); + prval("ipv6OutForwDatagrams", ip6->ipv6OutForwDatagrams); + prval("ipv6OutRequests", ip6->ipv6OutRequests); + prval("ipv6OutDiscards", ip6->ipv6OutDiscards); + prval("ipv6OutNoRoutes", ip6->ipv6OutNoRoutes); + prval("ipv6OutFragOKs", ip6->ipv6OutFragOKs); + prval("ipv6OutFragFails", ip6->ipv6OutFragFails); + prval("ipv6OutFragCreates", ip6->ipv6OutFragCreates); + prval("ipv6ReasmReqds", ip6->ipv6ReasmReqds); + prval("ipv6ReasmOKs", ip6->ipv6ReasmOKs); + prval("ipv6ReasmFails", ip6->ipv6ReasmFails); + prval("ipv6InMcastPkts", ip6->ipv6InMcastPkts); + prval("ipv6OutMcastPkts", ip6->ipv6OutMcastPkts); + prval("ipv6ReasmDuplicates", ip6->ipv6ReasmDuplicates); + prval("ipv6ReasmPartDups", ip6->ipv6ReasmPartDups); + prval("ipv6ForwProhibits", ip6->ipv6ForwProhibits); + prval("udpInCksumErrs", ip6->udpInCksumErrs); + prval("udpInOverflows", ip6->udpInOverflows); + prval("rawipInOverflows", ip6->rawipInOverflows); + prval("ipv6InIPv4", ip6->ipv6InIPv4); + prval("ipv6OutIPv4", ip6->ipv6OutIPv4); + prval("ipv6OutSwitchIPv4", ip6->ipv6OutSwitchIPv4); + prval_end(); +} + +static void +print_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6) +{ + prval_init(); + prval("icmp6InMsgs", icmp6->ipv6IfIcmpInMsgs); + prval("icmp6InErrors", icmp6->ipv6IfIcmpInErrors); + prval("icmp6InDestUnreachs", icmp6->ipv6IfIcmpInDestUnreachs); + prval("icmp6InAdminProhibs", icmp6->ipv6IfIcmpInAdminProhibs); + prval("icmp6InTimeExcds", icmp6->ipv6IfIcmpInTimeExcds); + prval("icmp6InParmProblems", icmp6->ipv6IfIcmpInParmProblems); + prval("icmp6InPktTooBigs", icmp6->ipv6IfIcmpInPktTooBigs); + prval("icmp6InEchos", icmp6->ipv6IfIcmpInEchos); + prval("icmp6InEchoReplies", icmp6->ipv6IfIcmpInEchoReplies); + prval("icmp6InRouterSols", icmp6->ipv6IfIcmpInRouterSolicits); + prval("icmp6InRouterAds", + icmp6->ipv6IfIcmpInRouterAdvertisements); + prval("icmp6InNeighborSols", icmp6->ipv6IfIcmpInNeighborSolicits); + prval("icmp6InNeighborAds", + icmp6->ipv6IfIcmpInNeighborAdvertisements); + prval("icmp6InRedirects", icmp6->ipv6IfIcmpInRedirects); + prval("icmp6InBadRedirects", icmp6->ipv6IfIcmpInBadRedirects); + prval("icmp6InGroupQueries", icmp6->ipv6IfIcmpInGroupMembQueries); + prval("icmp6InGroupResps", icmp6->ipv6IfIcmpInGroupMembResponses); + prval("icmp6InGroupReds", icmp6->ipv6IfIcmpInGroupMembReductions); + prval("icmp6InOverflows", icmp6->ipv6IfIcmpInOverflows); + prval_end(); + prval_init(); + prval("icmp6OutMsgs", icmp6->ipv6IfIcmpOutMsgs); + prval("icmp6OutErrors", icmp6->ipv6IfIcmpOutErrors); + prval("icmp6OutDestUnreachs", icmp6->ipv6IfIcmpOutDestUnreachs); + prval("icmp6OutAdminProhibs", icmp6->ipv6IfIcmpOutAdminProhibs); + prval("icmp6OutTimeExcds", icmp6->ipv6IfIcmpOutTimeExcds); + prval("icmp6OutParmProblems", icmp6->ipv6IfIcmpOutParmProblems); + prval("icmp6OutPktTooBigs", icmp6->ipv6IfIcmpOutPktTooBigs); + prval("icmp6OutEchos", icmp6->ipv6IfIcmpOutEchos); + prval("icmp6OutEchoReplies", icmp6->ipv6IfIcmpOutEchoReplies); + prval("icmp6OutRouterSols", icmp6->ipv6IfIcmpOutRouterSolicits); + prval("icmp6OutRouterAds", + icmp6->ipv6IfIcmpOutRouterAdvertisements); + prval("icmp6OutNeighborSols", icmp6->ipv6IfIcmpOutNeighborSolicits); + prval("icmp6OutNeighborAds", + icmp6->ipv6IfIcmpOutNeighborAdvertisements); + prval("icmp6OutRedirects", icmp6->ipv6IfIcmpOutRedirects); + prval("icmp6OutGroupQueries", icmp6->ipv6IfIcmpOutGroupMembQueries); + prval("icmp6OutGroupResps", + icmp6->ipv6IfIcmpOutGroupMembResponses); + prval("icmp6OutGroupReds", + icmp6->ipv6IfIcmpOutGroupMembReductions); + prval_end(); +} + +static void +print_sctp_stats(mib2_sctp_t *sctp) +{ + prval_init(); + pr_sctp_rtoalgo("sctpRtoAlgorithm", sctp->sctpRtoAlgorithm); + prval("sctpRtoMin", sctp->sctpRtoMin); + prval("sctpRtoMax", sctp->sctpRtoMax); + prval("sctpRtoInitial", sctp->sctpRtoInitial); + pr_int_val("sctpMaxAssocs", sctp->sctpMaxAssocs); + prval("sctpValCookieLife", sctp->sctpValCookieLife); + prval("sctpMaxInitRetr", sctp->sctpMaxInitRetr); + prval("sctpCurrEstab", sctp->sctpCurrEstab); + prval("sctpActiveEstab", sctp->sctpActiveEstab); + prval("sctpPassiveEstab", sctp->sctpPassiveEstab); + prval("sctpAborted", sctp->sctpAborted); + prval("sctpShutdowns", sctp->sctpShutdowns); + prval("sctpOutOfBlue", sctp->sctpOutOfBlue); + prval("sctpChecksumError", sctp->sctpChecksumError); + prval64("sctpOutCtrlChunks", sctp->sctpOutCtrlChunks); + prval64("sctpOutOrderChunks", sctp->sctpOutOrderChunks); + prval64("sctpOutUnorderChunks", sctp->sctpOutUnorderChunks); + prval64("sctpRetransChunks", sctp->sctpRetransChunks); + prval("sctpOutAck", sctp->sctpOutAck); + prval("sctpOutAckDelayed", sctp->sctpOutAckDelayed); + prval("sctpOutWinUpdate", sctp->sctpOutWinUpdate); + prval("sctpOutFastRetrans", sctp->sctpOutFastRetrans); + prval("sctpOutWinProbe", sctp->sctpOutWinProbe); + prval64("sctpInCtrlChunks", sctp->sctpInCtrlChunks); + prval64("sctpInOrderChunks", sctp->sctpInOrderChunks); + prval64("sctpInUnorderChunks", sctp->sctpInUnorderChunks); + prval("sctpInAck", sctp->sctpInAck); + prval("sctpInDupAck", sctp->sctpInDupAck); + prval("sctpInAckUnsent", sctp->sctpInAckUnsent); + prval64("sctpFragUsrMsgs", sctp->sctpFragUsrMsgs); + prval64("sctpReasmUsrMsgs", sctp->sctpReasmUsrMsgs); + prval64("sctpOutSCTPPkts", sctp->sctpOutSCTPPkts); + prval64("sctpInSCTPPkts", sctp->sctpInSCTPPkts); + prval("sctpInInvalidCookie", sctp->sctpInInvalidCookie); + prval("sctpTimRetrans", sctp->sctpTimRetrans); + prval("sctpTimRetransDrop", sctp->sctpTimRetransDrop); + prval("sctpTimHearBeatProbe", sctp->sctpTimHeartBeatProbe); + prval("sctpTimHearBeatDrop", sctp->sctpTimHeartBeatDrop); + prval("sctpListenDrop", sctp->sctpListenDrop); + prval("sctpInClosed", sctp->sctpInClosed); + prval_end(); +} + +static void +print_tcp_stats(mib2_tcp_t *tcp) +{ + prval_init(); + pr_int_val("tcpRtoAlgorithm", tcp->tcpRtoAlgorithm); + pr_int_val("tcpRtoMin", tcp->tcpRtoMin); + pr_int_val("tcpRtoMax", tcp->tcpRtoMax); + pr_int_val("tcpMaxConn", tcp->tcpMaxConn); + prval("tcpActiveOpens", tcp->tcpActiveOpens); + prval("tcpPassiveOpens", tcp->tcpPassiveOpens); + prval("tcpAttemptFails", tcp->tcpAttemptFails); + prval("tcpEstabResets", tcp->tcpEstabResets); + prval("tcpCurrEstab", tcp->tcpCurrEstab); + prval("tcpOutSegs", tcp->tcpOutSegs); + prval("tcpOutDataSegs", tcp->tcpOutDataSegs); + prval("tcpOutDataBytes", tcp->tcpOutDataBytes); + prval("tcpRetransSegs", tcp->tcpRetransSegs); + prval("tcpRetransBytes", tcp->tcpRetransBytes); + prval("tcpOutAck", tcp->tcpOutAck); + prval("tcpOutAckDelayed", tcp->tcpOutAckDelayed); + prval("tcpOutUrg", tcp->tcpOutUrg); + prval("tcpOutWinUpdate", tcp->tcpOutWinUpdate); + prval("tcpOutWinProbe", tcp->tcpOutWinProbe); + prval("tcpOutControl", tcp->tcpOutControl); + prval("tcpOutRsts", tcp->tcpOutRsts); + prval("tcpOutFastRetrans", tcp->tcpOutFastRetrans); + prval("tcpInSegs", tcp->tcpInSegs); + prval_end(); + prval("tcpInAckSegs", tcp->tcpInAckSegs); + prval("tcpInAckBytes", tcp->tcpInAckBytes); + prval("tcpInDupAck", tcp->tcpInDupAck); + prval("tcpInAckUnsent", tcp->tcpInAckUnsent); + prval("tcpInInorderSegs", tcp->tcpInDataInorderSegs); + prval("tcpInInorderBytes", tcp->tcpInDataInorderBytes); + prval("tcpInUnorderSegs", tcp->tcpInDataUnorderSegs); + prval("tcpInUnorderBytes", tcp->tcpInDataUnorderBytes); + prval("tcpInDupSegs", tcp->tcpInDataDupSegs); + prval("tcpInDupBytes", tcp->tcpInDataDupBytes); + prval("tcpInPartDupSegs", tcp->tcpInDataPartDupSegs); + prval("tcpInPartDupBytes", tcp->tcpInDataPartDupBytes); + prval("tcpInPastWinSegs", tcp->tcpInDataPastWinSegs); + prval("tcpInPastWinBytes", tcp->tcpInDataPastWinBytes); + prval("tcpInWinProbe", tcp->tcpInWinProbe); + prval("tcpInWinUpdate", tcp->tcpInWinUpdate); + prval("tcpInClosed", tcp->tcpInClosed); + prval("tcpRttNoUpdate", tcp->tcpRttNoUpdate); + prval("tcpRttUpdate", tcp->tcpRttUpdate); + prval("tcpTimRetrans", tcp->tcpTimRetrans); + prval("tcpTimRetransDrop", tcp->tcpTimRetransDrop); + prval("tcpTimKeepalive", tcp->tcpTimKeepalive); + prval("tcpTimKeepaliveProbe", tcp->tcpTimKeepaliveProbe); + prval("tcpTimKeepaliveDrop", tcp->tcpTimKeepaliveDrop); + prval("tcpListenDrop", tcp->tcpListenDrop); + prval("tcpListenDropQ0", tcp->tcpListenDropQ0); + prval("tcpHalfOpenDrop", tcp->tcpHalfOpenDrop); + prval("tcpOutSackRetrans", tcp->tcpOutSackRetransSegs); + prval_end(); + +} + +static void +print_udp_stats(mib2_udp_t *udp) +{ + prval_init(); + prval("udpInDatagrams", udp->udpInDatagrams); + prval("udpInErrors", udp->udpInErrors); + prval("udpOutDatagrams", udp->udpOutDatagrams); + prval("udpOutErrors", udp->udpOutErrors); + prval_end(); +} + +static void +print_rawip_stats(mib2_rawip_t *rawip) +{ + prval_init(); + prval("rawipInDatagrams", rawip->rawipInDatagrams); + prval("rawipInErrors", rawip->rawipInErrors); + prval("rawipInCksumErrs", rawip->rawipInCksumErrs); + prval("rawipOutDatagrams", rawip->rawipOutDatagrams); + prval("rawipOutErrors", rawip->rawipOutErrors); + prval_end(); +} + +void +print_igmp_stats(struct igmpstat *igps) +{ + (void) printf(" %10u message%s received\n", + igps->igps_rcv_total, PLURAL(igps->igps_rcv_total)); + (void) printf(" %10u message%s received with too few bytes\n", + igps->igps_rcv_tooshort, PLURAL(igps->igps_rcv_tooshort)); + (void) printf(" %10u message%s received with bad checksum\n", + igps->igps_rcv_badsum, PLURAL(igps->igps_rcv_badsum)); + (void) printf(" %10u membership quer%s received\n", + igps->igps_rcv_queries, PLURALY(igps->igps_rcv_queries)); + (void) printf(" %10u membership quer%s received with invalid " + "field(s)\n", + igps->igps_rcv_badqueries, PLURALY(igps->igps_rcv_badqueries)); + (void) printf(" %10u membership report%s received\n", + igps->igps_rcv_reports, PLURAL(igps->igps_rcv_reports)); + (void) printf(" %10u membership report%s received with invalid " + "field(s)\n", + igps->igps_rcv_badreports, PLURAL(igps->igps_rcv_badreports)); + (void) printf(" %10u membership report%s received for groups to " + "which we belong\n", + igps->igps_rcv_ourreports, PLURAL(igps->igps_rcv_ourreports)); + (void) printf(" %10u membership report%s sent\n", + igps->igps_snd_reports, PLURAL(igps->igps_snd_reports)); +} + +static void +print_mrt_stats(struct mrtstat *mrts) +{ + (void) puts("DVMRP multicast routing:"); + (void) printf(" %10u hit%s - kernel forwarding cache hits\n", + mrts->mrts_mfc_hits, PLURAL(mrts->mrts_mfc_hits)); + (void) printf(" %10u miss%s - kernel forwarding cache misses\n", + mrts->mrts_mfc_misses, PLURALES(mrts->mrts_mfc_misses)); + (void) printf(" %10u packet%s potentially forwarded\n", + mrts->mrts_fwd_in, PLURAL(mrts->mrts_fwd_in)); + (void) printf(" %10u packet%s actually sent out\n", + mrts->mrts_fwd_out, PLURAL(mrts->mrts_fwd_out)); + (void) printf(" %10u upcall%s - upcalls made to mrouted\n", + mrts->mrts_upcalls, PLURAL(mrts->mrts_upcalls)); + (void) printf(" %10u packet%s not sent out due to lack of resources\n", + mrts->mrts_fwd_drop, PLURAL(mrts->mrts_fwd_drop)); + (void) printf(" %10u datagram%s with malformed tunnel options\n", + mrts->mrts_bad_tunnel, PLURAL(mrts->mrts_bad_tunnel)); + (void) printf(" %10u datagram%s with no room for tunnel options\n", + mrts->mrts_cant_tunnel, PLURAL(mrts->mrts_cant_tunnel)); + (void) printf(" %10u datagram%s arrived on wrong interface\n", + mrts->mrts_wrong_if, PLURAL(mrts->mrts_wrong_if)); + (void) printf(" %10u datagram%s dropped due to upcall Q overflow\n", + mrts->mrts_upq_ovflw, PLURAL(mrts->mrts_upq_ovflw)); + (void) printf(" %10u datagram%s cleaned up by the cache\n", + mrts->mrts_cache_cleanups, PLURAL(mrts->mrts_cache_cleanups)); + (void) printf(" %10u datagram%s dropped selectively by ratelimiter\n", + mrts->mrts_drop_sel, PLURAL(mrts->mrts_drop_sel)); + (void) printf(" %10u datagram%s dropped - bucket Q overflow\n", + mrts->mrts_q_overflow, PLURAL(mrts->mrts_q_overflow)); + (void) printf(" %10u datagram%s dropped - larger than bkt size\n", + mrts->mrts_pkt2large, PLURAL(mrts->mrts_pkt2large)); + (void) printf("\nPIM multicast routing:\n"); + (void) printf(" %10u datagram%s dropped - bad version number\n", + mrts->mrts_pim_badversion, PLURAL(mrts->mrts_pim_badversion)); + (void) printf(" %10u datagram%s dropped - bad checksum\n", + mrts->mrts_pim_rcv_badcsum, PLURAL(mrts->mrts_pim_rcv_badcsum)); + (void) printf(" %10u datagram%s dropped - bad register packets\n", + mrts->mrts_pim_badregisters, + PLURAL(mrts->mrts_pim_badregisters)); + (void) printf( + " %10u datagram%s potentially forwarded - register packets\n", + mrts->mrts_pim_regforwards, PLURAL(mrts->mrts_pim_regforwards)); + (void) printf(" %10u datagram%s dropped - register send drops\n", + mrts->mrts_pim_regsend_drops, + PLURAL(mrts->mrts_pim_regsend_drops)); + (void) printf(" %10u datagram%s dropped - packet malformed\n", + mrts->mrts_pim_malformed, PLURAL(mrts->mrts_pim_malformed)); + (void) printf(" %10u datagram%s dropped - no memory to forward\n", + mrts->mrts_pim_nomemory, PLURAL(mrts->mrts_pim_nomemory)); +} + +static void +sum_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6, mib2_ipv6IfStatsEntry_t *sum6) +{ + /* First few are not additive */ + sum6->ipv6Forwarding = ip6->ipv6Forwarding; + sum6->ipv6DefaultHopLimit = ip6->ipv6DefaultHopLimit; + + sum6->ipv6InReceives += ip6->ipv6InReceives; + sum6->ipv6InHdrErrors += ip6->ipv6InHdrErrors; + sum6->ipv6InTooBigErrors += ip6->ipv6InTooBigErrors; + sum6->ipv6InNoRoutes += ip6->ipv6InNoRoutes; + sum6->ipv6InAddrErrors += ip6->ipv6InAddrErrors; + sum6->ipv6InUnknownProtos += ip6->ipv6InUnknownProtos; + sum6->ipv6InTruncatedPkts += ip6->ipv6InTruncatedPkts; + sum6->ipv6InDiscards += ip6->ipv6InDiscards; + sum6->ipv6InDelivers += ip6->ipv6InDelivers; + sum6->ipv6OutForwDatagrams += ip6->ipv6OutForwDatagrams; + sum6->ipv6OutRequests += ip6->ipv6OutRequests; + sum6->ipv6OutDiscards += ip6->ipv6OutDiscards; + sum6->ipv6OutFragOKs += ip6->ipv6OutFragOKs; + sum6->ipv6OutFragFails += ip6->ipv6OutFragFails; + sum6->ipv6OutFragCreates += ip6->ipv6OutFragCreates; + sum6->ipv6ReasmReqds += ip6->ipv6ReasmReqds; + sum6->ipv6ReasmOKs += ip6->ipv6ReasmOKs; + sum6->ipv6ReasmFails += ip6->ipv6ReasmFails; + sum6->ipv6InMcastPkts += ip6->ipv6InMcastPkts; + sum6->ipv6OutMcastPkts += ip6->ipv6OutMcastPkts; + sum6->ipv6OutNoRoutes += ip6->ipv6OutNoRoutes; + sum6->ipv6ReasmDuplicates += ip6->ipv6ReasmDuplicates; + sum6->ipv6ReasmPartDups += ip6->ipv6ReasmPartDups; + sum6->ipv6ForwProhibits += ip6->ipv6ForwProhibits; + sum6->udpInCksumErrs += ip6->udpInCksumErrs; + sum6->udpInOverflows += ip6->udpInOverflows; + sum6->rawipInOverflows += ip6->rawipInOverflows; +} + +static void +sum_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6, mib2_ipv6IfIcmpEntry_t *sum6) +{ + sum6->ipv6IfIcmpInMsgs += icmp6->ipv6IfIcmpInMsgs; + sum6->ipv6IfIcmpInErrors += icmp6->ipv6IfIcmpInErrors; + sum6->ipv6IfIcmpInDestUnreachs += icmp6->ipv6IfIcmpInDestUnreachs; + sum6->ipv6IfIcmpInAdminProhibs += icmp6->ipv6IfIcmpInAdminProhibs; + sum6->ipv6IfIcmpInTimeExcds += icmp6->ipv6IfIcmpInTimeExcds; + sum6->ipv6IfIcmpInParmProblems += icmp6->ipv6IfIcmpInParmProblems; + sum6->ipv6IfIcmpInPktTooBigs += icmp6->ipv6IfIcmpInPktTooBigs; + sum6->ipv6IfIcmpInEchos += icmp6->ipv6IfIcmpInEchos; + sum6->ipv6IfIcmpInEchoReplies += icmp6->ipv6IfIcmpInEchoReplies; + sum6->ipv6IfIcmpInRouterSolicits += icmp6->ipv6IfIcmpInRouterSolicits; + sum6->ipv6IfIcmpInRouterAdvertisements += + icmp6->ipv6IfIcmpInRouterAdvertisements; + sum6->ipv6IfIcmpInNeighborSolicits += + icmp6->ipv6IfIcmpInNeighborSolicits; + sum6->ipv6IfIcmpInNeighborAdvertisements += + icmp6->ipv6IfIcmpInNeighborAdvertisements; + sum6->ipv6IfIcmpInRedirects += icmp6->ipv6IfIcmpInRedirects; + sum6->ipv6IfIcmpInGroupMembQueries += + icmp6->ipv6IfIcmpInGroupMembQueries; + sum6->ipv6IfIcmpInGroupMembResponses += + icmp6->ipv6IfIcmpInGroupMembResponses; + sum6->ipv6IfIcmpInGroupMembReductions += + icmp6->ipv6IfIcmpInGroupMembReductions; + sum6->ipv6IfIcmpOutMsgs += icmp6->ipv6IfIcmpOutMsgs; + sum6->ipv6IfIcmpOutErrors += icmp6->ipv6IfIcmpOutErrors; + sum6->ipv6IfIcmpOutDestUnreachs += icmp6->ipv6IfIcmpOutDestUnreachs; + sum6->ipv6IfIcmpOutAdminProhibs += icmp6->ipv6IfIcmpOutAdminProhibs; + sum6->ipv6IfIcmpOutTimeExcds += icmp6->ipv6IfIcmpOutTimeExcds; + sum6->ipv6IfIcmpOutParmProblems += icmp6->ipv6IfIcmpOutParmProblems; + sum6->ipv6IfIcmpOutPktTooBigs += icmp6->ipv6IfIcmpOutPktTooBigs; + sum6->ipv6IfIcmpOutEchos += icmp6->ipv6IfIcmpOutEchos; + sum6->ipv6IfIcmpOutEchoReplies += icmp6->ipv6IfIcmpOutEchoReplies; + sum6->ipv6IfIcmpOutRouterSolicits += + icmp6->ipv6IfIcmpOutRouterSolicits; + sum6->ipv6IfIcmpOutRouterAdvertisements += + icmp6->ipv6IfIcmpOutRouterAdvertisements; + sum6->ipv6IfIcmpOutNeighborSolicits += + icmp6->ipv6IfIcmpOutNeighborSolicits; + sum6->ipv6IfIcmpOutNeighborAdvertisements += + icmp6->ipv6IfIcmpOutNeighborAdvertisements; + sum6->ipv6IfIcmpOutRedirects += icmp6->ipv6IfIcmpOutRedirects; + sum6->ipv6IfIcmpOutGroupMembQueries += + icmp6->ipv6IfIcmpOutGroupMembQueries; + sum6->ipv6IfIcmpOutGroupMembResponses += + icmp6->ipv6IfIcmpOutGroupMembResponses; + sum6->ipv6IfIcmpOutGroupMembReductions += + icmp6->ipv6IfIcmpOutGroupMembReductions; + sum6->ipv6IfIcmpInOverflows += icmp6->ipv6IfIcmpInOverflows; +} + +/* ----------------------------- MRT_STAT_REPORT --------------------------- */ + +static void +mrt_stat_report(mib_item_t *curritem) +{ + int jtemp = 0; + mib_item_t *tempitem; + + if (!(family_selected(AF_INET))) + return; + + (void) putchar('\n'); + /* 'for' loop 1: */ + for (tempitem = curritem; + tempitem; + tempitem = tempitem->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + tempitem->group, tempitem->mib_id, + tempitem->length, tempitem->valp); + } + + if (tempitem->mib_id == 0) { + switch (tempitem->group) { + case EXPER_DVMRP: { + struct mrtstat *mrts; + mrts = (struct mrtstat *)tempitem->valp; + + if (!(family_selected(AF_INET))) + continue; /* 'for' loop 1 */ + + print_mrt_stats(mrts); + break; + } + } + } + } /* 'for' loop 1 ends */ + (void) putchar('\n'); + (void) fflush(stdout); +} + +/* + * if_stat_total() - Computes totals for interface statistics + * and returns result by updating sumstats. + */ +static void +if_stat_total(struct ifstat *oldstats, struct ifstat *newstats, + struct ifstat *sumstats) +{ + sumstats->ipackets += newstats->ipackets - oldstats->ipackets; + sumstats->opackets += newstats->opackets - oldstats->opackets; + sumstats->ierrors += newstats->ierrors - oldstats->ierrors; + sumstats->oerrors += newstats->oerrors - oldstats->oerrors; + sumstats->collisions += newstats->collisions - oldstats->collisions; +} + +/* --------------------- IF_REPORT (netstat -i) -------------------------- */ + +static struct ifstat zerostat = { + 0LL, 0LL, 0LL, 0LL, 0LL +}; + +static void +if_report(mib_item_t *item, char *matchname, + int Iflag_only, boolean_t once_only) +{ + static boolean_t reentry = B_FALSE; + boolean_t alreadydone = B_FALSE; + int jtemp = 0; + uint32_t ifindex_v4 = 0; + uint32_t ifindex_v6 = 0; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, item->length, + item->valp); + } + + switch (item->group) { + case MIB2_IP: + if (item->mib_id != MIB2_IP_ADDR || + !family_selected(AF_INET)) + continue; /* 'for' loop 1 */ + { + static struct ifstat old = {0L, 0L, 0L, 0L, 0L}; + static struct ifstat new = {0L, 0L, 0L, 0L, 0L}; + struct ifstat sum; + struct iflist *newlist = NULL; + static struct iflist *oldlist = NULL; + kstat_t *ksp; + + if (once_only) { + char ifname[LIFNAMSIZ + 1]; + char logintname[LIFNAMSIZ + 1]; + mib2_ipAddrEntry_t *ap; + struct ifstat stat = {0L, 0L, 0L, 0L, 0L}; + boolean_t first = B_TRUE; + uint32_t new_ifindex; + + if (Dflag) + (void) printf("if_report: %d items\n", + (item->length) + / sizeof (mib2_ipAddrEntry_t)); + + /* 'for' loop 2a: */ + for (ap = (mib2_ipAddrEntry_t *)item->valp; + (char *)ap < (char *)item->valp + + item->length; + ap++) { + (void) octetstr(&ap->ipAdEntIfIndex, + 'a', logintname, + sizeof (logintname)); + (void) strcpy(ifname, logintname); + (void) strtok(ifname, ":"); + if (matchname != NULL && + strcmp(matchname, ifname) != 0 && + strcmp(matchname, logintname) != 0) + continue; /* 'for' loop 2a */ + new_ifindex = + if_nametoindex(logintname); + if (new_ifindex != ifindex_v4 && + (ksp = kstat_lookup(kc, NULL, -1, + ifname)) != NULL) { + (void) safe_kstat_read(kc, ksp, + NULL); + stat.ipackets = + kstat_named_value(ksp, + "ipackets"); + stat.ierrors = + kstat_named_value(ksp, + "ierrors"); + stat.opackets = + kstat_named_value(ksp, + "opackets"); + stat.oerrors = + kstat_named_value(ksp, + "oerrors"); + stat.collisions = + kstat_named_value(ksp, + "collisions"); + if (first) { + (void) printf( + "%-5.5s %-5.5s%-13.13s " + "%-14.14s %-6.6s %-5.5s " + "%-6.6s %-5.5s %-6.6s " + "%-6.6s\n", + "Name", "Mtu", "Net/Dest", + "Address", "Ipkts", + "Ierrs", "Opkts", "Oerrs", + "Collis", "Queue"); + first = B_FALSE; + } + if_report_ip4(ap, ifname, + logintname, &stat, B_TRUE); + ifindex_v4 = new_ifindex; + } else { + if_report_ip4(ap, ifname, + logintname, &stat, B_FALSE); + } + } /* 'for' loop 2a ends */ + if (!first) + (void) putchar('\n'); + } else if (!alreadydone) { + char ifname[LIFNAMSIZ + 1]; + char buf[LIFNAMSIZ + 1]; + mib2_ipAddrEntry_t *ap; + struct ifstat t; + struct iflist *tlp; + struct iflist **nextnew = &newlist; + struct iflist *walkold; + struct iflist *cleanlist; + + alreadydone = B_TRUE; /* ignore other case */ + /* + * 'for' loop 2b: find the "right" entry + */ + for (ap = (mib2_ipAddrEntry_t *)item->valp; + (char *)ap < (char *)item->valp + + item->length; + ap++) { + (void) octetstr(&ap->ipAdEntIfIndex, + 'a', ifname, sizeof (ifname)); + (void) strtok(ifname, ":"); + + if (matchname) { + if (strcmp(matchname, + ifname) == 0) + /* 'for' loop 2b */ + break; + } else if (strcmp(ifname, "lo0") != 0) { + matchname = ifname; + break; /* 'for' loop 2b */ + } + } /* 'for' loop 2b ends */ + + if (Iflag_only == 0 || !reentry) { + (void) printf(" input %-6.6s " + "output ", + matchname); + (void) printf(" input (Total) " + "output\n"); + (void) printf("%-7.7s %-5.5s %-7.7s " + "%-5.5s %-6.6s ", + "packets", "errs", "packets", + "errs", "colls"); + (void) printf("%-7.7s %-5.5s %-7.7s " + "%-5.5s %-6.6s\n", + "packets", "errs", "packets", + "errs", "colls"); + } + + sum = zerostat; + + /* 'for' loop 2c: */ + for (ap = (mib2_ipAddrEntry_t *)item->valp; + (char *)ap < (char *)item->valp + + item->length; + ap++) { + (void) octetstr(&ap->ipAdEntIfIndex, + 'a', buf, sizeof (buf)); + (void) strtok(buf, ":"); + ksp = kstat_lookup(kc, NULL, -1, buf); + if (ksp && + ksp->ks_type == KSTAT_TYPE_NAMED) + (void) safe_kstat_read(kc, ksp, + NULL); + + t.ipackets = kstat_named_value(ksp, + "ipackets"); + t.ierrors = kstat_named_value(ksp, + "ierrors"); + t.opackets = kstat_named_value(ksp, + "opackets"); + t.oerrors = kstat_named_value(ksp, + "oerrors"); + t.collisions = kstat_named_value(ksp, + "collisions"); + + if (strcmp(buf, matchname) == 0) + new = t; + + /* Build the interface list */ + + tlp = malloc(sizeof (struct iflist)); + (void) strlcpy(tlp->ifname, buf, + sizeof (tlp->ifname)); + tlp->tot = t; + *nextnew = tlp; + nextnew = &tlp->next_if; + + /* + * First time through. + * Just add up the interface stats. + */ + + if (oldlist == NULL) { + if_stat_total(&zerostat, + &t, &sum); + continue; + } + + /* + * Walk old list for the interface. + * + * If found, add difference to total. + * + * If not, an interface has been plumbed + * up. In this case, we will simply + * ignore the new interface until the + * next interval; as there's no easy way + * to acquire statistics between time + * of the plumb and the next interval + * boundary. This results in inaccurate + * total values for current interval. + * + * Note the case when an interface is + * unplumbed; as similar problems exist. + * The unplumbed interface is not in the + * current list, and there's no easy way + * to account for the statistics between + * the previous interval and time of the + * unplumb. Therefore, we (in a sense) + * ignore the removed interface by only + * involving "current" interfaces when + * computing the total statistics. + * Unfortunately, this also results in + * inaccurate values for interval total. + */ + + for (walkold = oldlist; + walkold != NULL; + walkold = walkold->next_if) { + if (strcmp(walkold->ifname, + buf) == 0) { + if_stat_total( + &walkold->tot, + &t, &sum); + break; + } + } + + } /* 'for' loop 2c ends */ + + *nextnew = NULL; + + (void) printf("%-7llu %-5llu %-7llu " + "%-5llu %-6llu ", + new.ipackets - old.ipackets, + new.ierrors - old.ierrors, + new.opackets - old.opackets, + new.oerrors - old.oerrors, + new.collisions - old.collisions); + + (void) printf("%-7llu %-5llu %-7llu " + "%-5llu %-6llu\n", sum.ipackets, + sum.ierrors, sum.opackets, + sum.oerrors, sum.collisions); + + /* + * Tidy things up once finished. + */ + + old = new; + cleanlist = oldlist; + oldlist = newlist; + while (cleanlist != NULL) { + tlp = cleanlist->next_if; + free(cleanlist); + cleanlist = tlp; + } + } + break; + } + case MIB2_IP6: + if (item->mib_id != MIB2_IP6_ADDR || + !family_selected(AF_INET6)) + continue; /* 'for' loop 1 */ + { + static struct ifstat old6 = {0L, 0L, 0L, 0L, 0L}; + static struct ifstat new6 = {0L, 0L, 0L, 0L, 0L}; + struct ifstat sum6; + struct iflist *newlist6 = NULL; + static struct iflist *oldlist6 = NULL; + kstat_t *ksp; + + if (once_only) { + char ifname[LIFNAMSIZ + 1]; + char logintname[LIFNAMSIZ + 1]; + mib2_ipv6AddrEntry_t *ap6; + struct ifstat stat = {0L, 0L, 0L, 0L, 0L}; + boolean_t first = B_TRUE; + uint32_t new_ifindex; + + if (Dflag) + (void) printf("if_report: %d items\n", + (item->length) + / sizeof (mib2_ipv6AddrEntry_t)); + /* 'for' loop 2d: */ + for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp; + (char *)ap6 < (char *)item->valp + + item->length; + ap6++) { + (void) octetstr(&ap6->ipv6AddrIfIndex, + 'a', logintname, + sizeof (logintname)); + (void) strcpy(ifname, logintname); + (void) strtok(ifname, ":"); + if (matchname != NULL && + strcmp(matchname, ifname) != 0 && + strcmp(matchname, logintname) != 0) + continue; /* 'for' loop 2d */ + new_ifindex = + if_nametoindex(logintname); + if (new_ifindex != ifindex_v6 && + (ksp = kstat_lookup(kc, NULL, -1, + ifname)) != NULL) { + (void) safe_kstat_read(kc, ksp, + NULL); + stat.ipackets = + kstat_named_value(ksp, + "ipackets"); + stat.ierrors = + kstat_named_value(ksp, + "ierrors"); + stat.opackets = + kstat_named_value(ksp, + "opackets"); + stat.oerrors = + kstat_named_value(ksp, + "oerrors"); + stat.collisions = + kstat_named_value(ksp, + "collisions"); + if (first) { + (void) printf( + "%-5.5s %-5.5s%" + "-27.27s %-27.27s " + "%-6.6s %-5.5s " + "%-6.6s %-5.5s " + "%-6.6s\n", + "Name", "Mtu", + "Net/Dest", + "Address", "Ipkts", + "Ierrs", "Opkts", + "Oerrs", "Collis"); + first = B_FALSE; + } + if_report_ip6(ap6, ifname, + logintname, &stat, B_TRUE); + ifindex_v6 = new_ifindex; + } else { + if_report_ip6(ap6, ifname, + logintname, &stat, B_FALSE); + } + } /* 'for' loop 2d ends */ + if (!first) + (void) putchar('\n'); + } else if (!alreadydone) { + char ifname[LIFNAMSIZ + 1]; + char buf[IFNAMSIZ + 1]; + mib2_ipv6AddrEntry_t *ap6; + struct ifstat t; + struct iflist *tlp; + struct iflist **nextnew = &newlist6; + struct iflist *walkold; + struct iflist *cleanlist; + + alreadydone = B_TRUE; /* ignore other case */ + /* + * 'for' loop 2e: find the "right" entry + */ + for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp; + (char *)ap6 < (char *)item->valp + + item->length; + ap6++) { + (void) octetstr(&ap6->ipv6AddrIfIndex, + 'a', ifname, sizeof (ifname)); + (void) strtok(ifname, ":"); + + if (matchname) { + if (strcmp(matchname, ifname) == + 0) + /* 'for' loop 2e */ + break; + } else if (strcmp(ifname, "lo0") != 0) { + matchname = ifname; + break; /* 'for' loop 2e */ + } + } /* 'for' loop 2e ends */ + + if (Iflag_only == 0 || !reentry) { + (void) printf( + " input %-6.6s" + " output ", + matchname); + (void) printf(" input (Total)" + " output\n"); + (void) printf("%-7.7s %-5.5s %-7.7s " + "%-5.5s %-6.6s ", + "packets", "errs", "packets", + "errs", "colls"); + (void) printf("%-7.7s %-5.5s %-7.7s " + "%-5.5s %-6.6s\n", + "packets", "errs", "packets", + "errs", "colls"); + } + + sum6 = zerostat; + + /* 'for' loop 2f: */ + for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp; + (char *)ap6 < (char *)item->valp + + item->length; + ap6++) { + (void) octetstr(&ap6->ipv6AddrIfIndex, + 'a', buf, sizeof (buf)); + (void) strtok(buf, ":"); + ksp = kstat_lookup(kc, NULL, -1, buf); + if (ksp && ksp->ks_type == + KSTAT_TYPE_NAMED) + (void) safe_kstat_read(kc, + ksp, NULL); + + t.ipackets = kstat_named_value(ksp, + "ipackets"); + t.ierrors = kstat_named_value(ksp, + "ierrors"); + t.opackets = kstat_named_value(ksp, + "opackets"); + t.oerrors = kstat_named_value(ksp, + "oerrors"); + t.collisions = kstat_named_value(ksp, + "collisions"); + + if (strcmp(buf, matchname) == 0) + new6 = t; + + /* Build the interface list */ + + tlp = malloc(sizeof (struct iflist)); + (void) strlcpy(tlp->ifname, buf, + sizeof (tlp->ifname)); + tlp->tot = t; + *nextnew = tlp; + nextnew = &tlp->next_if; + + /* + * First time through. + * Just add up the interface stats. + */ + + if (oldlist6 == NULL) { + if_stat_total(&zerostat, + &t, &sum6); + continue; + } + + /* + * Walk old list for the interface. + * + * If found, add difference to total. + * + * If not, an interface has been plumbed + * up. In this case, we will simply + * ignore the new interface until the + * next interval; as there's no easy way + * to acquire statistics between time + * of the plumb and the next interval + * boundary. This results in inaccurate + * total values for current interval. + * + * Note the case when an interface is + * unplumbed; as similar problems exist. + * The unplumbed interface is not in the + * current list, and there's no easy way + * to account for the statistics between + * the previous interval and time of the + * unplumb. Therefore, we (in a sense) + * ignore the removed interface by only + * involving "current" interfaces when + * computing the total statistics. + * Unfortunately, this also results in + * inaccurate values for interval total. + */ + + for (walkold = oldlist6; + walkold != NULL; + walkold = walkold->next_if) { + if (strcmp(walkold->ifname, + buf) == 0) { + if_stat_total( + &walkold->tot, + &t, &sum6); + break; + } + } + + } /* 'for' loop 2f ends */ + + *nextnew = NULL; + + (void) printf("%-7llu %-5llu %-7llu " + "%-5llu %-6llu ", + new6.ipackets - old6.ipackets, + new6.ierrors - old6.ierrors, + new6.opackets - old6.opackets, + new6.oerrors - old6.oerrors, + new6.collisions - old6.collisions); + + (void) printf("%-7llu %-5llu %-7llu " + "%-5llu %-6llu\n", sum6.ipackets, + sum6.ierrors, sum6.opackets, + sum6.oerrors, sum6.collisions); + + /* + * Tidy things up once finished. + */ + + old6 = new6; + cleanlist = oldlist6; + oldlist6 = newlist6; + while (cleanlist != NULL) { + tlp = cleanlist->next_if; + free(cleanlist); + cleanlist = tlp; + } + } + break; + } + } + if (Iflag_only == 0) + (void) putchar('\n'); + (void) fflush(stdout); + } /* 'for' loop 1 ends */ + reentry = B_TRUE; +} + +static void +if_report_ip4(mib2_ipAddrEntry_t *ap, + char ifname[], char logintname[], struct ifstat *statptr, + boolean_t ksp_not_null) { + + char abuf[MAXHOSTNAMELEN + 1]; + char dstbuf[MAXHOSTNAMELEN + 1]; + + if (ksp_not_null) { + (void) printf("%-5s %-5u", + ifname, ap->ipAdEntInfo.ae_mtu); + if (ap->ipAdEntInfo.ae_flags & IFF_POINTOPOINT) + (void) pr_addr(ap->ipAdEntInfo.ae_pp_dst_addr, + abuf, sizeof (abuf)); + else + (void) pr_netaddr(ap->ipAdEntAddr, + ap->ipAdEntNetMask, abuf, sizeof (abuf)); + (void) printf("%-13s %-14s %-6llu %-5llu %-6llu %-5llu " + "%-6llu %-6llu\n", + abuf, pr_addr(ap->ipAdEntAddr, dstbuf, sizeof (dstbuf)), + statptr->ipackets, statptr->ierrors, + statptr->opackets, statptr->oerrors, + statptr->collisions, 0LL); + } + /* + * Print logical interface info if Aflag set (including logical unit 0) + */ + if (Aflag) { + *statptr = zerostat; + statptr->ipackets = ap->ipAdEntInfo.ae_ibcnt; + statptr->opackets = ap->ipAdEntInfo.ae_obcnt; + + (void) printf("%-5s %-5u", logintname, ap->ipAdEntInfo.ae_mtu); + if (ap->ipAdEntInfo.ae_flags & IFF_POINTOPOINT) + (void) pr_addr(ap->ipAdEntInfo.ae_pp_dst_addr, abuf, + sizeof (abuf)); + else + (void) pr_netaddr(ap->ipAdEntAddr, ap->ipAdEntNetMask, + abuf, sizeof (abuf)); + + (void) printf("%-13s %-14s %-6llu %-5s %-6llu " + "%-5s %-6s %-6llu\n", abuf, + pr_addr(ap->ipAdEntAddr, dstbuf, sizeof (dstbuf)), + statptr->ipackets, "N/A", statptr->opackets, "N/A", "N/A", + 0LL); + } +} + +static void +if_report_ip6(mib2_ipv6AddrEntry_t *ap6, + char ifname[], char logintname[], struct ifstat *statptr, + boolean_t ksp_not_null) { + + char abuf[MAXHOSTNAMELEN + 1]; + char dstbuf[MAXHOSTNAMELEN + 1]; + + if (ksp_not_null) { + (void) printf("%-5s %-5u", ifname, ap6->ipv6AddrInfo.ae_mtu); + if (ap6->ipv6AddrInfo.ae_flags & + IFF_POINTOPOINT) { + (void) pr_addr6(&ap6->ipv6AddrInfo.ae_pp_dst_addr, + abuf, sizeof (abuf)); + } else { + (void) pr_prefix6(&ap6->ipv6AddrAddress, + ap6->ipv6AddrPfxLength, abuf, + sizeof (abuf)); + } + (void) printf("%-27s %-27s %-6llu %-5llu " + "%-6llu %-5llu %-6llu\n", + abuf, pr_addr6(&ap6->ipv6AddrAddress, dstbuf, + sizeof (dstbuf)), + statptr->ipackets, statptr->ierrors, statptr->opackets, + statptr->oerrors, statptr->collisions); + } + /* + * Print logical interface info if Aflag set (including logical unit 0) + */ + if (Aflag) { + *statptr = zerostat; + statptr->ipackets = ap6->ipv6AddrInfo.ae_ibcnt; + statptr->opackets = ap6->ipv6AddrInfo.ae_obcnt; + + (void) printf("%-5s %-5u", logintname, + ap6->ipv6AddrInfo.ae_mtu); + if (ap6->ipv6AddrInfo.ae_flags & IFF_POINTOPOINT) + (void) pr_addr6(&ap6->ipv6AddrInfo.ae_pp_dst_addr, + abuf, sizeof (abuf)); + else + (void) pr_prefix6(&ap6->ipv6AddrAddress, + ap6->ipv6AddrPfxLength, abuf, sizeof (abuf)); + (void) printf("%-27s %-27s %-6llu %-5s %-6llu %-5s %-6s\n", + abuf, pr_addr6(&ap6->ipv6AddrAddress, dstbuf, + sizeof (dstbuf)), + statptr->ipackets, "N/A", + statptr->opackets, "N/A", "N/A"); + } +} + +/* --------------------- DHCP_REPORT (netstat -D) ------------------------- */ + +dhcp_ipc_reply_t * +dhcp_do_ipc(dhcp_ipc_type_t type, const char *ifname) +{ + dhcp_ipc_request_t *request; + dhcp_ipc_reply_t *reply; + int error; + + request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE); + if (request == NULL) + fail(0, "dhcp_do_ipc: out of memory"); + + error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT); + if (error != 0) { + free(request); + fail(0, "dhcp_do_ipc: %s", dhcp_ipc_strerror(error)); + } + + free(request); + error = reply->return_code; + if (error != 0) { + free(reply); + fail(0, "dhcp_do_ipc: %s", dhcp_ipc_strerror(error)); + } + + return (reply); +} + +/* + * get_ifnames: return a dynamically allocated string of all interface + * names which have all of the IFF_* flags listed in `flags_on' on and + * all of the IFF_* flags in `flags_off' off. If no such interfaces + * are found, "" is returned. If an unexpected failure occurs, NULL + * is returned. + */ +static char * +get_ifnames(int flags_on, int flags_off) +{ + struct ifconf ifc; + int n_ifs, i, sock_fd; + char *ifnames; + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd == -1) + return (NULL); + + if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) { + (void) close(sock_fd); + return (NULL); + } + + ifnames = calloc(1, n_ifs * (IFNAMSIZ + 1)); + ifc.ifc_len = n_ifs * sizeof (struct ifreq); + ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq)); + if (ifc.ifc_req != NULL && ifnames != NULL) { + + if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) { + (void) close(sock_fd); + free(ifnames); + free(ifc.ifc_req); + return (NULL); + } + + /* 'for' loop 1: */ + for (i = 0; i < n_ifs; i++) { + + if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0) + if ((ifc.ifc_req[i].ifr_flags & + (flags_on | flags_off)) != flags_on) + continue; /* 'for' loop 1 */ + + (void) strcat(ifnames, ifc.ifc_req[i].ifr_name); + (void) strcat(ifnames, " "); + } /* 'for' loop 1 ends */ + + if (strlen(ifnames) > 1) + ifnames[strlen(ifnames) - 1] = '\0'; + } + + (void) close(sock_fd); + if (ifc.ifc_req != NULL) + free(ifc.ifc_req); + return (ifnames); +} + +static void +dhcp_report(char *ifname) +{ + int did_alloc = 0; + dhcp_ipc_reply_t *reply; + + if (!(family_selected(AF_INET))) + return; + + if (ifname == NULL) { + ifname = get_ifnames(IFF_DHCPRUNNING, 0); + if (ifname == NULL) + fail(0, "dhcp_report: unable to retrieve list of" + " interfaces using DHCP"); + did_alloc++; + } + + (void) printf("%s", dhcp_status_hdr_string()); + + for (ifname = strtok(ifname, " "); + ifname != NULL; + ifname = strtok(NULL, " ")) { + reply = dhcp_do_ipc(DHCP_STATUS, ifname); + (void) printf("%s", dhcp_status_reply_to_string(reply)); + free(reply); + } + + if (did_alloc) + free(ifname); +} + +/* --------------------- GROUP_REPORT (netstat -g) ------------------------- */ + +static void +group_report(mib_item_t *item) +{ + mib_item_t *v4grp = NULL, *v4src = NULL; + mib_item_t *v6grp = NULL, *v6src = NULL; + int jtemp = 0; + char ifname[LIFNAMSIZ + 1]; + char abuf[MAXHOSTNAMELEN + 1]; + ip_member_t *ipmp; + ip_grpsrc_t *ips; + ipv6_member_t *ipmp6; + ipv6_grpsrc_t *ips6; + char *ifnamep; + boolean_t first, first_src; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, item->length, + item->valp); + } + if (item->group == MIB2_IP && family_selected(AF_INET)) { + switch (item->mib_id) { + case EXPER_IP_GROUP_MEMBERSHIP: + v4grp = item; + if (Dflag) + (void) printf("item is v4grp info\n"); + break; + case EXPER_IP_GROUP_SOURCES: + v4src = item; + if (Dflag) + (void) printf("item is v4src info\n"); + break; + default: + continue; + } + continue; + } + if (item->group == MIB2_IP6 && family_selected(AF_INET6)) { + switch (item->mib_id) { + case EXPER_IP6_GROUP_MEMBERSHIP: + v6grp = item; + if (Dflag) + (void) printf("item is v6grp info\n"); + break; + case EXPER_IP6_GROUP_SOURCES: + v6src = item; + if (Dflag) + (void) printf("item is v6src info\n"); + break; + default: + continue; + } + } + } + + if (family_selected(AF_INET) && v4grp != NULL) { + if (Dflag) + (void) printf("%u records for ipGroupMember:\n", + v4grp->length / sizeof (ip_member_t)); + + first = B_TRUE; + for (ipmp = (ip_member_t *)v4grp->valp; + (char *)ipmp < (char *)v4grp->valp + v4grp->length; + /* LINTED: (note 1) */ + ipmp = (ip_member_t *)((char *)ipmp + ipMemberEntrySize)) { + if (first) { + (void) puts(v4compat ? + "Group Memberships" : + "Group Memberships: IPv4"); + (void) puts("Interface " + "Group RefCnt"); + (void) puts("--------- " + "-------------------- ------"); + first = B_FALSE; + } + + (void) printf("%-9s %-20s %6u\n", + octetstr(&ipmp->ipGroupMemberIfIndex, 'a', + ifname, sizeof (ifname)), + pr_addr(ipmp->ipGroupMemberAddress, + abuf, sizeof (abuf)), + ipmp->ipGroupMemberRefCnt); + + + if (!Vflag || v4src == NULL) + continue; + + if (Dflag) + (void) printf("scanning %u ipGroupSource " + "records...\n", + v4src->length/sizeof (ip_grpsrc_t)); + + first_src = B_TRUE; + for (ips = (ip_grpsrc_t *)v4src->valp; + (char *)ips < (char *)v4src->valp + v4src->length; + /* LINTED: (note 1) */ + ips = (ip_grpsrc_t *)((char *)ips + + ipGroupSourceEntrySize)) { + /* + * We assume that all source addrs for a given + * interface/group pair are contiguous, so on + * the first non-match after we've found at + * least one, we bail. + */ + if ((ipmp->ipGroupMemberAddress != + ips->ipGroupSourceGroup) || + (!octetstrmatch(&ipmp->ipGroupMemberIfIndex, + &ips->ipGroupSourceIfIndex))) { + if (first_src) + continue; + else + break; + } + if (first_src) { + (void) printf("\t%s: %s\n", + fmodestr( + ipmp->ipGroupMemberFilterMode), + pr_addr(ips->ipGroupSourceAddress, + abuf, sizeof (abuf))); + first_src = B_FALSE; + continue; + } + + (void) printf("\t %s\n", + pr_addr(ips->ipGroupSourceAddress, abuf, + sizeof (abuf))); + } + } + (void) putchar('\n'); + } + + if (family_selected(AF_INET6) && v6grp != NULL) { + if (Dflag) + (void) printf("%u records for ipv6GroupMember:\n", + v6grp->length / sizeof (ipv6_member_t)); + + first = B_TRUE; + for (ipmp6 = (ipv6_member_t *)v6grp->valp; + (char *)ipmp6 < (char *)v6grp->valp + v6grp->length; + /* LINTED: (note 1) */ + ipmp6 = (ipv6_member_t *)((char *)ipmp6 + + ipv6MemberEntrySize)) { + if (first) { + (void) puts("Group Memberships: " + "IPv6"); + (void) puts(" If " + "Group RefCnt"); + (void) puts("----- " + "--------------------------- ------"); + first = B_FALSE; + } + + ifnamep = if_indextoname( + ipmp6->ipv6GroupMemberIfIndex, ifname); + if (ifnamep == NULL) { + (void) printf("Invalid ifindex %d\n", + ipmp6->ipv6GroupMemberIfIndex); + continue; + } + (void) printf("%-5s %-27s %5u\n", + ifnamep, + pr_addr6(&ipmp6->ipv6GroupMemberAddress, + abuf, sizeof (abuf)), + ipmp6->ipv6GroupMemberRefCnt); + + if (!Vflag || v6src == NULL) + continue; + + if (Dflag) + (void) printf("scanning %u ipv6GroupSource " + "records...\n", + v6src->length/sizeof (ipv6_grpsrc_t)); + + first_src = B_TRUE; + for (ips6 = (ipv6_grpsrc_t *)v6src->valp; + (char *)ips6 < (char *)v6src->valp + v6src->length; + /* LINTED: (note 1) */ + ips6 = (ipv6_grpsrc_t *)((char *)ips6 + + ipv6GroupSourceEntrySize)) { + /* same assumption as in the v4 case above */ + if ((ipmp6->ipv6GroupMemberIfIndex != + ips6->ipv6GroupSourceIfIndex) || + (!IN6_ARE_ADDR_EQUAL( + &ipmp6->ipv6GroupMemberAddress, + &ips6->ipv6GroupSourceGroup))) { + if (first_src) + continue; + else + break; + } + if (first_src) { + (void) printf("\t%s: %s\n", + fmodestr( + ipmp6->ipv6GroupMemberFilterMode), + pr_addr6( + &ips6->ipv6GroupSourceAddress, + abuf, sizeof (abuf))); + first_src = B_FALSE; + continue; + } + + (void) printf("\t %s\n", + pr_addr6(&ips6->ipv6GroupSourceAddress, + abuf, sizeof (abuf))); + } + } + (void) putchar('\n'); + } + + (void) putchar('\n'); + (void) fflush(stdout); +} + +/* --------------------- ARP_REPORT (netstat -p) -------------------------- */ + +static void +arp_report(mib_item_t *item) +{ + int jtemp = 0; + char ifname[LIFNAMSIZ + 1]; + char abuf[MAXHOSTNAMELEN + 1]; + char maskbuf[STR_EXPAND * OCTET_LENGTH + 1]; + char flbuf[32]; /* ACE_F_ flags */ + char xbuf[STR_EXPAND * OCTET_LENGTH + 1]; + mib2_ipNetToMediaEntry_t *np; + int flags; + boolean_t first; + + if (!(family_selected(AF_INET))) + return; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, item->length, + item->valp); + } + if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_MEDIA)) + continue; /* 'for' loop 1 */ + + if (Dflag) + (void) printf("%u records for " + "ipNetToMediaEntryTable:\n", + item->length/sizeof (mib2_ipNetToMediaEntry_t)); + + first = B_TRUE; + /* 'for' loop 2: */ + for (np = (mib2_ipNetToMediaEntry_t *)item->valp; + (char *)np < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + np = (mib2_ipNetToMediaEntry_t *)((char *)np + + ipNetToMediaEntrySize)) { + if (first) { + (void) puts(v4compat ? + "Net to Media Table" : + "Net to Media Table: IPv4"); + (void) fputs("Device " + "IP Address Mask ", + stdout); + (void) puts("Flags Phys Addr "); + (void) puts("------ -------------------- " + "--------------- ----- ---------------"); + first = B_FALSE; + } + + flbuf[0] = '\0'; + flags = np->ipNetToMediaInfo.ntm_flags; + if (flags & ACE_F_PERMANENT) + (void) strcat(flbuf, "S"); + if (flags & ACE_F_PUBLISH) + (void) strcat(flbuf, "P"); + if (flags & ACE_F_DYING) + (void) strcat(flbuf, "D"); + if (!(flags & ACE_F_RESOLVED)) + (void) strcat(flbuf, "U"); + if (flags & ACE_F_MAPPING) + (void) strcat(flbuf, "M"); + (void) printf("%-6s %-20s %-15s %-5s %s\n", + octetstr(&np->ipNetToMediaIfIndex, 'a', + ifname, sizeof (ifname)), + pr_addr(np->ipNetToMediaNetAddress, + abuf, sizeof (abuf)), + octetstr(&np->ipNetToMediaInfo.ntm_mask, 'd', + maskbuf, sizeof (maskbuf)), + flbuf, + octetstr(&np->ipNetToMediaPhysAddress, 'h', + xbuf, sizeof (xbuf))); + } /* 'for' loop 2 ends */ + } /* 'for' loop 1 ends */ + (void) fflush(stdout); +} + +/* --------------------- NDP_REPORT (netstat -p) -------------------------- */ + +static void +ndp_report(mib_item_t *item) +{ + int jtemp = 0; + char abuf[MAXHOSTNAMELEN + 1]; + char *state; + char *type; + char xbuf[STR_EXPAND * OCTET_LENGTH + 1]; + mib2_ipv6NetToMediaEntry_t *np6; + char ifname[LIFNAMSIZ + 1]; + char *ifnamep; + boolean_t first; + + if (!(family_selected(AF_INET6))) + return; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, item->length, + item->valp); + } + if (!(item->group == MIB2_IP6 && + item->mib_id == MIB2_IP6_MEDIA)) + continue; /* 'for' loop 1 */ + + first = B_TRUE; + /* 'for' loop 2: */ + for (np6 = (mib2_ipv6NetToMediaEntry_t *)item->valp; + (char *)np6 < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + np6 = (mib2_ipv6NetToMediaEntry_t *)((char *)np6 + + ipv6NetToMediaEntrySize)) { + if (first) { + (void) puts("\nNet to Media Table: IPv6"); + (void) puts(" If Physical Address " + " Type State Destination/Mask"); + (void) puts("----- ----------------- " + "------- ------------ " + "---------------------------"); + first = B_FALSE; + } + + ifnamep = if_indextoname(np6->ipv6NetToMediaIfIndex, + ifname); + if (ifnamep == NULL) { + (void) printf("Invalid ifindex %d\n", + np6->ipv6NetToMediaIfIndex); + continue; /* 'for' loop 2 */ + } + switch (np6->ipv6NetToMediaState) { + case ND_INCOMPLETE: + state = "INCOMPLETE"; + break; + case ND_REACHABLE: + state = "REACHABLE"; + break; + case ND_STALE: + state = "STALE"; + break; + case ND_DELAY: + state = "DELAY"; + break; + case ND_PROBE: + state = "PROBE"; + break; + case ND_UNREACHABLE: + state = "UNREACHABLE"; + break; + default: + state = "UNKNOWN"; + } + + switch (np6->ipv6NetToMediaType) { + case 1: + type = "other"; + break; + case 2: + type = "dynamic"; + break; + case 3: + type = "static"; + break; + case 4: + type = "local"; + break; + } + (void) printf("%-5s %-17s %-7s %-12s %-27s\n", + ifnamep, + octetstr(&np6->ipv6NetToMediaPhysAddress, 'h', + xbuf, sizeof (xbuf)), + type, + state, + pr_addr6(&np6->ipv6NetToMediaNetAddress, + abuf, sizeof (abuf))); + } /* 'for' loop 2 ends */ + } /* 'for' loop 1 ends */ + (void) putchar('\n'); + (void) fflush(stdout); +} + +/* ------------------------- ire_report (netstat -r) ------------------------ */ + +static boolean_t ire_report_item_v4(mib2_ipRouteEntry_t *rp, boolean_t first); +static boolean_t ire_report_item_v4src(mib2_ipRouteEntry_t *rp, + boolean_t first); +static boolean_t ire_report_item_v6(mib2_ipv6RouteEntry_t *rp6, + boolean_t first); + +static void +ire_report(mib_item_t *item) +{ + int jtemp = 0; + boolean_t print_hdr_once_v4 = B_TRUE; + boolean_t print_hdr_once_v6 = B_TRUE; + mib2_ipRouteEntry_t *rp; + mib2_ipv6RouteEntry_t *rp6; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, + item->length, item->valp); + } + if (!((item->group == MIB2_IP && + item->mib_id == MIB2_IP_ROUTE) || + (item->group == MIB2_IP6 && + item->mib_id == MIB2_IP6_ROUTE))) + continue; /* 'for' loop 1 */ + + if (item->group == MIB2_IP && !family_selected(AF_INET)) + continue; /* 'for' loop 1 */ + else if (item->group == MIB2_IP6 && !family_selected(AF_INET6)) + continue; /* 'for' loop 1 */ + + if (Dflag) { + if (item->group == MIB2_IP) { + (void) printf("%u records for " + "ipRouteEntryTable:\n", + item->length/sizeof (mib2_ipRouteEntry_t)); + } else { + (void) printf("%u records for " + "ipv6RouteEntryTable:\n", + item->length/ + sizeof (mib2_ipv6RouteEntry_t)); + } + } + + if (item->group == MIB2_IP) { + for (rp = (mib2_ipRouteEntry_t *)item->valp; + (char *)rp < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + rp = (mib2_ipRouteEntry_t *)((char *)rp + + ipRouteEntrySize)) { + print_hdr_once_v4 = ire_report_item_v4(rp, + print_hdr_once_v4); + } + print_hdr_once_v4 = B_TRUE; + for (rp = (mib2_ipRouteEntry_t *)item->valp; + (char *)rp < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + rp = (mib2_ipRouteEntry_t *)((char *)rp + + ipRouteEntrySize)) { + print_hdr_once_v4 = ire_report_item_v4src(rp, + print_hdr_once_v4); + } + } else { + for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp; + (char *)rp6 < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + rp6 = (mib2_ipv6RouteEntry_t *)((char *)rp6 + + ipv6RouteEntrySize)) { + print_hdr_once_v6 = ire_report_item_v6(rp6, + print_hdr_once_v6); + } + } + } /* 'for' loop 1 ends */ + (void) fflush(stdout); +} + +/* + * Match a user-supplied device name. We do this by string because + * the MIB2 interface gives us interface name strings rather than + * ifIndex numbers. The "none" rule matches only routes with no + * interface. The "any" rule matches routes with any non-blank + * interface. A base name ("hme0") matches all aliases as well + * ("hme0:1"). + */ +static boolean_t +dev_name_match(const DeviceName *devnam, const char *ifname) +{ + int iflen; + + if (ifname == NULL) + return (devnam->o_length == 0); /* "none" */ + if (*ifname == '\0') + return (devnam->o_length != 0); /* "any" */ + iflen = strlen(ifname); + /* The check for ':' here supports interface aliases. */ + if (iflen > devnam->o_length || + (iflen < devnam->o_length && devnam->o_bytes[iflen] != ':')) + return (B_FALSE); + return (strncmp(ifname, devnam->o_bytes, iflen) == 0); +} + +/* + * Match a user-supplied IP address list. The "any" rule matches any + * non-zero address. The "none" rule matches only the zero address. + * IPv6 addresses supplied by the user are ignored. If the user + * supplies a subnet mask, then match routes that are at least that + * specific (use the user's mask). If the user supplies only an + * address, then select any routes that would match (use the route's + * mask). + */ +static boolean_t +v4_addr_match(IpAddress addr, IpAddress mask, const filter_t *fp) +{ + char **app; + char *aptr; + in_addr_t faddr, fmask; + + if (fp->u.a.f_address == NULL) { + if (IN6_IS_ADDR_UNSPECIFIED(&fp->u.a.f_mask)) + return (addr != INADDR_ANY); /* "any" */ + else + return (addr == INADDR_ANY); /* "none" */ + } + if (!IN6_IS_V4MASK(fp->u.a.f_mask)) + return (B_FALSE); + IN6_V4MAPPED_TO_IPADDR(&fp->u.a.f_mask, fmask); + if (fmask != IP_HOST_MASK) { + if (fmask > mask) + return (B_FALSE); + mask = fmask; + } + for (app = fp->u.a.f_address->h_addr_list; (aptr = *app) != NULL; app++) + /* LINTED: (note 1) */ + if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)aptr)) { + /* LINTED: (note 1) */ + IN6_V4MAPPED_TO_IPADDR((in6_addr_t *)aptr, faddr); + if (((faddr ^ addr) & mask) == 0) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Run through the filter list for an IPv4 MIB2 route entry. If all + * filters of a given type fail to match, then the route is filtered + * out (not displayed). If no filter is given or at least one filter + * of each type matches, then display the route. + */ +static boolean_t +ire_filter_match_v4(mib2_ipRouteEntry_t *rp, uint_t flag_b) +{ + filter_t *fp; + int idx; + + /* 'for' loop 1: */ + for (idx = 0; idx < NFILTERKEYS; idx++) + if ((fp = filters[idx]) != NULL) { + /* 'for' loop 2: */ + for (; fp != NULL; fp = fp->f_next) { + switch (idx) { + case FK_AF: + if (fp->u.f_family != AF_INET) + continue; /* 'for' loop 2 */ + break; + case FK_INIF: + if (!dev_name_match(&rp->ipRouteInfo. + re_in_ill, fp->u.f_ifname)) + continue; /* 'for' loop 2 */ + break; + case FK_OUTIF: + if (!dev_name_match(&rp->ipRouteIfIndex, + fp->u.f_ifname)) + continue; /* 'for' loop 2 */ + break; + case FK_SRC: + if (!v4_addr_match(rp->ipRouteInfo. + re_in_src_addr, IP_HOST_MASK, fp)) + continue; /* 'for' loop 2 */ + break; + case FK_DST: + if (!v4_addr_match(rp->ipRouteDest, + rp->ipRouteMask, fp)) + continue; /* 'for' loop 2 */ + break; + case FK_FLAGS: + if ((flag_b & fp->u.f.f_flagset) != + fp->u.f.f_flagset || + (flag_b & fp->u.f.f_flagclear)) + continue; /* 'for' loop 2 */ + break; + } + break; + } /* 'for' loop 2 ends */ + if (fp == NULL) + return (B_FALSE); + } + /* 'for' loop 1 ends */ + return (B_TRUE); +} + +/* + * Given an IPv4 MIB2 route entry, form the list of flags for the + * route. + */ +static uint_t +form_v4_route_flags(mib2_ipRouteEntry_t *rp, char *flags) +{ + uint_t flag_b; + + flag_b = FLF_U; + (void) strcpy(flags, "U"); + if (rp->ipRouteInfo.re_ire_type == IRE_DEFAULT || + rp->ipRouteInfo.re_ire_type == IRE_PREFIX || + rp->ipRouteInfo.re_ire_type == IRE_HOST || + rp->ipRouteInfo.re_ire_type == IRE_HOST_REDIRECT) { + (void) strcat(flags, "G"); + flag_b |= FLF_G; + } + if (rp->ipRouteMask == IP_HOST_MASK) { + (void) strcat(flags, "H"); + flag_b |= FLF_H; + } + if (rp->ipRouteInfo.re_ire_type == IRE_HOST_REDIRECT) { + (void) strcat(flags, "D"); + flag_b |= FLF_D; + } + if (rp->ipRouteInfo.re_ire_type == IRE_CACHE) { + /* Address resolution */ + (void) strcat(flags, "A"); + flag_b |= FLF_A; + } + if (rp->ipRouteInfo.re_ire_type == IRE_BROADCAST) { /* Broadcast */ + (void) strcat(flags, "B"); + flag_b |= FLF_B; + } + if (rp->ipRouteInfo.re_ire_type == IRE_LOCAL) { /* Local */ + (void) strcat(flags, "L"); + flag_b |= FLF_L; + } + if (rp->ipRouteInfo.re_flags & RTF_MULTIRT) { + (void) strcat(flags, "M"); /* Multiroute */ + flag_b |= FLF_M; + } + if (rp->ipRouteInfo.re_flags & RTF_SETSRC) { + (void) strcat(flags, "S"); /* Setsrc */ + flag_b |= FLF_S; + } + return (flag_b); +} + +static boolean_t +ire_report_item_v4(mib2_ipRouteEntry_t *rp, boolean_t first) +{ + char dstbuf[MAXHOSTNAMELEN + 1]; + char maskbuf[MAXHOSTNAMELEN + 1]; + char gwbuf[MAXHOSTNAMELEN + 1]; + char ifname[LIFNAMSIZ + 1]; + char flags[10]; /* RTF_ flags */ + uint_t flag_b; + + if (rp->ipRouteInfo.re_in_src_addr != 0 || + rp->ipRouteInfo.re_in_ill.o_length != 0 || + !(Aflag || (rp->ipRouteInfo.re_ire_type != IRE_CACHE && + rp->ipRouteInfo.re_ire_type != IRE_BROADCAST && + rp->ipRouteInfo.re_ire_type != IRE_LOCAL))) { + return (first); + } + + flag_b = form_v4_route_flags(rp, flags); + + if (!ire_filter_match_v4(rp, flag_b)) + return (first); + + if (first) { + if (Vflag) { + (void) puts(v4compat ? + "\nIRE Table:" : + "\nIRE Table: IPv4"); + (void) puts(" Destination Mask " + " Gateway " + "Device Mxfrg Rtt Ref Flg Out " + "In/Fwd"); + (void) puts("-------------------- --------------- " + "-------------------- " + "------ ----- ----- --- --- ----- " + "------"); + } else { + (void) puts(v4compat ? + "\nRouting Table:" : + "\nRouting Table: IPv4"); + (void) puts(" Destination " + " Gateway Flags Ref Use " + "Interface"); + (void) puts("-------------------- " + "-------------------- ----- ----- ------ " + "---------"); + } + first = B_FALSE; + } + + if (flag_b & FLF_H) { + (void) pr_addr(rp->ipRouteDest, dstbuf, sizeof (dstbuf)); + } else { + (void) pr_net(rp->ipRouteDest, rp->ipRouteMask, + dstbuf, sizeof (dstbuf)); + } + if (Vflag) { + (void) printf("%-20s %-15s %-20s %-6s %5u%c %4u %3u " + "%-4s%6u%6u\n", + dstbuf, + pr_mask(rp->ipRouteMask, maskbuf, sizeof (maskbuf)), + pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf)), + octetstr(&rp->ipRouteIfIndex, 'a', ifname, sizeof (ifname)), + rp->ipRouteInfo.re_max_frag, + rp->ipRouteInfo.re_frag_flag ? '*' : ' ', + rp->ipRouteInfo.re_rtt, + rp->ipRouteInfo.re_ref, + flags, + rp->ipRouteInfo.re_obpkt, + rp->ipRouteInfo.re_ibpkt); + } else { + (void) printf("%-20s %-20s %-5s %4u%7u %s\n", + dstbuf, + pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf)), + flags, + rp->ipRouteInfo.re_ref, + rp->ipRouteInfo.re_obpkt + rp->ipRouteInfo.re_ibpkt, + octetstr(&rp->ipRouteIfIndex, 'a', + ifname, sizeof (ifname))); + } + return (first); +} + +/* + * Report a source-specific route. + */ +static boolean_t +ire_report_item_v4src(mib2_ipRouteEntry_t *rp, boolean_t first) +{ + char dstbuf[MAXHOSTNAMELEN + 1]; + char srcbuf[MAXHOSTNAMELEN + 1]; + char gwbuf[MAXHOSTNAMELEN + 1]; + char inif[LIFNAMSIZ + 1]; + char outif[LIFNAMSIZ + 1]; + uint_t flag_b; + char flags[10]; + + /* + * If this isn't a source specific route, or if it's filtered + * out, then ignore it. + */ + if ((rp->ipRouteInfo.re_in_src_addr == 0 && + rp->ipRouteInfo.re_in_ill.o_length == 0) || + !(Aflag || (rp->ipRouteInfo.re_ire_type != IRE_CACHE && + rp->ipRouteInfo.re_ire_type != IRE_BROADCAST && + rp->ipRouteInfo.re_ire_type != IRE_LOCAL))) { + return (first); + } + + flag_b = form_v4_route_flags(rp, flags); + + if (!ire_filter_match_v4(rp, flag_b)) + return (first); + + if (first) { + if (Vflag) { + (void) printf("\nIRE Table: %sSource-Specific\n", + v4compat ? "" : "IPv4 "); + (void) puts(" Destination In If " + " Source Gateway " + " Out If Mxfrg Rtt Ref Flg Out In/Fwd"); + (void) puts("------------------ ----------- " + "----------------- ----------------- " + "----------- ----- ----- --- --- ----- ------"); + } else { + (void) printf("\nRouting Table: %sSource-Specific\n", + v4compat ? "" : "IPv4 "); + (void) puts(" Destination In If " + " Source Gateway Flags Use " + " Out If"); + (void) puts("--------------- -------- " + "--------------- --------------- ----- ------ " + "--------"); + } + first = B_FALSE; + } + + /* + * This is special-cased here because the kernel doesn't actually + * pay any attention to the destination address on mrtun entries. + * Saying "default" would be misleading, though technically correct. + */ + if (rp->ipRouteInfo.re_in_src_addr != 0 && rp->ipRouteDest == 0 && + rp->ipRouteMask == 0) + (void) strlcpy(dstbuf, " --", sizeof (dstbuf)); + else + (void) pr_netclassless(rp->ipRouteDest, rp->ipRouteMask, + dstbuf, sizeof (dstbuf)); + (void) octetstr(&rp->ipRouteInfo.re_in_ill, 'a', inif, sizeof (inif)); + (void) pr_addrnz(rp->ipRouteInfo.re_in_src_addr, srcbuf, + sizeof (srcbuf)); + (void) octetstr(&rp->ipRouteIfIndex, 'a', outif, sizeof (outif)); + (void) pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf)); + if (Vflag) { + (void) printf("%-18s %-11s %-17s %-17s %-11s %4u%c %5u %3u " + "%-3s %5u %6u\n", + dstbuf, inif, srcbuf, gwbuf, outif, + rp->ipRouteInfo.re_max_frag, + rp->ipRouteInfo.re_frag_flag ? '*' : ' ', + rp->ipRouteInfo.re_rtt, rp->ipRouteInfo.re_ref, flags, + rp->ipRouteInfo.re_obpkt, rp->ipRouteInfo.re_ibpkt); + } else { + (void) printf("%-15s %-8s %-15s %-15s %-5s %6u %-8s\n", dstbuf, + inif, srcbuf, gwbuf, flags, + rp->ipRouteInfo.re_obpkt + rp->ipRouteInfo.re_ibpkt, + outif); + } + return (first); +} + +/* + * Match a user-supplied IP address list against an IPv6 route entry. + * If the user specified "any," then any non-zero address matches. If + * the user specified "none," then only the zero address matches. If + * the user specified a subnet mask length, then use that in matching + * routes (select routes that are at least as specific). If the user + * specified only an address, then use the route's mask (select routes + * that would match that address). IPv4 addresses are ignored. + */ +static boolean_t +v6_addr_match(const Ip6Address *addr, int masklen, const filter_t *fp) +{ + const uint8_t *ucp; + int fmasklen; + int i; + char **app; + char *aptr; + + if (fp->u.a.f_address == NULL) { + if (IN6_IS_ADDR_UNSPECIFIED(&fp->u.a.f_mask)) /* any */ + return (!IN6_IS_ADDR_UNSPECIFIED(addr)); + return (IN6_IS_ADDR_UNSPECIFIED(addr)); /* "none" */ + } + fmasklen = 0; + /* 'for' loop 1a: */ + for (ucp = fp->u.a.f_mask.s6_addr; + ucp < fp->u.a.f_mask.s6_addr + sizeof (fp->u.a.f_mask.s6_addr); + ucp++) { + if (*ucp != 0xff) { + if (*ucp != 0) + fmasklen += 9 - ffs(*ucp); + break; /* 'for' loop 1a */ + } + fmasklen += 8; + } /* 'for' loop 1a ends */ + if (fmasklen != IPV6_ABITS) { + if (fmasklen > masklen) + return (B_FALSE); + masklen = fmasklen; + } + /* 'for' loop 1b: */ + for (app = fp->u.a.f_address->h_addr_list; (aptr = *app) != NULL; + app++) { + /* LINTED: (note 1) */ + if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)aptr)) + continue; /* 'for' loop 1b */ + ucp = addr->s6_addr; + for (i = masklen; i >= 8; i -= 8) + if (*ucp++ != *aptr++) + break; /* 'for' loop 1b */ + if (i == 0 || + (i < 8 && ((*ucp ^ *aptr) & ~(0xff >> i)) == 0)) + return (B_TRUE); + } /* 'for' loop 1b ends */ + return (B_FALSE); +} + +/* + * Run through the filter list for an IPv6 MIB2 IRE. For a given + * type, if there's at least one filter and all filters of that type + * fail to match, then the route doesn't match and isn't displayed. + * If at least one matches, or none are specified, for each of the + * types, then the route is selected and displayed. + */ +static boolean_t +ire_filter_match_v6(mib2_ipv6RouteEntry_t *rp6, uint_t flag_b) +{ + filter_t *fp; + int idx; + + /* 'for' loop 1: */ + for (idx = 0; idx < NFILTERKEYS; idx++) + if ((fp = filters[idx]) != NULL) { + /* 'for' loop 2: */ + for (; fp != NULL; fp = fp->f_next) { + switch (idx) { + case FK_AF: + if (fp->u.f_family != AF_INET6) + /* 'for' loop 2 */ + continue; + break; + case FK_INIF: + if (fp->u.f_ifname != NULL) + /* 'for' loop 2 */ + continue; + break; + case FK_OUTIF: + if (!dev_name_match(&rp6-> + ipv6RouteIfIndex, fp->u.f_ifname)) + /* 'for' loop 2 */ + continue; + break; + case FK_SRC: + if (!v6_addr_match(&rp6->ipv6RouteInfo. + re_src_addr, IPV6_ABITS, fp)) + /* 'for' loop 2 */ + continue; + break; + case FK_DST: + if (!v6_addr_match(&rp6->ipv6RouteDest, + rp6->ipv6RoutePfxLength, fp)) + /* 'for' loop 2 */ + continue; + break; + case FK_FLAGS: + if ((flag_b & fp->u.f.f_flagset) != + fp->u.f.f_flagset || + (flag_b & fp->u.f.f_flagclear)) + /* 'for' loop 2 */ + continue; + break; + } + break; + } /* 'for' loop 2 ends */ + if (fp == NULL) + return (B_FALSE); + } + /* 'for' loop 1 ends */ + return (B_TRUE); +} + +static boolean_t +ire_report_item_v6(mib2_ipv6RouteEntry_t *rp6, boolean_t first) +{ + char dstbuf[MAXHOSTNAMELEN + 1]; + char gwbuf[MAXHOSTNAMELEN + 1]; + char ifname[LIFNAMSIZ + 1]; + char flags[10]; /* RTF_ flags */ + uint_t flag_b; + + if (!(Aflag || (rp6->ipv6RouteInfo.re_ire_type != IRE_CACHE && + rp6->ipv6RouteInfo.re_ire_type != IRE_LOCAL))) { + return (first); + } + + flag_b = FLF_U; + (void) strcpy(flags, "U"); + if (rp6->ipv6RouteInfo.re_ire_type == IRE_DEFAULT || + rp6->ipv6RouteInfo.re_ire_type == IRE_PREFIX || + rp6->ipv6RouteInfo.re_ire_type == IRE_HOST || + rp6->ipv6RouteInfo.re_ire_type == IRE_HOST_REDIRECT) { + (void) strcat(flags, "G"); + flag_b |= FLF_G; + } + + if (rp6->ipv6RoutePfxLength == IPV6_ABITS) { + (void) strcat(flags, "H"); + flag_b |= FLF_H; + } + + if (rp6->ipv6RouteInfo.re_ire_type == IRE_HOST_REDIRECT) { + (void) strcat(flags, "D"); + flag_b |= FLF_D; + } + if (rp6->ipv6RouteInfo.re_ire_type == IRE_CACHE) { + /* Address resolution */ + (void) strcat(flags, "A"); + flag_b |= FLF_A; + } + if (rp6->ipv6RouteInfo.re_ire_type == IRE_LOCAL) { /* Local */ + (void) strcat(flags, "L"); + flag_b |= FLF_L; + } + if (rp6->ipv6RouteInfo.re_flags & RTF_MULTIRT) { + (void) strcat(flags, "M"); /* Multiroute */ + flag_b |= FLF_M; + } + if (rp6->ipv6RouteInfo.re_flags & RTF_SETSRC) { + (void) strcat(flags, "S"); /* Setsrc */ + flag_b |= FLF_S; + } + + if (!ire_filter_match_v6(rp6, flag_b)) + return (first); + + if (first) { + if (Vflag) { + (void) puts("\nIRE Table: IPv6"); + (void) puts(" Destination/Mask " + " Gateway " + " If PMTU Rtt Ref Flags Out In/Fwd"); + (void) puts("--------------------------- " + "--------------------------- " + "----- ------ ----- --- ----- ------ ------"); + } else { + (void) puts("\nRouting Table: IPv6"); + (void) puts(" Destination/Mask " + " Gateway Flags Ref Use " + " If "); + (void) puts("--------------------------- " + "--------------------------- ----- --- ------ " + "-----"); + } + first = B_FALSE; + } + + if (Vflag) { + (void) printf("%-27s %-27s %-5s %5u%c %5u %3u " + "%-5s %6u %6u\n", + pr_prefix6(&rp6->ipv6RouteDest, + rp6->ipv6RoutePfxLength, dstbuf, sizeof (dstbuf)), + IN6_IS_ADDR_UNSPECIFIED(&rp6->ipv6RouteNextHop) ? + " --" : + pr_addr6(&rp6->ipv6RouteNextHop, gwbuf, sizeof (gwbuf)), + octetstr(&rp6->ipv6RouteIfIndex, 'a', + ifname, sizeof (ifname)), + rp6->ipv6RouteInfo.re_max_frag, + rp6->ipv6RouteInfo.re_frag_flag ? '*' : ' ', + rp6->ipv6RouteInfo.re_rtt, + rp6->ipv6RouteInfo.re_ref, + flags, + rp6->ipv6RouteInfo.re_obpkt, + rp6->ipv6RouteInfo.re_ibpkt); + } else { + (void) printf("%-27s %-27s %-5s %3u %6u %-5s\n", + pr_prefix6(&rp6->ipv6RouteDest, + rp6->ipv6RoutePfxLength, dstbuf, sizeof (dstbuf)), + IN6_IS_ADDR_UNSPECIFIED(&rp6->ipv6RouteNextHop) ? + " --" : + pr_addr6(&rp6->ipv6RouteNextHop, gwbuf, sizeof (gwbuf)), + flags, + rp6->ipv6RouteInfo.re_ref, + rp6->ipv6RouteInfo.re_obpkt + rp6->ipv6RouteInfo.re_ibpkt, + octetstr(&rp6->ipv6RouteIfIndex, 'a', + ifname, sizeof (ifname))); + } + return (first); +} + +/* ------------------------------ TCP_REPORT------------------------------- */ + +static const char tcp_hdr_v4[] = +"\nTCP: IPv4\n"; +static const char tcp_hdr_v4_compat[] = +"\nTCP\n"; +static const char tcp_hdr_v4_verbose[] = +"Local/Remote Address Swind Snext Suna Rwind Rnext Rack " +" Rto Mss State\n" +"-------------------- ----- -------- -------- ----- -------- -------- " +"----- ----- -----\n"; +static const char tcp_hdr_v4_normal[] = +" Local Address Remote Address Swind Send-Q Rwind Recv-Q State\n" +"-------------------- -------------------- ----- ------ ----- ------ -------\n"; + +static const char tcp_hdr_v6[] = +"\nTCP: IPv6\n"; +static const char tcp_hdr_v6_verbose[] = +"Local/Remote Address Swind Snext Suna Rwind Rnext " +" Rack Rto Mss State If \n" +"--------------------------------- ----- -------- -------- ----- -------- " +"-------- ----- ----- ----------- -----\n"; +static const char tcp_hdr_v6_normal[] = +" Local Address Remote Address " +"Swind Send-Q Rwind Recv-Q State If \n" +"--------------------------------- --------------------------------- " +"----- ------ ----- ------ ----------- -----\n"; + +static boolean_t tcp_report_item_v4(mib2_tcpConnEntry_t *tp, boolean_t first); +static boolean_t tcp_report_item_v6(mib2_tcp6ConnEntry_t *tp6, boolean_t first); + +static void +tcp_report(mib_item_t *item) +{ + int jtemp = 0; + boolean_t print_hdr_once_v4 = B_TRUE; + boolean_t print_hdr_once_v6 = B_TRUE; + mib2_tcpConnEntry_t *tp; + mib2_tcp6ConnEntry_t *tp6; + + if (!protocol_selected(IPPROTO_TCP)) + return; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, + item->length, item->valp); + } + + if (!((item->group == MIB2_TCP && + item->mib_id == MIB2_TCP_CONN) || + (item->group == MIB2_TCP6 && + item->mib_id == MIB2_TCP6_CONN))) + continue; /* 'for' loop 1 */ + + if (item->group == MIB2_TCP && !family_selected(AF_INET)) + continue; /* 'for' loop 1 */ + else if (item->group == MIB2_TCP6 && !family_selected(AF_INET6)) + continue; /* 'for' loop 1 */ + + if (item->group == MIB2_TCP) { + for (tp = (mib2_tcpConnEntry_t *)item->valp; + (char *)tp < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + tp = (mib2_tcpConnEntry_t *)((char *)tp + + tcpConnEntrySize)) { + print_hdr_once_v4 = tcp_report_item_v4(tp, + print_hdr_once_v4); + } + } else { + for (tp6 = (mib2_tcp6ConnEntry_t *)item->valp; + (char *)tp6 < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + tp6 = (mib2_tcp6ConnEntry_t *)((char *)tp6 + + tcp6ConnEntrySize)) { + print_hdr_once_v6 = tcp_report_item_v6(tp6, + print_hdr_once_v6); + } + } + } /* 'for' loop 1 ends */ + (void) fflush(stdout); +} + +static boolean_t +tcp_report_item_v4(mib2_tcpConnEntry_t *tp, boolean_t first) +{ + /* + * lname and fname below are for the hostname as well as the portname + * There is no limit on portname length so we assume MAXHOSTNAMELEN + * as the limit + */ + char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + + if (!(Aflag || tp->tcpConnEntryInfo.ce_state >= TCPS_ESTABLISHED)) + return (first); /* Nothing to print */ + + if (first) { + (void) fputs(v4compat ? tcp_hdr_v4_compat : tcp_hdr_v4, stdout); + (void) fputs(Vflag ? tcp_hdr_v4_verbose : tcp_hdr_v4_normal, + stdout); + first = B_FALSE; + } + + if (Vflag) { + (void) printf("%-20s\n%-20s %5u %08x %08x %5u %08x %08x " + "%5u %5u %s\n", + pr_ap(tp->tcpConnLocalAddress, + tp->tcpConnLocalPort, "tcp", lname, sizeof (lname)), + pr_ap(tp->tcpConnRemAddress, + tp->tcpConnRemPort, "tcp", fname, sizeof (fname)), + tp->tcpConnEntryInfo.ce_swnd, + tp->tcpConnEntryInfo.ce_snxt, + tp->tcpConnEntryInfo.ce_suna, + tp->tcpConnEntryInfo.ce_rwnd, + tp->tcpConnEntryInfo.ce_rnxt, + tp->tcpConnEntryInfo.ce_rack, + tp->tcpConnEntryInfo.ce_rto, + tp->tcpConnEntryInfo.ce_mss, + mitcp_state(tp->tcpConnEntryInfo.ce_state)); + } else { + int sq = (int)tp->tcpConnEntryInfo.ce_snxt - + (int)tp->tcpConnEntryInfo.ce_suna - 1; + int rq = (int)tp->tcpConnEntryInfo.ce_rnxt - + (int)tp->tcpConnEntryInfo.ce_rack; + + (void) printf("%-20s %-20s %5u %6d %5u %6d %s\n", + pr_ap(tp->tcpConnLocalAddress, + tp->tcpConnLocalPort, "tcp", lname, sizeof (lname)), + pr_ap(tp->tcpConnRemAddress, + tp->tcpConnRemPort, "tcp", fname, sizeof (fname)), + tp->tcpConnEntryInfo.ce_swnd, + (sq >= 0) ? sq : 0, + tp->tcpConnEntryInfo.ce_rwnd, + (rq >= 0) ? rq : 0, + mitcp_state(tp->tcpConnEntryInfo.ce_state)); + } + return (first); +} + +static boolean_t +tcp_report_item_v6(mib2_tcp6ConnEntry_t *tp6, boolean_t first) +{ + /* + * lname and fname below are for the hostname as well as the portname + * There is no limit on portname length so we assume MAXHOSTNAMELEN + * as the limit + */ + char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + char ifname[LIFNAMSIZ + 1]; + char *ifnamep; + + if (!(Aflag || tp6->tcp6ConnEntryInfo.ce_state >= TCPS_ESTABLISHED)) + return (first); /* Nothing to print */ + + if (first) { + (void) fputs(tcp_hdr_v6, stdout); + (void) fputs(Vflag ? tcp_hdr_v6_verbose : tcp_hdr_v6_normal, + stdout); + first = B_FALSE; + } + + ifnamep = (tp6->tcp6ConnIfIndex != 0) ? + if_indextoname(tp6->tcp6ConnIfIndex, ifname) : NULL; + + if (Vflag) { + (void) printf("%-33s\n%-33s %5u %08x %08x %5u %08x %08x " + "%5u %5u %-11s %-5s\n", + pr_ap6(&tp6->tcp6ConnLocalAddress, + tp6->tcp6ConnLocalPort, "tcp", lname, sizeof (lname)), + pr_ap6(&tp6->tcp6ConnRemAddress, + tp6->tcp6ConnRemPort, "tcp", fname, sizeof (fname)), + tp6->tcp6ConnEntryInfo.ce_swnd, + tp6->tcp6ConnEntryInfo.ce_snxt, + tp6->tcp6ConnEntryInfo.ce_suna, + tp6->tcp6ConnEntryInfo.ce_rwnd, + tp6->tcp6ConnEntryInfo.ce_rnxt, + tp6->tcp6ConnEntryInfo.ce_rack, + tp6->tcp6ConnEntryInfo.ce_rto, + tp6->tcp6ConnEntryInfo.ce_mss, + mitcp_state(tp6->tcp6ConnEntryInfo.ce_state), + (ifnamep == NULL) ? "" : ifnamep); + } else { + int sq = (int)tp6->tcp6ConnEntryInfo.ce_snxt - + (int)tp6->tcp6ConnEntryInfo.ce_suna - 1; + int rq = (int)tp6->tcp6ConnEntryInfo.ce_rnxt - + (int)tp6->tcp6ConnEntryInfo.ce_rack; + + (void) printf("%-33s %-33s %5u %6d %5u %6d %-11s %-5s\n", + pr_ap6(&tp6->tcp6ConnLocalAddress, + tp6->tcp6ConnLocalPort, "tcp", lname, sizeof (lname)), + pr_ap6(&tp6->tcp6ConnRemAddress, + tp6->tcp6ConnRemPort, "tcp", fname, sizeof (fname)), + tp6->tcp6ConnEntryInfo.ce_swnd, + (sq >= 0) ? sq : 0, + tp6->tcp6ConnEntryInfo.ce_rwnd, + (rq >= 0) ? rq : 0, + mitcp_state(tp6->tcp6ConnEntryInfo.ce_state), + (ifnamep == NULL) ? "" : ifnamep); + } + return (first); +} + +/* ------------------------------- UDP_REPORT------------------------------- */ + +static boolean_t udp_report_item_v4(mib2_udpEntry_t *ude, boolean_t first); +static boolean_t udp_report_item_v6(mib2_udp6Entry_t *ude6, boolean_t first); + +static char *udp_hdr_v6 = +" Local Address Remote Address " +" State If \n" +"--------------------------------- --------------------------------- " +"---------- -----\n"; + +static void +udp_report(mib_item_t *item) +{ + int jtemp = 0; + boolean_t print_hdr_once_v4 = B_TRUE; + boolean_t print_hdr_once_v6 = B_TRUE; + mib2_udpEntry_t *ude; + mib2_udp6Entry_t *ude6; + + if (!protocol_selected(IPPROTO_UDP)) + return; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, + item->length, item->valp); + } + if (!((item->group == MIB2_UDP && + item->mib_id == MIB2_UDP_ENTRY) || + (item->group == MIB2_UDP6 && + item->mib_id == MIB2_UDP6_ENTRY))) + continue; /* 'for' loop 1 */ + + if (item->group == MIB2_UDP && !family_selected(AF_INET)) + continue; /* 'for' loop 1 */ + else if (item->group == MIB2_UDP6 && !family_selected(AF_INET6)) + continue; /* 'for' loop 1 */ + + /* xxx.xxx.xxx.xxx,pppp sss... */ + if (item->group == MIB2_UDP) { + for (ude = (mib2_udpEntry_t *)item->valp; + (char *)ude < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + ude = (mib2_udpEntry_t *)((char *)ude + + udpEntrySize)) { + print_hdr_once_v4 = udp_report_item_v4(ude, + print_hdr_once_v4); + } + } else { + for (ude6 = (mib2_udp6Entry_t *)item->valp; + (char *)ude6 < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + ude6 = (mib2_udp6Entry_t *)((char *)ude6 + + udp6EntrySize)) { + print_hdr_once_v6 = udp_report_item_v6(ude6, + print_hdr_once_v6); + } + } + } /* 'for' loop 1 ends */ + (void) fflush(stdout); +} + +static boolean_t +udp_report_item_v4(mib2_udpEntry_t *ude, boolean_t first) +{ + char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + /* hostname + portname */ + char *cp; + + if (!(Aflag || ude->udpEntryInfo.ue_state >= MIB2_UDP_connected)) + return (first); /* Nothing to print */ + + if (first) { + (void) puts(v4compat ? "\nUDP" : "\nUDP: IPv4"); + (void) puts( + " Local Address Remote Address State"); + (void) puts( + "-------------------- -------------------- -------"); + first = B_FALSE; + } + + switch (ude->udpEntryInfo.ue_state) { + case MIB2_UDP_unbound: + cp = "Unbound"; + break; + case MIB2_UDP_idle: + cp = "Idle"; + break; + case MIB2_UDP_connected: + cp = "Connected"; + break; + default: + cp = "Unknown"; + break; + } + (void) printf("%-20s ", + pr_ap(ude->udpLocalAddress, ude->udpLocalPort, "udp", + lname, sizeof (lname))); + if (ude->udpEntryInfo.ue_state == MIB2_UDP_connected) { + (void) printf("%-20s ", + pr_ap(ude->udpEntryInfo.ue_RemoteAddress, + ude->udpEntryInfo.ue_RemotePort, "udp", + lname, sizeof (lname))); + } else { + (void) printf("%-20s ", ""); + } + (void) printf(" %s\n", cp); + return (first); +} + +static boolean_t +udp_report_item_v6(mib2_udp6Entry_t *ude6, boolean_t first) +{ + char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + /* hostname + portname */ + char *cp; + char ifname[LIFNAMSIZ + 1]; + + if (!(Aflag || ude6->udp6EntryInfo.ue_state >= MIB2_UDP_connected)) + return (first); /* Nothing to print */ + + if (first) { + (void) printf("\nUDP: IPv6\n%s", udp_hdr_v6); + first = B_FALSE; + } + + switch (ude6->udp6EntryInfo.ue_state) { + case MIB2_UDP_unbound: + cp = "Unbound"; + break; + case MIB2_UDP_idle: + cp = "Idle"; + break; + case MIB2_UDP_connected: + cp = "Connected"; + break; + default: + cp = "Unknown"; + break; + } + (void) printf("%-33s ", + pr_ap6(&ude6->udp6LocalAddress, + ude6->udp6LocalPort, "udp", lname, sizeof (lname))); + if (ude6->udp6EntryInfo.ue_state == MIB2_UDP_connected) { + (void) printf("%-33s ", + pr_ap6(&ude6->udp6EntryInfo.ue_RemoteAddress, + ude6->udp6EntryInfo.ue_RemotePort, "udp", + lname, sizeof (lname))); + } else { + (void) printf("%-33s ", ""); + } + if (ude6->udp6IfIndex != 0 && + (if_indextoname(ude6->udp6IfIndex, ifname) != NULL)) { + (void) printf("%-10s %-5s\n", cp, ifname); + } else { + (void) printf("%-10s\n", cp); + } + return (first); +} + +/* ------------------------------ SCTP_REPORT------------------------------- */ + +static const char sctp_hdr[] = +"\nSCTP:"; +static const char sctp_hdr_normal[] = +" Local Address Remote Address " +"Swind Send-Q Rwind Recv-Q StrsI/O State\n" +"------------------------------- ------------------------------- " +"------ ------ ------ ------ ------- -----------"; + +static const char * +nssctp_state(int state) +{ + switch (state) { + case MIB2_SCTP_closed: + return ("CLOSED"); + case MIB2_SCTP_cookieWait: + return ("COOKIE_WAIT"); + case MIB2_SCTP_cookieEchoed: + return ("COOKIE_ECHOED"); + case MIB2_SCTP_established: + return ("ESTABLISHED"); + case MIB2_SCTP_shutdownPending: + return ("SHUTDOWN_PENDING"); + case MIB2_SCTP_shutdownSent: + return ("SHUTDOWN_SENT"); + case MIB2_SCTP_shutdownReceived: + return ("SHUTDOWN_RECEIVED"); + case MIB2_SCTP_shutdownAckSent: + return ("SHUTDOWN_ACK_SENT"); + case MIB2_SCTP_listen: + return ("LISTEN"); + default: + return ("UNKNOWN STATE"); + } +} + +static mib2_sctpConnRemoteEntry_t * +sctp_getnext_rem(mib_item_t **itemp, mib2_sctpConnRemoteEntry_t *current, + uint32_t associd) +{ + mib_item_t *item = *itemp; + mib2_sctpConnRemoteEntry_t *sre; + + for (; item != NULL; item = item->next_item, current = NULL) { + if (!(item->group == MIB2_SCTP && + item->mib_id == MIB2_SCTP_CONN_REMOTE)) { + continue; + } + + if (current != NULL) { + /* LINTED: (note 1) */ + sre = (mib2_sctpConnRemoteEntry_t *)((char *)current + + sctpRemoteEntrySize); + } else { + sre = item->valp; + } + for (; (char *)sre < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + sre = (mib2_sctpConnRemoteEntry_t *)((char *)sre + + sctpRemoteEntrySize)) { + if (sre->sctpAssocId != associd) { + continue; + } + *itemp = item; + return (sre); + } + } + *itemp = NULL; + return (NULL); +} + +static mib2_sctpConnLocalEntry_t * +sctp_getnext_local(mib_item_t **itemp, mib2_sctpConnLocalEntry_t *current, + uint32_t associd) +{ + mib_item_t *item = *itemp; + mib2_sctpConnLocalEntry_t *sle; + + for (; item != NULL; item = item->next_item, current = NULL) { + if (!(item->group == MIB2_SCTP && + item->mib_id == MIB2_SCTP_CONN_LOCAL)) { + continue; + } + + if (current != NULL) { + /* LINTED: (note 1) */ + sle = (mib2_sctpConnLocalEntry_t *)((char *)current + + sctpLocalEntrySize); + } else { + sle = item->valp; + } + for (; (char *)sle < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + sle = (mib2_sctpConnLocalEntry_t *)((char *)sle + + sctpLocalEntrySize)) { + if (sle->sctpAssocId != associd) { + continue; + } + *itemp = item; + return (sle); + } + } + *itemp = NULL; + return (NULL); +} + +static void +sctp_pr_addr(int type, char *name, int namelen, const in6_addr_t *addr, + int port) +{ + ipaddr_t v4addr; + in6_addr_t v6addr; + + /* + * Address is either a v4 mapped or v6 addr. If + * it's a v4 mapped, convert to v4 before + * displaying. + */ + switch (type) { + case MIB2_SCTP_ADDR_V4: + /* v4 */ + v6addr = *addr; + + IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr); + if (port > 0) { + (void) pr_ap(v4addr, port, "sctp", name, namelen); + } else { + (void) pr_addr(v4addr, name, namelen); + } + break; + + case MIB2_SCTP_ADDR_V6: + /* v6 */ + if (port > 0) { + (void) pr_ap6(addr, port, "sctp", name, namelen); + } else { + (void) pr_addr6(addr, name, namelen); + } + break; + + default: + (void) snprintf(name, namelen, "<unknown addr type>"); + break; + } +} + +static void +sctp_conn_report_item(mib_item_t *head, mib2_sctpConnEntry_t *sp) +{ + char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1]; + mib2_sctpConnRemoteEntry_t *sre = NULL; + mib2_sctpConnLocalEntry_t *sle = NULL; + mib_item_t *local = head; + mib_item_t *remote = head; + uint32_t id = sp->sctpAssocId; + boolean_t printfirst = B_TRUE; + + sctp_pr_addr(sp->sctpAssocRemPrimAddrType, fname, sizeof (fname), + &sp->sctpAssocRemPrimAddr, sp->sctpAssocRemPort); + sctp_pr_addr(sp->sctpAssocRemPrimAddrType, lname, sizeof (lname), + &sp->sctpAssocLocPrimAddr, sp->sctpAssocLocalPort); + + (void) printf("%-31s %-31s %6u %6d %6u %6d %3d/%-3d %s\n", + lname, fname, + sp->sctpConnEntryInfo.ce_swnd, + sp->sctpConnEntryInfo.ce_sendq, + sp->sctpConnEntryInfo.ce_rwnd, + sp->sctpConnEntryInfo.ce_recvq, + sp->sctpAssocInStreams, sp->sctpAssocOutStreams, + nssctp_state(sp->sctpAssocState)); + + if (!Vflag) { + return; + } + + /* Print remote addresses/local addresses on following lines */ + while ((sre = sctp_getnext_rem(&remote, sre, id)) != NULL) { + if (!IN6_ARE_ADDR_EQUAL(&sre->sctpAssocRemAddr, + &sp->sctpAssocRemPrimAddr)) { + if (printfirst == B_TRUE) { + (void) fputs("\t<Remote: ", stdout); + printfirst = B_FALSE; + } else { + (void) fputs(", ", stdout); + } + sctp_pr_addr(sre->sctpAssocRemAddrType, fname, + sizeof (fname), &sre->sctpAssocRemAddr, -1); + if (sre->sctpAssocRemAddrActive == MIB2_SCTP_ACTIVE) { + (void) fputs(fname, stdout); + } else { + (void) printf("(%s)", fname); + } + } + } + if (printfirst == B_FALSE) { + (void) puts(">"); + printfirst = B_TRUE; + } + while ((sle = sctp_getnext_local(&local, sle, id)) != NULL) { + if (!IN6_ARE_ADDR_EQUAL(&sle->sctpAssocLocalAddr, + &sp->sctpAssocLocPrimAddr)) { + if (printfirst == B_TRUE) { + (void) fputs("\t<Local: ", stdout); + printfirst = B_FALSE; + } else { + (void) fputs(", ", stdout); + } + sctp_pr_addr(sle->sctpAssocLocalAddrType, lname, + sizeof (lname), &sle->sctpAssocLocalAddr, -1); + (void) fputs(lname, stdout); + } + } + if (printfirst == B_FALSE) { + (void) puts(">"); + } +} + +static void +sctp_report(mib_item_t *item) +{ + mib_item_t *head; + mib2_sctpConnEntry_t *sp; + boolean_t first = B_TRUE; + + head = item; + for (; item != NULL; item = item->next_item) { + + if (!(item->group == MIB2_SCTP && + item->mib_id == MIB2_SCTP_CONN)) + continue; + + for (sp = item->valp; + (char *)sp < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + sp = (mib2_sctpConnEntry_t *)((char *)sp + + sctpEntrySize)) { + + if (Aflag || + sp->sctpAssocState >= MIB2_SCTP_established) { + if (first == B_TRUE) { + (void) puts(sctp_hdr); + (void) puts(sctp_hdr_normal); + first = B_FALSE; + } + sctp_conn_report_item(head, sp); + } + } + } +} + +static char * +plural(int n) +{ + return (n != 1 ? "s" : ""); +} + +static char * +pluraly(int n) +{ + return (n != 1 ? "ies" : "y"); +} + +static char * +plurales(int n) +{ + return (n != 1 ? "es" : ""); +} + +static char * +pktscale(n) + int n; +{ + static char buf[6]; + char t; + + if (n < 1024) { + t = ' '; + } else if (n < 1024 * 1024) { + t = 'k'; + n /= 1024; + } else if (n < 1024 * 1024 * 1024) { + t = 'm'; + n /= 1024 * 1024; + } else { + t = 'g'; + n /= 1024 * 1024 * 1024; + } + + (void) snprintf(buf, sizeof (buf), "%4u%c", n, t); + return (buf); +} + +/* --------------------- mrt_report (netstat -m) -------------------------- */ + +static void +mrt_report(mib_item_t *item) +{ + int jtemp = 0; + struct vifctl *vip; + vifi_t vifi; + struct mfcctl *mfccp; + int numvifs = 0; + int nmfc = 0; + char abuf[MAXHOSTNAMELEN + 1]; + + if (!(family_selected(AF_INET))) + return; + + /* 'for' loop 1: */ + for (; item; item = item->next_item) { + if (Dflag) { + (void) printf("\n--- Entry %d ---\n", ++jtemp); + (void) printf("Group = %d, mib_id = %d, " + "length = %d, valp = 0x%p\n", + item->group, item->mib_id, item->length, + item->valp); + } + if (item->group != EXPER_DVMRP) + continue; /* 'for' loop 1 */ + + switch (item->mib_id) { + + case EXPER_DVMRP_VIF: + if (Dflag) + (void) printf("%u records for ipVifTable:\n", + item->length/sizeof (struct vifctl)); + if (item->length/sizeof (struct vifctl) == 0) { + (void) puts("\nVirtual Interface Table is " + "empty"); + break; + } + + (void) puts("\nVirtual Interface Table\n" + " Vif Threshold Rate_Limit Local-Address" + " Remote-Address Pkt_in Pkt_out"); + + /* 'for' loop 2: */ + for (vip = (struct vifctl *)item->valp; + (char *)vip < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + vip = (struct vifctl *)((char *)vip + + vifctlSize)) { + if (vip->vifc_lcl_addr.s_addr == 0) + continue; /* 'for' loop 2 */ + /* numvifs = vip->vifc_vifi; */ + + numvifs++; + (void) printf(" %2u %3u " + "%4u %-15.15s", + vip->vifc_vifi, + vip->vifc_threshold, + vip->vifc_rate_limit, + pr_addr(vip->vifc_lcl_addr.s_addr, + abuf, sizeof (abuf))); + (void) printf(" %-15.15s %8u %8u\n", + (vip->vifc_flags & VIFF_TUNNEL) ? + pr_addr(vip->vifc_rmt_addr.s_addr, + abuf, sizeof (abuf)) : "", + vip->vifc_pkt_in, + vip->vifc_pkt_out); + } /* 'for' loop 2 ends */ + + (void) printf("Numvifs: %d\n", numvifs); + break; + + case EXPER_DVMRP_MRT: + if (Dflag) + (void) printf("%u records for ipMfcTable:\n", + item->length/sizeof (struct vifctl)); + if (item->length/sizeof (struct vifctl) == 0) { + (void) puts("\nMulticast Forwarding Cache is " + "empty"); + break; + } + + (void) puts("\nMulticast Forwarding Cache\n" + " Origin-Subnet Mcastgroup " + "# Pkts In-Vif Out-vifs/Forw-ttl"); + + for (mfccp = (struct mfcctl *)item->valp; + (char *)mfccp < (char *)item->valp + item->length; + /* LINTED: (note 1) */ + mfccp = (struct mfcctl *)((char *)mfccp + + mfcctlSize)) { + + nmfc++; + (void) printf(" %-30.15s", + pr_addr(mfccp->mfcc_origin.s_addr, + abuf, sizeof (abuf))); + (void) printf("%-15.15s %6s %3u ", + pr_net(mfccp->mfcc_mcastgrp.s_addr, + mfccp->mfcc_mcastgrp.s_addr, + abuf, sizeof (abuf)), + pktscale((int)mfccp->mfcc_pkt_cnt), + mfccp->mfcc_parent); + + for (vifi = 0; vifi < MAXVIFS; ++vifi) { + if (mfccp->mfcc_ttls[vifi]) { + (void) printf(" %u (%u)", + vifi, + mfccp->mfcc_ttls[vifi]); + } + + } + (void) putchar('\n'); + } + (void) printf("\nTotal no. of entries in cache: %d\n", + nmfc); + break; + } + } /* 'for' loop 1 ends */ + (void) putchar('\n'); + (void) fflush(stdout); +} + +/* + * Get the stats for the cache named 'name'. If prefix != 0, then + * interpret the name as a prefix, and sum up stats for all caches + * named 'name*'. + */ +static void +kmem_cache_stats(char *title, char *name, int prefix, int64_t *total_bytes) +{ + int len; + int alloc; + int64_t total_alloc = 0; + int alloc_fail, total_alloc_fail = 0; + int buf_size = 0; + int buf_avail; + int buf_total; + int buf_max, total_buf_max = 0; + int buf_inuse, total_buf_inuse = 0; + kstat_t *ksp; + char buf[256]; + + len = prefix ? strlen(name) : 256; + + /* 'for' loop 1: */ + for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + + if (strcmp(ksp->ks_class, "kmem_cache") != 0) + continue; /* 'for' loop 1 */ + + /* + * Hack alert: because of the way streams messages are + * allocated, every constructed free dblk has an associated + * mblk. From the allocator's viewpoint those mblks are + * allocated (because they haven't been freed), but from + * our viewpoint they're actually free (because they're + * not currently in use). To account for this caching + * effect we subtract the total constructed free dblks + * from the total allocated mblks to derive mblks in use. + */ + if (strcmp(name, "streams_mblk") == 0 && + strncmp(ksp->ks_name, "streams_dblk", 12) == 0) { + (void) safe_kstat_read(kc, ksp, NULL); + total_buf_inuse -= + kstat_named_value(ksp, "buf_constructed"); + continue; /* 'for' loop 1 */ + } + + if (strncmp(ksp->ks_name, name, len) != 0) + continue; /* 'for' loop 1 */ + + (void) safe_kstat_read(kc, ksp, NULL); + + alloc = kstat_named_value(ksp, "alloc"); + alloc_fail = kstat_named_value(ksp, "alloc_fail"); + buf_size = kstat_named_value(ksp, "buf_size"); + buf_avail = kstat_named_value(ksp, "buf_avail"); + buf_total = kstat_named_value(ksp, "buf_total"); + buf_max = kstat_named_value(ksp, "buf_max"); + buf_inuse = buf_total - buf_avail; + + if (Vflag && prefix) { + (void) snprintf(buf, sizeof (buf), "%s%s", title, + ksp->ks_name + len); + (void) printf(" %-18s %6u %9u %11u %11u\n", + buf, buf_inuse, buf_max, alloc, alloc_fail); + } + + total_alloc += alloc; + total_alloc_fail += alloc_fail; + total_buf_max += buf_max; + total_buf_inuse += buf_inuse; + *total_bytes += (int64_t)buf_inuse * buf_size; + } /* 'for' loop 1 ends */ + + if (buf_size == 0) { + (void) printf("%-22s [couldn't find statistics for %s]\n", + title, name); + return; + } + + if (Vflag && prefix) + (void) snprintf(buf, sizeof (buf), "%s_total", title); + else + (void) snprintf(buf, sizeof (buf), "%s", title); + + (void) printf("%-22s %6d %9d %11lld %11d\n", buf, + total_buf_inuse, total_buf_max, total_alloc, total_alloc_fail); +} + +static void +m_report(void) +{ + int64_t total_bytes = 0; + + (void) puts("streams allocation:"); + (void) printf("%63s\n", "cumulative allocation"); + (void) printf("%63s\n", + "current maximum total failures"); + + kmem_cache_stats("streams", + "stream_head_cache", 0, &total_bytes); + kmem_cache_stats("queues", "queue_cache", 0, &total_bytes); + kmem_cache_stats("mblk", "streams_mblk", 0, &total_bytes); + kmem_cache_stats("dblk", "streams_dblk", 1, &total_bytes); + kmem_cache_stats("linkblk", "linkinfo_cache", 0, &total_bytes); + kmem_cache_stats("syncq", "syncq_cache", 0, &total_bytes); + kmem_cache_stats("qband", "qband_cache", 0, &total_bytes); + + (void) printf("\n%lld Kbytes allocated for streams data\n", + total_bytes / 1024); + + (void) putchar('\n'); + (void) fflush(stdout); +} + +/* --------------------------------- */ + +/* + * Print an IPv4 address. Remove the matching part of the domain name + * from the returned name. + */ +static char * +pr_addr(uint_t addr, char *dst, uint_t dstlen) +{ + char *cp; + struct hostent *hp = NULL; + static char domain[MAXHOSTNAMELEN + 1]; + static boolean_t first = B_TRUE; + int error_num; + + if (first) { + first = B_FALSE; + if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 && + (cp = strchr(domain, '.'))) { + (void) strncpy(domain, cp + 1, sizeof (domain)); + } else + domain[0] = 0; + } + cp = NULL; + if (!Nflag) { + hp = getipnodebyaddr((char *)&addr, sizeof (uint_t), AF_INET, + &error_num); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) != NULL && + strcasecmp(cp + 1, domain) == 0) + *cp = 0; + cp = hp->h_name; + } + } + if (cp != NULL) { + (void) strncpy(dst, cp, dstlen); + dst[dstlen - 1] = 0; + } else { + (void) inet_ntop(AF_INET, (char *)&addr, dst, dstlen); + } + if (hp != NULL) + freehostent(hp); + return (dst); +} + +/* + * Print a non-zero IPv4 address. Print " --" if the address is zero. + */ +static char * +pr_addrnz(ipaddr_t addr, char *dst, uint_t dstlen) +{ + if (addr == INADDR_ANY) { + (void) strlcpy(dst, " --", dstlen); + return (dst); + } + return (pr_addr(addr, dst, dstlen)); +} + +/* + * Print an IPv6 address. Remove the matching part of the domain name + * from the returned name. + */ +static char * +pr_addr6(const struct in6_addr *addr, char *dst, uint_t dstlen) +{ + char *cp; + struct hostent *hp = NULL; + static char domain[MAXHOSTNAMELEN + 1]; + static boolean_t first = B_TRUE; + int error_num; + + if (first) { + first = B_FALSE; + if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 && + (cp = strchr(domain, '.'))) { + (void) strncpy(domain, cp + 1, sizeof (domain)); + } else + domain[0] = 0; + } + cp = NULL; + if (!Nflag) { + hp = getipnodebyaddr((char *)addr, + sizeof (struct in6_addr), AF_INET6, &error_num); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) != NULL && + strcasecmp(cp + 1, domain) == 0) + *cp = 0; + cp = hp->h_name; + } + } + if (cp != NULL) { + (void) strncpy(dst, cp, dstlen); + dst[dstlen - 1] = 0; + } else { + (void) inet_ntop(AF_INET6, (void *)addr, dst, dstlen); + } + if (hp != NULL) + freehostent(hp); + return (dst); +} + +/* For IPv4 masks */ +static char * +pr_mask(uint_t addr, char *dst, uint_t dstlen) +{ + uint8_t *ip_addr = (uint8_t *)&addr; + + (void) snprintf(dst, dstlen, "%d.%d.%d.%d", + ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]); + return (dst); +} + +/* + * For ipv6 masks format is : dest/mask + * Does not print /128 to save space in printout. H flag carries this notion. + */ +static char * +pr_prefix6(struct in6_addr *addr, uint_t prefixlen, char *dst, uint_t dstlen) +{ + char *cp; + + if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefixlen == 0) { + (void) strncpy(dst, "default", dstlen); + dst[dstlen - 1] = 0; + return (dst); + } + + (void) pr_addr6(addr, dst, dstlen); + if (prefixlen != IPV6_ABITS) { + /* How much room is left? */ + cp = strchr(dst, '\0'); + if (dst + dstlen > cp) { + dstlen -= (cp - dst); + (void) snprintf(cp, dstlen, "/%d", prefixlen); + } + } + return (dst); +} + +/* Print IPv4 address and port */ +static char * +pr_ap(uint_t addr, uint_t port, char *proto, + char *dst, uint_t dstlen) +{ + char *cp; + + if (addr == INADDR_ANY) { + (void) strncpy(dst, " *", dstlen); + dst[dstlen - 1] = 0; + } else { + (void) pr_addr(addr, dst, dstlen); + } + /* How much room is left? */ + cp = strchr(dst, '\0'); + if (dst + dstlen > cp + 1) { + *cp++ = '.'; + dstlen -= (cp - dst); + dstlen--; + (void) portname(port, proto, cp, dstlen); + } + return (dst); +} + +/* Print IPv6 address and port */ +static char * +pr_ap6(const in6_addr_t *addr, uint_t port, char *proto, + char *dst, uint_t dstlen) +{ + char *cp; + + if (IN6_IS_ADDR_UNSPECIFIED(addr)) { + (void) strncpy(dst, " *", dstlen); + dst[dstlen - 1] = 0; + } else { + (void) pr_addr6(addr, dst, dstlen); + } + /* How much room is left? */ + cp = strchr(dst, '\0'); + if (dst + dstlen + 1 > cp) { + *cp++ = '.'; + dstlen -= (cp - dst); + dstlen--; + (void) portname(port, proto, cp, dstlen); + } + return (dst); +} + +/* + * 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. + */ +static char * +pr_net(uint_t addr, uint_t mask, char *dst, uint_t dstlen) +{ + char *cp = NULL; + struct netent *np = NULL; + struct hostent *hp = NULL; + uint_t net; + int subnetshift; + int error_num; + + if (addr == INADDR_ANY && mask == INADDR_ANY) { + (void) strncpy(dst, "default", dstlen); + dst[dstlen - 1] = 0; + return (dst); + } + + if (!Nflag && addr) { + if (mask == 0) { + if (IN_CLASSA(addr)) { + mask = (uint_t)IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(addr)) { + mask = (uint_t)IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = (uint_t)IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. Guess at + * the subnet mask, assuming reasonable width subnet + * fields. + */ + while (addr & ~mask) + /* compiler doesn't sign extend! */ + mask = (mask | ((int)mask >> subnetshift)); + } + net = addr & mask; + while ((mask & 1) == 0) + mask >>= 1, net >>= 1; + np = getnetbyaddr(net, AF_INET); + if (np && np->n_net == net) + cp = np->n_name; + else { + /* + * Look for subnets in hosts map. + */ + hp = getipnodebyaddr((char *)&addr, sizeof (uint_t), + AF_INET, &error_num); + if (hp) + cp = hp->h_name; + } + } + if (cp != NULL) { + (void) strncpy(dst, cp, dstlen); + dst[dstlen - 1] = 0; + } else { + (void) inet_ntop(AF_INET, (char *)&addr, dst, dstlen); + } + if (hp != NULL) + freehostent(hp); + return (dst); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be a host address. + */ +static char * +pr_netaddr(uint_t addr, uint_t mask, char *dst, uint_t dstlen) +{ + char *cp = NULL; + struct netent *np = NULL; + struct hostent *hp = NULL; + uint_t net; + uint_t netshifted; + int subnetshift; + struct in_addr in; + int error_num; + uint_t nbo_addr = addr; /* network byte order */ + + addr = ntohl(addr); + mask = ntohl(mask); + if (addr == INADDR_ANY && mask == INADDR_ANY) { + (void) strncpy(dst, "default", dstlen); + dst[dstlen - 1] = 0; + return (dst); + } + + /* Figure out network portion of address (with host portion = 0) */ + if (addr) { + /* Try figuring out mask if unknown (all 0s). */ + if (mask == 0) { + if (IN_CLASSA(addr)) { + mask = (uint_t)IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(addr)) { + mask = (uint_t)IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = (uint_t)IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. Guess at + * the subnet mask, assuming reasonable width subnet + * fields. + */ + while (addr & ~mask) + /* compiler doesn't sign extend! */ + mask = (mask | ((int)mask >> subnetshift)); + } + net = netshifted = addr & mask; + while ((mask & 1) == 0) + mask >>= 1, netshifted >>= 1; + } + else + net = netshifted = 0; + + /* Try looking up name unless -n was specified. */ + if (!Nflag) { + np = getnetbyaddr(netshifted, AF_INET); + if (np && np->n_net == netshifted) + cp = np->n_name; + else { + /* + * Look for subnets in hosts map. + */ + hp = getipnodebyaddr((char *)&nbo_addr, sizeof (uint_t), + AF_INET, &error_num); + if (hp) + cp = hp->h_name; + } + + if (cp != NULL) { + (void) strncpy(dst, cp, dstlen); + dst[dstlen - 1] = 0; + if (hp != NULL) + freehostent(hp); + return (dst); + } + /* + * No name found for net: fallthru and return in decimal + * dot notation. + */ + } + + in.s_addr = htonl(net); + (void) inet_ntop(AF_INET, (char *)&in, dst, dstlen); + if (hp != NULL) + freehostent(hp); + return (dst); +} + + +/* + * Return the standard IPv4 classess host or network identifier. + * + * Returns "default" for the default route. + * Returns "x.x.x.x" or host name if mask is 255.255.255.255. + * Returns "x.x.x.x/y" (y is bit count) if mask is contiguous. + * Otherwise, returns "x.x.x.x/m.m.m.m" (undesirable mask). + * + * Can also return "****" if inet_ntop fails -- insufficient dst space + * available. (Shouldn't happen otherwise.) + */ +static char * +pr_netclassless(ipaddr_t addr, ipaddr_t mask, char *dst, size_t dstlen) +{ + struct hostent *hp; + int error_num; + struct in_addr in; + char *cp; + int slen; + + if (addr == INADDR_ANY && mask == INADDR_ANY) { + (void) strlcpy(dst, "default", dstlen); + return (dst); + } + if (mask == IP_HOST_MASK && !Nflag && + (hp = getipnodebyaddr(&addr, sizeof (addr), AF_INET, + &error_num)) != NULL) { + (void) strlcpy(dst, hp->h_name, dstlen); + freehostent(hp); + return (dst); + } + in.s_addr = addr; + if (inet_ntop(AF_INET, &in, dst, dstlen) == NULL) { + (void) strlcpy(dst, "****", dstlen); + return (dst); + } + if (mask != IP_HOST_MASK) { + slen = strlen(dst); + cp = dst + slen; + dstlen -= slen; + if (mask == 0) { + /* Illegal on non-zero addresses */ + (void) strlcpy(cp, "/0", dstlen); + } else if ((mask | (mask - 1)) == IP_HOST_MASK) { + (void) snprintf(cp, dstlen, "/%d", + IP_ABITS - ffs(mask) + 1); + } else { + /* Ungood; non-contiguous mask */ + (void) pr_mask(mask, cp, dstlen); + } + } + return (dst); +} + +/* + * Return the filter mode as a string: + * 1 => "INCLUDE" + * 2 => "EXCLUDE" + * otherwise "<unknown>" + */ +static char * +fmodestr(uint_t fmode) +{ + switch (fmode) { + case 1: + return ("INCLUDE"); + case 2: + return ("EXCLUDE"); + default: + return ("<unknown>"); + } +} + +/* + * Pretty print a port number. If the Nflag was + * specified, use numbers instead of names. + */ +static char * +portname(uint_t port, char *proto, char *dst, uint_t dstlen) +{ + struct servent *sp = NULL; + + if (!Nflag && port) + sp = getservbyport(htons(port), proto); + if (sp || port == 0) + (void) snprintf(dst, dstlen, "%.*s", MAXHOSTNAMELEN, + sp ? sp->s_name : "*"); + else + (void) snprintf(dst, dstlen, "%d", port); + dst[dstlen - 1] = 0; + return (dst); +} + +/*PRINTFLIKE2*/ +void +fail(int do_perror, char *message, ...) +{ + va_list args; + + va_start(args, message); + (void) fputs("netstat: ", stderr); + (void) vfprintf(stderr, message, args); + va_end(args); + if (do_perror) + (void) fprintf(stderr, ": %s", strerror(errno)); + (void) fputc('\n', stderr); + exit(2); +} + +/* + * Return value of named statistic for given kstat_named kstat; + * return 0LL if named statistic is not in list (use "ll" as a + * type qualifier when printing 64-bit int's with printf() ) + */ +static uint64_t +kstat_named_value(kstat_t *ksp, char *name) +{ + kstat_named_t *knp; + uint64_t value; + + if (ksp == NULL) + return (0LL); + + knp = kstat_data_lookup(ksp, name); + if (knp == NULL) + return (0LL); + + switch (knp->data_type) { + case KSTAT_DATA_INT32: + case KSTAT_DATA_UINT32: + value = (uint64_t)(knp->value.ui32); + break; + case KSTAT_DATA_INT64: + case KSTAT_DATA_UINT64: + value = knp->value.ui64; + break; + default: + value = 0LL; + break; + } + + return (value); +} + +kid_t +safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data) +{ + kid_t kstat_chain_id = kstat_read(kc, ksp, data); + + if (kstat_chain_id == -1) + fail(1, "kstat_read(%p, '%s') failed", (void *)kc, + ksp->ks_name); + return (kstat_chain_id); +} + +/* + * Parse a list of IRE flag characters into a bit field. + */ +static uint_t +flag_bits(const char *arg) +{ + const char *cp; + uint_t val; + + if (*arg == '\0') + fatal(1, "missing flag list\n"); + + val = 0; + while (*arg != '\0') { + if ((cp = strchr(flag_list, *arg)) == NULL) + fatal(1, "%c: illegal flag\n", *arg); + val |= 1 << (cp - flag_list); + arg++; + } + return (val); +} + +/* + * Handle -f argument. Validate input format, sort by keyword, and + * save off digested results. + */ +static void +process_filter(char *arg) +{ + int idx; + int klen = 0; + char *cp, *cp2; + int val; + filter_t *newf; + struct hostent *hp; + int error_num; + uint8_t *ucp; + int maxv; + + /* Look up the keyword first */ + if (strchr(arg, ':') == NULL) { + idx = FK_AF; + } else { + for (idx = 0; idx < NFILTERKEYS; idx++) { + klen = strlen(filter_keys[idx]); + if (strncmp(filter_keys[idx], arg, klen) == 0 && + arg[klen] == ':') + break; + } + if (idx >= NFILTERKEYS) + fatal(1, "%s: unknown filter keyword\n", arg); + + /* Advance past keyword and separator. */ + arg += klen + 1; + } + + if ((newf = malloc(sizeof (*newf))) == NULL) { + perror("filter"); + exit(1); + } + switch (idx) { + case FK_AF: + if (strcmp(arg, "inet") == 0) { + newf->u.f_family = AF_INET; + } else if (strcmp(arg, "inet6") == 0) { + newf->u.f_family = AF_INET6; + } else if (strcmp(arg, "unix") == 0) { + newf->u.f_family = AF_UNIX; + } else { + newf->u.f_family = strtol(arg, &cp, 0); + if (arg == cp || *cp != '\0') + fatal(1, "%s: unknown address family.\n", arg); + } + break; + + case FK_INIF: + case FK_OUTIF: + if (strcmp(arg, "none") == 0) { + newf->u.f_ifname = NULL; + break; + } + if (strcmp(arg, "any") == 0) { + newf->u.f_ifname = ""; + break; + } + val = strtol(arg, &cp, 0); + if (val <= 0 || arg == cp || cp[0] != '\0') { + if ((val = if_nametoindex(arg)) == 0) { + perror(arg); + exit(1); + } + } + newf->u.f_ifname = arg; + break; + + case FK_SRC: + case FK_DST: + V4MASK_TO_V6(IP_HOST_MASK, newf->u.a.f_mask); + if (strcmp(arg, "any") == 0) { + /* Special semantics; any address *but* zero */ + newf->u.a.f_address = NULL; + (void) memset(&newf->u.a.f_mask, 0, + sizeof (newf->u.a.f_mask)); + break; + } + if (strcmp(arg, "none") == 0) { + newf->u.a.f_address = NULL; + break; + } + if ((cp = strrchr(arg, '/')) != NULL) + *cp++ = '\0'; + hp = getipnodebyname(arg, AF_INET6, AI_V4MAPPED|AI_ALL, + &error_num); + if (hp == NULL) + fatal(1, "%s: invalid or unknown host address\n", arg); + newf->u.a.f_address = hp; + if (cp == NULL) { + V4MASK_TO_V6(IP_HOST_MASK, newf->u.a.f_mask); + } else { + val = strtol(cp, &cp2, 0); + if (cp != cp2 && cp2[0] == '\0') { + /* + * If decode as "/n" works, then translate + * into a mask. + */ + if (hp->h_addr_list[0] != NULL && + /* LINTED: (note 1) */ + IN6_IS_ADDR_V4MAPPED((in6_addr_t + *)hp->h_addr_list[0])) { + maxv = IP_ABITS; + } else { + maxv = IPV6_ABITS; + } + if (val < 0 || val >= maxv) + fatal(1, "%d: not in range 0 to %d\n", + val, maxv - 1); + if (maxv == IP_ABITS) + val += IPV6_ABITS - IP_ABITS; + ucp = newf->u.a.f_mask.s6_addr; + while (val >= 8) + *ucp++ = 0xff, val -= 8; + *ucp++ = (0xff << (8 - val)) & 0xff; + while (ucp < newf->u.a.f_mask.s6_addr + + sizeof (newf->u.a.f_mask.s6_addr)) + *ucp++ = 0; + /* Otherwise, try as numeric address */ + } else if (inet_pton(AF_INET6, + cp, &newf->u.a.f_mask) <= 0) { + fatal(1, "%s: illegal mask format\n", cp); + } + } + break; + + case FK_FLAGS: + if (*arg == '+') { + newf->u.f.f_flagset = flag_bits(arg + 1); + newf->u.f.f_flagclear = 0; + } else if (*arg == '-') { + newf->u.f.f_flagset = 0; + newf->u.f.f_flagclear = flag_bits(arg + 1); + } else { + newf->u.f.f_flagset = flag_bits(arg); + newf->u.f.f_flagclear = ~newf->u.f.f_flagset; + } + break; + + default: + assert(0); + } + newf->f_next = filters[idx]; + filters[idx] = newf; +} + +/* Determine if user wants this address family printed. */ +static boolean_t +family_selected(int family) +{ + const filter_t *fp; + + if (v4compat && family == AF_INET6) + return (B_FALSE); + if ((fp = filters[FK_AF]) == NULL) + return (B_TRUE); + while (fp != NULL) { + if (fp->u.f_family == family) + return (B_TRUE); + fp = fp->f_next; + } + return (B_FALSE); +} + +/* + * print the usage line + */ +static void +usage(char *cmdname) +{ + (void) fprintf(stderr, "usage: %s [-anv] [-f address_family]\n", + cmdname); + (void) fprintf(stderr, " %s [-n] [-f address_family] " + "[-P protocol] [-g | -p | -s [interval [count]]]\n", cmdname); + (void) fprintf(stderr, " %s -m [-v] " + "[interval [count]]\n", cmdname); + (void) fprintf(stderr, " %s -i [-I interface] [-an] " + "[-f address_family] [interval [count]]\n", cmdname); + (void) fprintf(stderr, " %s -r [-anv] " + "[-f address_family|filter]\n", cmdname); + (void) fprintf(stderr, " %s -M [-ns] [-f address_family]\n", + cmdname); + (void) fprintf(stderr, " %s -D [-I interface] " + "[-f address_family]\n", cmdname); + exit(EXIT_FAILURE); +} + +/* + * fatal: print error message to stderr and + * call exit(errcode) + */ +/*PRINTFLIKE2*/ +static void +fatal(int errcode, char *format, ...) { + va_list argp; + + if (format == NULL) + return; + + va_start(argp, format); + (void) vfprintf(stderr, format, argp); + va_end(argp); + + exit(errcode); +} |