From: Antonio Quartulli <[email protected]>

The by_transp_addr table is keyed on the peer's remote transport
address, but the float rehash hashed bind->remote directly, while the
two other sites that touch the table build a clean key first:
ovpn_peer_add_mp() and the lookup in ovpn_peer_get_by_transp_addr()
both hash a sockaddr holding only family/address/port.

For a link-local IPv6 peer, bind->remote carries sin6_scope_id (set
from ipv6_iface_scope_id() when the endpoint is learned), and that
field is folded into the jhash() over sizeof(struct sockaddr_in6).
The lookup never sets sin6_scope_id, so after such a peer floats it is
rehashed into a scope_id-dependent bucket that lookups (scope_id 0)
never visit, making the peer unreachable through the by_transp_addr
fallback. ovpn_peer_transp_match() only compares address and port, so
the hash was keying on a field the match ignores.

sin6_scope_id must stay in bind->remote because the TX path uses it as
flowi6_oif, so it cannot just be cleared there. Instead build the hash
key from family/address/port only, exactly like ovpn_peer_add_mp() and
the lookup, so all three sites agree on the bucket.

Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP 
endpoint")
Signed-off-by: Antonio Quartulli <[email protected]>
---
 drivers/net/ovpn/peer.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 4aa5edc75dec..96a46ac7dbe3 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -911,7 +911,10 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct 
sk_buff *skb,
 static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
                                         const struct ovpn_bind *bind)
 {
+       struct sockaddr_storage sa = {};
        struct hlist_nulls_head *nhead;
+       struct sockaddr_in6 *sa6;
+       struct sockaddr_in *sa4;
        size_t salen;
 
        lockdep_assert_held(&peer->ovpn->lock);
@@ -927,12 +930,26 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer 
*peer,
        if (unlikely(hlist_unhashed(&peer->hash_entry_id)))
                return;
 
+       /* Build the hash key from the transport identity only
+        * (family/address/port), matching ovpn_peer_add_mp() and the lookup
+        * in ovpn_peer_get_by_transp_addr(). Hashing bind->remote directly
+        * would fold in sin6_scope_id (set on the float path but never by the
+        * lookup), scattering the peer into a bucket lookups cannot reach.
+        */
        switch (bind->remote.in4.sin_family) {
        case AF_INET:
-               salen = sizeof(struct sockaddr_in);
+               sa4 = (struct sockaddr_in *)&sa;
+               sa4->sin_family = AF_INET;
+               sa4->sin_addr.s_addr = bind->remote.in4.sin_addr.s_addr;
+               sa4->sin_port = bind->remote.in4.sin_port;
+               salen = sizeof(*sa4);
                break;
        case AF_INET6:
-               salen = sizeof(struct sockaddr_in6);
+               sa6 = (struct sockaddr_in6 *)&sa;
+               sa6->sin6_family = AF_INET6;
+               sa6->sin6_addr = bind->remote.in6.sin6_addr;
+               sa6->sin6_port = bind->remote.in6.sin6_port;
+               salen = sizeof(*sa6);
                break;
        default:
                return;
@@ -941,8 +958,8 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer 
*peer,
        /* remove old hashing (no-op if entry is not currently linked) */
        hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
        /* re-add with current transport address */
-       nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr,
-                                  &bind->remote, salen);
+       nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, &sa,
+                                  salen);
        hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
 }
 
-- 
2.53.0



_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to