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# avoid neighbor lookups and enable martian IPv6 pings 65ns2_hwaddr=$(ip -net "$ns2" link show dev v0 | \ 66 sed -n 's, *link/ether \([^ ]*\) .*,\1,p') 67ns1_hwaddr=$(ip -net "$ns1" link show dev v0 | \ 68 sed -n 's, *link/ether \([^ ]*\) .*,\1,p') 69ip -net "$ns1" neigh add fec0:42::1 lladdr "$ns2_hwaddr" nud permanent dev v0 70ip -net "$ns1" neigh add fec0:23::1 lladdr "$ns2_hwaddr" nud permanent dev v0 71ip -net "$ns2" neigh add fec0:42::2 lladdr "$ns1_hwaddr" nud permanent dev d0 72ip -net "$ns2" neigh add fec0:23::2 lladdr "$ns1_hwaddr" nud permanent dev v0 73 74# firewall matches to test 75[ -n "$iptables" ] && { 76 common='-t raw -A PREROUTING -s 192.168.0.0/16' 77 common+=' -p icmp --icmp-type echo-request' 78 if ! ip netns exec "$ns2" "$iptables" $common -m rpfilter;then 79 echo "Cannot add rpfilter rule" 80 exit $ksft_skip 81 fi 82 ip netns exec "$ns2" "$iptables" $common -m rpfilter --invert 83} 84[ -n "$ip6tables" ] && { 85 common='-t raw -A PREROUTING -s fec0::/16' 86 common+=' -p icmpv6 --icmpv6-type echo-request' 87 if ! ip netns exec "$ns2" "$ip6tables" $common -m rpfilter;then 88 echo "Cannot add rpfilter rule" 89 exit $ksft_skip 90 fi 91 ip netns exec "$ns2" "$ip6tables" $common -m rpfilter --invert 92} 93[ -n "$nft" ] && ip netns exec "$ns2" $nft -f - <<EOF 94table inet t { 95 chain c { 96 type filter hook prerouting priority raw; 97 ip saddr 192.168.0.0/16 icmp type echo-request \ 98 fib saddr . iif oif exists counter 99 ip6 saddr fec0::/16 icmpv6 type echo-request \ 100 fib saddr . iif oif exists counter 101 } 102} 103EOF 104 105die() { 106 echo "FAIL: $*" 107 #ip netns exec "$ns2" "$iptables" -t raw -vS 108 #ip netns exec "$ns2" "$ip6tables" -t raw -vS 109 #ip netns exec "$ns2" nft list ruleset 110 exit 1 111} 112 113# check rule counters, return true if rule did not match 114ipt_zero_rule() { # (command) 115 [ -n "$1" ] || return 0 116 ip netns exec "$ns2" "$1" -t raw -vS | grep -q -- "-m rpfilter -c 0 0" 117} 118ipt_zero_reverse_rule() { # (command) 119 [ -n "$1" ] || return 0 120 ip netns exec "$ns2" "$1" -t raw -vS | \ 121 grep -q -- "-m rpfilter --invert -c 0 0" 122} 123nft_zero_rule() { # (family) 124 [ -n "$nft" ] || return 0 125 ip netns exec "$ns2" "$nft" list chain inet t c | \ 126 grep -q "$1 saddr .* counter packets 0 bytes 0" 127} 128 129netns_ping() { # (netns, args...) 130 local netns="$1" 131 shift 132 ip netns exec "$netns" ping -q -c 1 -W 1 "$@" >/dev/null 133} 134 135clear_counters() { 136 [ -n "$iptables" ] && ip netns exec "$ns2" "$iptables" -t raw -Z 137 [ -n "$ip6tables" ] && ip netns exec "$ns2" "$ip6tables" -t raw -Z 138 if [ -n "$nft" ]; then 139 ( 140 echo "delete table inet t"; 141 ip netns exec "$ns2" $nft -s list table inet t; 142 ) | ip netns exec "$ns2" $nft -f - 143 fi 144} 145 146testrun() { 147 clear_counters 148 149 # test 1: martian traffic should fail rpfilter matches 150 netns_ping "$ns1" -I v0 192.168.42.1 && \ 151 die "martian ping 192.168.42.1 succeeded" 152 netns_ping "$ns1" -I v0 fec0:42::1 && \ 153 die "martian ping fec0:42::1 succeeded" 154 155 ipt_zero_rule "$iptables" || die "iptables matched martian" 156 ipt_zero_rule "$ip6tables" || die "ip6tables matched martian" 157 ipt_zero_reverse_rule "$iptables" && die "iptables not matched martian" 158 ipt_zero_reverse_rule "$ip6tables" && die "ip6tables not matched martian" 159 nft_zero_rule ip || die "nft IPv4 matched martian" 160 nft_zero_rule ip6 || die "nft IPv6 matched martian" 161 162 clear_counters 163 164 # test 2: rpfilter match should pass for regular traffic 165 netns_ping "$ns1" 192.168.23.1 || \ 166 die "regular ping 192.168.23.1 failed" 167 netns_ping "$ns1" fec0:23::1 || \ 168 die "regular ping fec0:23::1 failed" 169 170 ipt_zero_rule "$iptables" && die "iptables match not effective" 171 ipt_zero_rule "$ip6tables" && die "ip6tables match not effective" 172 ipt_zero_reverse_rule "$iptables" || die "iptables match over-effective" 173 ipt_zero_reverse_rule "$ip6tables" || die "ip6tables match over-effective" 174 nft_zero_rule ip && die "nft IPv4 match not effective" 175 nft_zero_rule ip6 && die "nft IPv6 match not effective" 176 177} 178 179testrun 180 181# repeat test with vrf device in $ns2 182ip -net "$ns2" link add vrf0 type vrf table 10 183ip -net "$ns2" link set vrf0 up 184ip -net "$ns2" link set v0 master vrf0 185 186testrun 187 188echo "PASS: netfilter reverse path match works as intended" 189exit 0 190