version 1.44, 2015/01/20 18:26:57 |
version 1.45, 2015/03/12 01:03:00 |
|
|
* netstat |
* netstat |
*/ |
*/ |
|
|
#include <sys/signal.h> |
#include <kvm.h> |
|
#include <sys/types.h> |
|
#include <sys/sysctl.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/socketvar.h> |
#define _KERNEL |
#include <sys/mbuf.h> |
#include <sys/file.h> |
#include <sys/protosw.h> |
#undef _KERNEL |
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <net/route.h> |
|
#include <netinet/ip.h> |
|
#include <netinet/in_pcb.h> |
|
#include <netinet/ip_icmp.h> |
|
#include <netinet/icmp_var.h> |
|
#include <netinet/ip_var.h> |
|
#include <netinet/tcp.h> |
#include <netinet/tcp.h> |
#include <netinet/tcp_seq.h> |
#include <netinet/tcp_seq.h> |
#define TCPSTATES |
#define TCPSTATES |
#include <netinet/tcp_fsm.h> |
#include <netinet/tcp_fsm.h> |
#include <netinet/tcp_timer.h> |
|
#include <netinet/tcp_var.h> |
|
#include <netinet/udp.h> |
|
#include <netinet/udp_var.h> |
|
#include <arpa/inet.h> |
#include <arpa/inet.h> |
|
|
#include <netdb.h> |
#include <netdb.h> |
|
#include <signal.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <err.h> |
#include <err.h> |
|
|
#include "systat.h" |
#include "systat.h" |
#include "engine.h" |
#include "engine.h" |
|
|
|
#define TCP 0x1 |
|
#define UDP 0x2 |
|
#define OTHER 0x4 |
|
|
struct netinfo { |
struct netinfo { |
union { |
union { |
struct in_addr nif_laddr; /* local address */ |
struct in_addr nif_laddr; /* local address */ |
|
|
struct in_addr nif_faddr; /* foreign address */ |
struct in_addr nif_faddr; /* foreign address */ |
struct in6_addr nif_faddr6; /* foreign address */ |
struct in6_addr nif_faddr6; /* foreign address */ |
} f; |
} f; |
char *nif_proto; /* protocol */ |
|
long nif_rcvcc; /* rcv buffer character count */ |
long nif_rcvcc; /* rcv buffer character count */ |
long nif_sndcc; /* snd buffer character count */ |
long nif_sndcc; /* snd buffer character count */ |
short nif_lport; /* local port */ |
short nif_lport; /* local port */ |
short nif_fport; /* foreign port */ |
short nif_fport; /* foreign port */ |
short nif_state; /* tcp state */ |
short nif_state; /* tcp state */ |
short nif_family; |
short nif_family; |
|
short nif_proto; /* protocol */ |
|
short nif_ipproto; |
}; |
}; |
|
|
#define nif_laddr l.nif_laddr |
#define nif_laddr l.nif_laddr |
|
|
#define nif_faddr f.nif_faddr |
#define nif_faddr f.nif_faddr |
#define nif_faddr6 f.nif_faddr6 |
#define nif_faddr6 f.nif_faddr6 |
|
|
static void enter(struct inpcb *, struct socket *, int, char *); |
static void enter(struct kinfo_file *); |
|
static int kf_comp(const void *, const void *); |
static void inetprint(struct in_addr *, int, char *, field_def *); |
static void inetprint(struct in_addr *, int, char *, field_def *); |
static void inet6print(struct in6_addr *, int, char *, field_def *); |
static void inet6print(struct in6_addr *, int, char *, field_def *); |
static void shownetstat(struct netinfo *p); |
static void shownetstat(struct netinfo *p); |
|
|
|
|
static int aflag = 0; |
static int aflag = 0; |
|
|
static struct nlist namelist[] = { |
|
#define X_TCBTABLE 0 /* no sysctl */ |
|
{ "_tcbtable" }, |
|
#define X_UDBTABLE 1 /* no sysctl */ |
|
{ "_udbtable" }, |
|
{ "" }, |
|
}; |
|
#define ADD_ALLOC 1000 |
#define ADD_ALLOC 1000 |
|
|
|
|
int protos; |
int protos; |
|
|
struct netinfo *netinfos = NULL; |
struct netinfo *netinfos = NULL; |
|
|
} |
} |
|
|
static void |
static void |
enter(struct inpcb *inp, struct socket *so, int state, char *proto) |
enter(struct kinfo_file *kf) |
{ |
{ |
|
#define s6_addr32 __u6_addr.__u6_addr32 |
struct netinfo *p; |
struct netinfo *p; |
|
|
|
/* first filter out unwanted sockets */ |
|
if (kf->so_family != AF_INET && kf->so_family != AF_INET6) |
|
return; |
|
|
|
switch (kf->so_protocol) { |
|
case IPPROTO_TCP: |
|
if ((protos & TCP) == 0) |
|
return; |
|
break; |
|
case IPPROTO_UDP: |
|
if ((protos & UDP) == 0) |
|
return; |
|
break; |
|
default: |
|
if ((protos & OTHER) == 0) |
|
return; |
|
break; |
|
} |
|
|
|
if (!aflag) { |
|
struct in6_addr faddr6; |
|
|
|
switch (kf->so_family) { |
|
case AF_INET: |
|
if (kf->inp_faddru[0] == INADDR_ANY) |
|
return; |
|
break; |
|
case AF_INET6: |
|
faddr6.s6_addr32[0] = kf->inp_faddru[0]; |
|
faddr6.s6_addr32[1] = kf->inp_faddru[1]; |
|
faddr6.s6_addr32[2] = kf->inp_faddru[2]; |
|
faddr6.s6_addr32[3] = kf->inp_faddru[3]; |
|
if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) |
|
return; |
|
break; |
|
} |
|
} |
|
|
|
/* finally enter the socket to the table */ |
p = next_ns(); |
p = next_ns(); |
if (p == NULL) { |
if (p == NULL) { |
error("Out of Memory!"); |
error("Out of Memory!"); |
return; |
return; |
} |
} |
|
|
p->nif_lport = inp->inp_lport; |
p->nif_lport = kf->inp_lport; |
p->nif_fport = inp->inp_fport; |
p->nif_fport = kf->inp_fport; |
p->nif_proto = proto; |
p->nif_proto = kf->so_protocol; |
|
p->nif_ipproto = kf->inp_proto; |
if (inp->inp_flags & INP_IPV6) { |
|
p->nif_laddr6 = inp->inp_laddr6; |
switch (kf->so_family) { |
p->nif_faddr6 = inp->inp_faddr6; |
case AF_INET: |
p->nif_family = AF_INET6; |
|
} else { |
|
p->nif_laddr = inp->inp_laddr; |
|
p->nif_faddr = inp->inp_faddr; |
|
p->nif_family = AF_INET; |
p->nif_family = AF_INET; |
|
p->nif_laddr.s_addr = kf->inp_laddru[0]; |
|
p->nif_faddr.s_addr = kf->inp_faddru[0]; |
|
break; |
|
case AF_INET6: |
|
p->nif_family = AF_INET6; |
|
p->nif_laddr6.s6_addr32[0] = kf->inp_laddru[0]; |
|
p->nif_laddr6.s6_addr32[1] = kf->inp_laddru[1]; |
|
p->nif_laddr6.s6_addr32[2] = kf->inp_laddru[2]; |
|
p->nif_laddr6.s6_addr32[3] = kf->inp_laddru[3]; |
|
p->nif_faddr6.s6_addr32[0] = kf->inp_faddru[0]; |
|
p->nif_faddr6.s6_addr32[1] = kf->inp_faddru[1]; |
|
p->nif_faddr6.s6_addr32[2] = kf->inp_faddru[2]; |
|
p->nif_faddr6.s6_addr32[3] = kf->inp_faddru[3]; |
|
break; |
} |
} |
|
|
p->nif_rcvcc = so->so_rcv.sb_cc; |
p->nif_rcvcc = kf->so_rcv_cc; |
p->nif_sndcc = so->so_snd.sb_cc; |
p->nif_sndcc = kf->so_snd_cc; |
p->nif_state = state; |
p->nif_state = kf->t_state; |
|
#undef s6_addr32 |
} |
} |
|
|
|
|
|
|
int |
int |
select_ns(void) |
select_ns(void) |
{ |
{ |
if (kd == NULL) { |
|
num_disp = 1; |
|
return (0); |
|
} |
|
num_disp = num_ns; |
num_disp = num_ns; |
return (0); |
return (0); |
} |
} |
|
|
|
static int type_map[] = { -1, 2, 3, 1, 4, 5 }; |
|
|
|
static int |
|
kf_comp(const void *a, const void *b) |
|
{ |
|
const struct kinfo_file *ka = a, *kb = b; |
|
|
|
if (ka->so_family != kb->so_family) { |
|
/* AF_INET < AF_INET6 < AF_LOCAL */ |
|
if (ka->so_family == AF_INET) |
|
return (-1); |
|
if (ka->so_family == AF_LOCAL) |
|
return (1); |
|
if (kb->so_family == AF_LOCAL) |
|
return (-1); |
|
return (1); |
|
} |
|
if (ka->so_family == AF_LOCAL) { |
|
if (type_map[ka->so_type] < type_map[kb->so_type]) |
|
return (-1); |
|
if (type_map[ka->so_type] > type_map[kb->so_type]) |
|
return (1); |
|
} else if (ka->so_family == AF_INET || ka->so_family == AF_INET6) { |
|
if (ka->so_protocol < kb->so_protocol) |
|
return (-1); |
|
if (ka->so_protocol > kb->so_protocol) |
|
return (1); |
|
if (ka->so_type == SOCK_DGRAM || ka->so_type == SOCK_STREAM) { |
|
/* order sockets by remote port desc */ |
|
if (ka->inp_fport > kb->inp_fport) |
|
return (-1); |
|
if (ka->inp_fport < kb->inp_fport) |
|
return (1); |
|
} else if (ka->so_type == SOCK_RAW) { |
|
if (ka->inp_proto > kb->inp_proto) |
|
return (-1); |
|
if (ka->inp_proto < kb->inp_proto) |
|
return (1); |
|
} |
|
} |
|
return (0); |
|
} |
|
|
|
|
int |
int |
read_ns(void) |
read_ns(void) |
{ |
{ |
struct inpcbtable pcbtable; |
struct kinfo_file *kf; |
struct inpcb *next, *prev; |
int i, fcnt; |
struct inpcb inpcb, prevpcb; |
|
struct socket sockb; |
|
struct tcpcb tcpcb; |
|
void *off; |
|
int istcp; |
|
|
|
if (kd == NULL) { |
if (kd == NULL) { |
|
error("Failed to initialize KVM!"); |
return (0); |
return (0); |
} |
} |
|
kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET, |
num_ns = 0; |
sizeof(*kf), &fcnt); |
|
if (kf == NULL) { |
if (namelist[X_TCBTABLE].n_value == 0) |
error("Out of Memory!"); |
return 0; |
return (0); |
|
|
if (protos & TCP) { |
|
off = NPTR(X_TCBTABLE); |
|
istcp = 1; |
|
} else if (protos & UDP) { |
|
off = NPTR(X_UDBTABLE); |
|
istcp = 0; |
|
} else { |
|
error("No protocols to display"); |
|
return 0; |
|
} |
} |
|
|
again: |
/* sort sockets by AF, proto and type */ |
KREAD(off, &pcbtable, sizeof (struct inpcbtable)); |
qsort(kf, fcnt, sizeof(*kf), kf_comp); |
|
|
prev = NULL; |
num_ns = 0; |
next = TAILQ_FIRST(&pcbtable.inpt_queue); |
|
|
|
while (next != NULL) { |
for (i = 0; i < fcnt; i++) |
KREAD(next, &inpcb, sizeof (inpcb)); |
enter(&kf[i]); |
if (prev != NULL) { |
|
KREAD(prev, &prevpcb, sizeof (prevpcb)); |
|
if (TAILQ_NEXT(&prevpcb, inp_queue) != next) { |
|
error("Kernel state in transition"); |
|
return 0; |
|
} |
|
} |
|
prev = next; |
|
next = TAILQ_NEXT(&inpcb, inp_queue); |
|
|
|
if (!aflag) { |
|
if (!(inpcb.inp_flags & INP_IPV6) && |
|
inet_lnaof(inpcb.inp_faddr) == INADDR_ANY) |
|
continue; |
|
if ((inpcb.inp_flags & INP_IPV6) && |
|
IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_faddr6)) |
|
continue; |
|
} |
|
KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); |
|
if (istcp) { |
|
KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); |
|
if (!aflag && tcpcb.t_state <= TCPS_LISTEN) |
|
continue; |
|
enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); |
|
} else |
|
enter(&inpcb, &sockb, 0, "udp"); |
|
} |
|
if (istcp && (protos & UDP)) { |
|
istcp = 0; |
|
off = NPTR(X_UDBTABLE); |
|
goto again; |
|
} |
|
|
|
num_disp = num_ns; |
num_disp = num_ns; |
return 0; |
return 0; |
} |
} |
|
|
{ |
{ |
int n, count = 0; |
int n, count = 0; |
|
|
if (kd == NULL) { |
|
print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!"); |
|
print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!"); |
|
end_line(); |
|
return; |
|
} |
|
|
|
for (n = dispstart; n < num_disp; n++) { |
for (n = dispstart; n < num_disp; n++) { |
shownetstat(netinfos + n); |
shownetstat(netinfos + n); |
count++; |
count++; |
|
|
initnetstat(void) |
initnetstat(void) |
{ |
{ |
field_view *v; |
field_view *v; |
int ret; |
|
|
|
if (kd) { |
protos = TCP|UDP|OTHER; |
if ((ret = kvm_nlist(kd, namelist)) == -1) |
|
errx(1, "%s", kvm_geterr(kd)); |
|
else if (ret) |
|
nlisterr(namelist); |
|
|
|
if (namelist[X_TCBTABLE].n_value == 0) { |
|
error("No symbols in namelist"); |
|
return(0); |
|
} |
|
} |
|
protos = TCP|UDP; |
|
|
|
for (v = views_ns; v->name != NULL; v++) |
for (v = views_ns; v->name != NULL; v++) |
add_view(v); |
add_view(v); |
|
|
|
|
static void |
static void |
shownetstat(struct netinfo *p) |
shownetstat(struct netinfo *p) |
{ |
{ |
|
char *proto = NULL; |
|
|
|
switch (p->nif_proto) { |
|
case IPPROTO_TCP: |
|
proto = "tcp"; |
|
break; |
|
case IPPROTO_UDP: |
|
proto = "udp"; |
|
break; |
|
} |
|
|
switch (p->nif_family) { |
switch (p->nif_family) { |
case AF_INET: |
case AF_INET: |
inetprint(&p->nif_laddr, p->nif_lport, |
inetprint(&p->nif_laddr, p->nif_lport, |
p->nif_proto, FLD_NS_LOCAL); |
proto, FLD_NS_LOCAL); |
inetprint(&p->nif_faddr, p->nif_fport, |
inetprint(&p->nif_faddr, p->nif_fport, |
p->nif_proto, FLD_NS_FOREIGN); |
proto, FLD_NS_FOREIGN); |
break; |
break; |
case AF_INET6: |
case AF_INET6: |
inet6print(&p->nif_laddr6, p->nif_lport, |
inet6print(&p->nif_laddr6, p->nif_lport, |
p->nif_proto, FLD_NS_LOCAL); |
proto, FLD_NS_LOCAL); |
inet6print(&p->nif_faddr6, p->nif_fport, |
inet6print(&p->nif_faddr6, p->nif_fport, |
p->nif_proto, FLD_NS_FOREIGN); |
proto, FLD_NS_FOREIGN); |
break; |
break; |
} |
} |
|
|
tb_start(); |
tb_start(); |
tbprintf("%s", p->nif_proto); |
switch (p->nif_proto) { |
if (p->nif_family == AF_INET6) |
case IPPROTO_TCP: |
tbprintf("6"); |
case IPPROTO_UDP: |
|
tbprintf(proto); |
|
if (p->nif_family == AF_INET6) |
|
tbprintf("6"); |
|
break; |
|
case IPPROTO_DIVERT: |
|
tbprintf("divert"); |
|
if (p->nif_family == AF_INET6) |
|
tbprintf("6"); |
|
break; |
|
default: |
|
tbprintf("%d", p->nif_ipproto); |
|
break; |
|
} |
|
|
print_fld_tb(FLD_NS_PROTO); |
print_fld_tb(FLD_NS_PROTO); |
|
|
print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); |
print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); |
print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); |
print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); |
|
|
if (streq(p->nif_proto, "tcp")) { |
if (p->nif_proto == IPPROTO_TCP) { |
if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) |
if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) |
print_fld_uint(FLD_NS_STATE, p->nif_state); |
print_fld_uint(FLD_NS_STATE, p->nif_state); |
else |
else |
|
|
} |
} |
|
|
int |
int |
kvm_ckread(void *a, void *b, size_t l) |
|
{ |
|
if (kvm_read(kd, (u_long)a, b, l) != l) { |
|
if (verbose) |
|
error("error reading kmem\n"); |
|
return (0); |
|
} else |
|
return (1); |
|
} |
|
|
|
|
|
int |
|
ns_keyboard_callback(int ch) |
ns_keyboard_callback(int ch) |
{ |
{ |
switch (ch) { |
switch (ch) { |
|
|
break; |
break; |
case 'n': |
case 'n': |
nflag = !nflag; |
nflag = !nflag; |
|
gotsig_alarm = 1; |
|
break; |
|
case 'o': |
|
protos ^= OTHER; |
gotsig_alarm = 1; |
gotsig_alarm = 1; |
break; |
break; |
case 'r': |
case 'r': |