-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests: netfilter: add a vrf+conntrack testcase
Rework the reproducer for the vrf+conntrack regression reported by Eugene into a selftest and also add a test for ip masquerading that Lahav fixed recently. With net or net-next tree, the first test fails and the latter two pass. With 09e856d ("vrf: Reset skb conntrack connection on VRF rcv") reverted first test passes but the last two fail. A proper fix needs more work, for time being a revert seems to be the best choice, snat/masquerade did not work before the fix. Link: https://lore.kernel.org/netdev/378ca299-4474-7e9a-3d36-2350c8c98995@gmail.com/T/#m95358a31810df7392f541f99d187227bc75c9963 Reported-by: Eugene Crosser <crosser@average.org> Cc: Lahav Schlesinger <lschlesinger@drivenets.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
- Loading branch information
Florian Westphal
authored and
Pablo Neira Ayuso
committed
Nov 8, 2021
1 parent
c45231a
commit 33b8aad
Showing
2 changed files
with
221 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
#!/bin/sh | ||
|
||
# This script demonstrates interaction of conntrack and vrf. | ||
# The vrf driver calls the netfilter hooks again, with oif/iif | ||
# pointing at the VRF device. | ||
# | ||
# For ingress, this means first iteration has iifname of lower/real | ||
# device. In this script, thats veth0. | ||
# Second iteration is iifname set to vrf device, tvrf in this script. | ||
# | ||
# For egress, this is reversed: first iteration has the vrf device, | ||
# second iteration is done with the lower/real/veth0 device. | ||
# | ||
# test_ct_zone_in demonstrates unexpected change of nftables | ||
# behavior # caused by commit 09e856d54bda5f28 "vrf: Reset skb conntrack | ||
# connection on VRF rcv" | ||
# | ||
# It was possible to assign conntrack zone to a packet (or mark it for | ||
# `notracking`) in the prerouting chain before conntrack, based on real iif. | ||
# | ||
# After the change, the zone assignment is lost and the zone is assigned based | ||
# on the VRF master interface (in case such a rule exists). | ||
# assignment is lost. Instead, assignment based on the `iif` matching | ||
# Thus it is impossible to distinguish packets based on the original | ||
# interface. | ||
# | ||
# test_masquerade_vrf and test_masquerade_veth0 demonstrate the problem | ||
# that was supposed to be fixed by the commit mentioned above to make sure | ||
# that any fix to test case 1 won't break masquerade again. | ||
|
||
ksft_skip=4 | ||
|
||
IP0=172.30.30.1 | ||
IP1=172.30.30.2 | ||
PFXL=30 | ||
ret=0 | ||
|
||
sfx=$(mktemp -u "XXXXXXXX") | ||
ns0="ns0-$sfx" | ||
ns1="ns1-$sfx" | ||
|
||
cleanup() | ||
{ | ||
ip netns pids $ns0 | xargs kill 2>/dev/null | ||
ip netns pids $ns1 | xargs kill 2>/dev/null | ||
|
||
ip netns del $ns0 $ns1 | ||
} | ||
|
||
nft --version > /dev/null 2>&1 | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not run test without nft tool" | ||
exit $ksft_skip | ||
fi | ||
|
||
ip -Version > /dev/null 2>&1 | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not run test without ip tool" | ||
exit $ksft_skip | ||
fi | ||
|
||
ip netns add "$ns0" | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not create net namespace $ns0" | ||
exit $ksft_skip | ||
fi | ||
ip netns add "$ns1" | ||
|
||
trap cleanup EXIT | ||
|
||
ip netns exec $ns0 sysctl -q -w net.ipv4.conf.default.rp_filter=0 | ||
ip netns exec $ns0 sysctl -q -w net.ipv4.conf.all.rp_filter=0 | ||
ip netns exec $ns0 sysctl -q -w net.ipv4.conf.all.rp_filter=0 | ||
|
||
ip link add veth0 netns "$ns0" type veth peer name veth0 netns "$ns1" > /dev/null 2>&1 | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not add veth device" | ||
exit $ksft_skip | ||
fi | ||
|
||
ip -net $ns0 li add tvrf type vrf table 9876 | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not add vrf device" | ||
exit $ksft_skip | ||
fi | ||
|
||
ip -net $ns0 li set lo up | ||
|
||
ip -net $ns0 li set veth0 master tvrf | ||
ip -net $ns0 li set tvrf up | ||
ip -net $ns0 li set veth0 up | ||
ip -net $ns1 li set veth0 up | ||
|
||
ip -net $ns0 addr add $IP0/$PFXL dev veth0 | ||
ip -net $ns1 addr add $IP1/$PFXL dev veth0 | ||
|
||
ip netns exec $ns1 iperf3 -s > /dev/null 2>&1& | ||
if [ $? -ne 0 ];then | ||
echo "SKIP: Could not start iperf3" | ||
exit $ksft_skip | ||
fi | ||
|
||
# test vrf ingress handling. | ||
# The incoming connection should be placed in conntrack zone 1, | ||
# as decided by the first iteration of the ruleset. | ||
test_ct_zone_in() | ||
{ | ||
ip netns exec $ns0 nft -f - <<EOF | ||
table testct { | ||
chain rawpre { | ||
type filter hook prerouting priority raw; | ||
iif { veth0, tvrf } counter meta nftrace set 1 | ||
iif veth0 counter ct zone set 1 counter return | ||
iif tvrf counter ct zone set 2 counter return | ||
ip protocol icmp counter | ||
notrack counter | ||
} | ||
chain rawout { | ||
type filter hook output priority raw; | ||
oif veth0 counter ct zone set 1 counter return | ||
oif tvrf counter ct zone set 2 counter return | ||
notrack counter | ||
} | ||
} | ||
EOF | ||
ip netns exec $ns1 ping -W 1 -c 1 -I veth0 $IP0 > /dev/null | ||
|
||
# should be in zone 1, not zone 2 | ||
count=$(ip netns exec $ns0 conntrack -L -s $IP1 -d $IP0 -p icmp --zone 1 2>/dev/null | wc -l) | ||
if [ $count -eq 1 ]; then | ||
echo "PASS: entry found in conntrack zone 1" | ||
else | ||
echo "FAIL: entry not found in conntrack zone 1" | ||
count=$(ip netns exec $ns0 conntrack -L -s $IP1 -d $IP0 -p icmp --zone 2 2> /dev/null | wc -l) | ||
if [ $count -eq 1 ]; then | ||
echo "FAIL: entry found in zone 2 instead" | ||
else | ||
echo "FAIL: entry not in zone 1 or 2, dumping table" | ||
ip netns exec $ns0 conntrack -L | ||
ip netns exec $ns0 nft list ruleset | ||
fi | ||
fi | ||
} | ||
|
||
# add masq rule that gets evaluated w. outif set to vrf device. | ||
# This tests the first iteration of the packet through conntrack, | ||
# oifname is the vrf device. | ||
test_masquerade_vrf() | ||
{ | ||
ip netns exec $ns0 conntrack -F 2>/dev/null | ||
|
||
ip netns exec $ns0 nft -f - <<EOF | ||
flush ruleset | ||
table ip nat { | ||
chain postrouting { | ||
type nat hook postrouting priority 0; | ||
# NB: masquerade should always be combined with 'oif(name) bla', | ||
# lack of this is intentional here, we want to exercise double-snat. | ||
ip saddr 172.30.30.0/30 counter masquerade random | ||
} | ||
} | ||
EOF | ||
ip netns exec $ns0 ip vrf exec tvrf iperf3 -t 1 -c $IP1 >/dev/null | ||
if [ $? -ne 0 ]; then | ||
echo "FAIL: iperf3 connect failure with masquerade + sport rewrite on vrf device" | ||
ret=1 | ||
return | ||
fi | ||
|
||
# must also check that nat table was evaluated on second (lower device) iteration. | ||
ip netns exec $ns0 nft list table ip nat |grep -q 'counter packets 2' | ||
if [ $? -eq 0 ]; then | ||
echo "PASS: iperf3 connect with masquerade + sport rewrite on vrf device" | ||
else | ||
echo "FAIL: vrf masq rule has unexpected counter value" | ||
ret=1 | ||
fi | ||
} | ||
|
||
# add masq rule that gets evaluated w. outif set to veth device. | ||
# This tests the 2nd iteration of the packet through conntrack, | ||
# oifname is the lower device (veth0 in this case). | ||
test_masquerade_veth() | ||
{ | ||
ip netns exec $ns0 conntrack -F 2>/dev/null | ||
ip netns exec $ns0 nft -f - <<EOF | ||
flush ruleset | ||
table ip nat { | ||
chain postrouting { | ||
type nat hook postrouting priority 0; | ||
meta oif veth0 ip saddr 172.30.30.0/30 counter masquerade random | ||
} | ||
} | ||
EOF | ||
ip netns exec $ns0 ip vrf exec tvrf iperf3 -t 1 -c $IP1 > /dev/null | ||
if [ $? -ne 0 ]; then | ||
echo "FAIL: iperf3 connect failure with masquerade + sport rewrite on veth device" | ||
ret=1 | ||
return | ||
fi | ||
|
||
# must also check that nat table was evaluated on second (lower device) iteration. | ||
ip netns exec $ns0 nft list table ip nat |grep -q 'counter packets 2' | ||
if [ $? -eq 0 ]; then | ||
echo "PASS: iperf3 connect with masquerade + sport rewrite on veth device" | ||
else | ||
echo "FAIL: vrf masq rule has unexpected counter value" | ||
ret=1 | ||
fi | ||
} | ||
|
||
test_ct_zone_in | ||
test_masquerade_vrf | ||
test_masquerade_veth | ||
|
||
exit $ret |