From nobody Wed Aug 13 13:37:46 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4c28ZL5G7lz64t7L; Wed, 13 Aug 2025 13:37:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4c28ZL2wt2z3St4; Wed, 13 Aug 2025 13:37:46 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1755092266; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=rGoWmQonPj4Gfzg9/0vJVcpDzTGElFgotNcqbMXjo3Q=; b=hJW6f+3GHcDnBFC6ipsKHo5HjPvfNUP2T0xJibGSvGIA927voqHpDokCtltSmenol5HgdU Pyx+njjS8JziFSktsHyMb4ops+Xya71JQ7QXnMWmv52nbyAQRoU8pUdhyHBpPpAFzFr4Pq y6zzCUjltKikG9gDyyb53TZYTYLD8M2rp+1IYWFXcT0rB2+TA8Iu9OxiNP2qC3bSodxSj2 bMluGERQS26HxJ2UvH3bKoWgtp2oSLPXt857zLjtLXv38gS48yPaCxG69+B1fGOazMK59u HFdpMeTN414tZOJmAHmDESxX6iQ2atOjvWVmQmoDWzULVU/aFVR16k+luYWlhQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1755092266; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=rGoWmQonPj4Gfzg9/0vJVcpDzTGElFgotNcqbMXjo3Q=; b=HjoqNvklAjPptsT4jJF0mU7lo0U3gq6V4r2zvRCmBeKzh4g/DDn2pNCJSN3Ar3kZzGLd9d /ZxibtFuQbnChGcaH8OcxlmAw+YWuyX1Xitjanah0xpFEZd6CmLdaW8H+kU9JAOLI9pLUQ UKDIXzkND1wn8XjsPwYd1gtQQjks2wJIM9h0nwdSno+Yk7HHYq43IfHTa0RHG8yIyrY2sY zgDCcltM5ZXCHNAQ4RNvsoCEL8UOMjTS/PUgUyYn/ZEvM1kk3KHoUkvucbnO37ZpyHN4z5 EGlUAvTSQp5y3XMB7QqomK7otYx9guKYyER5H6+LOAzKyHZgQz34de7n163v9w== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1755092266; a=rsa-sha256; cv=none; b=DOlZFPrASA6TjShWZY1Am1nEGJYQkYbof9lcYpOJq8KJtZu1rDCHEEOiT/1Il1/EgrB4OZ /jWqv0/oSKF4NxeoxHLliHoF5dtRMfeJHvpk3z17ssv+HLaWe7HFl1xUwsM1tX8rARUJCX Vwu/PMtkNhzgRxe/6q57rrV94+4R977pbtzcL/rsD9H+I0Y/3n9A5dsvhGhFjCyS9BrRex P7a+efGZe0afMSD0QQB2OKt/rgu2NjQ0t79IozlBjhM6TQ/8OrGztdJeBHy1VkQ3eyEdNk lMRV8UjuB45KWxcPJSQDC3FuPCcGxII8G+yBN7AXQ+ZSlZm5gWOZnDJ/ezxLWg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4c28ZL210sz1NgF; Wed, 13 Aug 2025 13:37:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 57DDbk1R084616; Wed, 13 Aug 2025 13:37:46 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 57DDbkBj084613; Wed, 13 Aug 2025 13:37:46 GMT (envelope-from git) Date: Wed, 13 Aug 2025 13:37:46 GMT Message-Id: <202508131337.57DDbkBj084613@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: fc387ed68f3c - stable/14 - if_ovpn: support floating clients List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: fc387ed68f3c7b0b8da9bab13492b7bbafecb5bf Auto-Submitted: auto-generated The branch stable/14 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=fc387ed68f3c7b0b8da9bab13492b7bbafecb5bf commit fc387ed68f3c7b0b8da9bab13492b7bbafecb5bf Author: Kristof Provost AuthorDate: 2025-07-22 12:50:52 +0000 Commit: Kristof Provost CommitDate: 2025-08-13 13:24:52 +0000 if_ovpn: support floating clients If a client changes its IP address notify userspace of this. The UDP filtering function supplies the remote IP address, so we check if the address changed there. If so, we tag the packet with the new address. Once the packet is decrypted (and as part of that, has had its signature checked) we can commit to the address change. Take the write lock and notify userspace of the change. Reviewed by: markj MFC after: 3 weeks Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D51468 (cherry picked from commit 9c52600a5a150117b4396df3b868cf2516e1674c) --- sys/net/if_ovpn.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++- sys/net/if_ovpn.h | 1 + sys/sys/mbuf.h | 1 + 3 files changed, 194 insertions(+), 1 deletion(-) diff --git a/sys/net/if_ovpn.c b/sys/net/if_ovpn.c index 54d7cbd22c81..f33218798eb4 100644 --- a/sys/net/if_ovpn.c +++ b/sys/net/if_ovpn.c @@ -133,6 +133,9 @@ struct ovpn_notification { /* Delete notification */ enum ovpn_del_reason del_reason; struct ovpn_peer_counters counters; + + /* Float notification */ + struct sockaddr_storage address; }; struct ovpn_softc; @@ -197,6 +200,10 @@ struct ovpn_softc { struct epoch_context epoch_ctx; }; +struct ovpn_mtag { + struct sockaddr_storage addr; +}; + static struct ovpn_kpeer *ovpn_find_peer(struct ovpn_softc *, uint32_t); static bool ovpn_udp_input(struct mbuf *, int, struct inpcb *, const struct sockaddr *, void *); @@ -208,6 +215,8 @@ static void ovpn_free_kkey_dir(struct ovpn_kkey_dir *); static bool ovpn_check_replay(struct ovpn_kkey_dir *, uint32_t); static int ovpn_peer_compare(const struct ovpn_kpeer *, const struct ovpn_kpeer *); +static bool ovpn_sockaddr_compare(const struct sockaddr *, + const struct sockaddr *); static RB_PROTOTYPE(ovpn_kpeers, ovpn_kpeer, tree, ovpn_peer_compare); static RB_GENERATE(ovpn_kpeers, ovpn_kpeer, tree, ovpn_peer_compare); @@ -285,6 +294,43 @@ ovpn_peer_compare(const struct ovpn_kpeer *a, const struct ovpn_kpeer *b) return (a->peerid - b->peerid); } +static bool +ovpn_sockaddr_compare(const struct sockaddr *a, + const struct sockaddr *b) +{ + if (a->sa_family != b->sa_family) + return (false); + MPASS(a->sa_len == b->sa_len); + + switch (a->sa_family) { + case AF_INET: { + const struct sockaddr_in *a4, *b4; + + a4 = (const struct sockaddr_in *)a; + b4 = (const struct sockaddr_in *)b; + + if (a4->sin_port != b4->sin_port) + return (false); + + return (a4->sin_addr.s_addr == b4->sin_addr.s_addr); + } + case AF_INET6: { + const struct sockaddr_in6 *a6, *b6; + + a6 = (const struct sockaddr_in6 *)a; + b6 = (const struct sockaddr_in6 *)b; + + if (a6->sin6_port != b6->sin6_port) + return (false); + + return (memcmp(&a6->sin6_addr, &b6->sin6_addr, + sizeof(a6->sin6_addr)) == 0); + } + default: + panic("Unknown address family %d", a->sa_family); + } +} + static struct ovpn_kpeer * ovpn_find_peer(struct ovpn_softc *sc, uint32_t peerid) { @@ -427,6 +473,44 @@ ovpn_rele_so(struct ovpn_softc *sc, struct ovpn_kpeer *peer) sc->so = NULL; } +static int +ovpn_add_sockaddr(nvlist_t *parent, const char *name, const struct sockaddr *s) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (ENOMEM); + + nvlist_add_number(nvl, "af", s->sa_family); + + switch (s->sa_family) { + case AF_INET: { + const struct sockaddr_in *s4 = (const struct sockaddr_in *)s; + + nvlist_add_number(nvl, "port", s4->sin_port); + nvlist_add_binary(nvl, "address", &s4->sin_addr, + sizeof(s4->sin_addr)); + break; + } + case AF_INET6: { + const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)s; + + nvlist_add_number(nvl, "port", s6->sin6_port); + nvlist_add_binary(nvl, "address", &s6->sin6_addr, + sizeof(s6->sin6_addr)); + break; + } + default: + nvlist_destroy(nvl); + return (EINVAL); + } + + nvlist_move_nvlist(parent, name, nvl); + + return (0); +} + static void ovpn_notify_del_peer(struct ovpn_softc *sc, struct ovpn_kpeer *peer) { @@ -479,6 +563,33 @@ ovpn_notify_key_rotation(struct ovpn_softc *sc, struct ovpn_kpeer *peer) } } +static int +ovpn_notify_float(struct ovpn_softc *sc, uint32_t peerid, + const struct sockaddr_storage *remote) +{ + struct ovpn_notification *n; + + n = malloc(sizeof(*n), M_OVPN, M_NOWAIT | M_ZERO); + if (n == NULL) + return (ENOMEM); + + n->peerid = peerid; + n->type = OVPN_NOTIF_FLOAT; + memcpy(&n->address, remote, sizeof(n->address)); + + if (buf_ring_enqueue(sc->notifring, n) != 0) { + free(n, M_OVPN); + return (ENOMEM); + } else if (sc->so != NULL) { + /* Wake up userspace */ + sc->so->so_error = EAGAIN; + sorwakeup(sc->so); + sowwakeup(sc->so); + } + + return (0); +} + static void ovpn_peer_release_ref(struct ovpn_kpeer *peer, bool locked) { @@ -1416,12 +1527,36 @@ opvn_get_pkt(struct ovpn_softc *sc, nvlist_t **onvl) } nvlist_add_number(nvl, "peerid", n->peerid); nvlist_add_number(nvl, "notification", n->type); - if (n->type == OVPN_NOTIF_DEL_PEER) { + switch (n->type) { + case OVPN_NOTIF_DEL_PEER: { nvlist_add_number(nvl, "del_reason", n->del_reason); /* No error handling, because we want to send the notification * even if we can't attach the counters. */ ovpn_notif_add_counters(nvl, n); + break; + } + case OVPN_NOTIF_FLOAT: { + int ret; + + ret = ovpn_add_sockaddr(nvl, "address", + (struct sockaddr *)&n->address); + + if (ret) { + /* + * Try to re-enqueue the notification. Maybe we'll + * have better luck next time. No error handling, + * because if we fail to re-enqueue there's nothing we can do. + */ + (void)ovpn_notify_float(sc, n->peerid, &n->address); + nvlist_destroy(nvl); + free(n, M_OVPN); + return (ret); + } + break; + } + default: + break; } free(n, M_OVPN); @@ -1577,6 +1712,7 @@ ovpn_finish_rx(struct ovpn_softc *sc, struct mbuf *m, struct rm_priotracker *_ovpn_lock_trackerp) { uint32_t af; + struct m_tag *mtag; OVPN_RASSERT(sc); NET_EPOCH_ASSERT(); @@ -1595,6 +1731,38 @@ ovpn_finish_rx(struct ovpn_softc *sc, struct mbuf *m, OVPN_RUNLOCK(sc); + /* Check if the peer changed to a new source address. */ + mtag = m_tag_find(m, PACKET_TAG_OVPN, NULL); + if (mtag != NULL) { + struct ovpn_mtag *ot = (struct ovpn_mtag *)(mtag + 1); + + OVPN_WLOCK(sc); + + /* + * Check the address against the peer's remote again, because we may race + * against ourselves (i.e. we may have tagged multiple packets to indicate we + * floated). + */ + if (ovpn_sockaddr_compare((struct sockaddr *)&ot->addr, + (struct sockaddr *)&peer->remote)) { + OVPN_WUNLOCK(sc); + goto skip_float; + } + + /* And notify userspace. */ + if (ovpn_notify_float(sc, peer->peerid, &ot->addr) == 0) { + /* + * Update the 'remote' for this peer, but only if + * we've actually enqueued the notification. + * Otherwise we can try again later. + */ + memcpy(&peer->remote, &ot->addr, sizeof(peer->remote)); + } + + OVPN_WUNLOCK(sc); + } + +skip_float: OVPN_COUNTER_ADD(sc, received_data_pkts, 1); OVPN_COUNTER_ADD(sc, tunnel_bytes_received, m->m_pkthdr.len); OVPN_PEER_COUNTER_ADD(peer, pkt_in, 1); @@ -2357,6 +2525,29 @@ ovpn_udp_input(struct mbuf *m, int off, struct inpcb *inp, return (true); } + /* + * If we got this from a different address than we expected tag the packet. + * We'll deal with notifiying userspace later, after we've decrypted and + * verified. + */ + if (! ovpn_sockaddr_compare((struct sockaddr *)&peer->remote, sa)) { + struct m_tag *mt; + struct ovpn_mtag *ot; + + MPASS(sa->sa_len <= sizeof(ot->addr)); + mt = m_tag_get(PACKET_TAG_OVPN, sizeof(*ot), M_NOWAIT); + /* + * If we fail to allocate here we'll just try again on the next + * packet. + */ + if (mt != NULL) { + ot = (struct ovpn_mtag *)(mt + 1); + memcpy(&ot->addr, sa, sa->sa_len); + + m_tag_prepend(m, mt); + } + } + if (key->decrypt->cipher == OVPN_CIPHER_ALG_NONE) { /* Now remove the outer headers */ m_adj_decap(m, sizeof(struct udphdr) + ohdrlen); diff --git a/sys/net/if_ovpn.h b/sys/net/if_ovpn.h index 2d6b8c1e7eff..2a24c35788a9 100644 --- a/sys/net/if_ovpn.h +++ b/sys/net/if_ovpn.h @@ -37,6 +37,7 @@ enum ovpn_notif_type { OVPN_NOTIF_DEL_PEER, OVPN_NOTIF_ROTATE_KEY, + OVPN_NOTIF_FLOAT, }; enum ovpn_del_reason { diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 65a328cb52a1..41da07fa77f9 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1390,6 +1390,7 @@ extern bool mb_use_ext_pgs; /* Use ext_pgs for sendfile */ #define PACKET_TAG_IPSEC_NAT_T_PORTS 29 /* two uint16_t */ #define PACKET_TAG_ND_OUTGOING 30 /* ND outgoing */ #define PACKET_TAG_PF_REASSEMBLED 31 +#define PACKET_TAG_OVPN 34 /* if_ovpn */ /* Specific cookies and tags. */