ovpn: fix UDP route cache and endpoint handling
Subsystems (2)
Ralf Lici
reviewerv3 cover ovpn: fix UDP route cache and endpoint handling
Hi,
This is v3 of a net series that fixes several related issues in ovpn's UDP endpoint and route-cache handling. This version was rebased on top of net and includes suggestions from Sabrina. The former patch 5/5 was dropped from this series and will be sent as RFC on netdev, where it is more appropriate since it involves changes broader than ovpn alone.
This series preserves IPv6 scope IDs supplied through netlink, handles unspecified source addresses correctly, invalidates cached routes when mutable socket route inputs change and avoids in-place updates of RCU-published peer binds.
Support for honoring UDP sockets bound to a device or local address will follow separately for net-next, after these route-cache fixes are available there.
Cheers,
Ralf Lici
Mandelbit Srl
---
Changes since v2 https://lore.kernel.org/openvpn-devel/[email protected]/
- Split former patch 4/5 into three separate patches. (Sabrina)
- Dropped former patch 5/5 from this series as it introduces IPv6
routing changes and needs to be discussed as an RFC on netdev.
(Sabrina)
Changes since v1 https://lore.kernel.org/openvpn-devel/[email protected]
- Patch 5/5: add memory barriers around IPv6 FIB generation reads to
avoid reordering on weakly ordered architectures (Sashiko).
Ralf Lici (6):
ovpn: preserve IPv6 scope id for netlink peer endpoints
ovpn: skip UDP source validation for unspecified addresses
ovpn: track UDP socket route key for peer dst cache
ovpn: validate peer state before caching UDP dst
ovpn: replace bind when learning local endpoint
ovpn: replace bind when clearing stale local source
drivers/net/ovpn/netlink.c | 6 ++ drivers/net/ovpn/peer.c | 44 ++++++----- drivers/net/ovpn/peer.h | 19 ++++- drivers/net/ovpn/udp.c | 193 +++++++++++++++++++++++++++++++++++++-------- 4 files changed, 206 insertions(+), 56 deletions(-)
base-commit: 1b78070aaef63512688aebfbc82365ef9d6660f1
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 1/6 ovpn: preserve IPv6 scope id for netlink peer endpoints
ovpn accepts OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID and reports bind->remote.in6.sin6_scope_id in peer dumps, but the netlink endpoint parser never copied the attribute into the sockaddr_in6 used to create or update the peer bind.
As a result, an IPv6 link-local remote endpoint configured through netlink loses its interface scope, unlike on the peer float path where ipv6_iface_scope_id populates the field. The UDPv6 output path then builds a flow with flowi6_oif set to zero and route lookup can fail or select the wrong interface.
Copy the scope id when parsing non-v4-mapped IPv6 remote endpoints. The existing precheck already rejects the scope-id attribute for IPv4 and v4-mapped IPv6 remotes.
---
No changes since v2 https://lore.kernel.org/openvpn-devel/87f7c1a6eea0005a067889e9b6f73fc6bd4f40e1.1785308184.git.ralf@mandelbit.com/
No changes since v1 https://lore.kernel.org/openvpn-devel/87f7c1a6eea0005a067889e9b6f73fc6bd4f40e1.1785253480.git.ralf@mandelbit.com/
drivers/net/ovpn/netlink.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/ovpn/netlink.c +6 -0
@@ -100,6 +100,8 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,struct sockaddr_in6 *sin6;struct sockaddr_in *sin;struct in6_addr *in6; +struct nlattr *scope; +u32 scope_id =0 ;__be16 port =0 ;__be32 *in;
@@ -114,6 +116,9 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs, }else if (attrs[OVPN_A_PEER_REMOTE_IPV6 ]) { ss->ss_family =AF_INET6 ; in6 =nla_data (attrs[OVPN_A_PEER_REMOTE_IPV6 ]); + scope = attrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID ]; +if (scope) + scope_id =nla_get_u32 (scope); }else {return false; }
@@ -126,6 +131,7 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,if (!ipv6_addr_v4mapped (in6)) { sin6 = (struct sockaddr_in6 *)ss; sin6->sin6_port = port; + sin6->sin6_scope_id = scope_id;memcpy (&sin6->sin6_addr, in6,sizeof (*in6));break ; }
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 2/6 ovpn: skip UDP source validation for unspecified addresses
In reply to Ralf Liciovpn validates the cached local UDP source address before reusing or refreshing a peer dst cache. This is only meaningful when a concrete source address is selected.
For IPv6, calling ipv6_chk_addr with :: checks whether the unspecified address itself is configured on the host. A peer may legitimately have bind->local.ipv6 set to :: when no local endpoint was configured or after a stale learned address was cleared. In that case the source should be left unspecified and selected by ip6_dst_lookup_flow().
For IPv4, inet_confirm_addr(..., local = 0, ...) asks for local address autoselection rather than validating a chosen source. Skip the precheck there as well and let ip_route_output_flow select or reject the source.
Only validate non-zero/non-any source addresses.
---
No changes since v2 https://lore.kernel.org/openvpn-devel/cb51001bfdaeba899b6ca9b22186ea2ebb49c23c.1785308184.git.ralf@mandelbit.com/
No changes since v1 https://lore.kernel.org/openvpn-devel/cb51001bfdaeba899b6ca9b22186ea2ebb49c23c.1785253480.git.ralf@mandelbit.com/
drivers/net/ovpn/udp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/net/ovpn/udp.c +4 -3
@@ -161,8 +161,8 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,if (rt) goto transmit; -if (unlikely (!inet_confirm_addr (sock_net (sk),NULL ,0 , fl.saddr, -RT_SCOPE_HOST ))) { +if (fl.saddr &&unlikely (!inet_confirm_addr (sock_net (sk),NULL ,0 , + fl.saddr,RT_SCOPE_HOST ))) {/* we may end up here when the cached address is not usable * anymore. In this case we reset address/cache and perform a * new look up
@@ -238,7 +238,8 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,if (dst) goto transmit; -if (unlikely (!ipv6_chk_addr (sock_net (sk), &fl.saddr,NULL ,0 ))) { +if (!ipv6_addr_any (&fl.saddr) && +unlikely (!ipv6_chk_addr (sock_net (sk), &fl.saddr,NULL ,0 ))) {/* we may end up here when the cached address is not usable * anymore. In this case we reset address/cache and perform a * new look up
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 3/6 ovpn: track UDP socket route key for peer dst cache
In reply to Ralf Liciovpn stores the route used to transmit UDP packets in a per-peer dst cache. A cached dst is only valid for the route lookup inputs used when it was resolved.
Some of those inputs are mutable while userspace still owns the UDP socket. In particular, changes to the socket mark or UDP source port do not invalidate ovpn's peer dst cache, so ovpn can keep using a route selected with an old socket route key.
Replace the cached mark with a route key containing the socket-owned lookup inputs currently used by ovpn, and reset the peer dst cache when the key changes. Before storing a newly looked-up dst, recheck the route key under the peer lock so a dst resolved for stale socket state is not published.
---
No changes since v2 https://lore.kernel.org/openvpn-devel/15a79f79de3cf192bc862acfd07534c1995f7ad8.1785308184.git.ralf@mandelbit.com/
No changes since v1 https://lore.kernel.org/openvpn-devel/15a79f79de3cf192bc862acfd07534c1995f7ad8.1785253480.git.ralf@mandelbit.com/
drivers/net/ovpn/peer.c | 1 + drivers/net/ovpn/peer.h | 19 ++++++++- drivers/net/ovpn/udp.c | 90 +++++++++++++++++++++++++++++++++++------ 3 files changed, 95 insertions(+), 15 deletions(-)
drivers/net/ovpn/peer.c +1 -0
@@ -113,6 +113,7 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id)RCU_INIT_POINTER (peer->bind,NULL );ovpn_crypto_state_init (&peer->crypto);spin_lock_init (&peer->lock); +seqcount_spinlock_init (&peer->route_key_seq, &peer->lock);kref_init (&peer->refcount);ovpn_peer_stats_init (&peer->vpn_stats);ovpn_peer_stats_init (&peer->link_stats);
drivers/net/ovpn/peer.h +17 -2
@@ -10,6 +10,7 @@#ifndef _NET_OVPN_OVPNPEER_H_#define _NET_OVPN_OVPNPEER_H_ +#include <linux/seqlock.h> #include <net/dst_cache.h> #include <net/strparser.h>
@@ -17,6 +18,16 @@#include "socket.h" #include "stats.h" +/** +* struct ovpn_route_key - route key used for the peer dst cache +* @mark: fwmark used for route lookup +* @sport: UDP source port used for route lookup +*/ +struct ovpn_route_key { +u32 mark; +__be16 sport; +}; +/** * struct ovpn_peer - the main remote peer object * @ovpn: main openvpn instance this peer belongs to
@@ -45,6 +56,8 @@* @tcp.sk_cb.ops: pointer to the original prot_ops object (TCP only) * @crypto: the crypto configuration (ciphers, keys, etc..) * @dst_cache: cache for dst_entry used to send to peer +* @route_key: route key matching the current dst cache contents +* @route_key_seq: seqcount protecting lockless route_key reads * @bind: remote peer binding * @keepalive_interval: seconds after which a new keepalive should be sent * @keepalive_xmit_exp: future timestamp when next keepalive should be sent
@@ -55,7 +68,7 @@* @vpn_stats: per-peer in-VPN TX/RX stats * @link_stats: per-peer link/transport TX/RX stats * @delete_reason: why peer was deleted (i.e. timeout, transport error, ..) -* @lock: protects binding to peer (bind) and keepalive* fields +* @lock: protects binding to peer (bind), route_key and keepalive* fields * @refcount: reference counter * @rcu: used to free peer in an RCU safe way * @release_entry: entry for the socket release list
@@ -99,6 +112,8 @@ struct ovpn_peer { } tcp;struct ovpn_crypto_state crypto;struct dst_cache dst_cache; +struct ovpn_route_key route_key; +seqcount_spinlock_t route_key_seq;struct ovpn_bind __rcu *bind;unsigned long keepalive_interval;unsigned long keepalive_xmit_exp;
@@ -109,7 +124,7 @@ struct ovpn_peer {struct ovpn_peer_stats vpn_stats;struct ovpn_peer_stats link_stats;enum ovpn_del_peer_reason delete_reason; -spinlock_t lock;/* protects bind and keepalive* */ +spinlock_t lock;/* protects bind, route_key and keepalive* */ struct kref refcount;struct rcu_head rcu;struct llist_node release_entry;
drivers/net/ovpn/udp.c +77 -13
179 lines not on this page · raw message
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 4/6 ovpn: validate peer state before caching UDP dst
In reply to Ralf LiciUDP route lookup runs without peer->lock while the bind is protected by RCU. The route key is snapshotted separately. Either can change while the lookup is in progress.
The TX path currently checks only the route key before publishing the looked-up dst. If the bind changes but the route key does not, a dst resolved from the old endpoint can be installed in the cache after the bind replacement.
Compare both the bind pointer and the route key under peer->lock before updating the cache. The RCU read-side critical section keeps the old bind alive throughout the lookup, so pointer identity is sufficient to detect a replacement.
---
Changes since v2 https://lore.kernel.org/openvpn-devel/082540583b9145d89e1cdd5a74c485ea3a53d285.1785308184.git.ralf@mandelbit.com/
- Split former 4/5 into this plus the next two patches. (Sabrina)
- No functional changes.
No changes since v1 https://lore.kernel.org/openvpn-devel/082540583b9145d89e1cdd5a74c485ea3a53d285.1785253480.git.ralf@mandelbit.com/
drivers/net/ovpn/udp.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
drivers/net/ovpn/udp.c +31 -2
@@ -173,6 +173,35 @@ static void ovpn_dst_cache_check_key(struct ovpn_peer *peer,spin_unlock_bh (&peer->lock); } +/** +* ovpn_dst_cache_current - check whether a route lookup matches peer state +* @peer: the peer owning the bind and dst cache +* @bind: the RCU bind used for the route lookup +* @key: the route key used for the route lookup +* +* Check that @bind is still the current peer bind and that @key still matches +* the peer route key. The caller must hold @peer->lock. The TX path keeps +* @bind inside an RCU read-side critical section, so pointer identity is enough +* to detect whether the bind was replaced while the route lookup was running. +* +* Return: true if the lookup result still matches the current peer state and +* may update the dst cache. +*/ +static bool ovpn_dst_cache_current (const struct ovpn_peer *peer, +const struct ovpn_bind *bind, +const struct ovpn_route_key *key) +{ +const struct ovpn_bind *curr_bind; + +lockdep_assert_held (&peer->lock); + + curr_bind =rcu_dereference_protected (peer->bind, +lockdep_is_held (&peer->lock)); + +return curr_bind == bind && +ovpn_route_key_equal (key, &peer->route_key); +} +/** * ovpn_udp4_output - send IPv4 packet over udp socket * @peer: the destination peer
@@ -240,7 +269,7 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,/* avoid storing a stale cache */ spin_lock_bh (&peer->lock); -if (likely (ovpn_route_key_equal (key, &peer->route_key))) +if (likely (ovpn_dst_cache_current (peer, bind, key)))dst_cache_set_ip4 (cache, &rt->dst, fl.saddr);spin_unlock_bh (&peer->lock);
@@ -313,7 +342,7 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,/* avoid storing a stale cache */ spin_lock_bh (&peer->lock); -if (likely (ovpn_route_key_equal (key, &peer->route_key))) +if (likely (ovpn_dst_cache_current (peer, bind, key)))dst_cache_set_ip6 (cache, dst, &fl.saddr);spin_unlock_bh (&peer->lock);
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 5/6 ovpn: replace bind when learning local endpoint
In reply to Ralf Licistruct ovpn_bind is published through peer->bind with RCU, but local endpoint learning updates bind->local in place under peer->lock. UDP TX reads the field without that lock. In particular, a concurrent IPv6 update can therefore result in a torn address read.
Use ovpn_peer_reset_sockaddr to publish a replacement bind when learning a new local endpoint, just as a remote endpoint change does. Preserve the current remote address and reset the dst cache only after the new bind has been published successfully.
Track remote endpoint changes separately so that float notification and transport-address rehashing remain limited to actual peer floats.
---
Changes since v2 https://lore.kernel.org/openvpn-devel/082540583b9145d89e1cdd5a74c485ea3a53d285.1785308184.git.ralf@mandelbit.com/
- Split former 4/5 into this plus the previous and the next patch.
(Sabrina)
- Tracked float with the specific floated boolean rather than indirectly
through salen.
- Refactored the code into a cleaner shape. (Sabrina)
drivers/net/ovpn/peer.c | 43 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-)
drivers/net/ovpn/peer.c +23 -20
@@ -200,13 +200,12 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,*/ void ovpn_peer_endpoints_update (struct ovpn_peer *peer,struct sk_buff *skb) { +const void *local_ip =NULL ;struct sockaddr_storage ss;struct sockaddr_in6 *sa6; -bool reset_cache = false;struct sockaddr_in *sa;struct ovpn_bind *bind; -const void *local_ip; -size_t salen =0 ; +bool floated = false;spin_lock_bh (&peer->lock); bind = rcu_dereference_protected(peer->bind,
@@ -233,8 +232,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) .sin_addr.s_addr = ip_hdr(skb)->saddr, .sin_port = udp_hdr(skb)->source, }; - salen =sizeof (*sa); - reset_cache = true; + floated = true;break ; }
@@ -246,10 +244,12 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)netdev_name (peer->ovpn->dev), peer->id, &bind->local.ipv4.s_addr, &ip_hdr(skb)->daddr); - bind->local.ipv4.s_addr =ip_hdr (skb)->daddr; - reset_cache = true; + local_ip = &ip_hdr (skb)->daddr; +memcpy (&ss, &bind->remote,sizeof (struct sockaddr_in )); +break ; } -break ; +/* nothing changed */ + goto unlock;case htons (ETH_P_IPV6 ):/* float check */ if (unlikely (!ovpn_bind_skb_src_match (bind, skb))) {
@@ -271,8 +271,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)ipv6_iface_scope_id (&ipv6_hdr (skb)->saddr, skb->skb_iif), }; - salen =sizeof (*sa6); - reset_cache = true; + floated = true;break ; }
@@ -285,26 +284,30 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)netdev_name (peer->ovpn->dev), peer->id, &bind->local.ipv6, &ipv6_hdr(skb)->daddr); - bind->local.ipv6 =ipv6_hdr (skb)->daddr; - reset_cache = true; + local_ip = &ipv6_hdr (skb)->daddr; +memcpy (&ss, &bind->remote,sizeof (struct sockaddr_in6 )); +break ; } -break ; +/* nothing changed */ + goto unlock;default : goto unlock; } -if (unlikely (reset_cache)) -dst_cache_reset (&peer->dst_cache); - -/* if the peer did not float, we can bail out now */ -if (likely (!salen)) - goto unlock; -if (unlikely (ovpn_peer_reset_sockaddr (peer, (struct sockaddr_storage *)&ss, local_ip) <0 )) goto unlock; +/* reset the cache only after a successful bind update to avoid useless +* cache misses on concurrent TX +*/ +dst_cache_reset (&peer->dst_cache); + +/* if only the local address changed, bail out now */ +if (!floated) + goto unlock; +net_dbg_ratelimited ("%s: peer %d floated to %pIScp" ,netdev_name (peer->ovpn->dev), peer->id, &ss);
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Ralf Lici
reviewerPATCH v3 6/6 ovpn: replace bind when clearing stale local source
In reply to Ralf LiciThe UDP output fallback clears bind->local in place when the remembered source address is no longer usable. The bind is RCU-published and read locklessly by concurrent TX, so an IPv6 reader can observe a torn address.
Retry the route lookup with source address autoselection without modifying the bind. After a successful lookup, revalidate the bind and route key under peer->lock, reset the dst cache, and best-effort publish a replacement bind with a wildcard local address.
Do not cache the resolved dst when clearing the local source. Replacing the source invalidates all per-CPU cache entries, while dst_cache_set_ip4 and dst_cache_set_ip6 update only the current CPU slot. The current packet can still use the resolved route; if bind allocation fails, a later cache miss retries the repair.
---
Changes since v2 https://lore.kernel.org/openvpn-devel/082540583b9145d89e1cdd5a74c485ea3a53d285.1785308184.git.ralf@mandelbit.com/
- Split former 4/5 into this plus the previous two patches. (Sabrina)
- No functional changes.
No changes since v1 https://lore.kernel.org/openvpn-devel/082540583b9145d89e1cdd5a74c485ea3a53d285.1785253480.git.ralf@mandelbit.com/
drivers/net/ovpn/udp.c | 81 +++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 25 deletions(-)
drivers/net/ovpn/udp.c +56 -25
@@ -185,7 +185,7 @@ static void ovpn_dst_cache_check_key(struct ovpn_peer *peer,* to detect whether the bind was replaced while the route lookup was running. * * Return: true if the lookup result still matches the current peer state and -* may update the dst cache. +* may update the dst cache or replace the bind. */ static bool ovpn_dst_cache_current(const struct ovpn_peer *peer, const struct ovpn_bind *bind,
@@ -218,6 +218,9 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,struct sk_buff *skb, const struct ovpn_route_key *key) { + struct sockaddr_storage remote; +struct in_addr local = {}; +bool reset_local = false;struct rtable *rt; struct flowi4 fl = { .saddr = bind->local.ipv4.s_addr,
@@ -236,24 +239,17 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,if (fl.saddr &&unlikely (!inet_confirm_addr (sock_net (sk),NULL ,0 , fl.saddr,RT_SCOPE_HOST ))) { -/* we may end up here when the cached address is not usable -* anymore. In this case we reset address/cache and perform a -* new look up +/* The learned local address is not usable anymore. +* Retry with source address autoselection. */ fl.saddr =0 ; -spin_lock_bh (&peer->lock); - bind->local.ipv4.s_addr =0 ; -spin_unlock_bh (&peer->lock); -dst_cache_reset (cache); + reset_local = true; } rt =ip_route_output_flow (sock_net (sk), &fl, sk);if (IS_ERR (rt) &&PTR_ERR (rt) == -EINVAL ) { fl.saddr =0 ; -spin_lock_bh (&peer->lock); - bind->local.ipv4.s_addr =0 ; -spin_unlock_bh (&peer->lock); -dst_cache_reset (cache); + reset_local = true; rt =ip_route_output_flow (sock_net (sk), &fl, sk); }
@@ -267,10 +263,28 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind, goto err; } -/* avoid storing a stale cache */ +/* avoid storing a stale cache or local address */ spin_lock_bh (&peer->lock); -if (likely (ovpn_dst_cache_current (peer, bind, key))) -dst_cache_set_ip4 (cache, &rt->dst, fl.saddr); +if (likely (ovpn_dst_cache_current (peer, bind, key))) { +if (!reset_local) { +dst_cache_set_ip4 (cache, &rt->dst, fl.saddr); +spin_unlock_bh (&peer->lock); + goto transmit; + } + +/* invalidate per-CPU dst entries that may still carry +* the stale source +*/ +dst_cache_reset (cache); + +/* preserve the current remote */ +memcpy (&remote, &bind->remote,sizeof (struct sockaddr_in )); +/* The current packet already has a valid wildcard-source route. +* If replacing the bind fails, leave the stale local in place; +* a later cache miss will retry the repair. +*/ +ovpn_peer_reset_sockaddr (peer, &remote, &local); + }spin_unlock_bh (&peer->lock); transmit:
@@ -300,6 +314,9 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,struct sk_buff *skb, const struct ovpn_route_key *key) { + struct in6_addr local = in6addr_any; +struct sockaddr_storage remote; +bool reset_local = false;struct dst_entry *dst;int ret;
@@ -320,15 +337,11 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,if (!ipv6_addr_any (&fl.saddr) &&unlikely (!ipv6_chk_addr (sock_net (sk), &fl.saddr,NULL ,0 ))) { -/* we may end up here when the cached address is not usable -* anymore. In this case we reset address/cache and perform a -* new look up +/* The learned local address is not usable anymore. +* Retry with source address autoselection. */ fl.saddr = in6addr_any; -spin_lock_bh (&peer->lock); - bind->local.ipv6 = in6addr_any; -spin_unlock_bh (&peer->lock); -dst_cache_reset (cache); + reset_local = true; } dst =ip6_dst_lookup_flow (sock_net (sk), sk, &fl,NULL );
@@ -340,10 +353,28 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind, goto err; } -/* avoid storing a stale cache */ +/* avoid storing a stale cache or local address */ spin_lock_bh (&peer->lock); -if (likely (ovpn_dst_cache_current (peer, bind, key))) -dst_cache_set_ip6 (cache, dst, &fl.saddr); +if (likely (ovpn_dst_cache_current (peer, bind, key))) { +if (!reset_local) { +dst_cache_set_ip6 (cache, dst, &fl.saddr); +spin_unlock_bh (&peer->lock); + goto transmit; + } + +/* invalidate per-CPU dst entries that may still carry +* the stale source +*/ +dst_cache_reset (cache); + +/* preserve the current remote */ +memcpy (&remote, &bind->remote,sizeof (struct sockaddr_in6 )); +/* The current packet already has a valid wildcard-source route. +* If replacing the bind fails, leave the stale local in place; +* a later cache miss will retry the repair. +*/ +ovpn_peer_reset_sockaddr (peer, &remote, &local); + }spin_unlock_bh (&peer->lock); transmit:
Signature · 8 lines
2.55.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Antonio Quartulli
AppliedFrom: Antonio Quartulli <[email protected]>
Quoted 6 lines · Ralf Lici
This is v3 of a net series that fixes several related issues in ovpn's UDP endpoint and route-cache handling. This version was rebased on top of net and includes suggestions from Sabrina. The former patch 5/5 was dropped from this series and will be sent as RFC on netdev, where it is more appropriate since it involves changes broader than ovpn alone.
Quoted 6 lines
This series preserves IPv6 scope IDs supplied through netlink, handles unspecified source addresses correctly, invalidates cached routes when mutable socket route inputs change and avoids in-place updates of RCU-published peer binds.
[...]
Applied, thanks!
[1/6] ovpn: preserve IPv6 scope id for netlink peer endpoints
(no commit info)
[2/6] ovpn: skip UDP source validation for unspecified addresses
(no commit info)
[3/6] ovpn: track UDP socket route key for peer dst cache
(no commit info)
[4/6] ovpn: validate peer state before caching UDP dst
(no commit info)
[5/6] ovpn: replace bind when learning local endpoint
(no commit info)
[6/6] ovpn: replace bind when clearing stale local source
(no commit info)
Best regards,
Signature · 7 lines
Antonio Quartulli <[email protected]> _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel