diff options
Diffstat (limited to 'usr.bin/netstat/inet.c')
-rw-r--r-- | usr.bin/netstat/inet.c | 403 |
1 files changed, 306 insertions, 97 deletions
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 1be7b87..eee2fef 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/queue.h> +#include <sys/domain.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/sysctl.h> @@ -90,6 +91,208 @@ void inetprint (struct in_addr *, int, const char *, int); static int udp_done, tcp_done; #endif /* INET6 */ +static int +pcblist_sysctl(int proto, char **bufp, int istcp) +{ + const char *mibvar; + char *buf; + size_t len; + + switch (proto) { + case IPPROTO_TCP: + mibvar = "net.inet.tcp.pcblist"; + break; + case IPPROTO_UDP: + mibvar = "net.inet.udp.pcblist"; + break; + case IPPROTO_DIVERT: + mibvar = "net.inet.divert.pcblist"; + break; + default: + mibvar = "net.inet.raw.pcblist"; + break; + } + + len = 0; + if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { + if (errno != ENOENT) + warn("sysctl: %s", mibvar); + return (0); + } + if ((buf = malloc(len)) == 0) { + warnx("malloc %lu bytes", (u_long)len); + return (0); + } + if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { + warn("sysctl: %s", mibvar); + free(buf); + return (0); + } + *bufp = buf; + return (1); +} + +/* + * Copied directly from uipc_socket2.c. We leave out some fields that are in + * nested structures that aren't used to avoid extra work. + */ +static void +sbtoxsockbuf(struct sockbuf *sb, struct xsockbuf *xsb) +{ + xsb->sb_cc = sb->sb_cc; + xsb->sb_hiwat = sb->sb_hiwat; + xsb->sb_mbcnt = sb->sb_mbcnt; + xsb->sb_mbmax = sb->sb_mbmax; + xsb->sb_lowat = sb->sb_lowat; + xsb->sb_flags = sb->sb_flags; + xsb->sb_timeo = sb->sb_timeo; +} + +int +sotoxsocket(struct socket *so, struct xsocket *xso) +{ + struct protosw proto; + struct domain domain; + + bzero(xso, sizeof *xso); + xso->xso_len = sizeof *xso; + xso->xso_so = so; + xso->so_type = so->so_type; + xso->so_options = so->so_options; + xso->so_linger = so->so_linger; + xso->so_state = so->so_state; + xso->so_pcb = so->so_pcb; + if (kread((uintptr_t)so->so_proto, &proto, sizeof(proto)) != 0) + return (-1); + xso->xso_protocol = proto.pr_protocol; + if (kread((uintptr_t)proto.pr_domain, &domain, sizeof(domain)) != 0) + return (-1); + xso->xso_family = domain.dom_family; + xso->so_qlen = so->so_qlen; + xso->so_incqlen = so->so_incqlen; + xso->so_qlimit = so->so_qlimit; + xso->so_timeo = so->so_timeo; + xso->so_error = so->so_error; + xso->so_oobmark = so->so_oobmark; + sbtoxsockbuf(&so->so_snd, &xso->so_snd); + sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); + return (0); +} + +static int +pcblist_kvm(u_long off, char **bufp, int istcp) +{ + struct inpcbinfo pcbinfo; + struct inpcbhead listhead; + struct inpcb *inp; + struct xinpcb xi; + struct xinpgen xig; + struct xtcpcb xt; + struct socket so; + struct xsocket *xso; + char *buf, *p; + size_t len; + + if (off == 0) + return (0); + kread(off, &pcbinfo, sizeof(pcbinfo)); + if (istcp) + len = 2 * sizeof(xig) + + (pcbinfo.ipi_count + pcbinfo.ipi_count / 8) * + sizeof(struct xtcpcb); + else + len = 2 * sizeof(xig) + + (pcbinfo.ipi_count + pcbinfo.ipi_count / 8) * + sizeof(struct xinpcb); + if ((buf = malloc(len)) == 0) { + warnx("malloc %lu bytes", (u_long)len); + return (0); + } + p = buf; + +#define COPYOUT(obj, size) do { \ + if (len < (size)) { \ + warnx("buffer size exceeded"); \ + goto fail; \ + } \ + bcopy((obj), p, (size)); \ + len -= (size); \ + p += (size); \ +} while (0) + +#define KREAD(off, buf, len) do { \ + if (kread((uintptr_t)(off), (buf), (len)) != 0) \ + goto fail; \ +} while (0) + + /* Write out header. */ + xig.xig_len = sizeof xig; + xig.xig_count = pcbinfo.ipi_count; + xig.xig_gen = pcbinfo.ipi_gencnt; + xig.xig_sogen = 0; + COPYOUT(&xig, sizeof xig); + + /* Walk the PCB list. */ + xt.xt_len = sizeof xt; + xi.xi_len = sizeof xi; + if (istcp) + xso = &xt.xt_socket; + else + xso = &xi.xi_socket; + KREAD(pcbinfo.ipi_listhead, &listhead, sizeof(listhead)); + LIST_FOREACH(inp, &listhead, inp_list) { + if (istcp) { + KREAD(inp, &xt.xt_inp, sizeof(*inp)); + inp = &xt.xt_inp; + } else { + KREAD(inp, &xi.xi_inp, sizeof(*inp)); + inp = &xi.xi_inp; + } + + if (inp->inp_gencnt > pcbinfo.ipi_gencnt) + continue; + + if (istcp) { + if (inp->inp_ppcb == NULL) + bzero(&xt.xt_tp, sizeof xt.xt_tp); + else if (inp->inp_vflag & INP_TIMEWAIT) { + bzero(&xt.xt_tp, sizeof xt.xt_tp); + xt.xt_tp.t_state = TCPS_TIME_WAIT; + } else + KREAD(inp->inp_ppcb, &xt.xt_tp, + sizeof xt.xt_tp); + } + if (inp->inp_socket) { + KREAD(inp->inp_socket, &so, sizeof(so)); + if (sotoxsocket(&so, xso) != 0) + goto fail; + } else { + bzero(xso, sizeof(*xso)); + if (istcp) + xso->xso_protocol = IPPROTO_TCP; + } + if (istcp) + COPYOUT(&xt, sizeof xt); + else + COPYOUT(&xi, sizeof xi); + } + + /* Reread the pcbinfo and write out the footer. */ + kread(off, &pcbinfo, sizeof(pcbinfo)); + xig.xig_count = pcbinfo.ipi_count; + xig.xig_gen = pcbinfo.ipi_gencnt; + COPYOUT(&xig, sizeof xig); + + *bufp = buf; + return (1); + +fail: + free(buf); + return (0); +#undef COPYOUT +#undef KREAD +} + /* * Print a summary of connections related to an Internet * protocol. For TCP, also give state of connection. @@ -97,18 +300,16 @@ static int udp_done, tcp_done; * -a (all) flag is specified. */ void -protopr(u_long proto, /* for sysctl version we pass proto # */ - const char *name, int af1) +protopr(u_long off, const char *name, int af1, int proto) { int istcp; static int first = 1; char *buf; - const char *mibvar, *vchar; + const char *vchar; struct tcpcb *tp = NULL; struct inpcb *inp; struct xinpgen *xig, *oxig; struct xsocket *so; - size_t len; istcp = 0; switch (proto) { @@ -120,7 +321,6 @@ protopr(u_long proto, /* for sysctl version we pass proto # */ tcp_done = 1; #endif istcp = 1; - mibvar = "net.inet.tcp.pcblist"; break; case IPPROTO_UDP: #ifdef INET6 @@ -129,29 +329,14 @@ protopr(u_long proto, /* for sysctl version we pass proto # */ else udp_done = 1; #endif - mibvar = "net.inet.udp.pcblist"; - break; - case IPPROTO_DIVERT: - mibvar = "net.inet.divert.pcblist"; - break; - default: - mibvar = "net.inet.raw.pcblist"; break; } - len = 0; - if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { - if (errno != ENOENT) - warn("sysctl: %s", mibvar); - return; - } - if ((buf = malloc(len)) == 0) { - warnx("malloc %lu bytes", (u_long)len); - return; - } - if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { - warn("sysctl: %s", mibvar); - free(buf); - return; + if (live) { + if (!pcblist_sysctl(proto, &buf, istcp)) + return; + } else { + if (!pcblist_kvm(off, &buf, istcp)) + return; } oxig = xig = (struct xinpgen *)buf; @@ -168,7 +353,7 @@ protopr(u_long proto, /* for sysctl version we pass proto # */ } /* Ignore sockets for protocols other than the desired one. */ - if (so->xso_protocol != (int)proto) + if (so->xso_protocol != proto) continue; /* Ignore PCBs which were freed during copyout. */ @@ -347,18 +532,10 @@ protopr(u_long proto, /* for sysctl version we pass proto # */ * Dump TCP statistics structure. */ void -tcp_stats(u_long off __unused, const char *name, int af1 __unused) +tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct tcpstat tcpstat, zerostat; size_t len = sizeof tcpstat; - - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - warn("sysctl: net.inet.tcp.stats"); - return; - } #ifdef INET6 if (tcp_done != 0) @@ -367,6 +544,17 @@ tcp_stats(u_long off __unused, const char *name, int af1 __unused) tcp_done = 1; #endif + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + warn("sysctl: net.inet.tcp.stats"); + return; + } + } else + kread(off, &tcpstat, len); + printf ("%s:\n", name); #define p(f, m) if (tcpstat.f || sflag <= 1) \ @@ -480,20 +668,12 @@ tcp_stats(u_long off __unused, const char *name, int af1 __unused) * Dump UDP statistics structure. */ void -udp_stats(u_long off __unused, const char *name, int af1 __unused) +udp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct udpstat udpstat, zerostat; size_t len = sizeof udpstat; u_long delivered; - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - warn("sysctl: net.inet.udp.stats"); - return; - } - #ifdef INET6 if (udp_done != 0) return; @@ -501,6 +681,17 @@ udp_stats(u_long off __unused, const char *name, int af1 __unused) udp_done = 1; #endif + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + warn("sysctl: net.inet.udp.stats"); + return; + } + } else + kread(off, &udpstat, len); + printf("%s:\n", name); #define p(f, m) if (udpstat.f || sflag <= 1) \ printf(m, udpstat.f, plural(udpstat.f)) @@ -537,18 +728,24 @@ udp_stats(u_long off __unused, const char *name, int af1 __unused) * Dump CARP statistics structure. */ void -carp_stats(u_long off __unused, const char *name, int af1 __unused) +carp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct carpstats carpstat, zerostat; size_t len = sizeof(struct carpstats); - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.carp.stats", &carpstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - if (errno != ENOENT) - warn("sysctl: net.inet.carp.stats"); - return; + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.carp.stats", &carpstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + if (errno != ENOENT) + warn("sysctl: net.inet.carp.stats"); + return; + } + } else { + if (off == 0) + return; + kread(off, &carpstat, len); } printf("%s:\n", name); @@ -582,18 +779,21 @@ carp_stats(u_long off __unused, const char *name, int af1 __unused) * Dump IP statistics structure. */ void -ip_stats(u_long off __unused, const char *name, int af1 __unused) +ip_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ipstat ipstat, zerostat; size_t len = sizeof ipstat; - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - warn("sysctl: net.inet.ip.stats"); - return; - } + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + warn("sysctl: net.inet.ip.stats"); + return; + } + } else + kread(off, &ipstat, len); printf("%s:\n", name); @@ -687,26 +887,23 @@ static const char *icmpnames[ICMP_MAXTYPE + 1] = { * Dump ICMP statistics. */ void -icmp_stats(u_long off __unused, const char *name, int af1 __unused) +icmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct icmpstat icmpstat, zerostat; int i, first; - int mib[4]; /* CTL_NET + PF_INET + IPPROTO_ICMP + req */ size_t len; - mib[0] = CTL_NET; - mib[1] = PF_INET; - mib[2] = IPPROTO_ICMP; - mib[3] = ICMPCTL_STATS; - len = sizeof icmpstat; - if (zflag) - memset(&zerostat, 0, len); - if (sysctl(mib, 4, &icmpstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - warn("sysctl: net.inet.icmp.stats"); - return; - } + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + warn("sysctl: net.inet.icmp.stats"); + return; + } + } else + kread(off, &icmpstat, len); printf("%s:\n", name); @@ -758,30 +955,35 @@ icmp_stats(u_long off __unused, const char *name, int af1 __unused) #undef p #undef p1a #undef p2 - mib[3] = ICMPCTL_MASKREPL; - len = sizeof i; - if (sysctl(mib, 4, &i, &len, (void *)0, 0) < 0) - return; - printf("\tICMP address mask responses are %sabled\n", - i ? "en" : "dis"); + if (live) { + len = sizeof i; + if (sysctlbyname("net.inet.icmp.maskrepl", &i, &len, NULL, 0) < + 0) + return; + printf("\tICMP address mask responses are %sabled\n", + i ? "en" : "dis"); + } } /* * Dump IGMP statistics structure. */ void -igmp_stats(u_long off __unused, const char *name, int af1 __unused) +igmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct igmpstat igmpstat, zerostat; size_t len = sizeof igmpstat; - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.igmp.stats", &igmpstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - warn("sysctl: net.inet.igmp.stats"); - return; - } + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.igmp.stats", &igmpstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + warn("sysctl: net.inet.igmp.stats"); + return; + } + } else + kread(off, &igmpstat, len); printf("%s:\n", name); @@ -806,18 +1008,25 @@ igmp_stats(u_long off __unused, const char *name, int af1 __unused) * Dump PIM statistics structure. */ void -pim_stats(u_long off __unused, const char *name, int af1 __unused) +pim_stats(u_long off __unused, const char *name, int af1 __unused, + int proto __unused) { struct pimstat pimstat, zerostat; size_t len = sizeof pimstat; - if (zflag) - memset(&zerostat, 0, len); - if (sysctlbyname("net.inet.pim.stats", &pimstat, &len, - zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { - if (errno != ENOENT) - warn("sysctl: net.inet.pim.stats"); - return; + if (live) { + if (zflag) + memset(&zerostat, 0, len); + if (sysctlbyname("net.inet.pim.stats", &pimstat, &len, + zflag ? &zerostat : NULL, zflag ? len : 0) < 0) { + if (errno != ENOENT) + warn("sysctl: net.inet.pim.stats"); + return; + } + } else { + if (off == 0) + return; + kread(off, &pimstat, len); } printf("%s:\n", name); |