Skip to content

Instantly share code, notes, and snippets.

@aojea
Last active May 20, 2026 15:01
Show Gist options
  • Select an option

  • Save aojea/c17a8bbf40d98c3f59c8ed2712ec43a0 to your computer and use it in GitHub Desktop.

Select an option

Save aojea/c17a8bbf40d98c3f59c8ed2712ec43a0 to your computer and use it in GitHub Desktop.
nftables user space compatibility check
  1. Run ./nft-builder.sh to create all the docker images with the different nft versions
  2. Run ./run-matrix.sh to test between the different versions

🏁 Matrix Testing Complete! Generating Summary...

Reader (kube-proxy) \ Writer (Host) v1.0.1 v1.0.3 v1.0.4 v1.0.5 v1.0.6 v1.0.6.1 v1.0.7 v1.0.8 v1.0.9 v1.1.0 v1.1.1 v1.1.2 v1.1.3 v1.1.4 v1.1.5 v1.1.6
v1.0.1 βœ… βœ… βœ… βœ… βœ… ❌ βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.3 -- βœ… βœ… βœ… βœ… ❌ βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.4 -- -- βœ… βœ… βœ… ❌ βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.5 -- -- -- βœ… βœ… ❌ βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.6 -- -- -- -- βœ… ❌ βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.6.1 -- -- -- -- -- βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ…
v1.0.7 -- -- -- -- -- -- βœ… βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.8 -- -- -- -- -- -- -- βœ… βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.0.9 -- -- -- -- -- -- -- -- βœ… βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.1.0 -- -- -- -- -- -- -- -- -- βœ… βœ… ❌ ❌ ❌ ❌ ❌
v1.1.1 -- -- -- -- -- -- -- -- -- -- βœ… ❌ ❌ ❌ ❌ ❌
v1.1.2 -- -- -- -- -- -- -- -- -- -- -- βœ… βœ… βœ… βœ… βœ…
v1.1.3 -- -- -- -- -- -- -- -- -- -- -- -- βœ… βœ… βœ… βœ…
v1.1.4 -- -- -- -- -- -- -- -- -- -- -- -- -- βœ… βœ… βœ…
v1.1.5 -- -- -- -- -- -- -- -- -- -- -- -- -- -- βœ… βœ…
v1.1.6 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- βœ…

Manual check

 ./test-skew.sh v1.0.6 v1.1.2
🌐 Creating shared network namespace...
πŸ’‰ Injecting rules with v1.1.2...
πŸ“– Reading rules with v1.0.6...
=== Execution Results ===
❌ FAILED: v1.0.6 crashed when reading rules created by v1.1.2!
./test-skew.sh v1.0.6.1 v1.1.2
🌐 Creating shared network namespace...
πŸ’‰ Injecting rules with v1.1.2...
πŸ“– Reading rules with v1.0.6.1...
=== Execution Results ===
βœ… PASSED: v1.0.6.1 successfully read rules created by v1.1.2.

