git: 5246f8fade5b - main - if_ovpn: pass control packets through the socket

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 15 Nov 2022 09:38:46 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=5246f8fade5bdb72d6e61f333d8b815b456a4ef5

commit 5246f8fade5bdb72d6e61f333d8b815b456a4ef5
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-11-09 13:48:05 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-11-15 09:01:18 +0000

    if_ovpn: pass control packets through the socket
    
    Rather than passing control packets through the ioctl interface allow
    them to pass through the normal UDP socket flow.
    This simplifies both kernel and userspace, and matches the approach
    taken (or the one that will be taken) on the Linux side of things.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D37317
---
 sys/net/if_ovpn.c | 102 ++++++++++++------------------------------------------
 1 file changed, 23 insertions(+), 79 deletions(-)

diff --git a/sys/net/if_ovpn.c b/sys/net/if_ovpn.c
index 6ce5d07dc230..276927275a2b 100644
--- a/sys/net/if_ovpn.c
+++ b/sys/net/if_ovpn.c
@@ -170,8 +170,7 @@ struct ovpn_softc {
 	int			 peercount;
 	struct ovpn_kpeer	*peers[OVPN_MAX_PEERS]; /* XXX Hard limit for now? */
 
-	/* Pending packets */
-	struct buf_ring		*rxring;
+	/* Pending notification */
 	struct buf_ring		*notifring;
 
 	counter_u64_t 		 counters[OVPN_COUNTER_SIZE];
@@ -1275,8 +1274,7 @@ ovpn_poll_pkt(struct ovpn_softc *sc, nvlist_t **onvl)
 	if (nvl == NULL)
 		return (ENOMEM);
 
-	nvlist_add_number(nvl, "pending",
-	    buf_ring_count(sc->rxring) + buf_ring_count(sc->notifring));
+	nvlist_add_number(nvl, "pending", buf_ring_count(sc->notifring));
 
 	*onvl = nvl;
 
@@ -1287,57 +1285,24 @@ static int
 opvn_get_pkt(struct ovpn_softc *sc, nvlist_t **onvl)
 {
 	struct ovpn_notification *n;
-	struct ovpn_wire_header *ohdr;
-	struct mbuf *m;
-	uint8_t *buf;
 	nvlist_t *nvl;
-	uint32_t peerid;
-	u_int mlength;
 
 	/* Check if we have notifications pending. */
 	n = buf_ring_dequeue_mc(sc->notifring);
-	if (n != NULL) {
-		nvl = nvlist_create(0);
-		if (nvl == NULL) {
-			free(n, M_OVPN);
-			return (ENOMEM);
-		}
-		nvlist_add_number(nvl, "peerid", n->peerid);
-		nvlist_add_number(nvl, "notification", n->type);
-		free(n, M_OVPN);
-
-		*onvl = nvl;
-
-		return (0);
-	}
-
-	/* Queued packet. */
-	m = buf_ring_dequeue_mc(sc->rxring);
-	if (m == NULL)
+	if (n == NULL)
 		return (ENOENT);
 
-	mlength = m_length(m, NULL);
-	buf = malloc(mlength, M_NVLIST, M_WAITOK);
-	m_copydata(m, 0, mlength, buf);
-	ohdr = (struct ovpn_wire_header *)buf;
-	peerid = ntohl(ohdr->opcode) & 0x00ffffff;
-
 	nvl = nvlist_create(0);
 	if (nvl == NULL) {
-		OVPN_COUNTER_ADD(sc, lost_ctrl_pkts_in, 1);
-		m_freem(m);
-		free(buf, M_NVLIST);
+		free(n, M_OVPN);
 		return (ENOMEM);
 	}
-
-	nvlist_move_binary(nvl, "packet", buf, mlength);
-	buf = NULL;
-	nvlist_add_number(nvl, "peerid", peerid);
+	nvlist_add_number(nvl, "peerid", n->peerid);
+	nvlist_add_number(nvl, "notification", n->type);
+	free(n, M_OVPN);
 
 	*onvl = nvl;
 
-	m_freem(m);
-
 	return (0);
 }
 
@@ -2086,22 +2051,6 @@ ovpn_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
 	return (ovpn_transmit_to_peer(ifp, m, peer, _ovpn_lock_trackerp));
 }
 
