[Openvpn-devel,net] ovpn: drop packets looping back into their own TCP transport socket

Message ID 20261028220430.968756-1-a@unstable.cc
State Changes Requested
Headers
Series [Openvpn-devel,net] ovpn: drop packets looping back into their own TCP transport socket |

Commit Message

Antonio Quartulli Oct. 28, 2026, 10:04 p.m. UTC
  From: Antonio Quartulli <antonio@openvpn.net>

If the outer packets of a TCP peer are routed into the ovpn interface,
ovpn_net_xmit() selects that same peer and ovpn_tcp_send_skb() tries to
take sk->sk_lock.slock, which the TCP transmit path already holds.
The CPU deadlocks.

Drop such packets in ovpn_net_xmit().

Fixes: 11851cbd60ea ("ovpn: implement TCP transport")
Cc: stable@vger.kernel.org
Reported-by: Bernhard Schmidt <berni@birkenwald.de>
Closes: https://lore.kernel.org/openvpn-devel/auJj5Ld6uMjPb2eS@fliwatuet.svr02.mucip.net/
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
 drivers/net/ovpn/io.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
  

Patch

diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index f14eb4e6c46a..57cdea45dce7 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -349,6 +349,31 @@  static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb,
 	ovpn_peer_put(peer);
 }
 
+/**
+ * ovpn_skb_is_looping - check if skb comes from the peer's own socket
+ * @peer: the peer the skb is about to be sent to
+ * @skb: the packet to check
+ *
+ * Return: true if @skb was generated by the transport socket of @peer,
+ * false otherwise
+ */
+static bool ovpn_skb_is_looping(struct ovpn_peer *peer,
+				const struct sk_buff *skb)
+{
+	struct ovpn_socket *sock;
+	bool ret;
+
+	if (!skb->sk)
+		return false;
+
+	rcu_read_lock();
+	sock = rcu_dereference(peer->sock);
+	ret = sock && sock->sk == skb->sk;
+	rcu_read_unlock();
+
+	return ret;
+}
+
 /* Send user data to the network
  */
 netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -386,6 +411,13 @@  netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
 		}
 		goto drop_no_peer;
 	}
+
+	if (unlikely(ovpn_skb_is_looping(peer, skb))) {
+		net_warn_ratelimited("%s: recursive routing detected, dropping TCP packet to peer %u\n",
+				     netdev_name(ovpn->dev), peer->id);
+		goto drop;
+	}
+
 	/* dst was needed for peer selection - it can now be dropped */
 	skb_dst_drop(skb);