git: 2b1c72171e3e - main - divert(4): provide statistics

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Tue, 30 Aug 2022 22:49:26 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=2b1c72171e3e44b26793013f04fb98f0b975cbad

commit 2b1c72171e3e44b26793013f04fb98f0b975cbad
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2022-08-30 22:09:21 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2022-08-30 22:09:21 +0000

    divert(4): provide statistics
    
    Instead of incrementing pretty random counters in the IP statistics,
    create divert socket statistics structure.  Export via netstat(1).
    
    Differential revision:  https://reviews.freebsd.org/D36381
---
 sys/netinet/ip_divert.c   | 42 ++++++++++++++++++++++++------------------
 sys/netinet/ip_divert.h   | 10 ++++++++--
 usr.bin/netstat/inet.c    | 31 +++++++++++++++++++++++++++++++
 usr.bin/netstat/main.c    |  2 +-
 usr.bin/netstat/netstat.h |  1 +
 5 files changed, 65 insertions(+), 21 deletions(-)

diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
index b09d7e1dda7a..0cecbbd0d15d 100644
--- a/sys/netinet/ip_divert.c
+++ b/sys/netinet/ip_divert.c
@@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in_var.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
+#include <netinet/ip_divert.h>
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
@@ -110,8 +111,19 @@ __FBSDID("$FreeBSD$");
  * 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 @@ divert_packet(struct mbuf *m, bool incoming)
 		    (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 @@ div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
 	/* 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 @@ div_output_outbound(int family, struct socket *so, struct mbuf *m)
 #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 @@ div_output_outbound(int family, struct socket *so, struct mbuf *m)
 		break;
 #endif
 	}
+	if (error == 0)
+		DIVSTAT_INC(outbound);
 	if (options != NULL)
 		m_freem(options);
 
@@ -549,10 +560,12 @@ div_output_inbound(int family, struct socket *so, struct mbuf *m,
 		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 @@ div_pcblist(SYSCTL_HANDLER_ARGS)
 
 	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/sys/netinet/ip_divert.h b/sys/netinet/ip_divert.h
index a426afeca217..dd0568bbfb07 100644
--- 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 <sys/types.h>
 
 /*
- * 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/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
index e848874d1695..468bd9aba3e5 100644
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp_var.h>
 #include <netinet/igmp_var.h>
+#include <netinet/ip_divert.h>
 #include <netinet/ip_var.h>
 #include <netinet/pim_var.h>
 #include <netinet/tcp.h>
@@ -1432,6 +1433,36 @@ pim_stats(u_long off __unused, const char *name, int af1 __unused,
 	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
index d1b069f38f0c..078d8cca3c61 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -101,7 +101,7 @@ static struct protox {
 	 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
index eb5b77eade0e..32dbbc9d5576 100644
--- a/usr.bin/netstat/netstat.h
+++ b/usr.bin/netstat/netstat.h
@@ -92,6 +92,7 @@ void	sctp_protopr(u_long, const char *, int, int);
 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);