git: 16ae7b665c7e - stable/13 - nhop: hash ifnet pointer instead of if_index

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Mon, 23 Jan 2023 22:12:31 UTC
The branch stable/13 has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=16ae7b665c7eac41fc717d9f09da2a2fb4ed1973

commit 16ae7b665c7eac41fc717d9f09da2a2fb4ed1973
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2021-12-04 18:05:46 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-01-23 22:10:07 +0000

    nhop: hash ifnet pointer instead of if_index
    
    Yet another problem created by VIMAGE/if_vmove/epair design that
    relocates ifnet between vnets and changes if_index.  Since if_index
    changes, nhop hash values also changes, unlink_nhop() isn't able to
    find entry in hash and leaks the nhop.  Since nhop references ifnet,
    the latter is also leaked.  As result running network tests leaks
    memory on every single test that creates vnet jail.
    
    While here, rewrite whole hash_priv() to use static initializer,
    per Alexander's suggestion.
    
    Reviewed by:    melifaro
    
    (cherry picked from commit ad2a0aec295478e750158b8985422f15deee0e54)
---
 sys/net/route/nhop.c | 30 ++++++++++++------------------
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/sys/net/route/nhop.c b/sys/net/route/nhop.c
index 782bdd037d3e..07bced3e8b11 100644
--- a/sys/net/route/nhop.c
+++ b/sys/net/route/nhop.c
@@ -186,13 +186,13 @@ nhops_destroy_rib(struct rib_head *rh)
  * With that in mind, hash nexthops by the combination of the interface
  *  and GW IP address.
  *
- * To optimize hash calculation, ignore higher bytes of ifindex, as they
- *  give very little entropy.
+ * To optimize hash calculation, ignore lower bits of ifnet pointer,
+ * as they  give very little entropy.
  * Similarly, use lower 4 bytes of IPv6 address to distinguish between the
  *  neighbors.
  */
 struct _hash_data {
-	uint16_t	ifindex;
+	uint16_t	ifentropy;
 	uint8_t		family;
 	uint8_t		nh_type;
 	uint32_t	gw_addr;
@@ -213,21 +213,15 @@ djb_hash(const unsigned char *h, const int len)
 static uint32_t
 hash_priv(const struct nhop_priv *priv)
 {
-	struct nhop_object *nh;
-	uint16_t ifindex;
-	struct _hash_data key;
-
-	nh = priv->nh;
-	ifindex = nh->nh_ifp->if_index & 0xFFFF;
-	memset(&key, 0, sizeof(key));
-
-	key.ifindex = ifindex;
-	key.family = nh->gw_sa.sa_family;
-	key.nh_type = priv->nh_type & 0xFF;
-	if (nh->gw_sa.sa_family == AF_INET6)
-		memcpy(&key.gw_addr, &nh->gw6_sa.sin6_addr.s6_addr32[3], 4);
-	else if (nh->gw_sa.sa_family == AF_INET)
-		memcpy(&key.gw_addr, &nh->gw4_sa.sin_addr, 4);
+	struct nhop_object *nh = priv->nh;
+	struct _hash_data key = {
+	    .ifentropy = (uint16_t)((((uintptr_t)nh->nh_ifp) >> 6) & 0xFFFF),
+	    .family = nh->gw_sa.sa_family,
+	    .nh_type = priv->nh_type & 0xFF,
+	    .gw_addr = (nh->gw_sa.sa_family == AF_INET6) ?
+		nh->gw6_sa.sin6_addr.s6_addr32[3] :
+		nh->gw4_sa.sin_addr.s_addr
+	};
 
 	return (uint32_t)(djb_hash((const unsigned char *)&key, sizeof(key)));
 }