FROM debian:bookworm
# 1. Install compilation toolchain and dependencies
RUN apt-get update && apt-get install -y \
git build-essential autoconf automake libtool \
bison flex pkg-config libgmp-dev libreadline-dev \
libedit-dev libjansson-dev \
&& rm -rf /var/lib/apt/lists/*
# 2. Build libmnl (rarely changes, master is fine)
RUN git clone --depth 1 git://git.netfilter.org/libmnl && \
cd libmnl && \
./autogen.sh && ./configure && make && make install
# 3. Build libnftnl (Using master ensures compatibility with newer nftables tags)
RUN git clone --depth 1 git://git.netfilter.org/libnftnl && \
cd libnftnl && \
./autogen.sh && ./configure && make && make install
# 4. Build nftables from the requested Git tag
ARG NFT_TAG=v1.1.3
RUN git clone --depth 1 --branch ${NFT_TAG} git://git.netfilter.org/nftables && \
cd nftables && \
./autogen.sh && \
# Disabling python bindings saves build time and dependencies
./configure --disable-python --disable-man-doc && \
make && make install
# 5. Update shared library cache so the binary finds libnftnl and libmnl
RUN ldconfig
# 6. Set the entrypoint for easy execution
ENTRYPOINT ["/usr/local/sbin/nft"]
#!/bin/bash
echo "πŸ” Fetching tags from git.netfilter.org..."
# Grab tags, extract the version, filter for v1.*, remove commit dereferences (^{}), and version-sort
TAGS=$(git ls-remote --tags git://git.netfilter.org/nftables | awk -F'/' '{print $3}' | grep -E '^v1\.' | grep -v '\^{}' | sort -V)
for TAG in $TAGS; do
# Skip v1.0.0 to strictly test versions > 1.0.0
if [ "$TAG" = "v1.0.0" ]; then
continue
fi
IMAGE_NAME="aojea/nft:${TAG}"
# Check if the image already exists locally
if docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
echo "βœ… Image $IMAGE_NAME exists, skipping."
continue
fi
echo "πŸ”¨ Building image $IMAGE_NAME..."
docker build --build-arg NFT_TAG=${TAG} -t ${IMAGE_NAME} -f Dockerfile.nft-builder .
done
#!/bin/bash
echo "πŸ“Š Gathering built aojea/nft tags..."
# Get all tags for the nft-tester image and sort them cleanly by version
TAGS=$(docker images --format '{{.Tag}}' aojea/nft | sort -V | tr '\n' ' ')
if [ -z "$TAGS" ]; then
echo "❌ Error: No nft-tester images found. Run ./build-matrix.sh first!"
exit 1
fi
# Convert tags list into an array for easy indexing
read -r -a TAG_ARRAY <<< "$TAGS"
# Define temporary files for tracking results
RESULTS_FILE="matrix_results.tmp"
rm -f "$RESULTS_FILE"
echo "πŸš€ Starting automated version-skew matrix test..."
echo "------------------------------------------------"
# Outer loop: The OLD version trying to READ (e.g., Kube-proxy behavior)
for OLD in "${TAG_ARRAY[@]}"; do
# Inner loop: The NEW version that WROTE the rules (e.g., Host behavior)
for NEW in "${TAG_ARRAY[@]}"; do
# Optimization: We are primarily interested in userspace skew where
# the host (NEW) is a higher version than the container (OLD).
# If you want to test ALL combinations, remove this if-statement block.
if [ "$(echo -e "$OLD\n$NEW" | sort -V | head -n1)" != "$OLD" ]; then
# NEW is actually older than OLD, skip or mark as N/A
echo "$OLD:$NEW:N/A" >> "$RESULTS_FILE"
continue
fi
echo -n "πŸ”„ Testing Reader: $OLD vs Writer: $NEW ... "
# Run the skew test script and suppress its detailed output to keep CI clean
./test-skew.sh "$OLD" "$NEW" > /dev/null 2>&1
STATUS=$?
if [ $STATUS -eq 0 ]; then
echo "βœ… PASSED"
echo "$OLD:$NEW:PASS" >> "$RESULTS_FILE"
else
echo "❌ FAILED"
echo "$OLD:$NEW:FAIL" >> "$RESULTS_FILE"
fi
done
done
echo -e "\n------------------------------------------------"
echo "🏁 Matrix Testing Complete! Generating Summary..."
echo -e "------------------------------------------------\n"
# --- GENERATE MARKDOWN MATRIX REPORT ---
# Print Header Line 1 (Writers / Host versions)
printf "| Reader (kube-proxy) \\ Writer (Host) "
for NEW in "${TAG_ARRAY[@]}"; do
printf "| %-6s " "$NEW"
done
echo "|"
# Print Header Separator Line
printf "|-------------------------------------"
for NEW in "${TAG_ARRAY[@]}"; do
printf "|--------"
done
echo "|"
# Print Rows
for OLD in "${TAG_ARRAY[@]}"; do
printf "| %-35s " "**$OLD**"
for NEW in "${TAG_ARRAY[@]}"; do
# Fetch the status from our temporary file
CELL_STATUS=$(grep "^${OLD}:${NEW}:" "$RESULTS_FILE" | cut -d':' -f3)
case "$CELL_STATUS" in
"PASS")
printf "| βœ… "
;;
"FAIL")
printf "| ❌ "
;;
"N/A")
printf "| -- "
;;
*)
printf "| ?? "
;;
esac
done
echo "|"
done
# Clean up
rm -f "$RESULTS_FILE"
#!/bin/bash
OLD_TAG=$1
NEW_TAG=$2
if [ -z "$OLD_TAG" ] || [ -z "$NEW_TAG" ]; then
echo "Usage: ./test-skew.sh <old-tag> <new-tag>"
echo "Example: ./test-skew.sh v1.0.6 v1.1.3"
exit 1
fi
# Unique name for the network holding container
NET_CONTAINER="skew-ns-$$"
# 1. Create a persistent network namespace using a sleeping alpine container
echo "🌐 Creating shared network namespace..."
docker run -d --name $NET_CONTAINER alpine sleep 60 > /dev/null
# Ensure the network namespace container is destroyed when the script exits (even on failure)
trap "docker rm -f $NET_CONTAINER > /dev/null" EXIT
# 2. Inject rules using the NEW version
echo "πŸ’‰ Injecting rules with ${NEW_TAG}..."
docker run --rm --cap-add=NET_ADMIN --network=container:$NET_CONTAINER --entrypoint /bin/sh aojea/nft:${NEW_TAG} -c "
/usr/local/sbin/nft add table inet skew_table
/usr/local/sbin/nft add set inet skew_table skew_set '{ type ifname; elements = { \"lo\" }; }'
"
# 3. Read rules using the OLD version
echo "πŸ“– Reading rules with ${OLD_TAG}..."
# Disable exit-on-error so we can capture the segfault instead of crashing the script
set +e
OUTPUT=$(docker run --rm --cap-add=NET_ADMIN --network=container:$NET_CONTAINER --entrypoint /bin/sh aojea/nft:${OLD_TAG} -c "/usr/local/sbin/nft list ruleset" 2>&1)
EXIT_CODE=$?
set -e
# 4. Evaluate the results
echo "=== Execution Results ==="
if [ $EXIT_CODE -ne 0 ] || echo "$OUTPUT" | grep -qiq "segmentation fault"; then
echo "❌ FAILED: ${OLD_TAG} crashed when reading rules created by ${NEW_TAG}!"
exit 1
else
echo "βœ… PASSED: ${OLD_TAG} successfully read rules created by ${NEW_TAG}."
exit 0
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment