From c84cb092b72119033a82b4d995c13cbd1a81f112 Mon Sep 17 00:00:00 2001 From: Andrew Yager Date: Mon, 23 Mar 2026 13:34:59 +1100 Subject: [PATCH 1/3] kernel-module: detect nft_expr_ops.validate signature from headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current LINUX_VERSION_CODE check assumes the nft_expr_ops.validate callback signature changed only in kernel 6.12+ (upstream commit eaf9b2c875ec "netfilter: nf_tables: drop unused 3rd argument from validate callback ops", Florian Westphal, 2024-09-03). However, distribution kernels may backport this change to earlier versions without updating LINUX_VERSION_CODE. For example, Ubuntu 24.04's 6.8.0-106 kernel (stable patchset 2026-01-27, LP: #2139158) includes this backport, causing DKMS builds to fail with: error: initialization of 'int (*)(const struct nft_ctx *, const struct nft_expr *)' from incompatible pointer type 'int (*)(const struct nft_ctx *, const struct nft_expr *, const struct nft_data **)' [-Werror=incompatible-pointer-types] Replace the version-based #if with compile-time header detection: the Makefile inspects the installed kernel headers for the actual validate callback signature and sets NFT_EXPR_OPS_VALIDATE_HAS_DATA accordingly. Tested against Ubuntu 6.8.0-90 (3-param, old API) and 6.8.0-106 (2-param, backported API) — both compile cleanly. --- kernel-module/Makefile | 9 +++++++++ kernel-module/nft_rtpengine.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kernel-module/Makefile b/kernel-module/Makefile index be0f349e13..9198cbd517 100644 --- a/kernel-module/Makefile +++ b/kernel-module/Makefile @@ -8,6 +8,15 @@ include $(M)/rtpengine-kmod.mk ccflags-y += -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" +# Detect nft_expr_ops.validate callback signature from kernel headers. +# Distribution kernels (e.g. Ubuntu) may backport the 2-param validate +# callback from kernel 6.12+ to earlier kernel versions without changing +# LINUX_VERSION_CODE, which breaks the version-based #if in the C code. +# Detect the actual API by inspecting the installed kernel headers. +ifneq ($(shell grep -A3 '(\*validate)' $(KSRC)/include/net/netfilter/nf_tables.h 2>/dev/null | grep -c nft_data),0) +ccflags-y += -DNFT_EXPR_OPS_VALIDATE_HAS_DATA +endif + obj-m += nft_rtpengine.o .PHONY: modules clean install diff --git a/kernel-module/nft_rtpengine.c b/kernel-module/nft_rtpengine.c index 5d1f8c9ab9..e510647790 100644 --- a/kernel-module/nft_rtpengine.c +++ b/kernel-module/nft_rtpengine.c @@ -6844,7 +6844,7 @@ static int rtpengine_expr_dump(struct sk_buff *skb, const struct nft_expr *expr } static int rtpengine_expr_validate(const struct nft_ctx *ctx, const struct nft_expr *expr -#if LINUX_VERSION_CODE < KERNEL_VERSION(6,12,0) +#if defined(NFT_EXPR_OPS_VALIDATE_HAS_DATA) , const struct nft_data **data #endif ) From 4e81efe25a26353453acd9ffc9f54857877ab544 Mon Sep 17 00:00:00 2001 From: Andrew Yager Date: Tue, 24 Mar 2026 02:26:50 +1100 Subject: [PATCH 2/3] kernel-module: use compile test instead of header grep for API detection Replace the fragile grep-based header inspection with a proper compile test in gen-rtpengine-kmod-flags, which already serves as the configure phase for the kernel module build. The test tries to compile a small module that assigns a 3-param function to nft_expr_ops.validate. If it compiles, the old API is present and NFT_EXPR_OPS_VALIDATE_HAS_DATA is set. If it fails, the kernel has the new 2-param version (mainline 6.12+ or distro backport). This approach: - Avoids fragile pattern matching on header contents - Works with any distribution's kernel regardless of version defines - Follows the established pattern used by ZFS, DAHDI, and other out-of-tree kernel modules for cross-distro API detection - Runs during the existing gen-rtpengine-kmod-flags configure phase, not inline in the kbuild Makefile (which would deadlock on the jobserver) Tested against Ubuntu 6.8.0-90 (3-param) and 6.8.0-106 (2-param). --- kernel-module/Makefile | 9 -------- kernel-module/gen-rtpengine-kmod-flags | 32 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/kernel-module/Makefile b/kernel-module/Makefile index 9198cbd517..be0f349e13 100644 --- a/kernel-module/Makefile +++ b/kernel-module/Makefile @@ -8,15 +8,6 @@ include $(M)/rtpengine-kmod.mk ccflags-y += -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" -# Detect nft_expr_ops.validate callback signature from kernel headers. -# Distribution kernels (e.g. Ubuntu) may backport the 2-param validate -# callback from kernel 6.12+ to earlier kernel versions without changing -# LINUX_VERSION_CODE, which breaks the version-based #if in the C code. -# Detect the actual API by inspecting the installed kernel headers. -ifneq ($(shell grep -A3 '(\*validate)' $(KSRC)/include/net/netfilter/nf_tables.h 2>/dev/null | grep -c nft_data),0) -ccflags-y += -DNFT_EXPR_OPS_VALIDATE_HAS_DATA -endif - obj-m += nft_rtpengine.o .PHONY: modules clean install diff --git a/kernel-module/gen-rtpengine-kmod-flags b/kernel-module/gen-rtpengine-kmod-flags index 0dee170f16..153cbb5eab 100755 --- a/kernel-module/gen-rtpengine-kmod-flags +++ b/kernel-module/gen-rtpengine-kmod-flags @@ -31,3 +31,35 @@ if [ -z "${RTPENGINE_VERSION}" ]; then fi echo "RTPENGINE_VERSION := ${RTPENGINE_VERSION}" fi + +# Compile test: detect nft_expr_ops.validate callback signature. +# Distribution kernels (e.g. Ubuntu) may backport the 2-param validate +# callback (mainline commit eaf9b2c875ec, merged in 6.12) to earlier +# kernel versions without changing LINUX_VERSION_CODE. A compile test +# is the only reliable way to detect the actual API. +# +# The test tries to assign a 3-param function to .validate. If it +# compiles, the old API is present and we set the flag. If it fails +# (incompatible pointer types), the kernel has the new 2-param version. + +KSRC="${KSRC:-/lib/modules/$(uname -r)/build}" + +if [ -d "${KSRC}" ]; then + nft_test_dir=$(mktemp -d) + cat > "${nft_test_dir}/nft_test.c" <<'TESTEOF' +#include +#include +static int test_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data) { return 0; } +static struct nft_expr_ops __attribute__((unused)) test_ops = { + .validate = test_validate }; +MODULE_LICENSE("GPL"); +TESTEOF + echo "obj-m := nft_test.o" > "${nft_test_dir}/Makefile" + + if make -j1 -C "${KSRC}" M="${nft_test_dir}" modules >/dev/null 2>&1; then + echo "ccflags-y += -DNFT_EXPR_OPS_VALIDATE_HAS_DATA" + fi + + rm -rf "${nft_test_dir}" +fi From 6d09edda4f2a8d72d97c2f67d8f885966e8768dc Mon Sep 17 00:00:00 2001 From: Andrew Yager Date: Tue, 24 Mar 2026 16:12:03 +1100 Subject: [PATCH 3/3] kernel-module: use KERNELRELEASE for compile test target kernel kbuild exports KERNELRELEASE as the target kernel version, which may differ from the running kernel during DKMS cross-kernel builds (e.g. installing a new kernel before rebooting). Use it to construct KSRC so the compile test runs against the correct kernel headers. Falls back to uname -r for standalone builds outside kbuild. --- kernel-module/gen-rtpengine-kmod-flags | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel-module/gen-rtpengine-kmod-flags b/kernel-module/gen-rtpengine-kmod-flags index 153cbb5eab..4700cc98c0 100755 --- a/kernel-module/gen-rtpengine-kmod-flags +++ b/kernel-module/gen-rtpengine-kmod-flags @@ -42,7 +42,9 @@ fi # compiles, the old API is present and we set the flag. If it fails # (incompatible pointer types), the kernel has the new 2-param version. -KSRC="${KSRC:-/lib/modules/$(uname -r)/build}" +# KERNELRELEASE is set by kbuild to the target kernel version, which +# may differ from the running kernel during DKMS cross-kernel builds. +KSRC="${KSRC:-/lib/modules/${KERNELRELEASE:-$(uname -r)}/build}" if [ -d "${KSRC}" ]; then nft_test_dir=$(mktemp -d)