1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# return code to signal skipped test
ksft_skip=4
# search for legacy iptables (it uses the xtables extensions
if iptables-legacy --version >/dev/null 2>&1; then
iptables='iptables-legacy'
elif iptables --version >/dev/null 2>&1; then
iptables='iptables'
else
iptables=''
fi
if ip6tables-legacy --version >/dev/null 2>&1; then
ip6tables='ip6tables-legacy'
elif ip6tables --version >/dev/null 2>&1; then
ip6tables='ip6tables'
else
ip6tables=''
fi
if nft --version >/dev/null 2>&1; then
nft='nft'
else
nft=''
fi
if [ -z "$iptables$ip6tables$nft" ]; then
echo "SKIP: Test needs iptables, ip6tables or nft"
exit $ksft_skip
fi
sfx=$(mktemp -u "XXXXXXXX")
ns1="ns1-$sfx"
ns2="ns2-$sfx"
trap "ip netns del $ns1; ip netns del $ns2" EXIT
# create two netns, disable rp_filter in ns2 and
# keep IPv6 address when moving into VRF
ip netns add "$ns1"
ip netns add "$ns2"
ip netns exec "$ns2" sysctl -q net.ipv4.conf.all.rp_filter=0
ip netns exec "$ns2" sysctl -q net.ipv4.conf.default.rp_filter=0
ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.keep_addr_on_down=1
# a standard connection between the netns, should not trigger rp filter
ip -net "$ns1" link add v0 type veth peer name v0 netns "$ns2"
ip -net "$ns1" link set v0 up; ip -net "$ns2" link set v0 up
ip -net "$ns1" a a 192.168.23.2/24 dev v0
ip -net "$ns2" a a 192.168.23.1/24 dev v0
ip -net "$ns1" a a fec0:23::2/64 dev v0 nodad
ip -net "$ns2" a a fec0:23::1/64 dev v0 nodad
# rp filter testing: ns1 sends packets via v0 which ns2 would route back via d0
ip -net "$ns2" link add d0 type dummy
ip -net "$ns2" link set d0 up
ip -net "$ns1" a a 192.168.42.2/24 dev v0
ip -net "$ns2" a a 192.168.42.1/24 dev d0
ip -net "$ns1" a a fec0:42::2/64 dev v0 nodad
ip -net "$ns2" a a fec0:42::1/64 dev d0 nodad
# firewall matches to test
[ -n "$iptables" ] && ip netns exec "$ns2" \
"$iptables" -t raw -A PREROUTING -s 192.168.0.0/16 -m rpfilter
[ -n "$ip6tables" ] && ip netns exec "$ns2" \
"$ip6tables" -t raw -A PREROUTING -s fec0::/16 -m rpfilter
[ -n "$nft" ] && ip netns exec "$ns2" $nft -f - <<EOF
table inet t {
chain c {
type filter hook prerouting priority raw;
ip saddr 192.168.0.0/16 fib saddr . iif oif exists counter
ip6 saddr fec0::/16 fib saddr . iif oif exists counter
}
}
EOF
die() {
echo "FAIL: $*"
#ip netns exec "$ns2" "$iptables" -t raw -vS
#ip netns exec "$ns2" "$ip6tables" -t raw -vS
#ip netns exec "$ns2" nft list ruleset
exit 1
}
# check rule counters, return true if rule did not match
ipt_zero_rule() { # (command)
[ -n "$1" ] || return 0
ip netns exec "$ns2" "$1" -t raw -vS | grep -q -- "-m rpfilter -c 0 0"
}
nft_zero_rule() { # (family)
[ -n "$nft" ] || return 0
ip netns exec "$ns2" "$nft" list chain inet t c | \
grep -q "$1 saddr .* counter packets 0 bytes 0"
}
netns_ping() { # (netns, args...)
local netns="$1"
shift
ip netns exec "$netns" ping -q -c 1 -W 1 "$@" >/dev/null
}
testrun() {
# clear counters first
[ -n "$iptables" ] && ip netns exec "$ns2" "$iptables" -t raw -Z
[ -n "$ip6tables" ] && ip netns exec "$ns2" "$ip6tables" -t raw -Z
if [ -n "$nft" ]; then
(
echo "delete table inet t";
ip netns exec "$ns2" $nft -s list table inet t;
) | ip netns exec "$ns2" $nft -f -
fi
# test 1: martian traffic should fail rpfilter matches
netns_ping "$ns1" -I v0 192.168.42.1 && \
die "martian ping 192.168.42.1 succeeded"
netns_ping "$ns1" -I v0 fec0:42::1 && \
die "martian ping fec0:42::1 succeeded"
ipt_zero_rule "$iptables" || die "iptables matched martian"
ipt_zero_rule "$ip6tables" || die "ip6tables matched martian"
nft_zero_rule ip || die "nft IPv4 matched martian"
nft_zero_rule ip6 || die "nft IPv6 matched martian"
# test 2: rpfilter match should pass for regular traffic
netns_ping "$ns1" 192.168.23.1 || \
die "regular ping 192.168.23.1 failed"
netns_ping "$ns1" fec0:23::1 || \
die "regular ping fec0:23::1 failed"
ipt_zero_rule "$iptables" && die "iptables match not effective"
ipt_zero_rule "$ip6tables" && die "ip6tables match not effective"
nft_zero_rule ip && die "nft IPv4 match not effective"
nft_zero_rule ip6 && die "nft IPv6 match not effective"
}
testrun
# repeat test with vrf device in $ns2
ip -net "$ns2" link add vrf0 type vrf table 10
ip -net "$ns2" link set vrf0 up
ip -net "$ns2" link set v0 master vrf0
testrun
echo "PASS: netfilter reverse path match works as intended"
exit 0
|