1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Check xfrm policy resolution.  Topology:
5#
6# 1.2   1.1   3.1  3.10    2.1   2.2
7# eth1  eth1 veth0 veth0 eth1   eth1
8# ns1 ---- ns3 ----- ns4 ---- ns2
9#
10# ns3 and ns4 are connected via ipsec tunnel.
11# pings from ns1 to ns2 (and vice versa) are supposed to work like this:
12# ns1: ping 10.0.2.2: passes via ipsec tunnel.
13# ns2: ping 10.0.1.2: passes via ipsec tunnel.
14
15# ns1: ping 10.0.1.253: passes via ipsec tunnel (direct policy)
16# ns2: ping 10.0.2.253: passes via ipsec tunnel (direct policy)
17#
18# ns1: ping 10.0.2.254: does NOT pass via ipsec tunnel (exception)
19# ns2: ping 10.0.1.254: does NOT pass via ipsec tunnel (exception)
20
21# Kselftest framework requirement - SKIP code is 4.
22ksft_skip=4
23ret=0
24policy_checks_ok=1
25
26KEY_SHA=0xdeadbeef1234567890abcdefabcdefabcdefabcd
27KEY_AES=0x0123456789abcdef0123456789012345
28SPI1=0x1
29SPI2=0x2
30
31do_esp_policy() {
32    local ns=$1
33    local me=$2
34    local remote=$3
35    local lnet=$4
36    local rnet=$5
37
38    # to encrypt packets as they go out (includes forwarded packets that need encapsulation)
39    ip -net $ns xfrm policy add src $lnet dst $rnet dir out tmpl src $me dst $remote proto esp mode tunnel priority 100 action allow
40    # to fwd decrypted packets after esp processing:
41    ip -net $ns xfrm policy add src $rnet dst $lnet dir fwd tmpl src $remote dst $me proto esp mode tunnel priority 100 action allow
42}
43
44do_esp() {
45    local ns=$1
46    local me=$2
47    local remote=$3
48    local lnet=$4
49    local rnet=$5
50    local spi_out=$6
51    local spi_in=$7
52
53    ip -net $ns xfrm state add src $remote dst $me proto esp spi $spi_in  enc aes $KEY_AES  auth sha1 $KEY_SHA  mode tunnel sel src $rnet dst $lnet
54    ip -net $ns xfrm state add src $me  dst $remote proto esp spi $spi_out enc aes $KEY_AES auth sha1 $KEY_SHA mode tunnel sel src $lnet dst $rnet
55
56    do_esp_policy $ns $me $remote $lnet $rnet
57}
58
59# add policies with different netmasks, to make sure kernel carries
60# the policies contained within new netmask over when search tree is
61# re-built.
62# peer netns that are supposed to be encapsulated via esp have addresses
63# in the 10.0.1.0/24 and 10.0.2.0/24 subnets, respectively.
64#
65# Adding a policy for '10.0.1.0/23' will make it necessary to
66# alter the prefix of 10.0.1.0 subnet.
67# In case new prefix overlaps with existing node, the node and all
68# policies it carries need to be merged with the existing one(s).
69#
70# Do that here.
71do_overlap()
72{
73    local ns=$1
74
75    # adds new nodes to tree (neither network exists yet in policy database).
76    ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block
77
78    # adds a new node in the 10.0.0.0/24 tree (dst node exists).
79    ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block
80
81    # adds a 10.2.0.0/23 node, but for different dst.
82    ip -net $ns xfrm policy add src 10.2.0.0/23 dst 10.0.1.0/24 dir fwd priority 200 action block
83
84    # dst now overlaps with the 10.0.1.0/24 ESP policy in fwd.
85    # kernel must 'promote' existing one (10.0.0.0/24) to 10.0.0.0/23.
86    # But 10.0.0.0/23 also includes existing 10.0.1.0/24, so that node
87    # also has to be merged too, including source-sorted subtrees.
88    # old:
89    # 10.0.0.0/24 (node 1 in dst tree of the bin)
90    #    10.1.0.0/24 (node in src tree of dst node 1)
91    #    10.2.0.0/24 (node in src tree of dst node 1)
92    # 10.0.1.0/24 (node 2 in dst tree of the bin)
93    #    10.0.2.0/24 (node in src tree of dst node 2)
94    #    10.2.0.0/24 (node in src tree of dst node 2)
95    #
96    # The next 'policy add' adds dst '10.0.0.0/23', which means
97    # that dst node 1 and dst node 2 have to be merged including
98    # the sub-tree.  As no duplicates are allowed, policies in
99    # the two '10.0.2.0/24' are also merged.
100    #
101    # after the 'add', internal search tree should look like this:
102    # 10.0.0.0/23 (node in dst tree of bin)
103    #     10.0.2.0/24 (node in src tree of dst node)
104    #     10.1.0.0/24 (node in src tree of dst node)
105    #     10.2.0.0/24 (node in src tree of dst node)
106    #
107    # 10.0.0.0/24 and 10.0.1.0/24 nodes have been merged as 10.0.0.0/23.
108    ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/23 dir fwd priority 200 action block
109}
110
111do_esp_policy_get_check() {
112    local ns=$1
113    local lnet=$2
114    local rnet=$3
115
116    ip -net $ns xfrm policy get src $lnet dst $rnet dir out > /dev/null
117    if [ $? -ne 0 ] && [ $policy_checks_ok -eq 1 ] ;then
118        policy_checks_ok=0
119        echo "FAIL: ip -net $ns xfrm policy get src $lnet dst $rnet dir out"
120        ret=1
121    fi
122
123    ip -net $ns xfrm policy get src $rnet dst $lnet dir fwd > /dev/null
124    if [ $? -ne 0 ] && [ $policy_checks_ok -eq 1 ] ;then
125        policy_checks_ok=0
126        echo "FAIL: ip -net $ns xfrm policy get src $rnet dst $lnet dir fwd"
127        ret=1
128    fi
129}
130
131do_exception() {
132    local ns=$1
133    local me=$2
134    local remote=$3
135    local encryptip=$4
136    local plain=$5
137
138    # network $plain passes without tunnel
139    ip -net $ns xfrm policy add dst $plain dir out priority 10 action allow
140
141    # direct policy for $encryptip, use tunnel, higher prio takes precedence
142    ip -net $ns xfrm policy add dst $encryptip dir out tmpl src $me dst $remote proto esp mode tunnel priority 1 action allow
143}
144
145# policies that are not supposed to match any packets generated in this test.
146do_dummies4() {
147    local ns=$1
148
149    for i in $(seq 10 16);do
150      # dummy policy with wildcard src/dst.
151      echo netns exec $ns ip xfrm policy add src 0.0.0.0/0 dst 10.$i.99.0/30 dir out action block
152      echo netns exec $ns ip xfrm policy add src 10.$i.99.0/30 dst 0.0.0.0/0 dir out action block
153      for j in $(seq 32 64);do
154        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/30 dst 10.$i.$j.0/30 dir out action block
155        # silly, as it encompasses the one above too, but its allowed:
156        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/29 dst 10.$i.$j.0/29 dir out action block
157        # and yet again, even more broad one.
158        echo netns exec $ns ip xfrm policy add src 10.$i.1.0/24 dst 10.$i.$j.0/24 dir out action block
159        echo netns exec $ns ip xfrm policy add src 10.$i.$j.0/24 dst 10.$i.1.0/24 dir fwd action block
160      done
161    done | ip -batch /dev/stdin
162}
163
164do_dummies6() {
165    local ns=$1
166
167    for i in $(seq 10 16);do
168      for j in $(seq 32 64);do
169       echo netns exec $ns ip xfrm policy add src dead:$i::/64 dst dead:$i:$j::/64 dir out action block
170       echo netns exec $ns ip xfrm policy add src dead:$i:$j::/64 dst dead:$i::/24 dir fwd action block
171      done
172    done | ip -batch /dev/stdin
173}
174
175check_ipt_policy_count()
176{
177	ns=$1
178
179	ip netns exec $ns iptables-save -c |grep policy | ( read c rest
180		ip netns exec $ns iptables -Z
181		if [ x"$c" = x'[0:0]' ]; then
182			exit 0
183		elif [ x"$c" = x ]; then
184			echo "ERROR: No counters"
185			ret=1
186			exit 111
187		else
188			exit 1
189		fi
190	)
191}
192
193check_xfrm() {
194	# 0: iptables -m policy rule count == 0
195	# 1: iptables -m policy rule count != 0
196	rval=$1
197	ip=$2
198	lret=0
199
200	ip netns exec ns1 ping -q -c 1 10.0.2.$ip > /dev/null
201
202	check_ipt_policy_count ns3
203	if [ $? -ne $rval ] ; then
204		lret=1
205	fi
206	check_ipt_policy_count ns4
207	if [ $? -ne $rval ] ; then
208		lret=1
209	fi
210
211	ip netns exec ns2 ping -q -c 1 10.0.1.$ip > /dev/null
212
213	check_ipt_policy_count ns3
214	if [ $? -ne $rval ] ; then
215		lret=1
216	fi
217	check_ipt_policy_count ns4
218	if [ $? -ne $rval ] ; then
219		lret=1
220	fi
221
222	return $lret
223}
224
225check_exceptions()
226{
227	logpostfix="$1"
228	local lret=0
229
230	# ping to .254 should be excluded from the tunnel (exception is in place).
231	check_xfrm 0 254
232	if [ $? -ne 0 ]; then
233		echo "FAIL: expected ping to .254 to fail ($logpostfix)"
234		lret=1
235	else
236		echo "PASS: ping to .254 bypassed ipsec tunnel ($logpostfix)"
237	fi
238
239	# ping to .253 should use use ipsec due to direct policy exception.
240	check_xfrm 1 253
241	if [ $? -ne 0 ]; then
242		echo "FAIL: expected ping to .253 to use ipsec tunnel ($logpostfix)"
243		lret=1
244	else
245		echo "PASS: direct policy matches ($logpostfix)"
246	fi
247
248	# ping to .2 should use ipsec.
249	check_xfrm 1 2
250	if [ $? -ne 0 ]; then
251		echo "FAIL: expected ping to .2 to use ipsec tunnel ($logpostfix)"
252		lret=1
253	else
254		echo "PASS: policy matches ($logpostfix)"
255	fi
256
257	return $lret
258}
259
260#check for needed privileges
261if [ "$(id -u)" -ne 0 ];then
262	echo "SKIP: Need root privileges"
263	exit $ksft_skip
264fi
265
266ip -Version 2>/dev/null >/dev/null
267if [ $? -ne 0 ];then
268	echo "SKIP: Could not run test without the ip tool"
269	exit $ksft_skip
270fi
271
272# needed to check if policy lookup got valid ipsec result
273iptables --version 2>/dev/null >/dev/null
274if [ $? -ne 0 ];then
275	echo "SKIP: Could not run test without iptables tool"
276	exit $ksft_skip
277fi
278
279for i in 1 2 3 4; do
280    ip netns add ns$i
281    ip -net ns$i link set lo up
282done
283
284DEV=veth0
285ip link add $DEV netns ns1 type veth peer name eth1 netns ns3
286ip link add $DEV netns ns2 type veth peer name eth1 netns ns4
287
288ip link add $DEV netns ns3 type veth peer name veth0 netns ns4
289
290DEV=veth0
291for i in 1 2; do
292    ip -net ns$i link set $DEV up
293    ip -net ns$i addr add 10.0.$i.2/24 dev $DEV
294    ip -net ns$i addr add dead:$i::2/64 dev $DEV
295
296    ip -net ns$i addr add 10.0.$i.253 dev $DEV
297    ip -net ns$i addr add 10.0.$i.254 dev $DEV
298    ip -net ns$i addr add dead:$i::fd dev $DEV
299    ip -net ns$i addr add dead:$i::fe dev $DEV
300done
301
302for i in 3 4; do
303ip -net ns$i link set eth1 up
304ip -net ns$i link set veth0 up
305done
306
307ip -net ns1 route add default via 10.0.1.1
308ip -net ns2 route add default via 10.0.2.1
309
310ip -net ns3 addr add 10.0.1.1/24 dev eth1
311ip -net ns3 addr add 10.0.3.1/24 dev veth0
312ip -net ns3 addr add 2001:1::1/64 dev eth1
313ip -net ns3 addr add 2001:3::1/64 dev veth0
314
315ip -net ns3 route add default via 10.0.3.10
316
317ip -net ns4 addr add 10.0.2.1/24 dev eth1
318ip -net ns4 addr add 10.0.3.10/24 dev veth0
319ip -net ns4 addr add 2001:2::1/64 dev eth1
320ip -net ns4 addr add 2001:3::10/64 dev veth0
321ip -net ns4 route add default via 10.0.3.1
322
323for j in 4 6; do
324	for i in 3 4;do
325		ip netns exec ns$i sysctl net.ipv$j.conf.eth1.forwarding=1 > /dev/null
326		ip netns exec ns$i sysctl net.ipv$j.conf.veth0.forwarding=1 > /dev/null
327	done
328done
329
330# abuse iptables rule counter to check if ping matches a policy
331ip netns exec ns3 iptables -p icmp -A FORWARD -m policy --dir out --pol ipsec
332ip netns exec ns4 iptables -p icmp -A FORWARD -m policy --dir out --pol ipsec
333if [ $? -ne 0 ];then
334	echo "SKIP: Could not insert iptables rule"
335	for i in 1 2 3 4;do ip netns del ns$i;done
336	exit $ksft_skip
337fi
338
339#          localip  remoteip  localnet    remotenet
340do_esp ns3 10.0.3.1 10.0.3.10 10.0.1.0/24 10.0.2.0/24 $SPI1 $SPI2
341do_esp ns3 dead:3::1 dead:3::10 dead:1::/64 dead:2::/64 $SPI1 $SPI2
342do_esp ns4 10.0.3.10 10.0.3.1 10.0.2.0/24 10.0.1.0/24 $SPI2 $SPI1
343do_esp ns4 dead:3::10 dead:3::1 dead:2::/64 dead:1::/64 $SPI2 $SPI1
344
345do_dummies4 ns3
346do_dummies6 ns4
347
348do_esp_policy_get_check ns3 10.0.1.0/24 10.0.2.0/24
349do_esp_policy_get_check ns4 10.0.2.0/24 10.0.1.0/24
350do_esp_policy_get_check ns3 dead:1::/64 dead:2::/64
351do_esp_policy_get_check ns4 dead:2::/64 dead:1::/64
352
353# ping to .254 should use ipsec, exception is not installed.
354check_xfrm 1 254
355if [ $? -ne 0 ]; then
356	echo "FAIL: expected ping to .254 to use ipsec tunnel"
357	ret=1
358else
359	echo "PASS: policy before exception matches"
360fi
361
362# installs exceptions
363#                localip  remoteip   encryptdst  plaindst
364do_exception ns3 10.0.3.1 10.0.3.10 10.0.2.253 10.0.2.240/28
365do_exception ns4 10.0.3.10 10.0.3.1 10.0.1.253 10.0.1.240/28
366
367do_exception ns3 dead:3::1 dead:3::10 dead:2::fd  dead:2:f0::/96
368do_exception ns4 dead:3::10 dead:3::1 dead:1::fd  dead:1:f0::/96
369
370check_exceptions "exceptions"
371if [ $? -ne 0 ]; then
372	ret=1
373fi
374
375# insert block policies with adjacent/overlapping netmasks
376do_overlap ns3
377
378check_exceptions "exceptions and block policies"
379if [ $? -ne 0 ]; then
380	ret=1
381fi
382
383for n in ns3 ns4;do
384	ip -net $n xfrm policy set hthresh4 28 24 hthresh6 126 125
385	sleep $((RANDOM%5))
386done
387
388check_exceptions "exceptions and block policies after hresh changes"
389
390# full flush of policy db, check everything gets freed incl. internal meta data
391ip -net ns3 xfrm policy flush
392
393do_esp_policy ns3 10.0.3.1 10.0.3.10 10.0.1.0/24 10.0.2.0/24
394do_exception ns3 10.0.3.1 10.0.3.10 10.0.2.253 10.0.2.240/28
395
396# move inexact policies to hash table
397ip -net ns3 xfrm policy set hthresh4 16 16
398
399sleep $((RANDOM%5))
400check_exceptions "exceptions and block policies after hthresh change in ns3"
401
402# restore original hthresh settings -- move policies back to tables
403for n in ns3 ns4;do
404	ip -net $n xfrm policy set hthresh4 32 32 hthresh6 128 128
405	sleep $((RANDOM%5))
406done
407check_exceptions "exceptions and block policies after hresh change to normal"
408
409for i in 1 2 3 4;do ip netns del ns$i;done
410
411exit $ret
412