[PATCH net-next 1/4] ovpn: properly deconfigure UDP-tunnel
flat view
STALE517d
From: Antonio Quartulli <antonio@openvpn.net>
Date: 2025-05-27 13:52:54
Subsystem:
networking drivers, networking [general], networking [ipv4/ipv6], openvpn data channel offload, the rest, user datagram protocol (udp) · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Antonio Quartulli, Linus Torvalds, Willem de Bruijn
When deconfiguring a UDP-tunnel from a socket, we cannot
call setup_udp_tunnel_sock(), as it is expected to be invoked
only during setup.
Implement a new function named cleanup_udp_tunnel_sock(),
that reverts was what done during setup, and invoke it.
This new function takes care of reverting everything that
was done by setup_udp_tunnel_sock() (i.e. unsetting various
members and cleaning up the encap state in the kernel).
Note that cleanup_udp_tunnel_sock() takes 'struct sock'
as argument in preparation for a follow up patch, where
'struct ovpn' won't hold any reference to any 'struct socket'
any more, but rather to its 'struct sock' member only.
To properly implement cleanup_udp_tunnel_sock(), two extra
items were required:
1) implement udpv6_encap_disable() in order to
allow udpv6_encap_needed_key to be decreased outside of
ipv6.ko (similarly to udp_encap_disable() for IPv4).
2) avoid race conditions with udp_destroy_socket() by
atomically testing-and-clearing the ENCAP_ENABLED bit
on the socket before decreasing udp_encap_needed_key.
This way we prevent accidental double decreses.
Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Cc: Simon Horman <horms@kernel.org>
Cc: David Ahern <dsahern@kernel.org>
Cc: Sabrina Dubroca <sd@queasysnail.net>
Cc: Oleksandr Natalenko <redacted>
Fixes: ab66abbc769b ("ovpn: implement basic RX path (UDP)")
Reported-by: Paolo Abeni <pabeni@redhat.com>
Closes: https://lore.kernel.org/netdev/1a47ce02-fd42-4761-8697-f3f315011cc6@redhat.com (local)
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/udp.c | 5 +----
include/linux/udp.h | 2 ++
include/net/ipv6_stubs.h | 1 +
include/net/udp.h | 1 +
include/net/udp_tunnel.h | 13 +++++++++++++
net/ipv4/udp.c | 2 +-
net/ipv4/udp_tunnel_core.c | 22 ++++++++++++++++++++++
net/ipv6/af_inet6.c | 1 +
net/ipv6/udp.c | 8 +++++++-
9 files changed, 49 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c
index aef8c0406ec9..c8a81c4d6489 100644
--- a/drivers/net/ovpn/udp.c
+++ b/drivers/net/ovpn/udp.c@@ -442,8 +442,5 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, */ void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock) { - struct udp_tunnel_sock_cfg cfg = { }; - - setup_udp_tunnel_sock(sock_net(ovpn_sock->sock->sk), ovpn_sock->sock, - &cfg); + cleanup_udp_tunnel_sock(ovpn_sock->sock->sk); }
diff --git a/include/linux/udp.h b/include/linux/udp.h
index 4e1a672af4c5..97151f25953d 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h@@ -116,6 +116,8 @@ struct udp_sock { set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) #define udp_test_and_set_bit(nr, sk) \ test_and_set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) +#define udp_test_and_clear_bit(nr, sk) \ + test_and_clear_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) #define udp_clear_bit(nr, sk) \ clear_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) #define udp_assign_bit(nr, sk, val) \
diff --git a/include/net/ipv6_stubs.h b/include/net/ipv6_stubs.h
index 8a3465c8c2c5..2a57df87d11b 100644
--- a/include/net/ipv6_stubs.h
+++ b/include/net/ipv6_stubs.h@@ -55,6 +55,7 @@ struct ipv6_stub { struct nl_info *info); void (*udpv6_encap_enable)(void); + void (*udpv6_encap_disable)(void); void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, bool router, bool solicited, bool override, bool inc_opt);
diff --git a/include/net/udp.h b/include/net/udp.h
index a772510b2aa5..e3d7b8622f59 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h@@ -580,6 +580,7 @@ void udp_encap_disable(void); #if IS_ENABLED(CONFIG_IPV6) DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key); void udpv6_encap_enable(void); +void udpv6_encap_disable(void); #endif static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 2df3b8344eb5..a14b171e7dd5 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h@@ -92,6 +92,7 @@ struct udp_tunnel_sock_cfg { /* Setup the given (UDP) sock to receive UDP encapsulated packets */ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *sock_cfg); +void cleanup_udp_tunnel_sock(struct sock *sk); /* -- List of parsable UDP tunnel types -- *
@@ -218,6 +219,18 @@ static inline void udp_tunnel_encap_enable(struct sock *sk) udp_encap_enable(); } +static inline void udp_tunnel_encap_disable(struct sock *sk) +{ + if (!udp_test_and_clear_bit(ENCAP_ENABLED, sk)) + return; + +#if IS_ENABLED(CONFIG_IPV6) + if (READ_ONCE(sk->sk_family) == PF_INET6) + ipv6_stub->udpv6_encap_disable(); +#endif + udp_encap_disable(); +} + #define UDP_TUNNEL_NIC_MAX_TABLES 4 enum udp_tunnel_nic_info_flags {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index dde52b8050b8..9ffc4e0b1644 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c@@ -2900,7 +2900,7 @@ void udp_destroy_sock(struct sock *sk) if (encap_destroy) encap_destroy(sk); } - if (udp_test_bit(ENCAP_ENABLED, sk)) { + if (udp_test_and_clear_bit(ENCAP_ENABLED, sk)) { static_branch_dec(&udp_encap_needed_key); udp_tunnel_cleanup_gro(sk); }
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index 2326548997d3..624b6afcf812 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c@@ -98,6 +98,28 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, } EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); +void cleanup_udp_tunnel_sock(struct sock *sk) +{ + /* Re-enable multicast loopback */ + inet_set_bit(MC_LOOP, sk); + + /* Disable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */ + inet_dec_convert_csum(sk); + + udp_sk(sk)->encap_type = 0; + udp_sk(sk)->encap_rcv = NULL; + udp_sk(sk)->encap_err_rcv = NULL; + udp_sk(sk)->encap_err_lookup = NULL; + udp_sk(sk)->encap_destroy = NULL; + udp_sk(sk)->gro_receive = NULL; + udp_sk(sk)->gro_complete = NULL; + + rcu_assign_sk_user_data(sk, NULL); + udp_tunnel_encap_disable(sk); + udp_tunnel_cleanup_gro(sk); +} +EXPORT_SYMBOL_GPL(cleanup_udp_tunnel_sock); + void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, unsigned short type) {
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index acaff1296783..f3745705a811 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c@@ -1049,6 +1049,7 @@ static const struct ipv6_stub ipv6_stub_impl = { .fib6_rt_update = fib6_rt_update, .ip6_del_rt = ip6_del_rt, .udpv6_encap_enable = udpv6_encap_enable, + .udpv6_encap_disable = udpv6_encap_disable, .ndisc_send_na = ndisc_send_na, #if IS_ENABLED(CONFIG_XFRM) .xfrm6_local_rxpmtu = xfrm6_local_rxpmtu,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 7317f8e053f1..a919458772d6 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c@@ -602,6 +602,12 @@ void udpv6_encap_enable(void) } EXPORT_SYMBOL(udpv6_encap_enable); +void udpv6_encap_disable(void) +{ + static_branch_dec(&udpv6_encap_needed_key); +} +EXPORT_SYMBOL(udpv6_encap_disable); + /* Handler for tunnels with arbitrary destination ports: no socket lookup, go * through error handlers in encapsulations looking for a match. */
@@ -1823,7 +1829,7 @@ void udpv6_destroy_sock(struct sock *sk) if (encap_destroy) encap_destroy(sk); } - if (udp_test_bit(ENCAP_ENABLED, sk)) { + if (udp_test_and_clear_bit(ENCAP_ENABLED, sk)) { static_branch_dec(&udpv6_encap_needed_key); udp_encap_disable(); udp_tunnel_cleanup_gro(sk);
--
2.49.0