1#!/bin/bash
2#
3# This test is for basic NAT functionality: snat, dnat, redirect, masquerade.
4#
5
6# Kselftest framework requirement - SKIP code is 4.
7ksft_skip=4
8ret=0
9test_inet_nat=true
10
11sfx=$(mktemp -u "XXXXXXXX")
12ns0="ns0-$sfx"
13ns1="ns1-$sfx"
14ns2="ns2-$sfx"
15
16cleanup()
17{
18	for i in 0 1 2; do ip netns del ns$i-"$sfx";done
19}
20
21nft --version > /dev/null 2>&1
22if [ $? -ne 0 ];then
23	echo "SKIP: Could not run test without nft tool"
24	exit $ksft_skip
25fi
26
27ip -Version > /dev/null 2>&1
28if [ $? -ne 0 ];then
29	echo "SKIP: Could not run test without ip tool"
30	exit $ksft_skip
31fi
32
33ip netns add "$ns0"
34if [ $? -ne 0 ];then
35	echo "SKIP: Could not create net namespace $ns0"
36	exit $ksft_skip
37fi
38
39trap cleanup EXIT
40
41ip netns add "$ns1"
42if [ $? -ne 0 ];then
43	echo "SKIP: Could not create net namespace $ns1"
44	exit $ksft_skip
45fi
46
47ip netns add "$ns2"
48if [ $? -ne 0 ];then
49	echo "SKIP: Could not create net namespace $ns2"
50	exit $ksft_skip
51fi
52
53ip link add veth0 netns "$ns0" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1
54if [ $? -ne 0 ];then
55    echo "SKIP: No virtual ethernet pair device support in kernel"
56    exit $ksft_skip
57fi
58ip link add veth1 netns "$ns0" type veth peer name eth0 netns "$ns2"
59
60ip -net "$ns0" link set lo up
61ip -net "$ns0" link set veth0 up
62ip -net "$ns0" addr add 10.0.1.1/24 dev veth0
63ip -net "$ns0" addr add dead:1::1/64 dev veth0
64
65ip -net "$ns0" link set veth1 up
66ip -net "$ns0" addr add 10.0.2.1/24 dev veth1
67ip -net "$ns0" addr add dead:2::1/64 dev veth1
68
69for i in 1 2; do
70  ip -net ns$i-$sfx link set lo up
71  ip -net ns$i-$sfx link set eth0 up
72  ip -net ns$i-$sfx addr add 10.0.$i.99/24 dev eth0
73  ip -net ns$i-$sfx route add default via 10.0.$i.1
74  ip -net ns$i-$sfx addr add dead:$i::99/64 dev eth0
75  ip -net ns$i-$sfx route add default via dead:$i::1
76done
77
78bad_counter()
79{
80	local ns=$1
81	local counter=$2
82	local expect=$3
83	local tag=$4
84
85	echo "ERROR: $counter counter in $ns has unexpected value (expected $expect) at $tag" 1>&2
86	ip netns exec $ns nft list counter inet filter $counter 1>&2
87}
88
89check_counters()
90{
91	ns=$1
92	local lret=0
93
94	cnt=$(ip netns exec $ns nft list counter inet filter ns0in | grep -q "packets 1 bytes 84")
95	if [ $? -ne 0 ]; then
96		bad_counter $ns ns0in "packets 1 bytes 84" "check_counters 1"
97		lret=1
98	fi
99	cnt=$(ip netns exec $ns nft list counter inet filter ns0out | grep -q "packets 1 bytes 84")
100	if [ $? -ne 0 ]; then
101		bad_counter $ns ns0out "packets 1 bytes 84" "check_counters 2"
102		lret=1
103	fi
104
105	expect="packets 1 bytes 104"
106	cnt=$(ip netns exec $ns nft list counter inet filter ns0in6 | grep -q "$expect")
107	if [ $? -ne 0 ]; then
108		bad_counter $ns ns0in6 "$expect" "check_counters 3"
109		lret=1
110	fi
111	cnt=$(ip netns exec $ns nft list counter inet filter ns0out6 | grep -q "$expect")
112	if [ $? -ne 0 ]; then
113		bad_counter $ns ns0out6 "$expect" "check_counters 4"
114		lret=1
115	fi
116
117	return $lret
118}
119
120check_ns0_counters()
121{
122	local ns=$1
123	local lret=0
124
125	cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0in | grep -q "packets 0 bytes 0")
126	if [ $? -ne 0 ]; then
127		bad_counter "$ns0" ns0in "packets 0 bytes 0" "check_ns0_counters 1"
128		lret=1
129	fi
130
131	cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0")
132	if [ $? -ne 0 ]; then
133		bad_counter "$ns0" ns0in6 "packets 0 bytes 0"
134		lret=1
135	fi
136
137	cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0out | grep -q "packets 0 bytes 0")
138	if [ $? -ne 0 ]; then
139		bad_counter "$ns0" ns0out "packets 0 bytes 0" "check_ns0_counters 2"
140		lret=1
141	fi
142	cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0")
143	if [ $? -ne 0 ]; then
144		bad_counter "$ns0" ns0out6 "packets 0 bytes 0" "check_ns0_counters3 "
145		lret=1
146	fi
147
148	for dir in "in" "out" ; do
149		expect="packets 1 bytes 84"
150		cnt=$(ip netns exec "$ns0" nft list counter inet filter ${ns}${dir} | grep -q "$expect")
151		if [ $? -ne 0 ]; then
152			bad_counter "$ns0" $ns$dir "$expect" "check_ns0_counters 4"
153			lret=1
154		fi
155
156		expect="packets 1 bytes 104"
157		cnt=$(ip netns exec "$ns0" nft list counter inet filter ${ns}${dir}6 | grep -q "$expect")
158		if [ $? -ne 0 ]; then
159			bad_counter "$ns0" $ns$dir6 "$expect" "check_ns0_counters 5"
160			lret=1
161		fi
162	done
163
164	return $lret
165}
166
167reset_counters()
168{
169	for i in 0 1 2;do
170		ip netns exec ns$i-$sfx nft reset counters inet > /dev/null
171	done
172}
173
174test_local_dnat6()
175{
176	local family=$1
177	local lret=0
178	local IPF=""
179
180	if [ $family = "inet" ];then
181		IPF="ip6"
182	fi
183
184ip netns exec "$ns0" nft -f /dev/stdin <<EOF
185table $family nat {
186	chain output {
187		type nat hook output priority 0; policy accept;
188		ip6 daddr dead:1::99 dnat $IPF to dead:2::99
189	}
190}
191EOF
192	if [ $? -ne 0 ]; then
193		echo "SKIP: Could not add add $family dnat hook"
194		return $ksft_skip
195	fi
196
197	# ping netns1, expect rewrite to netns2
198	ip netns exec "$ns0" ping -q -c 1 dead:1::99 > /dev/null
199	if [ $? -ne 0 ]; then
200		lret=1
201		echo "ERROR: ping6 failed"
202		return $lret
203	fi
204
205	expect="packets 0 bytes 0"
206	for dir in "in6" "out6" ; do
207		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
208		if [ $? -ne 0 ]; then
209			bad_counter "$ns0" ns1$dir "$expect" "test_local_dnat6 1"
210			lret=1
211		fi
212	done
213
214	expect="packets 1 bytes 104"
215	for dir in "in6" "out6" ; do
216		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
217		if [ $? -ne 0 ]; then
218			bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat6 2"
219			lret=1
220		fi
221	done
222
223	# expect 0 count in ns1
224	expect="packets 0 bytes 0"
225	for dir in "in6" "out6" ; do
226		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
227		if [ $? -ne 0 ]; then
228			bad_counter "$ns1" ns0$dir "$expect" "test_local_dnat6 3"
229			lret=1
230		fi
231	done
232
233	# expect 1 packet in ns2
234	expect="packets 1 bytes 104"
235	for dir in "in6" "out6" ; do
236		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect")
237		if [ $? -ne 0 ]; then
238			bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat6 4"
239			lret=1
240		fi
241	done
242
243	test $lret -eq 0 && echo "PASS: ipv6 ping to $ns1 was $family NATted to $ns2"
244	ip netns exec "$ns0" nft flush chain ip6 nat output
245
246	return $lret
247}
248
249test_local_dnat()
250{
251	local family=$1
252	local lret=0
253	local IPF=""
254
255	if [ $family = "inet" ];then
256		IPF="ip"
257	fi
258
259ip netns exec "$ns0" nft -f /dev/stdin <<EOF 2>/dev/null
260table $family nat {
261	chain output {
262		type nat hook output priority 0; policy accept;
263		ip daddr 10.0.1.99 dnat $IPF to 10.0.2.99
264	}
265}
266EOF
267	if [ $? -ne 0 ]; then
268		if [ $family = "inet" ];then
269			echo "SKIP: inet nat tests"
270			test_inet_nat=false
271			return $ksft_skip
272		fi
273		echo "SKIP: Could not add add $family dnat hook"
274		return $ksft_skip
275	fi
276
277	# ping netns1, expect rewrite to netns2
278	ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null
279	if [ $? -ne 0 ]; then
280		lret=1
281		echo "ERROR: ping failed"
282		return $lret
283	fi
284
285	expect="packets 0 bytes 0"
286	for dir in "in" "out" ; do
287		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
288		if [ $? -ne 0 ]; then
289			bad_counter "$ns0" ns1$dir "$expect" "test_local_dnat 1"
290			lret=1
291		fi
292	done
293
294	expect="packets 1 bytes 84"
295	for dir in "in" "out" ; do
296		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
297		if [ $? -ne 0 ]; then
298			bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat 2"
299			lret=1
300		fi
301	done
302
303	# expect 0 count in ns1
304	expect="packets 0 bytes 0"
305	for dir in "in" "out" ; do
306		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
307		if [ $? -ne 0 ]; then
308			bad_counter "$ns1" ns0$dir "$expect" "test_local_dnat 3"
309			lret=1
310		fi
311	done
312
313	# expect 1 packet in ns2
314	expect="packets 1 bytes 84"
315	for dir in "in" "out" ; do
316		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect")
317		if [ $? -ne 0 ]; then
318			bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat 4"
319			lret=1
320		fi
321	done
322
323	test $lret -eq 0 && echo "PASS: ping to $ns1 was $family NATted to $ns2"
324
325	ip netns exec "$ns0" nft flush chain $family nat output
326
327	reset_counters
328	ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null
329	if [ $? -ne 0 ]; then
330		lret=1
331		echo "ERROR: ping failed"
332		return $lret
333	fi
334
335	expect="packets 1 bytes 84"
336	for dir in "in" "out" ; do
337		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
338		if [ $? -ne 0 ]; then
339			bad_counter "$ns1" ns1$dir "$expect" "test_local_dnat 5"
340			lret=1
341		fi
342	done
343	expect="packets 0 bytes 0"
344	for dir in "in" "out" ; do
345		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
346		if [ $? -ne 0 ]; then
347			bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat 6"
348			lret=1
349		fi
350	done
351
352	# expect 1 count in ns1
353	expect="packets 1 bytes 84"
354	for dir in "in" "out" ; do
355		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
356		if [ $? -ne 0 ]; then
357			bad_counter "$ns0" ns0$dir "$expect" "test_local_dnat 7"
358			lret=1
359		fi
360	done
361
362	# expect 0 packet in ns2
363	expect="packets 0 bytes 0"
364	for dir in "in" "out" ; do
365		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect")
366		if [ $? -ne 0 ]; then
367			bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat 8"
368			lret=1
369		fi
370	done
371
372	test $lret -eq 0 && echo "PASS: ping to $ns1 OK after $family nat output chain flush"
373
374	return $lret
375}
376
377test_local_dnat_portonly()
378{
379	local family=$1
380	local daddr=$2
381	local lret=0
382	local sr_s
383	local sr_r
384
385ip netns exec "$ns0" nft -f /dev/stdin <<EOF
386table $family nat {
387	chain output {
388		type nat hook output priority 0; policy accept;
389		meta l4proto tcp dnat to :2000
390
391	}
392}
393EOF
394	if [ $? -ne 0 ]; then
395		if [ $family = "inet" ];then
396			echo "SKIP: inet port test"
397			test_inet_nat=false
398			return
399		fi
400		echo "SKIP: Could not add $family dnat hook"
401		return
402	fi
403
404	echo SERVER-$family | ip netns exec "$ns1" timeout 5 socat -u STDIN TCP-LISTEN:2000 &
405	sc_s=$!
406
407	result=$(ip netns exec "$ns0" timeout 1 socat TCP:$daddr:2000 STDOUT)
408
409	if [ "$result" = "SERVER-inet" ];then
410		echo "PASS: inet port rewrite without l3 address"
411	else
412		echo "ERROR: inet port rewrite"
413		ret=1
414	fi
415}
416
417test_masquerade6()
418{
419	local family=$1
420	local natflags=$2
421	local lret=0
422
423	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
424
425	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
426	if [ $? -ne 0 ] ; then
427		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6"
428		return 1
429		lret=1
430	fi
431
432	expect="packets 1 bytes 104"
433	for dir in "in6" "out6" ; do
434		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
435		if [ $? -ne 0 ]; then
436			bad_counter "$ns1" ns2$dir "$expect" "test_masquerade6 1"
437			lret=1
438		fi
439
440		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
441		if [ $? -ne 0 ]; then
442			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 2"
443			lret=1
444		fi
445	done
446
447	reset_counters
448
449# add masquerading rule
450ip netns exec "$ns0" nft -f /dev/stdin <<EOF
451table $family nat {
452	chain postrouting {
453		type nat hook postrouting priority 0; policy accept;
454		meta oif veth0 masquerade $natflags
455	}
456}
457EOF
458	if [ $? -ne 0 ]; then
459		echo "SKIP: Could not add add $family masquerade hook"
460		return $ksft_skip
461	fi
462
463	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
464	if [ $? -ne 0 ] ; then
465		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
466		lret=1
467	fi
468
469	# ns1 should have seen packets from ns0, due to masquerade
470	expect="packets 1 bytes 104"
471	for dir in "in6" "out6" ; do
472		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
473		if [ $? -ne 0 ]; then
474			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 3"
475			lret=1
476		fi
477
478		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
479		if [ $? -ne 0 ]; then
480			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 4"
481			lret=1
482		fi
483	done
484
485	# ns1 should not have seen packets from ns2, due to masquerade
486	expect="packets 0 bytes 0"
487	for dir in "in6" "out6" ; do
488		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
489		if [ $? -ne 0 ]; then
490			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 5"
491			lret=1
492		fi
493
494		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
495		if [ $? -ne 0 ]; then
496			bad_counter "$ns0" ns1$dir "$expect" "test_masquerade6 6"
497			lret=1
498		fi
499	done
500
501	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
502	if [ $? -ne 0 ] ; then
503		echo "ERROR: cannot ping $ns1 from $ns2 with active ipv6 masquerade $natflags (attempt 2)"
504		lret=1
505	fi
506
507	ip netns exec "$ns0" nft flush chain $family nat postrouting
508	if [ $? -ne 0 ]; then
509		echo "ERROR: Could not flush $family nat postrouting" 1>&2
510		lret=1
511	fi
512
513	test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for $ns2"
514
515	return $lret
516}
517
518test_masquerade()
519{
520	local family=$1
521	local natflags=$2
522	local lret=0
523
524	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
525	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
526
527	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
528	if [ $? -ne 0 ] ; then
529		echo "ERROR: cannot ping $ns1 from "$ns2" $natflags"
530		lret=1
531	fi
532
533	expect="packets 1 bytes 84"
534	for dir in "in" "out" ; do
535		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
536		if [ $? -ne 0 ]; then
537			bad_counter "$ns1" ns2$dir "$expect" "test_masquerade 1"
538			lret=1
539		fi
540
541		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
542		if [ $? -ne 0 ]; then
543			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 2"
544			lret=1
545		fi
546	done
547
548	reset_counters
549
550# add masquerading rule
551ip netns exec "$ns0" nft -f /dev/stdin <<EOF
552table $family nat {
553	chain postrouting {
554		type nat hook postrouting priority 0; policy accept;
555		meta oif veth0 masquerade $natflags
556	}
557}
558EOF
559	if [ $? -ne 0 ]; then
560		echo "SKIP: Could not add add $family masquerade hook"
561		return $ksft_skip
562	fi
563
564	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
565	if [ $? -ne 0 ] ; then
566		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
567		lret=1
568	fi
569
570	# ns1 should have seen packets from ns0, due to masquerade
571	expect="packets 1 bytes 84"
572	for dir in "in" "out" ; do
573		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
574		if [ $? -ne 0 ]; then
575			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 3"
576			lret=1
577		fi
578
579		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
580		if [ $? -ne 0 ]; then
581			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 4"
582			lret=1
583		fi
584	done
585
586	# ns1 should not have seen packets from ns2, due to masquerade
587	expect="packets 0 bytes 0"
588	for dir in "in" "out" ; do
589		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
590		if [ $? -ne 0 ]; then
591			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 5"
592			lret=1
593		fi
594
595		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
596		if [ $? -ne 0 ]; then
597			bad_counter "$ns0" ns1$dir "$expect" "test_masquerade 6"
598			lret=1
599		fi
600	done
601
602	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
603	if [ $? -ne 0 ] ; then
604		echo "ERROR: cannot ping $ns1 from $ns2 with active ip masquerade $natflags (attempt 2)"
605		lret=1
606	fi
607
608	ip netns exec "$ns0" nft flush chain $family nat postrouting
609	if [ $? -ne 0 ]; then
610		echo "ERROR: Could not flush $family nat postrouting" 1>&2
611		lret=1
612	fi
613
614	test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for $ns2"
615
616	return $lret
617}
618
619test_redirect6()
620{
621	local family=$1
622	local lret=0
623
624	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
625
626	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
627	if [ $? -ne 0 ] ; then
628		echo "ERROR: cannnot ping $ns1 from $ns2 via ipv6"
629		lret=1
630	fi
631
632	expect="packets 1 bytes 104"
633	for dir in "in6" "out6" ; do
634		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
635		if [ $? -ne 0 ]; then
636			bad_counter "$ns1" ns2$dir "$expect" "test_redirect6 1"
637			lret=1
638		fi
639
640		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
641		if [ $? -ne 0 ]; then
642			bad_counter "$ns2" ns1$dir "$expect" "test_redirect6 2"
643			lret=1
644		fi
645	done
646
647	reset_counters
648
649# add redirect rule
650ip netns exec "$ns0" nft -f /dev/stdin <<EOF
651table $family nat {
652	chain prerouting {
653		type nat hook prerouting priority 0; policy accept;
654		meta iif veth1 meta l4proto icmpv6 ip6 saddr dead:2::99 ip6 daddr dead:1::99 redirect
655	}
656}
657EOF
658	if [ $? -ne 0 ]; then
659		echo "SKIP: Could not add add $family redirect hook"
660		return $ksft_skip
661	fi
662
663	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
664	if [ $? -ne 0 ] ; then
665		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6 with active $family redirect"
666		lret=1
667	fi
668
669	# ns1 should have seen no packets from ns2, due to redirection
670	expect="packets 0 bytes 0"
671	for dir in "in6" "out6" ; do
672		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
673		if [ $? -ne 0 ]; then
674			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 3"
675			lret=1
676		fi
677	done
678
679	# ns0 should have seen packets from ns2, due to masquerade
680	expect="packets 1 bytes 104"
681	for dir in "in6" "out6" ; do
682		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
683		if [ $? -ne 0 ]; then
684			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 4"
685			lret=1
686		fi
687	done
688
689	ip netns exec "$ns0" nft delete table $family nat
690	if [ $? -ne 0 ]; then
691		echo "ERROR: Could not delete $family nat table" 1>&2
692		lret=1
693	fi
694
695	test $lret -eq 0 && echo "PASS: $family IPv6 redirection for $ns2"
696
697	return $lret
698}
699
700test_redirect()
701{
702	local family=$1
703	local lret=0
704
705	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
706	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
707
708	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
709	if [ $? -ne 0 ] ; then
710		echo "ERROR: cannot ping $ns1 from $ns2"
711		lret=1
712	fi
713
714	expect="packets 1 bytes 84"
715	for dir in "in" "out" ; do
716		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
717		if [ $? -ne 0 ]; then
718			bad_counter "$ns1" $ns2$dir "$expect" "test_redirect 1"
719			lret=1
720		fi
721
722		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
723		if [ $? -ne 0 ]; then
724			bad_counter "$ns2" ns1$dir "$expect" "test_redirect 2"
725			lret=1
726		fi
727	done
728
729	reset_counters
730
731# add redirect rule
732ip netns exec "$ns0" nft -f /dev/stdin <<EOF
733table $family nat {
734	chain prerouting {
735		type nat hook prerouting priority 0; policy accept;
736		meta iif veth1 ip protocol icmp ip saddr 10.0.2.99 ip daddr 10.0.1.99 redirect
737	}
738}
739EOF
740	if [ $? -ne 0 ]; then
741		echo "SKIP: Could not add add $family redirect hook"
742		return $ksft_skip
743	fi
744
745	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
746	if [ $? -ne 0 ] ; then
747		echo "ERROR: cannot ping $ns1 from $ns2 with active $family ip redirect"
748		lret=1
749	fi
750
751	# ns1 should have seen no packets from ns2, due to redirection
752	expect="packets 0 bytes 0"
753	for dir in "in" "out" ; do
754
755		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
756		if [ $? -ne 0 ]; then
757			bad_counter "$ns1" ns0$dir "$expect" "test_redirect 3"
758			lret=1
759		fi
760	done
761
762	# ns0 should have seen packets from ns2, due to masquerade
763	expect="packets 1 bytes 84"
764	for dir in "in" "out" ; do
765		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
766		if [ $? -ne 0 ]; then
767			bad_counter "$ns0" ns0$dir "$expect" "test_redirect 4"
768			lret=1
769		fi
770	done
771
772	ip netns exec "$ns0" nft delete table $family nat
773	if [ $? -ne 0 ]; then
774		echo "ERROR: Could not delete $family nat table" 1>&2
775		lret=1
776	fi
777
778	test $lret -eq 0 && echo "PASS: $family IP redirection for $ns2"
779
780	return $lret
781}
782
783# test port shadowing.
784# create two listening services, one on router (ns0), one
785# on client (ns2), which is masqueraded from ns1 point of view.
786# ns2 sends udp packet coming from service port to ns1, on a highport.
787# Later, if n1 uses same highport to connect to ns0:service, packet
788# might be port-forwarded to ns2 instead.
789
790# second argument tells if we expect the 'fake-entry' to take effect
791# (CLIENT) or not (ROUTER).
792test_port_shadow()
793{
794	local test=$1
795	local expect=$2
796	local daddrc="10.0.1.99"
797	local daddrs="10.0.1.1"
798	local result=""
799	local logmsg=""
800
801	# make shadow entry, from client (ns2), going to (ns1), port 41404, sport 1405.
802	echo "fake-entry" | ip netns exec "$ns2" timeout 1 socat -u STDIN UDP:"$daddrc":41404,sourceport=1405
803
804	echo ROUTER | ip netns exec "$ns0" timeout 5 socat -u STDIN UDP4-LISTEN:1405 &
805	sc_r=$!
806
807	echo CLIENT | ip netns exec "$ns2" timeout 5 socat -u STDIN UDP4-LISTEN:1405,reuseport &
808	sc_c=$!
809
810	sleep 0.3
811
812	# ns1 tries to connect to ns0:1405.  With default settings this should connect
813	# to client, it matches the conntrack entry created above.
814
815	result=$(echo "data" | ip netns exec "$ns1" timeout 1 socat - UDP:"$daddrs":1405,sourceport=41404)
816
817	if [ "$result" = "$expect" ] ;then
818		echo "PASS: portshadow test $test: got reply from ${expect}${logmsg}"
819	else
820		echo "ERROR: portshadow test $test: got reply from \"$result\", not $expect as intended"
821		ret=1
822	fi
823
824	kill $sc_r $sc_c 2>/dev/null
825
826	# flush udp entries for next test round, if any
827	ip netns exec "$ns0" conntrack -F >/dev/null 2>&1
828}
829
830# This prevents port shadow of router service via packet filter,
831# packets claiming to originate from service port from internal
832# network are dropped.
833test_port_shadow_filter()
834{
835	local family=$1
836
837ip netns exec "$ns0" nft -f /dev/stdin <<EOF
838table $family filter {
839	chain forward {
840		type filter hook forward priority 0; policy accept;
841		meta iif veth1 udp sport 1405 drop
842	}
843}
844EOF
845	test_port_shadow "port-filter" "ROUTER"
846
847	ip netns exec "$ns0" nft delete table $family filter
848}
849
850# This prevents port shadow of router service via notrack.
851test_port_shadow_notrack()
852{
853	local family=$1
854
855ip netns exec "$ns0" nft -f /dev/stdin <<EOF
856table $family raw {
857	chain prerouting {
858		type filter hook prerouting priority -300; policy accept;
859		meta iif veth0 udp dport 1405 notrack
860	}
861	chain output {
862		type filter hook output priority -300; policy accept;
863		meta oif veth0 udp sport 1405 notrack
864	}
865}
866EOF
867	test_port_shadow "port-notrack" "ROUTER"
868
869	ip netns exec "$ns0" nft delete table $family raw
870}
871
872# This prevents port shadow of router service via sport remap.
873test_port_shadow_pat()
874{
875	local family=$1
876
877ip netns exec "$ns0" nft -f /dev/stdin <<EOF
878table $family pat {
879	chain postrouting {
880		type nat hook postrouting priority -1; policy accept;
881		meta iif veth1 udp sport <= 1405 masquerade to : 1406-65535 random
882	}
883}
884EOF
885	test_port_shadow "pat" "ROUTER"
886
887	ip netns exec "$ns0" nft delete table $family pat
888}
889
890test_port_shadowing()
891{
892	local family="ip"
893
894	conntrack -h >/dev/null 2>&1
895	if [ $? -ne 0 ];then
896		echo "SKIP: Could not run nat port shadowing test without conntrack tool"
897		return
898	fi
899
900	socat -h > /dev/null 2>&1
901	if [ $? -ne 0 ];then
902		echo "SKIP: Could not run nat port shadowing test without socat tool"
903		return
904	fi
905
906	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
907	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
908
909	ip netns exec "$ns0" nft -f /dev/stdin <<EOF
910table $family nat {
911	chain postrouting {
912		type nat hook postrouting priority 0; policy accept;
913		meta oif veth0 masquerade
914	}
915}
916EOF
917	if [ $? -ne 0 ]; then
918		echo "SKIP: Could not add add $family masquerade hook"
919		return $ksft_skip
920	fi
921
922	# test default behaviour. Packet from ns1 to ns0 is redirected to ns2.
923	test_port_shadow "default" "CLIENT"
924
925	# test packet filter based mitigation: prevent forwarding of
926	# packets claiming to come from the service port.
927	test_port_shadow_filter "$family"
928
929	# test conntrack based mitigation: connections going or coming
930	# from router:service bypass connection tracking.
931	test_port_shadow_notrack "$family"
932
933	# test nat based mitigation: fowarded packets coming from service port
934	# are masqueraded with random highport.
935	test_port_shadow_pat "$family"
936
937	ip netns exec "$ns0" nft delete table $family nat
938}
939
940test_stateless_nat_ip()
941{
942	local lret=0
943
944	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
945	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
946
947	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
948	if [ $? -ne 0 ] ; then
949		echo "ERROR: cannot ping $ns1 from $ns2 before loading stateless rules"
950		return 1
951	fi
952
953ip netns exec "$ns0" nft -f /dev/stdin <<EOF
954table ip stateless {
955	map xlate_in {
956		typeof meta iifname . ip saddr . ip daddr : ip daddr
957		elements = {
958			"veth1" . 10.0.2.99 . 10.0.1.99 : 10.0.2.2,
959		}
960	}
961	map xlate_out {
962		typeof meta iifname . ip saddr . ip daddr : ip daddr
963		elements = {
964			"veth0" . 10.0.1.99 . 10.0.2.2 : 10.0.2.99
965		}
966	}
967
968	chain prerouting {
969		type filter hook prerouting priority -400; policy accept;
970		ip saddr set meta iifname . ip saddr . ip daddr map @xlate_in
971		ip daddr set meta iifname . ip saddr . ip daddr map @xlate_out
972	}
973}
974EOF
975	if [ $? -ne 0 ]; then
976		echo "SKIP: Could not add ip statless rules"
977		return $ksft_skip
978	fi
979
980	reset_counters
981
982	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
983	if [ $? -ne 0 ] ; then
984		echo "ERROR: cannot ping $ns1 from $ns2 with stateless rules"
985		lret=1
986	fi
987
988	# ns1 should have seen packets from .2.2, due to stateless rewrite.
989	expect="packets 1 bytes 84"
990	cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect")
991	if [ $? -ne 0 ]; then
992		bad_counter "$ns1" ns0insl "$expect" "test_stateless 1"
993		lret=1
994	fi
995
996	for dir in "in" "out" ; do
997		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
998		if [ $? -ne 0 ]; then
999			bad_counter "$ns2" ns1$dir "$expect" "test_stateless 2"
1000			lret=1
1001		fi
1002	done
1003
1004	# ns1 should not have seen packets from ns2, due to masquerade
1005	expect="packets 0 bytes 0"
1006	for dir in "in" "out" ; do
1007		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
1008		if [ $? -ne 0 ]; then
1009			bad_counter "$ns1" ns0$dir "$expect" "test_stateless 3"
1010			lret=1
1011		fi
1012
1013		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
1014		if [ $? -ne 0 ]; then
1015			bad_counter "$ns0" ns1$dir "$expect" "test_stateless 4"
1016			lret=1
1017		fi
1018	done
1019
1020	reset_counters
1021
1022	socat -h > /dev/null 2>&1
1023	if [ $? -ne 0 ];then
1024		echo "SKIP: Could not run stateless nat frag test without socat tool"
1025		if [ $lret -eq 0 ]; then
1026			return $ksft_skip
1027		fi
1028
1029		ip netns exec "$ns0" nft delete table ip stateless
1030		return $lret
1031	fi
1032
1033	local tmpfile=$(mktemp)
1034	dd if=/dev/urandom of=$tmpfile bs=4096 count=1 2>/dev/null
1035
1036	local outfile=$(mktemp)
1037	ip netns exec "$ns1" timeout 3 socat -u UDP4-RECV:4233 OPEN:$outfile < /dev/null &
1038	sc_r=$!
1039
1040	sleep 1
1041	# re-do with large ping -> ip fragmentation
1042	ip netns exec "$ns2" timeout 3 socat - UDP4-SENDTO:"10.0.1.99:4233" < "$tmpfile" > /dev/null
1043	if [ $? -ne 0 ] ; then
1044		echo "ERROR: failed to test udp $ns1 to $ns2 with stateless ip nat" 1>&2
1045		lret=1
1046	fi
1047
1048	wait
1049
1050	cmp "$tmpfile" "$outfile"
1051	if [ $? -ne 0 ]; then
1052		ls -l "$tmpfile" "$outfile"
1053		echo "ERROR: in and output file mismatch when checking udp with stateless nat" 1>&2
1054		lret=1
1055	fi
1056
1057	rm -f "$tmpfile" "$outfile"
1058
1059	# ns1 should have seen packets from 2.2, due to stateless rewrite.
1060	expect="packets 3 bytes 4164"
1061	cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect")
1062	if [ $? -ne 0 ]; then
1063		bad_counter "$ns1" ns0insl "$expect" "test_stateless 5"
1064		lret=1
1065	fi
1066
1067	ip netns exec "$ns0" nft delete table ip stateless
1068	if [ $? -ne 0 ]; then
1069		echo "ERROR: Could not delete table ip stateless" 1>&2
1070		lret=1
1071	fi
1072
1073	test $lret -eq 0 && echo "PASS: IP statless for $ns2"
1074
1075	return $lret
1076}
1077
1078# ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99
1079for i in 0 1 2; do
1080ip netns exec ns$i-$sfx nft -f /dev/stdin <<EOF
1081table inet filter {
1082	counter ns0in {}
1083	counter ns1in {}
1084	counter ns2in {}
1085
1086	counter ns0out {}
1087	counter ns1out {}
1088	counter ns2out {}
1089
1090	counter ns0in6 {}
1091	counter ns1in6 {}
1092	counter ns2in6 {}
1093
1094	counter ns0out6 {}
1095	counter ns1out6 {}
1096	counter ns2out6 {}
1097
1098	map nsincounter {
1099		type ipv4_addr : counter
1100		elements = { 10.0.1.1 : "ns0in",
1101			     10.0.2.1 : "ns0in",
1102			     10.0.1.99 : "ns1in",
1103			     10.0.2.99 : "ns2in" }
1104	}
1105
1106	map nsincounter6 {
1107		type ipv6_addr : counter
1108		elements = { dead:1::1 : "ns0in6",
1109			     dead:2::1 : "ns0in6",
1110			     dead:1::99 : "ns1in6",
1111			     dead:2::99 : "ns2in6" }
1112	}
1113
1114	map nsoutcounter {
1115		type ipv4_addr : counter
1116		elements = { 10.0.1.1 : "ns0out",
1117			     10.0.2.1 : "ns0out",
1118			     10.0.1.99: "ns1out",
1119			     10.0.2.99: "ns2out" }
1120	}
1121
1122	map nsoutcounter6 {
1123		type ipv6_addr : counter
1124		elements = { dead:1::1 : "ns0out6",
1125			     dead:2::1 : "ns0out6",
1126			     dead:1::99 : "ns1out6",
1127			     dead:2::99 : "ns2out6" }
1128	}
1129
1130	chain input {
1131		type filter hook input priority 0; policy accept;
1132		counter name ip saddr map @nsincounter
1133		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 saddr map @nsincounter6
1134	}
1135	chain output {
1136		type filter hook output priority 0; policy accept;
1137		counter name ip daddr map @nsoutcounter
1138		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 daddr map @nsoutcounter6
1139	}
1140}
1141EOF
1142done
1143
1144# special case for stateless nat check, counter needs to
1145# be done before (input) ip defragmentation
1146ip netns exec ns1-$sfx nft -f /dev/stdin <<EOF
1147table inet filter {
1148	counter ns0insl {}
1149
1150	chain pre {
1151		type filter hook prerouting priority -400; policy accept;
1152		ip saddr 10.0.2.2 counter name "ns0insl"
1153	}
1154}
1155EOF
1156
1157sleep 3
1158# test basic connectivity
1159for i in 1 2; do
1160  ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99 > /dev/null
1161  if [ $? -ne 0 ];then
1162  	echo "ERROR: Could not reach other namespace(s)" 1>&2
1163	ret=1
1164  fi
1165
1166  ip netns exec "$ns0" ping -c 1 -q dead:$i::99 > /dev/null
1167  if [ $? -ne 0 ];then
1168	echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2
1169	ret=1
1170  fi
1171  check_counters ns$i-$sfx
1172  if [ $? -ne 0 ]; then
1173	ret=1
1174  fi
1175
1176  check_ns0_counters ns$i
1177  if [ $? -ne 0 ]; then
1178	ret=1
1179  fi
1180  reset_counters
1181done
1182
1183if [ $ret -eq 0 ];then
1184	echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2"
1185fi
1186
1187reset_counters
1188test_local_dnat ip
1189test_local_dnat6 ip6
1190
1191reset_counters
1192test_local_dnat_portonly inet 10.0.1.99
1193
1194reset_counters
1195$test_inet_nat && test_local_dnat inet
1196$test_inet_nat && test_local_dnat6 inet
1197
1198for flags in "" "fully-random"; do
1199reset_counters
1200test_masquerade ip $flags
1201test_masquerade6 ip6 $flags
1202reset_counters
1203$test_inet_nat && test_masquerade inet $flags
1204$test_inet_nat && test_masquerade6 inet $flags
1205done
1206
1207reset_counters
1208test_redirect ip
1209test_redirect6 ip6
1210reset_counters
1211$test_inet_nat && test_redirect inet
1212$test_inet_nat && test_redirect6 inet
1213
1214test_port_shadowing
1215test_stateless_nat_ip
1216
1217if [ $ret -ne 0 ];then
1218	echo -n "FAIL: "
1219	nft --version
1220fi
1221
1222exit $ret
1223