blob: 365ebeee81ec31e5cf1c398ba034aac9319ae29d [file] [log] [blame]
/* $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.
*/
#ifdef INHERITED_CODE
#ifndef lint
#if 0
static char sccsid[] = "@(#)inet.c 8.4 (Berkeley) 4/20/94";
#else
/*__RCSID("$OpenBSD: inet6.c,v 1.31 2004/11/17 01:47:20 itojun Exp $");*/
/*__RCSID("KAME Id: inet6.c,v 1.10 2000/02/09 10:49:31 itojun Exp");*/
#endif
#endif /* not lint */
#endif
#include <net-snmp/net-snmp-config.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_WINSOCK_H
#include "winstub.h"
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <net-snmp/net-snmp-includes.h>
#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)
return;
if ((var->type & 0xF0) == 0x80) /* Exception */
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)
return;
if ((var->type & 0xF0) == 0x80) /* Exception */
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((u_char *)&in6.sin6_addr.s6_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();
}