From: Stephen Hemminger <stephen@networkplumber.org>
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Stephen Hemminger <stephen@networkplumber.org>
Subject: [PATCH 1/2] lib: bound the hostname cache in resolve_address
Date: Sun, 8 Nov 2026 12:14:03 -0800 [thread overview]
Message-ID: <20261108201404.1133079-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-08 20:14 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20261108201404.1133079-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox