aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/testing/selftests/netfilter/rpath.sh
blob: 5289c8447a419b611befb82ddba6441ebee390ac (plain) (blame)
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/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" ] && {
	common='-t raw -A PREROUTING -s 192.168.0.0/16'
	ip netns exec "$ns2" "$iptables" $common -m rpfilter
	ip netns exec "$ns2" "$iptables" $common -m rpfilter --invert
}
[ -n "$ip6tables" ] && {
	common='-t raw -A PREROUTING -s fec0::/16'
	ip netns exec "$ns2" "$ip6tables" $common -m rpfilter
	ip netns exec "$ns2" "$ip6tables" $common -m rpfilter --invert
}
[ -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"
}
ipt_zero_reverse_rule() { # (command)
	[ -n "$1" ] || return 0
	ip netns exec "$ns2" "$1" -t raw -vS | \
		grep -q -- "-m rpfilter --invert -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
}

clear_counters() {
	[ -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
}

testrun() {
	clear_counters

	# 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"
	ipt_zero_reverse_rule "$iptables" && die "iptables not matched martian"
	ipt_zero_reverse_rule "$ip6tables" && die "ip6tables not matched martian"
	nft_zero_rule ip || die "nft IPv4 matched martian"
	nft_zero_rule ip6 || die "nft IPv6 matched martian"

	clear_counters

	# 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"
	ipt_zero_reverse_rule "$iptables" || die "iptables match over-effective"
	ipt_zero_reverse_rule "$ip6tables" || die "ip6tables match over-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