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