diff --git a/sys/netinet/ip_divert.h b/sys/netinet/ip_divert.h --- a/sys/netinet/ip_divert.h +++ b/sys/netinet/ip_divert.h @@ -36,10 +36,9 @@ #ifndef _NETINET_IP_DIVERT_H_ #define _NETINET_IP_DIVERT_H_ +#include /* - * divert has no custom kernel-userland API. - * * All communication occurs through a sockaddr_in socket where * * kernel-->userland @@ -54,4 +53,11 @@ * sin_addr = IN: address of the incoming interface; * OUT: INADDR_ANY */ + +struct divstat { + uint64_t div_diverted; /* successfully diverted to userland */ + uint64_t div_noport; /* failed due to no bound socket */ + uint64_t div_outbound; /* re-injected as outbound */ + uint64_t div_inbound; /* re-injected as inbound */ +}; #endif /* _NETINET_IP_DIVERT_H_ */ diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -66,6 +66,7 @@ #include #include #include +#include #ifdef INET6 #include #include @@ -110,8 +111,19 @@ * written in the sin_port (ipfw does not allow a rule #0, so sin_port=0 * will apply the entire ruleset to the packet). */ +static SYSCTL_NODE(_net_inet, OID_AUTO, divert, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "divert(4)"); + +VNET_PCPUSTAT_DEFINE_STATIC(struct divstat, divstat); +VNET_PCPUSTAT_SYSINIT(divstat); +#ifdef VIMAGE +VNET_PCPUSTAT_SYSUNINIT(divstat); +#endif +SYSCTL_VNET_PCPUSTAT(_net_inet_divert, OID_AUTO, stats, struct divstat, + divstat, "divert(4) socket statistics"); +#define DIVSTAT_INC(name) \ + VNET_PCPUSTAT_ADD(struct divstat, divstat, div_ ## name, 1) -/* Internal variables. */ VNET_DEFINE_STATIC(struct inpcbinfo, divcbinfo); #define V_divcbinfo VNET(divcbinfo) @@ -273,17 +285,18 @@ (struct sockaddr *)&divsrc, m, NULL) == 0) { soroverflow_locked(sa); sa = NULL; /* force mbuf reclaim below */ - } else + } else { sorwakeup_locked(sa); + DIVSTAT_INC(diverted); + } /* XXX why does only one socket match? */ INP_RUNLOCK(inp); break; } if (sa == NULL) { m_freem(m); - KMOD_IPSTAT_INC(ips_noproto); - KMOD_IPSTAT_DEC(ips_delivered); - } + DIVSTAT_INC(noport); + } } /* @@ -310,7 +323,6 @@ /* Packet must have a header (but that's about it) */ if (m->m_len < sizeof (struct ip) && (m = m_pullup(m, sizeof (struct ip))) == NULL) { - KMOD_IPSTAT_INC(ips_toosmall); m_freem(m); return (EINVAL); } @@ -447,9 +459,6 @@ #endif } - /* Send packet to output processing */ - KMOD_IPSTAT_INC(ips_rawout); /* XXX */ - #ifdef MAC mac_inpcb_create_mbuf(inp, m); #endif @@ -498,6 +507,8 @@ break; #endif } + if (error == 0) + DIVSTAT_INC(outbound); if (options != NULL) m_freem(options); @@ -549,10 +560,12 @@ else if (in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) m->m_flags |= M_BCAST; netisr_queue_src(NETISR_IP, (uintptr_t)so, m); + DIVSTAT_INC(inbound); break; #ifdef INET6 case AF_INET6: netisr_queue_src(NETISR_IPV6, (uintptr_t)so, m); + DIVSTAT_INC(inbound); break; #endif default: @@ -704,16 +717,9 @@ return (error); } - -#ifdef SYSCTL_NODE -static SYSCTL_NODE(_net_inet, IPPROTO_DIVERT, divert, - CTLFLAG_RW | CTLFLAG_MPSAFE, 0, - "IPDIVERT"); SYSCTL_PROC(_net_inet_divert, OID_AUTO, pcblist, - CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, - NULL, 0, div_pcblist, "S,xinpcb", - "List of active divert sockets"); -#endif + CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, div_pcblist, + "S,xinpcb", "List of active divert sockets"); static struct protosw div_protosw = { .pr_type = SOCK_RAW, diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -1432,6 +1433,36 @@ xo_close_container(name); } +/* + * Dump divert(4) statistics structure. + */ +void +divert_stats(u_long off, const char *name, int af1 __unused, int proto __unused) +{ + struct divstat divstat; + + if (fetch_stats("net.inet.divert.stats", off, &divstat, + sizeof(divstat), kread_counters) != 0) + return; + + xo_open_container(name); + xo_emit("{T:/%s}:\n", name); + +#define p(f, m) if (divstat.f || sflag <= 1) \ + xo_emit(m, (uintmax_t)divstat.f, plural(divstat.f)) + + p(div_diverted, "\t{:diverted-packets/%ju} " + "{N:/packet%s successfully diverted to userland}\n"); + p(div_noport, "\t{:noport-fails/%ju} " + "{N:/packet%s failed to divert due to no socket bound at port}\n"); + p(div_outbound, "\t{:outbound-packets/%ju} " + "{N:/packet%s successfully re-injected as outbound}\n"); + p(div_inbound, "\t{:inbound-packets/%ju} " + "{N:/packet%s successfully re-injected as inbound}\n"); +#undef p + xo_close_container(name); +} + #ifdef INET /* * Pretty print an Internet address (net address + port). diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c --- a/usr.bin/netstat/main.c +++ b/usr.bin/netstat/main.c @@ -101,7 +101,7 @@ NULL, NULL, "sdp", 1, IPPROTO_TCP }, #endif { N_DIVCBINFO, -1, 1, protopr, - NULL, NULL, "divert", 1, 0 }, + divert_stats, NULL, "divert", 1, 0 }, { N_RIPCBINFO, N_IPSTAT, 1, protopr, ip_stats, NULL, "ip", 1, IPPROTO_RAW }, { N_RIPCBINFO, N_ICMPSTAT, 1, protopr, diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h --- a/usr.bin/netstat/netstat.h +++ b/usr.bin/netstat/netstat.h @@ -92,6 +92,7 @@ void sctp_stats(u_long, const char *, int, int); #endif void arp_stats(u_long, const char *, int, int); +void divert_stats(u_long, const char *, int, int); void ip_stats(u_long, const char *, int, int); void icmp_stats(u_long, const char *, int, int); void igmp_stats(u_long, const char *, int, int);