diff --git a/src/glb-redirect/Makefile b/src/glb-redirect/Makefile
index 61cf6b1..7783359 100644
--- a/src/glb-redirect/Makefile
+++ b/src/glb-redirect/Makefile
@@ -34,6 +34,29 @@ endif
endif
endif
+# Probe the kernel headers for the socket-lookup helper arity. Upstream removed
+# the 'struct inet_hashinfo *hashinfo' argument (callers no longer pass
+# &tcp_hashinfo), but distributions may backport that change, so a
+# LINUX_VERSION_CODE check in the C code is not reliable on its own. When we can
+# read net/inet_hashtables.h we pass a definitive answer through to the C code;
+# otherwise it falls back to a version heuristic. The old declaration carries a
+# 'struct inet_hashinfo *hashinfo' parameter -- the presence of the 'inet_hashinfo'
+# type name in the inet_lookup_established() declaration distinguishes the two
+# forms. NB: as with the cookie probe above, keep the awk free of parentheses
+# and commas -- embedding either inside $(shell ...) breaks make's
+# function/conditional parser, so we terminate accumulation on the first ';'.
+HASHINFO_INET_H := $(firstword $(wildcard $(srctree)/include/net/inet_hashtables.h $(srctree)/source/include/net/inet_hashtables.h))
+ifneq ($(HASHINFO_INET_H),)
+HASHINFO_DECL := $(shell awk '/inet_lookup_established/{f=1} f{b=b $$0} f&&/;/{print b; exit}' $(HASHINFO_INET_H))
+ifneq ($(HASHINFO_DECL),)
+ifeq ($(findstring inet_hashinfo,$(HASHINFO_DECL)),)
+ccflags-y += -DGLB_INET_LOOKUP_NO_HASHINFO_ARG
+else
+ccflags-y += -DGLB_INET_LOOKUP_HAS_HASHINFO_ARG
+endif
+endif
+endif
+
all: lib kmod
kmod:
@@ -47,6 +70,16 @@ clean:
.PHONY: lib
lib: libxt_GLBREDIRECT.so
+# Test/debug helper: echo the socket-lookup arity flag that the header probe
+# above resolved (empty if it could not classify, in which case the C code
+# falls back to a LINUX_VERSION_CODE heuristic). Exercised by
+# script/test against fixture headers, so both detection
+# branches are validated without needing a full kernel build. Point it at a
+# fixture tree with `make srctree=
print-lookup-arg`.
+.PHONY: print-lookup-arg
+print-lookup-arg:
+ @echo "$(filter -DGLB_INET_LOOKUP_%,$(ccflags-y))"
+
.PHONY: install
install: lib kmod
install -d $(DESTDIR)$(shell pkg-config --variable=xtlibdir xtables)
diff --git a/src/glb-redirect/ipt_GLBREDIRECT.c b/src/glb-redirect/ipt_GLBREDIRECT.c
index 74b1957..6abf8ff 100644
--- a/src/glb-redirect/ipt_GLBREDIRECT.c
+++ b/src/glb-redirect/ipt_GLBREDIRECT.c
@@ -113,6 +113,35 @@ struct glbgue_stats {
# define GLB_COOKIE_V6_CHECK(iph, th) __cookie_v6_check((iph), (th))
#endif
+/*
+ * The socket-lookup helpers inet_lookup_established(), inet_lookup_listener(),
+ * __inet6_lookup_established() and inet6_lookup_listener() lost their
+ * 'struct inet_hashinfo *hashinfo' argument upstream in Linux 6.18 (commit
+ * cb16f4b6c73d, "tcp: Don't pass hashinfo to socket lookup helpers"): the
+ * global tcp_hashinfo is now reached internally via the net namespace, so
+ * callers no longer pass &tcp_hashinfo. v6.17 still carries the argument. As
+ * with the cookie-check change above, distributions may backport this
+ * independently of the mainline version, so the Makefile probes the kernel
+ * headers for the actual arity and defines one of the macros below; if it
+ * could not probe the header, we fall back to the mainline version boundary.
+ */
+#if !defined(GLB_INET_LOOKUP_HAS_HASHINFO_ARG) && !defined(GLB_INET_LOOKUP_NO_HASHINFO_ARG)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(6,18,0)
+# define GLB_INET_LOOKUP_NO_HASHINFO_ARG
+# else
+# define GLB_INET_LOOKUP_HAS_HASHINFO_ARG
+# endif
+#endif
+
+#ifdef GLB_INET_LOOKUP_HAS_HASHINFO_ARG
+ /* Expands to the leading '&tcp_hashinfo,' argument (note the trailing
+ * comma) for kernels that still expect it. */
+# define GLB_TCP_HASHINFO_ARG &tcp_hashinfo,
+#else
+ /* The hashinfo argument was removed; expand to nothing. */
+# define GLB_TCP_HASHINFO_ARG
+#endif
+
struct glbgue_stats __percpu *percpu_stats;
static unsigned int is_valid_locally(struct net *net, struct sk_buff *skb, int inner_ip_ofs, struct iphdr *iph_v4, struct ipv6hdr *iph_v6, struct tcphdr *th);
@@ -571,12 +600,12 @@ static unsigned int is_valid_locally(struct net *net, struct sk_buff *skb, int i
struct sock *nsk;
if (likely(iph_v4 != NULL)) {
- nsk = inet_lookup_established(net, &tcp_hashinfo,
+ nsk = inet_lookup_established(net, GLB_TCP_HASHINFO_ARG
iph_v4->saddr, th->source,
iph_v4->daddr, th->dest,
inet_iif(skb));
} else if (likely(iph_v6 != NULL)) {
- nsk = __inet6_lookup_established(net, &tcp_hashinfo,
+ nsk = __inet6_lookup_established(net, GLB_TCP_HASHINFO_ARG
&iph_v6->saddr, th->source,
&iph_v6->daddr, ntohs(th->dest),
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
@@ -607,7 +636,7 @@ static unsigned int is_valid_locally(struct net *net, struct sk_buff *skb, int i
if (likely(iph_v4 != NULL)) {
/* IPv4 */
- listen_sk = inet_lookup_listener(net, &tcp_hashinfo,
+ listen_sk = inet_lookup_listener(net, GLB_TCP_HASHINFO_ARG
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
skb, ip_hdrlen(skb) + __tcp_hdrlen(th),
#endif
@@ -647,7 +676,7 @@ static unsigned int is_valid_locally(struct net *net, struct sk_buff *skb, int i
} else if (likely(iph_v6 != NULL)) {
/* IPv6 */
- listen_sk = inet6_lookup_listener(net, &tcp_hashinfo,
+ listen_sk = inet6_lookup_listener(net, GLB_TCP_HASHINFO_ARG
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
skb, ip_hdrlen(skb) + __tcp_hdrlen(th),
#endif
diff --git a/src/glb-redirect/script/test b/src/glb-redirect/script/test
index a8236d9..6da4109 100755
--- a/src/glb-redirect/script/test
+++ b/src/glb-redirect/script/test
@@ -110,6 +110,82 @@ compile_check() {
echo "compile-check: ok (libxt_GLBREDIRECT.so + ipt_GLBREDIRECT.ko built)"
}
+# ---------------------------------------------------------------------------
+# Verify the Makefile's socket-lookup arity probe classifies kernel headers
+# correctly.
+#
+# Upstream dropped the `struct inet_hashinfo *hashinfo` argument from
+# inet_lookup_established() and friends (callers no longer pass &tcp_hashinfo).
+# ipt_GLBREDIRECT.c adapts via the GLB_TCP_HASHINFO_ARG macro, whose value is
+# chosen by a header probe in the Makefile (with a LINUX_VERSION_CODE fallback
+# in the C code). compile_check() above only builds against the running kernel,
+# so it exercises just one of the two API forms; this drives the probe against
+# fixture headers for BOTH forms, so a regression in either branch is caught
+# regardless of the host kernel. It needs only `make` + `awk`, so it runs even
+# where full kernel headers / the build toolchain are unavailable.
+# ---------------------------------------------------------------------------
+lookup_arg_detection_check() {
+ local fixtures fail=0
+
+ # Write a minimal net/inet_hashtables.h containing just the
+ # inet_lookup_established declaration in the requested form.
+ # $1 = variant name (subdir under $fixtures)
+ # $2 = "old" (carries hashinfo arg) | "new" (hashinfo arg removed)
+ write_lookup_header() {
+ local dir="$fixtures/$1/include/net"
+ mkdir -p "$dir"
+ if [ "$2" = "old" ]; then
+ cat > "$dir/inet_hashtables.h" <<'EOF'
+struct sock *__inet_lookup_established(struct net *net,
+ struct inet_hashinfo *hashinfo,
+ const __be32 saddr, const __be16 sport,
+ const __be32 daddr, const u16 hnum,
+ const int dif, const int sdif);
+EOF
+ else
+ cat > "$dir/inet_hashtables.h" <<'EOF'
+struct sock *__inet_lookup_established(const struct net *net,
+ const __be32 saddr, const __be16 sport,
+ const __be32 daddr, const u16 hnum,
+ const int dif, const int sdif);
+EOF
+ fi
+ }
+
+ # Ask the real Makefile probe how it classified a given fixture tree, and
+ # assert the expected macro was selected.
+ # $1 = variant, $2 = expected macro substring, $3 = human label
+ check_lookup() {
+ local got
+ got="$(make -s srctree="$fixtures/$1" print-lookup-arg 2>/dev/null || true)"
+ if [[ "$got" == *"$2"* ]]; then
+ echo "lookup-arg-detection: ok: $3 header => $2"
+ else
+ echo "lookup-arg-detection: FAIL: $3 header classified as [${got:-}], expected $2" >&2
+ fail=1
+ fi
+ }
+
+ fixtures="$(mktemp -d)"
+ trap 'rm -rf -- "$fixtures"' EXIT
+
+ write_lookup_header old old
+ write_lookup_header new new
+
+ check_lookup old GLB_INET_LOOKUP_HAS_HASHINFO_ARG old
+ check_lookup new GLB_INET_LOOKUP_NO_HASHINFO_ARG new
+
+ rm -rf -- "$fixtures"
+ trap - EXIT
+
+ if [ "$fail" -ne 0 ]; then
+ echo "lookup-arg-detection: FAILED" >&2
+ return 1
+ fi
+ echo "lookup-arg-detection: ok"
+}
+
compile_check
+lookup_arg_detection_check
PYTHONPATH=$(pwd)/../scapy-glb-gue/:$PYTHONPATH pytest -v tests/