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