All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Stephen Hemminger <stephen@networkplumber.org>
Subject: [PATCH iproute2] lib: bound the hostname cache in resolve_address
Date: Mon,  9 Nov 2026 10:08:32 -0800	[thread overview]
Message-ID: <20261109180832.300754-1-stephen@networkplumber.org> (raw)

The reverse lookup cache in resolve_address() grows without bound,
including negative entries, so a long running "ip -r monitor" can be
made to consume memory by churning addresses.

Replace it with a small fixed size open addressed table (32 slots,
multiplicative hash, oldest entry in the probe window is replaced)
and use getnameinfo() instead of the obsolete gethostbyaddr().

Reported-by: Anthropic OSS scanner (ANT-2026-XBIHX07L)
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/utils.c | 119 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 83 insertions(+), 36 deletions(-)

diff --git a/lib/utils.c b/lib/utils.c
index c4cd3c3d..f4bd53ff 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1092,57 +1092,104 @@ const char *family_name(int family)
 }
 
 #ifdef RESOLVE_HOSTNAMES
+/*
+ * Small fixed size cache of reverse lookups (including failures).
+ * Open addressing, the oldest entry in the probe window is replaced,
+ * so memory stays bounded for long running ip monitor.
+ */
+#define NHASH_BITS	5
+#define NHASH		(1u << NHASH_BITS)
+#define NPROBE		4
+
 struct namerec {
-	struct namerec *next;
-	const char *name;
-	inet_prefix addr;
+	unsigned int	stamp;
+	__u8		family;		/* 0 = unused */
+	__u8		len;
+	__u8		addr[16];
+	char		name[256];	/* "" = lookup failed */
 };
 
-#define NHASH 257
-static struct namerec *nht[NHASH];
+static struct namerec nht[NHASH];
+static unsigned int nstamp;
 
-static const char *resolve_address(const void *addr, int len, int af)
+static unsigned int name_hash(const void *addr, int len)
 {
-	struct namerec *n;
-	struct hostent *h_ent;
-	unsigned int hash;
-	static int notfirst;
+	__u32 h = 0, w;
+	int i;
 
+	for (i = 0; i < len; i += 4) {
+		memcpy(&w, addr + i, 4);
+		h ^= w;
+	}
+	return (h * 0x9e3779b1u) >> (32 - NHASH_BITS);
+}
+
+static void name_lookup(struct namerec *n)
+{
+	union {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} u = { };
+	socklen_t salen;
+
+	if (n->family == AF_INET) {
+		u.sin.sin_family = AF_INET;
+		memcpy(&u.sin.sin_addr, n->addr, 4);
+		salen = sizeof(u.sin);
+	} else {
+		u.sin6.sin6_family = AF_INET6;
+		memcpy(&u.sin6.sin6_addr, n->addr, 16);
+		salen = sizeof(u.sin6);
+	}
 
-	if (af == AF_INET6 && ((__u32 *)addr)[0] == 0 &&
-	    ((__u32 *)addr)[1] == 0 && ((__u32 *)addr)[2] == htonl(0xffff)) {
+	fflush(stdout);
+	if (getnameinfo(&u.sa, salen, n->name, sizeof(n->name),
+			NULL, 0, NI_NAMEREQD))
+		n->name[0] = '\0';
+}
+
+static const char *resolve_address(int af, int len, const void *addr,
+				   char *buf, int buflen)
+{
+	struct namerec *n, *victim = NULL;
+	unsigned int hash, i;
+
+	if (af == AF_INET6 && len == 16 &&
+	    IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)addr)) {
 		af = AF_INET;
 		addr += 12;
 		len = 4;
 	}
 
-	hash = *(__u32 *)(addr + len - 4) % NHASH;
+	if (!((af == AF_INET && len == 4) || (af == AF_INET6 && len == 16)))
+		return NULL;
+
+	hash = name_hash(addr, len);
+	for (i = 0; i < NPROBE; i++) {
+		n = &nht[(hash + i) % NHASH];
+
+		if (n->family == af && n->len == len &&
+		    memcmp(n->addr, addr, len) == 0)
+			goto found;
 
-	for (n = nht[hash]; n; n = n->next) {
-		if (n->addr.family == af &&
-		    n->addr.bytelen == len &&
-		    memcmp(n->addr.data, addr, len) == 0)
-			return n->name;
+		if (!victim || (victim->family &&
+				(!n->family || n->stamp < victim->stamp)))
+			victim = n;
 	}
-	n = malloc(sizeof(*n));
-	if (n == NULL)
-		return NULL;
-	n->addr.family = af;
-	n->addr.bytelen = len;
-	n->name = NULL;
-	memcpy(n->addr.data, addr, len);
-	n->next = nht[hash];
-	nht[hash] = n;
-	if (++notfirst == 1)
-		sethostent(1);
-	fflush(stdout);
 
-	h_ent = gethostbyaddr(addr, len, af);
-	if (h_ent != NULL)
-		n->name = strdup(h_ent->h_name);
+	n = victim;
+	n->family = af;
+	n->len = len;
+	memcpy(n->addr, addr, len);
+	name_lookup(n);
+found:
+	n->stamp = ++nstamp;
+	if (n->name[0] == '\0' ||
+	    strlcpy(buf, n->name, buflen) >= (size_t)buflen)
+		return NULL;
 
-	/* Even if we fail, "negative" entry is remembered. */
-	return n->name;
+	return buf;
 }
 #endif
 
@@ -1156,7 +1203,7 @@ const char *format_host_r(int af, int len, const void *addr,
 		len = len <= 0 ? af_byte_len(af) : len;
 
 		if (len > 0 &&
-		    (n = resolve_address(addr, len, af)) != NULL)
+		    (n = resolve_address(af, len, addr, buf, buflen)) != NULL)
 			return n;
 	}
 #endif
-- 
2.53.0


             reply	other threads:[~2026-11-09 18:08 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09 18:08 Stephen Hemminger [this message]
2026-11-11  0:00 ` [PATCH iproute2] lib: bound the hostname cache in resolve_address patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261109180832.300754-1-stephen@networkplumber.org \
    --to=stephen@networkplumber.org \
    --cc=dsahern@kernel.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.