[Openvpn-devel,net] ovpn: drop packets looping back into their own TCP transport socket
Commit Message
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(+)
@@ -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);