/* $OpenBSD: netstat.c,v 1.46 2023/10/10 09:27:03 tb 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 #define _KERNEL #include #undef _KERNEL #include #include #include #define TCPSTATES #include #include #include #include #include #include #include #include #include #include "systat.h" #include "engine.h" #define TCP 0x1 #define UDP 0x2 #define OTHER 0x4 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; 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; short nif_proto; /* protocol */ short nif_ipproto; }; #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 kinfo_file *); static int kf_comp(const void *, const void *); static void inetprint(struct in_addr *, int, char *, field_def *); 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; #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 FLD_NS_LOCAL FIELD_ADDR(fields_ns,0) #define FLD_NS_FOREIGN FIELD_ADDR(fields_ns,1) #define FLD_NS_PROTO FIELD_ADDR(fields_ns,2) #define FLD_NS_RECV_Q FIELD_ADDR(fields_ns,3) #define FLD_NS_SEND_Q FIELD_ADDR(fields_ns,4) #define FLD_NS_STATE FIELD_ADDR(fields_ns,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 = reallocarray(netinfos, a, sizeof(*ni)); if (ni == NULL) return NULL; netinfos = ni; num_alloc = a; } return &netinfos[num_ns++]; } static void enter(struct kinfo_file *kf) { #define s6_addr32 __u6_addr.__u6_addr32 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(); if (p == NULL) { error("Out of Memory!"); return; } p->nif_lport = kf->inp_lport; p->nif_fport = kf->inp_fport; p->nif_proto = kf->so_protocol; p->nif_ipproto = kf->inp_proto; switch (kf->so_family) { case 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 = kf->so_rcv_cc; p->nif_sndcc = kf->so_snd_cc; p->nif_state = kf->t_state; #undef s6_addr32 } /* netstat callback functions */ int select_ns(void) { num_disp = num_ns; 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 read_ns(void) { struct kinfo_file *kf; int i, fcnt; if (kd == NULL) { error("Failed to initialize KVM!"); return (0); } kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET, sizeof(*kf), &fcnt); if (kf == NULL) { error("Out of Memory!"); return (0); } /* sort sockets by AF, proto and type */ qsort(kf, fcnt, sizeof(*kf), kf_comp); num_ns = 0; for (i = 0; i < fcnt; i++) enter(&kf[i]); num_disp = num_ns; return 0; } void print_ns(void) { int n, count = 0; for (n = dispstart; n < num_disp; n++) { shownetstat(netinfos + n); count++; if (maxprint > 0 && count >= maxprint) break; } } int initnetstat(void) { field_view *v; protos = TCP|UDP|OTHER; for (v = views_ns; v->name != NULL; v++) add_view(v); return(1); } static void 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) { case AF_INET: inetprint(&p->nif_laddr, p->nif_lport, proto, FLD_NS_LOCAL); inetprint(&p->nif_faddr, p->nif_fport, proto, FLD_NS_FOREIGN); break; case AF_INET6: inet6print(&p->nif_laddr6, p->nif_lport, proto, FLD_NS_LOCAL); inet6print(&p->nif_faddr6, p->nif_fport, proto, FLD_NS_FOREIGN); break; } tb_start(); switch (p->nif_proto) { case IPPROTO_TCP: case IPPROTO_UDP: tbprintf("%s", 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_size(FLD_NS_RECV_Q, p->nif_rcvcc); print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); if (p->nif_proto == IPPROTO_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); } int ns_keyboard_callback(int ch) { switch (ch) { case 'a': aflag = !aflag; gotsig_alarm = 1; break; case 'n': nflag = !nflag; gotsig_alarm = 1; break; case 'o': protos ^= OTHER; gotsig_alarm = 1; break; case 'r': aflag = 0; nflag = 1; protos = TCP|UDP; 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; }