Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/glb-redirect/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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=<dir> 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)
Expand Down
37 changes: 33 additions & 4 deletions src/glb-redirect/ipt_GLBREDIRECT.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
76 changes: 76 additions & 0 deletions src/glb-redirect/script/test
Original file line number Diff line number Diff line change
Expand Up @@ -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:-<none>}], 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/