-static void
-ovpn_rcv_ctrl(struct ovpn_softc *sc, struct mbuf *m, int off)
-{
-	/* Lop off the IP and UDP headers */
-	m_adj_decap(m, off);
-
-	/* Keep in the local ring until userspace fetches it. */
-	if (buf_ring_enqueue(sc->rxring, m) != 0) {
-		OVPN_COUNTER_ADD(sc, lost_ctrl_pkts_in, 1);
-		m_freem(m);
-		return;
-	}
-
-	OVPN_COUNTER_ADD(sc, received_ctrl_pkts, 1);
-}
-
 static bool
 ovpn_check_replay(struct ovpn_kkey_dir *key, uint32_t seq)
 {
@@ -2174,6 +2123,7 @@ ovpn_udp_input(struct mbuf *m, int off, struct inpcb *inp,
     const struct sockaddr *sa, void *ctx)
 {
 	struct ovpn_softc *sc = ctx;
+	struct ovpn_wire_header tmphdr;
 	struct ovpn_wire_header *ohdr;
 	struct udphdr *uhdr;
 	struct ovpn_kkey *key;
@@ -2199,16 +2149,27 @@ ovpn_udp_input(struct mbuf *m, int off, struct inpcb *inp,
 		return (false);
 	}
 
+	if (m_length(m, NULL) < (off + sizeof(*uhdr) + ohdrlen)) {
+		/* Short packet. */
+		OVPN_RUNLOCK(sc);
+		return (false);
+	}
+
+	m_copydata(m, off + sizeof(*uhdr), ohdrlen, (caddr_t)&tmphdr);
+
+	op = ntohl(tmphdr.opcode) >> 24 >> OVPN_OP_SHIFT;
+	if (op != OVPN_OP_DATA_V2) {
+		/* Control packet? */
+		OVPN_RUNLOCK(sc);
+		return (false);
+	}
+
 	m = m_pullup(m, off + sizeof(*uhdr) + ohdrlen);
 	if (m == NULL) {
 		OVPN_RUNLOCK(sc);
 		OVPN_COUNTER_ADD(sc, nomem_data_pkts_in, 1);
 		return (true);
 	}
-	uhdr = mtodo(m, off);
-	ohdr = mtodo(m, off + sizeof(*uhdr));
-
-	op = ntohl(ohdr->opcode) >> 24 >> OVPN_OP_SHIFT;
 
 	/*
 	 * Simplify things by getting rid of the preceding headers, we don't
@@ -2219,15 +2180,6 @@ ovpn_udp_input(struct mbuf *m, int off, struct inpcb *inp,
 	uhdr = mtodo(m, 0);
 	ohdr = mtodo(m, sizeof(*uhdr));
 
-	if (op != OVPN_OP_DATA_V2) {
-		OVPN_RUNLOCK(sc);
-		ovpn_rcv_ctrl(sc, m, sizeof(struct udphdr));
-		INP_WLOCK(inp);
-		udp_notify(inp, EAGAIN);
-		INP_WUNLOCK(inp);
-		return (true);
-	}
-
 	key = ovpn_find_key(sc, peer, ohdr);
 	if (key == NULL || key->decrypt == NULL) {
 		OVPN_RUNLOCK(sc);
@@ -2313,16 +2265,10 @@ ovpn_qflush(struct ifnet *ifp __unused)
 static void
 ovpn_flush_rxring(struct ovpn_softc *sc)
 {
-	struct mbuf *m;
 	struct ovpn_notification *n;
 
 	OVPN_WASSERT(sc);
 
-	while (! buf_ring_empty(sc->rxring)) {
-		m = buf_ring_dequeue_sc(sc->rxring);
-		m_freem(m);
-	}
-
 	while (! buf_ring_empty(sc->notifring)) {
 		n = buf_ring_dequeue_sc(sc->notifring);
 		free(n, M_OVPN);
@@ -2409,7 +2355,6 @@ ovpn_clone_create(struct if_clone *ifc, char *name, size_t len,
 	rm_init_flags(&sc->lock, "if_ovpn_lock", RM_RECURSE);
 	sc->refcount = 0;
 
-	sc->rxring = buf_ring_alloc(32, M_OVPN, M_WAITOK, NULL);
 	sc->notifring = buf_ring_alloc(32, M_OVPN, M_WAITOK, NULL);
 
 	COUNTER_ARRAY_ALLOC(sc->counters, OVPN_COUNTER_SIZE, M_WAITOK);
@@ -2486,7 +2431,6 @@ ovpn_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
 	} while (i < OVPN_MAX_PEERS);
 
 	ovpn_flush_rxring(sc);
-	buf_ring_free(sc->rxring, M_OVPN);
 	buf_ring_free(sc->notifring, M_OVPN);
 
 	OVPN_WUNLOCK(sc);