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