/* $OpenBSD: netstat.c,v 1.31 2008/06/12 22:26:01 canacar Exp $ */ /* $NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $ */ /*- * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * netstat */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TCPSTATES #include #include #include #include #include #include #include #include #include #include #include #include #include #include "systat.h" #include "engine.h" struct netinfo { union { struct in_addr nif_laddr; /* local address */ struct in6_addr nif_laddr6; /* local address */ } l; union { struct in_addr nif_faddr; /* foreign address */ struct in6_addr nif_faddr6; /* foreign address */ } f; char *nif_proto; /* protocol */ long nif_rcvcc; /* rcv buffer character count */ long nif_sndcc; /* snd buffer character count */ short nif_lport; /* local port */ short nif_fport; /* foreign port */ short nif_state; /* tcp state */ short nif_family; }; #define nif_laddr l.nif_laddr #define nif_laddr6 l.nif_laddr6 #define nif_faddr f.nif_faddr #define nif_faddr6 f.nif_faddr6 static void enter(struct inpcb *, struct socket *, int, char *); static const char *inetname(struct in_addr); static void inetprint(struct in_addr *, int, char *, field_def *); static const char *inet6name(struct in6_addr *); static void inet6print(struct in6_addr *, int, char *, field_def *); static void shownetstat(struct netinfo *p); void print_ns(void); int read_ns(void); int select_ns(void); int ns_keyboard_callback(int); #define streq(a,b) (strcmp(a,b)==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 int protos; struct netinfo *netinfos = NULL; size_t num_ns = 0; static size_t num_alloc = 0; field_def fields_ns[] = { {"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0}, }; #define FIELD_ADDR(x) (&fields_ns[x]) #define FLD_NS_LOCAL FIELD_ADDR(0) #define FLD_NS_FOREIGN FIELD_ADDR(1) #define FLD_NS_PROTO FIELD_ADDR(2) #define FLD_NS_RECV_Q FIELD_ADDR(3) #define FLD_NS_SEND_Q FIELD_ADDR(4) #define FLD_NS_STATE FIELD_ADDR(5) /* Define views */ field_def *view_ns_0[] = { FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO, FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL }; /* Define view managers */ struct view_manager netstat_mgr = { "Netstat", select_ns, read_ns, NULL, print_header, print_ns, ns_keyboard_callback, NULL, NULL }; field_view views_ns[] = { {view_ns_0, "netstat", '0', &netstat_mgr}, {NULL, NULL, 0, NULL} }; struct netinfo * next_ns(void) { if (num_alloc <= num_ns) { struct netinfo *ni; size_t a = num_alloc + ADD_ALLOC; if (a < num_alloc) return NULL; ni = realloc(netinfos, a * sizeof(*ni)); if (ni == NULL) return NULL; netinfos = ni; num_alloc = a; } return &netinfos[num_ns++]; } static void enter(struct inpcb *inp, struct socket *so, int state, char *proto) { struct netinfo *p; p = next_ns(); if (p == NULL) { error("Out of Memory!"); return; } p->nif_lport = inp->inp_lport; p->nif_fport = inp->inp_fport; p->nif_proto = proto; if (inp->inp_flags & INP_IPV6) { p->nif_laddr6 = inp->inp_laddr6; p->nif_faddr6 = inp->inp_faddr6; 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_rcvcc = so->so_rcv.sb_cc; p->nif_sndcc = so->so_snd.sb_cc; p->nif_state = state; } /* netstat callback functions */ int select_ns(void) { static int init = 0; if (kd == NULL) { num_disp = 1; return (0); } if (!init) { sethostent(1); setnetent(1); init = 1; } num_disp = num_ns; return (0); } int read_ns(void) { struct inpcbtable pcbtable; struct inpcb *head, *prev, *next; struct inpcb inpcb; struct socket sockb; struct tcpcb tcpcb; void *off; int istcp; if (kd == NULL) { return (0); } num_ns = 0; if (namelist[X_TCBTABLE].n_value == 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: KREAD(off, &pcbtable, sizeof (struct inpcbtable)); prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; next = CIRCLEQ_FIRST(&pcbtable.inpt_queue); while (next != head) { KREAD(next, &inpcb, sizeof (inpcb)); if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) { error("Kernel state in transition"); return 0; } prev = next; next = CIRCLEQ_NEXT(&inpcb, inp_queue); if (!aflag) { if (!(inpcb.inp_flags & INP_IPV6) && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) continue; if ((inpcb.inp_flags & INP_IPV6) && IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6)) continue; } KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); if (istcp) { KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 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; return 0; } void print_ns(void) { 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++) { shownetstat(netinfos + n); count++; if (maxprint > 0 && count >= maxprint) break; } } int initnetstat(void) { field_view *v; int ret; if (kd) { 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++) add_view(v); return(1); } static void shownetstat(struct netinfo *p) { switch (p->nif_family) { case AF_INET: inetprint(&p->nif_laddr, p->nif_lport, p->nif_proto, FLD_NS_LOCAL); inetprint(&p->nif_faddr, p->nif_fport, p->nif_proto, FLD_NS_FOREIGN); break; case AF_INET6: inet6print(&p->nif_laddr6, p->nif_lport, p->nif_proto, FLD_NS_LOCAL); inet6print(&p->nif_faddr6, p->nif_fport, p->nif_proto, FLD_NS_FOREIGN); break; } tb_start(); tbprintf("%s", p->nif_proto); if (p->nif_family == AF_INET6) tbprintf("6"); print_fld_tb(FLD_NS_PROTO); print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); if (streq(p->nif_proto, "tcp")) { if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) print_fld_uint(FLD_NS_STATE, p->nif_state); else print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]); } end_line(); } /* * Pretty print an Internet address (net address + port). * If the nflag was specified, use numbers instead of names. */ static void inetprint(struct in_addr *in, int port, char *proto, field_def *fld) { struct servent *sp = 0; tb_start(); tbprintf("%s", inetname(*in)); if (!nflag && port) sp = getservbyport(port, proto); if (sp || port == 0) tbprintf(":%s", sp ? sp->s_name : "*"); else tbprintf(":%d", ntohs((u_short)port)); print_fld_tb(fld); } static void inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld) { struct servent *sp = 0; tb_start(); tbprintf("%s", inet6name(in6)); if (!nflag && port) sp = getservbyport(port, proto); if (sp || port == 0) tbprintf(":%s", sp ? sp->s_name : "*"); else tbprintf(":%d", ntohs((u_short)port)); print_fld_tb(fld); } static const char * inet6name(struct in6_addr *in6) { static char line[NI_MAXHOST]; struct sockaddr_in6 sin6; int flags; flags = nflag ? NI_NUMERICHOST : 0; if (IN6_IS_ADDR_UNSPECIFIED(in6)) return "*"; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_addr = *in6; if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, line, sizeof(line), NULL, 0, flags) == 0) return line; return "?"; } static const char * inetname(struct in_addr in) { static char line[NI_MAXHOST]; struct sockaddr_in sin; int flags, e; flags = nflag ? NI_NUMERICHOST : 0; if (in.s_addr == INADDR_ANY) return "*"; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr = in; e = getnameinfo((struct sockaddr *)&sin, sin.sin_len, line, sizeof(line), NULL, 0, flags); if (e == 0) return line; error("Lookup: %s", gai_strerror(e)); return "?"; } 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 at %x\n", a); return (0); } else return (1); } int ns_keyboard_callback(int ch) { switch (ch) { case 'n': nflag = !nflag; gotsig_alarm = 1; break; case 't': protos ^= TCP; gotsig_alarm = 1; break; case 'u': protos ^= UDP; gotsig_alarm = 1; break; default: return keyboard_callback(ch); }; return 1; }