/* * $OpenBSD: inet6.c,v 1.31 2004/11/17 01:47:20 itojun Exp $ */ /* * BSDI inet.c,v 2.3 1995/10/24 02:19:29 prb Exp */ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_UNISTD_H #include #endif #if HAVE_WINSOCK_H #include "winstub.h" #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETDB_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #include #include "main.h" #include "netstat.h" struct stat_table { unsigned int entry; /* entry number in table */ /* * format string to printf(description, value) * warning: the %d must be before the %s */ char description[80]; }; void inetxprint(int, struct sockaddr_in6, int, const char *, int); /* * Print a summary of TCP connections * Listening processes are suppressed unless the * -a (all) flag is specified. */ const char *tcpxstates[] = { "", "CLOSED", "LISTEN", "SYNSENT", "SYNRECEIVED", "ESTABLISHED", "FINWAIT1", "FINWAIT2", "CLOSEWAIT", "LASTACK", "CLOSING", "TIMEWAIT" }; #define TCP_NSTATES 11 typedef struct { int stat; int hcstat; const char *str; } systemstats_t; systemstats_t systemstats[] = { {3, 4, "datagrams input"}, {5, 6, "octets received"}, {7, 0, "hdr errors input"}, {8, 0, "no routes input"}, {9, 0, "address errors input"}, {10, 0, "unknown protocol input"}, {12, 13, "input datagrams forwarded"}, {11, 0, "truncated datagrams input"}, {14, 0, "input reassembly required"}, {15, 0, "input reassemled OK"}, {16, 0, "input reassembly failed"}, {17, 0, "input datagrams discarded"}, {18, 19, "input datagrams received"}, {20, 21, "output datagram requests"}, {22, 0, "output no route"}, {23, 24, "datagrams forwarded"}, {25, 0, "output datagrams discarded"}, {26, 0, "output datagrams fragmentation required"}, {27, 0, "output datagrams fragmented"}, {28, 0, "output fragmentation failed"}, {29, 0, "fragments created"}, {30, 31, "datagrams transmitted"}, {32, 33, "octets transmitted"}, {0} }; systemstats_t icmpstats[] = { {2, 0, "input messages"}, {3, 0, "input errors"}, {4, 0, "output messages"}, {5, 0, "output errors"}, {0} }; typedef struct { int code; const char *name; } codelist_t; codelist_t icmpcodes[] = { {0, "Echo reply"}, {3, "Destination unreachable"}, {4, "Source quench"}, {5, "Redirect"}, {6, "Alternate host address"}, {8, "Echo request"}, {9, "Router advertisement"}, {10, "Router selection"}, {11, "Time exceeded"}, {12, "Parameter problem"}, {13, "Timestamp request"}, {14, "Timestamp reply"}, {15, "Information request"}, {16, "Information reply"}, {17, "Address mask request"}, {18, "Address mask reply"}, {0} }; codelist_t icmp6codes[] = { {1, "Destination Unreachable"}, {2, "Packet Too Big"}, {3, "Time Exceeded"}, {4, "Parameter Problem"}, {100, "Private experimentation 100"}, {101, "Private experimentation 101"}, {127, "Reserved for expansion of ICMPv6 error messages"}, {128, "Echo Request"}, {129, "Echo Reply"}, {130, "Multicast Listener Query"}, {131, "Multicast Listener Report"}, {132, "Multicast Listener Done"}, {133, "Router Solicitation"}, {134, "Router Advertisement"}, {135, "Neighbor Solicitation"}, {136, "Neighbor Advertisement"}, {137, "Redirect Message"}, {138, "Router Renumbering"}, {139, "ICMP Node Information Query"}, {140, "ICMP Node Information Response"}, {141, "Inverse Neighbor Discovery Solicitation Message"}, {142, "Inverse Neighbor Discovery Advertisement Message"}, {143, "Version 2 Multicast Listener Report"}, {144, "Home Agent Address Discovery Request Message"}, {145, "Home Agent Address Discovery Reply Message"}, {146, "Mobile Prefix Solicitation"}, {147, "Mobile Prefix Advertisement"}, {148, "Certification Path Solicitation Message"}, {149, "Certification Path Advertisement Message"}, {151, "Multicast Router Advertisement"}, {152, "Multicast Router Solicitation"}, {153, "Multicast Router Termination"}, {154, "FMIPv6 Messages"}, {155, "RPL Control Message"}, {0} }; void tcpxprotopr(const char *name) { netsnmp_variable_list *var, *vp, *pvar; oid tcpConnectionState_oid[] = { 1, 3, 6, 1, 2, 1, 6, 19, 1, 7 }; size_t tcpConnectionState_len = OID_LENGTH(tcpConnectionState_oid); int state, i; struct sockaddr_in6 localAddr, remoteAddr; int localPort, remotePort, pid = 0; int localType, remoteType, inx; int first = 1; static int done = 0; if (done++) return; /* * Walking the v6 tcpConnectionState column will provide all * the necessary information. */ var = NULL; snmp_varlist_add_variable(&var, tcpConnectionState_oid, tcpConnectionState_len, ASN_NULL, NULL, 0); if (netsnmp_query_walk(var, ss) != SNMP_ERR_NOERROR) { snmp_free_varbind(var); return; } if ((var->type & 0xF0) == 0x80) { /* Exception */ snmp_free_varbind(var); return; } for (vp = var; vp; vp = vp->next_variable) { char lname[5]; state = *vp->val.integer; inx = tcpConnectionState_len; pvar = NULL; vp->name[inx - 1] = 8; snmp_varlist_add_variable(&pvar, vp->name, vp->name_length, ASN_NULL, NULL, 0); if (netsnmp_query_get(pvar, ss) != SNMP_ERR_NOERROR) { snmp_free_var(pvar); return; } if ((pvar->type & 0xF0) != 0x80) /* Exception */ pid = *pvar->val.integer; /* * Extract the local/remote information from the index values */ localType = vp->name[inx++]; for (i = 0; i < vp->name[inx]; i++) localAddr.sin6_addr.s6_addr[i] = vp->name[inx + i + 1]; inx += vp->name[inx] + 1; localPort = vp->name[inx++]; remoteType = vp->name[inx++]; for (i = 0; i < vp->name[inx]; i++) remoteAddr.sin6_addr.s6_addr[i] = vp->name[inx + i + 1]; inx += vp->name[inx] + 1; remotePort = vp->name[inx++]; snmp_free_varbind(pvar); if (af == AF_INET && localType == 2) continue; if (af == AF_INET6 && localType != 2) continue; if (first) { printf("Active Internet (%s) Connections", "tcp"); putchar('\n'); printf("%-5.5s %-27.27s %-27.27s %11.11s %5.5s\n", "Proto", "Local Address", "Remote Address", "State", "PID"); first = 0; } strcpy(lname, "tcp"); if (localType == 2) lname[3] = '6'; else lname[3] = '4'; lname[4] = 0; printf("%-5.5s", lname); inetxprint(localType, localAddr, localPort, "tcp", 1); inetxprint(remoteType, remoteAddr, remotePort, "tcp", 0); if (state < 1 || state > TCP_NSTATES) printf(" %11d %5d\n", state, pid); else printf(" %11s %5d\n", tcpxstates[state], pid); } snmp_free_varbind(var); if (aflag) listenxprotopr(name); } /* * Print a summary of listening "connections" */ void listenxprotopr(const char *name) { netsnmp_variable_list *var, *vp; oid tcpListenerProcess_oid[] = { 1, 3, 6, 1, 2, 1, 6, 20, 1, 4 }; size_t tcpListenerProcess_len = OID_LENGTH(tcpListenerProcess_oid); struct sockaddr_in6 localAddr; int localType, localPort, pid; int i, inx; /* * Walking a single column of the udpTable will provide * all the necessary information from the index values. */ var = NULL; snmp_varlist_add_variable(&var, tcpListenerProcess_oid, tcpListenerProcess_len, ASN_NULL, NULL, 0); if (netsnmp_query_walk(var, ss) != SNMP_ERR_NOERROR) return; if ((var->type & 0xF0) == 0x80) /* Exception */ return; printf("Listening Internet (%s) Connections\n", "tcp"); printf("%-5.5s %-27.27s %5s\n", "Proto", "Local Address", "PID"); for (vp = var; vp; vp = vp->next_variable) { char lname[5]; inx = tcpListenerProcess_len; /* * Extract the local port from the index values, but take * the IP address from the varbind value, (which is why * we walked udpLocalAddress rather than udpLocalPort) */ localType = vp->name[inx++]; if (af == AF_INET && localType == 2) continue; if (af == AF_INET6 && localType != 2) continue; for (i = 0; i < vp->name[inx]; i++) localAddr.sin6_addr.s6_addr[i] = vp->name[inx + i + 1]; inx += vp->name[inx] + 1; localPort = vp->name[inx++]; pid = *vp->val.integer; strcpy(lname, "tcp"); if (localType == 2) lname[3] = '6'; else lname[3] = '4'; lname[4] = 0; printf("%-5.5s", lname); inetxprint(localType, localAddr, localPort, "tcp", 1); printf(" %5d\n", pid); } snmp_free_varbind(var); } /* * Print a summary of UDPv6 "connections" * XXX - what about "listening" services ?? */ void udpxprotopr(const char *name) { netsnmp_variable_list *var, *vp; oid udpEndpointProcess_oid[] = { 1, 3, 6, 1, 2, 1, 7, 7, 1, 8 }; size_t udpEndpointProcess_len = OID_LENGTH(udpEndpointProcess_oid); struct sockaddr_in6 localAddr, remoteAddr; int localType, remoteType, localPort, remotePort, pid; int i, inx; static int done = 0; if (done++) return; /* * Walking a single column of the udpTable will provide * all the necessary information from the index values. */ var = NULL; snmp_varlist_add_variable(&var, udpEndpointProcess_oid, udpEndpointProcess_len, ASN_NULL, NULL, 0); if (netsnmp_query_walk(var, ss) != SNMP_ERR_NOERROR) { snmp_free_varbind(var); return; } if ((var->type & 0xF0) == 0x80) { /* Exception */ snmp_free_varbind(var); return; } printf("Active Internet (%s) Connections\n", "udp"); printf("%-5.5s %-27.27s %-27.27s %5s\n", "Proto", "Local Address", "Remote Address", "PID"); for (vp = var; vp; vp = vp->next_variable) { char lname[5]; inx = udpEndpointProcess_len; /* * Extract the local port from the index values, but take * the IP address from the varbind value, (which is why * we walked udpLocalAddress rather than udpLocalPort) */ localType = vp->name[inx++]; if (af == AF_INET && localType == 2) continue; if (af == AF_INET6 && localType != 2) continue; for (i = 0; i < vp->name[inx]; i++) localAddr.sin6_addr.s6_addr[i] = vp->name[inx + i + 1]; inx += vp->name[inx] + 1; localPort = vp->name[inx++]; remoteType = vp->name[inx++]; for (i = 0; i < vp->name[inx]; i++) remoteAddr.sin6_addr.s6_addr[i] = vp->name[inx + i + 1]; inx += vp->name[inx] + 1; remotePort = vp->name[inx++]; pid = *vp->val.integer; strcpy(lname, "udp"); if (localType == 2) lname[3] = '6'; else lname[3] = '4'; lname[4] = 0; printf("%-5.5s", lname); inetxprint(localType, localAddr, localPort, "udp", 1); inetxprint(remoteType, remoteAddr, remotePort, "udp", 1); printf(" %5d\n", pid); } snmp_free_varbind(var); } static void statsprint(const char *name, const systemstats_t * st, int proto, const oid * tbl, size_t tbllen) { oid var[32]; size_t len; netsnmp_variable_list *vb; memcpy(var, tbl, tbllen * sizeof(oid)); var[tbllen + 1] = proto; len = tbllen + 2; printf("%s:\n", name); while (st->stat) { vb = NULL; if (st->hcstat) { var[tbllen] = st->hcstat; snmp_varlist_add_variable(&vb, var, len, ASN_NULL, NULL, 0); if (netsnmp_query_get(vb, ss) != SNMP_ERR_NOERROR) { snmp_free_var(vb); vb = NULL; } } if (!vb) { var[tbllen] = st->stat; snmp_varlist_add_variable(&vb, var, len, ASN_NULL, NULL, 0); if (netsnmp_query_get(vb, ss) != SNMP_ERR_NOERROR) { snmp_free_var(vb); vb = NULL; } } if (vb) { if (vb->type == ASN_COUNTER) { if (*vb->val.integer > 0 || sflag == 1) printf("%14lu %s\n", *vb->val.integer, st->str); } else if (vb->type == ASN_COUNTER64) { char a64buf[I64CHARSZ + 1]; printU64(a64buf, vb->val.counter64); if (strcmp(a64buf, "0") != 0 || sflag == 1) printf("%14s %s\n", a64buf, st->str); } else printf("%14s %s\n", "-", st->str); snmp_free_varbind(vb); } else { printf("%14s %s\n", "-", st->str); } st++; } } static void prhisto(const char *name, const oid * var, size_t len, int ver, codelist_t * cs) { netsnmp_variable_list *vb = NULL, *vp; codelist_t *cp; int code; char nocode[32]; snmp_varlist_add_variable(&vb, var, len, ASN_NULL, NULL, 0); if (netsnmp_query_walk(vb, ss) != SNMP_ERR_NOERROR) { snmp_free_var(vb); return; } printf(" %s histogram:\n", name); printf(" %10s %10s %s\n", "input", "output", "type"); for (code = 0; code < 256; code++) { unsigned long inp = 0, out = 0; int found = 0; vp = vb; while (vp && found != 2) { if (vp->name[11] == code && vp->name[10] == ver) { if (vp->name[9] == 3) inp = *vp->val.integer; else out = *vp->val.integer; found++; } vp = vp->next_variable; } if (found) { cp = cs; while (cp->name && cp->code != code) cp++; if (inp || out || sflag == 1) { if (!cp->code) snprintf(nocode, sizeof nocode, "type %d", code); printf(" %10lu %10lu %s\n", inp, out, cp->name ? cp->name : nocode); } } } snmp_free_varbind(vb); } void ipx_stats(const char *name) { oid ipsysstat_oid[] = { 1, 3, 6, 1, 2, 1, 4, 31, 1, 1 }; size_t ipsysstat_len = sizeof(ipsysstat_oid) / sizeof(ipsysstat_oid[0]); static int first = 1; if (!first) return; first = 0; if (!name || strcmp(name, "ip") == 0) statsprint("ip", systemstats, 1, ipsysstat_oid, ipsysstat_len); if (!name || strcmp(name, "ip6") == 0) statsprint("ip6", systemstats, 2, ipsysstat_oid, ipsysstat_len); } void icmpx_stats(const char *name) { oid icmpstat_oid[] = { 1, 3, 6, 1, 2, 1, 5, 29, 1 }; size_t icmpstat_len = sizeof(icmpstat_oid) / sizeof(icmpstat_oid[0]); oid icmpmsg_oid[] = { 1, 3, 6, 1, 2, 1, 5, 30, 1 }; size_t icmpmsg_len = sizeof(icmpmsg_oid) / sizeof(icmpmsg_oid[0]); static int first = 1; if (!first) return; first = 0; if (!name || strcmp(name, "icmp") == 0) { statsprint("icmp", icmpstats, 1, icmpstat_oid, icmpstat_len); prhisto("icmp", icmpmsg_oid, icmpmsg_len, 1, icmpcodes); } if (!name || strcmp(name, "icmp6") == 0) { statsprint("icmp6", icmpstats, 2, icmpstat_oid, icmpstat_len); prhisto("icmp6", icmpmsg_oid, icmpmsg_len, 2, icmp6codes); } } static void unknownprint(void) { char line[80], *cp; int width = 27; if (vflag) snprintf(line, sizeof line, "%s.", "*"); else snprintf(line, sizeof line, "%.*s.", width - 9, "*"); cp = strchr(line, '\0'); snprintf(cp, line + sizeof line - cp, vflag ? "%s" : "%.8s", "*"); if (vflag && width < strlen(line)) width = strlen(line); printf(" %-*.*s", width, width, line); } /* * Pretty print an Internet address (net address + port). * If the nflag was specified, use numbers instead of names. */ void inetxprint(int proto, struct sockaddr_in6 in6, int port, const char *name, int local) { if (proto == 2) inet6print(&in6.sin6_addr, port, name, local); else if (proto == 1) inetprint((struct in_addr *) &in6.sin6_addr.s6_addr, port, name, local); else if (proto == 0) unknownprint(); else abort(); }