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	sleep 1
408
409	result=$(ip netns exec "$ns0" timeout 1 socat TCP:$daddr:2000 STDOUT)
410
411	if [ "$result" = "SERVER-inet" ];then
412		echo "PASS: inet port rewrite without l3 address"
413	else
414		echo "ERROR: inet port rewrite"
415		ret=1
416	fi
417}
418
419test_masquerade6()
420{
421	local family=$1
422	local natflags=$2
423	local lret=0
424
425	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
426
427	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
428	if [ $? -ne 0 ] ; then
429		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6"
430		return 1
431		lret=1
432	fi
433
434	expect="packets 1 bytes 104"
435	for dir in "in6" "out6" ; do
436		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
437		if [ $? -ne 0 ]; then
438			bad_counter "$ns1" ns2$dir "$expect" "test_masquerade6 1"
439			lret=1
440		fi
441
442		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
443		if [ $? -ne 0 ]; then
444			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 2"
445			lret=1
446		fi
447	done
448
449	reset_counters
450
451# add masquerading rule
452ip netns exec "$ns0" nft -f /dev/stdin <<EOF
453table $family nat {
454	chain postrouting {
455		type nat hook postrouting priority 0; policy accept;
456		meta oif veth0 masquerade $natflags
457	}
458}
459EOF
460	if [ $? -ne 0 ]; then
461		echo "SKIP: Could not add add $family masquerade hook"
462		return $ksft_skip
463	fi
464
465	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
466	if [ $? -ne 0 ] ; then
467		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
468		lret=1
469	fi
470
471	# ns1 should have seen packets from ns0, due to masquerade
472	expect="packets 1 bytes 104"
473	for dir in "in6" "out6" ; do
474		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
475		if [ $? -ne 0 ]; then
476			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 3"
477			lret=1
478		fi
479
480		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
481		if [ $? -ne 0 ]; then
482			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 4"
483			lret=1
484		fi
485	done
486
487	# ns1 should not have seen packets from ns2, due to masquerade
488	expect="packets 0 bytes 0"
489	for dir in "in6" "out6" ; do
490		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
491		if [ $? -ne 0 ]; then
492			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 5"
493			lret=1
494		fi
495
496		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
497		if [ $? -ne 0 ]; then
498			bad_counter "$ns0" ns1$dir "$expect" "test_masquerade6 6"
499			lret=1
500		fi
501	done
502
503	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
504	if [ $? -ne 0 ] ; then
505		echo "ERROR: cannot ping $ns1 from $ns2 with active ipv6 masquerade $natflags (attempt 2)"
506		lret=1
507	fi
508
509	ip netns exec "$ns0" nft flush chain $family nat postrouting
510	if [ $? -ne 0 ]; then
511		echo "ERROR: Could not flush $family nat postrouting" 1>&2
512		lret=1
513	fi
514
515	test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for $ns2"
516
517	return $lret
518}
519
520test_masquerade()
521{
522	local family=$1
523	local natflags=$2
524	local lret=0
525
526	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
527	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
528
529	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
530	if [ $? -ne 0 ] ; then
531		echo "ERROR: cannot ping $ns1 from "$ns2" $natflags"
532		lret=1
533	fi
534
535	expect="packets 1 bytes 84"
536	for dir in "in" "out" ; do
537		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
538		if [ $? -ne 0 ]; then
539			bad_counter "$ns1" ns2$dir "$expect" "test_masquerade 1"
540			lret=1
541		fi
542
543		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
544		if [ $? -ne 0 ]; then
545			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 2"
546			lret=1
547		fi
548	done
549
550	reset_counters
551
552# add masquerading rule
553ip netns exec "$ns0" nft -f /dev/stdin <<EOF
554table $family nat {
555	chain postrouting {
556		type nat hook postrouting priority 0; policy accept;
557		meta oif veth0 masquerade $natflags
558	}
559}
560EOF
561	if [ $? -ne 0 ]; then
562		echo "SKIP: Could not add add $family masquerade hook"
563		return $ksft_skip
564	fi
565
566	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
567	if [ $? -ne 0 ] ; then
568		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
569		lret=1
570	fi
571
572	# ns1 should have seen packets from ns0, due to masquerade
573	expect="packets 1 bytes 84"
574	for dir in "in" "out" ; do
575		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect")
576		if [ $? -ne 0 ]; then
577			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 3"
578			lret=1
579		fi
580
581		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
582		if [ $? -ne 0 ]; then
583			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 4"
584			lret=1
585		fi
586	done
587
588	# ns1 should not have seen packets from ns2, due to masquerade
589	expect="packets 0 bytes 0"
590	for dir in "in" "out" ; do
591		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
592		if [ $? -ne 0 ]; then
593			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 5"
594			lret=1
595		fi
596
597		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
598		if [ $? -ne 0 ]; then
599			bad_counter "$ns0" ns1$dir "$expect" "test_masquerade 6"
600			lret=1
601		fi
602	done
603
604	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
605	if [ $? -ne 0 ] ; then
606		echo "ERROR: cannot ping $ns1 from $ns2 with active ip masquerade $natflags (attempt 2)"
607		lret=1
608	fi
609
610	ip netns exec "$ns0" nft flush chain $family nat postrouting
611	if [ $? -ne 0 ]; then
612		echo "ERROR: Could not flush $family nat postrouting" 1>&2
613		lret=1
614	fi
615
616	test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for $ns2"
617
618	return $lret
619}
620
621test_redirect6()
622{
623	local family=$1
624	local lret=0
625
626	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
627
628	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
629	if [ $? -ne 0 ] ; then
630		echo "ERROR: cannnot ping $ns1 from $ns2 via ipv6"
631		lret=1
632	fi
633
634	expect="packets 1 bytes 104"
635	for dir in "in6" "out6" ; do
636		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
637		if [ $? -ne 0 ]; then
638			bad_counter "$ns1" ns2$dir "$expect" "test_redirect6 1"
639			lret=1
640		fi
641
642		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
643		if [ $? -ne 0 ]; then
644			bad_counter "$ns2" ns1$dir "$expect" "test_redirect6 2"
645			lret=1
646		fi
647	done
648
649	reset_counters
650
651# add redirect rule
652ip netns exec "$ns0" nft -f /dev/stdin <<EOF
653table $family nat {
654	chain prerouting {
655		type nat hook prerouting priority 0; policy accept;
656		meta iif veth1 meta l4proto icmpv6 ip6 saddr dead:2::99 ip6 daddr dead:1::99 redirect
657	}
658}
659EOF
660	if [ $? -ne 0 ]; then
661		echo "SKIP: Could not add add $family redirect hook"
662		return $ksft_skip
663	fi
664
665	ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
666	if [ $? -ne 0 ] ; then
667		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6 with active $family redirect"
668		lret=1
669	fi
670
671	# ns1 should have seen no packets from ns2, due to redirection
672	expect="packets 0 bytes 0"
673	for dir in "in6" "out6" ; do
674		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
675		if [ $? -ne 0 ]; then
676			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 3"
677			lret=1
678		fi
679	done
680
681	# ns0 should have seen packets from ns2, due to masquerade
682	expect="packets 1 bytes 104"
683	for dir in "in6" "out6" ; do
684		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
685		if [ $? -ne 0 ]; then
686			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 4"
687			lret=1
688		fi
689	done
690
691	ip netns exec "$ns0" nft delete table $family nat
692	if [ $? -ne 0 ]; then
693		echo "ERROR: Could not delete $family nat table" 1>&2
694		lret=1
695	fi
696
697	test $lret -eq 0 && echo "PASS: $family IPv6 redirection for $ns2"
698
699	return $lret
700}
701
702test_redirect()
703{
704	local family=$1
705	local lret=0
706
707	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
708	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
709
710	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
711	if [ $? -ne 0 ] ; then
712		echo "ERROR: cannot ping $ns1 from $ns2"
713		lret=1
714	fi
715
716	expect="packets 1 bytes 84"
717	for dir in "in" "out" ; do
718		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
719		if [ $? -ne 0 ]; then
720			bad_counter "$ns1" $ns2$dir "$expect" "test_redirect 1"
721			lret=1
722		fi
723
724		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
725		if [ $? -ne 0 ]; then
726			bad_counter "$ns2" ns1$dir "$expect" "test_redirect 2"
727			lret=1
728		fi
729	done
730
731	reset_counters
732
733# add redirect rule
734ip netns exec "$ns0" nft -f /dev/stdin <<EOF
735table $family nat {
736	chain prerouting {
737		type nat hook prerouting priority 0; policy accept;
738		meta iif veth1 ip protocol icmp ip saddr 10.0.2.99 ip daddr 10.0.1.99 redirect
739	}
740}
741EOF
742	if [ $? -ne 0 ]; then
743		echo "SKIP: Could not add add $family redirect hook"
744		return $ksft_skip
745	fi
746
747	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
748	if [ $? -ne 0 ] ; then
749		echo "ERROR: cannot ping $ns1 from $ns2 with active $family ip redirect"
750		lret=1
751	fi
752
753	# ns1 should have seen no packets from ns2, due to redirection
754	expect="packets 0 bytes 0"
755	for dir in "in" "out" ; do
756
757		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
758		if [ $? -ne 0 ]; then
759			bad_counter "$ns1" ns0$dir "$expect" "test_redirect 3"
760			lret=1
761		fi
762	done
763
764	# ns0 should have seen packets from ns2, due to masquerade
765	expect="packets 1 bytes 84"
766	for dir in "in" "out" ; do
767		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect")
768		if [ $? -ne 0 ]; then
769			bad_counter "$ns0" ns0$dir "$expect" "test_redirect 4"
770			lret=1
771		fi
772	done
773
774	ip netns exec "$ns0" nft delete table $family nat
775	if [ $? -ne 0 ]; then
776		echo "ERROR: Could not delete $family nat table" 1>&2
777		lret=1
778	fi
779
780	test $lret -eq 0 && echo "PASS: $family IP redirection for $ns2"
781
782	return $lret
783}
784
785# test port shadowing.
786# create two listening services, one on router (ns0), one
787# on client (ns2), which is masqueraded from ns1 point of view.
788# ns2 sends udp packet coming from service port to ns1, on a highport.
789# Later, if n1 uses same highport to connect to ns0:service, packet
790# might be port-forwarded to ns2 instead.
791
792# second argument tells if we expect the 'fake-entry' to take effect
793# (CLIENT) or not (ROUTER).
794test_port_shadow()
795{
796	local test=$1
797	local expect=$2
798	local daddrc="10.0.1.99"
799	local daddrs="10.0.1.1"
800	local result=""
801	local logmsg=""
802
803	# make shadow entry, from client (ns2), going to (ns1), port 41404, sport 1405.
804	echo "fake-entry" | ip netns exec "$ns2" timeout 1 socat -u STDIN UDP:"$daddrc":41404,sourceport=1405
805
806	echo ROUTER | ip netns exec "$ns0" timeout 5 socat -u STDIN UDP4-LISTEN:1405 &
807	sc_r=$!
808
809	echo CLIENT | ip netns exec "$ns2" timeout 5 socat -u STDIN UDP4-LISTEN:1405,reuseport &
810	sc_c=$!
811
812	sleep 0.3
813
814	# ns1 tries to connect to ns0:1405.  With default settings this should connect
815	# to client, it matches the conntrack entry created above.
816
817	result=$(echo "data" | ip netns exec "$ns1" timeout 1 socat - UDP:"$daddrs":1405,sourceport=41404)
818
819	if [ "$result" = "$expect" ] ;then
820		echo "PASS: portshadow test $test: got reply from ${expect}${logmsg}"
821	else
822		echo "ERROR: portshadow test $test: got reply from \"$result\", not $expect as intended"
823		ret=1
824	fi
825
826	kill $sc_r $sc_c 2>/dev/null
827
828	# flush udp entries for next test round, if any
829	ip netns exec "$ns0" conntrack -F >/dev/null 2>&1
830}
831
832# This prevents port shadow of router service via packet filter,
833# packets claiming to originate from service port from internal
834# network are dropped.
835test_port_shadow_filter()
836{
837	local family=$1
838
839ip netns exec "$ns0" nft -f /dev/stdin <<EOF
840table $family filter {
841	chain forward {
842		type filter hook forward priority 0; policy accept;
843		meta iif veth1 udp sport 1405 drop
844	}
845}
846EOF
847	test_port_shadow "port-filter" "ROUTER"
848
849	ip netns exec "$ns0" nft delete table $family filter
850}
851
852# This prevents port shadow of router service via notrack.
853test_port_shadow_notrack()
854{
855	local family=$1
856
857ip netns exec "$ns0" nft -f /dev/stdin <<EOF
858table $family raw {
859	chain prerouting {
860		type filter hook prerouting priority -300; policy accept;
861		meta iif veth0 udp dport 1405 notrack
862	}
863	chain output {
864		type filter hook output priority -300; policy accept;
865		meta oif veth0 udp sport 1405 notrack
866	}
867}
868EOF
869	test_port_shadow "port-notrack" "ROUTER"
870
871	ip netns exec "$ns0" nft delete table $family raw
872}
873
874# This prevents port shadow of router service via sport remap.
875test_port_shadow_pat()
876{
877	local family=$1
878
879ip netns exec "$ns0" nft -f /dev/stdin <<EOF
880table $family pat {
881	chain postrouting {
882		type nat hook postrouting priority -1; policy accept;
883		meta iif veth1 udp sport <= 1405 masquerade to : 1406-65535 random
884	}
885}
886EOF
887	test_port_shadow "pat" "ROUTER"
888
889	ip netns exec "$ns0" nft delete table $family pat
890}
891
892test_port_shadowing()
893{
894	local family="ip"
895
896	conntrack -h >/dev/null 2>&1
897	if [ $? -ne 0 ];then
898		echo "SKIP: Could not run nat port shadowing test without conntrack tool"
899		return
900	fi
901
902	socat -h > /dev/null 2>&1
903	if [ $? -ne 0 ];then
904		echo "SKIP: Could not run nat port shadowing test without socat tool"
905		return
906	fi
907
908	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
909	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
910
911	ip netns exec "$ns0" nft -f /dev/stdin <<EOF
912table $family nat {
913	chain postrouting {
914		type nat hook postrouting priority 0; policy accept;
915		meta oif veth0 masquerade
916	}
917}
918EOF
919	if [ $? -ne 0 ]; then
920		echo "SKIP: Could not add add $family masquerade hook"
921		return $ksft_skip
922	fi
923
924	# test default behaviour. Packet from ns1 to ns0 is redirected to ns2.
925	test_port_shadow "default" "CLIENT"
926
927	# test packet filter based mitigation: prevent forwarding of
928	# packets claiming to come from the service port.
929	test_port_shadow_filter "$family"
930
931	# test conntrack based mitigation: connections going or coming
932	# from router:service bypass connection tracking.
933	test_port_shadow_notrack "$family"
934
935	# test nat based mitigation: fowarded packets coming from service port
936	# are masqueraded with random highport.
937	test_port_shadow_pat "$family"
938
939	ip netns exec "$ns0" nft delete table $family nat
940}
941
942test_stateless_nat_ip()
943{
944	local lret=0
945
946	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
947	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
948
949	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
950	if [ $? -ne 0 ] ; then
951		echo "ERROR: cannot ping $ns1 from $ns2 before loading stateless rules"
952		return 1
953	fi
954
955ip netns exec "$ns0" nft -f /dev/stdin <<EOF
956table ip stateless {
957	map xlate_in {
958		typeof meta iifname . ip saddr . ip daddr : ip daddr
959		elements = {
960			"veth1" . 10.0.2.99 . 10.0.1.99 : 10.0.2.2,
961		}
962	}
963	map xlate_out {
964		typeof meta iifname . ip saddr . ip daddr : ip daddr
965		elements = {
966			"veth0" . 10.0.1.99 . 10.0.2.2 : 10.0.2.99
967		}
968	}
969
970	chain prerouting {
971		type filter hook prerouting priority -400; policy accept;
972		ip saddr set meta iifname . ip saddr . ip daddr map @xlate_in
973		ip daddr set meta iifname . ip saddr . ip daddr map @xlate_out
974	}
975}
976EOF
977	if [ $? -ne 0 ]; then
978		echo "SKIP: Could not add ip statless rules"
979		return $ksft_skip
980	fi
981
982	reset_counters
983
984	ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
985	if [ $? -ne 0 ] ; then
986		echo "ERROR: cannot ping $ns1 from $ns2 with stateless rules"
987		lret=1
988	fi
989
990	# ns1 should have seen packets from .2.2, due to stateless rewrite.
991	expect="packets 1 bytes 84"
992	cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect")
993	if [ $? -ne 0 ]; then
994		bad_counter "$ns1" ns0insl "$expect" "test_stateless 1"
995		lret=1
996	fi
997
998	for dir in "in" "out" ; do
999		cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect")
1000		if [ $? -ne 0 ]; then
1001			bad_counter "$ns2" ns1$dir "$expect" "test_stateless 2"
1002			lret=1
1003		fi
1004	done
1005
1006	# ns1 should not have seen packets from ns2, due to masquerade
1007	expect="packets 0 bytes 0"
1008	for dir in "in" "out" ; do
1009		cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect")
1010		if [ $? -ne 0 ]; then
1011			bad_counter "$ns1" ns0$dir "$expect" "test_stateless 3"
1012			lret=1
1013		fi
1014
1015		cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect")
1016		if [ $? -ne 0 ]; then
1017			bad_counter "$ns0" ns1$dir "$expect" "test_stateless 4"
1018			lret=1
1019		fi
1020	done
1021
1022	reset_counters
1023
1024	socat -h > /dev/null 2>&1
1025	if [ $? -ne 0 ];then
1026		echo "SKIP: Could not run stateless nat frag test without socat tool"
1027		if [ $lret -eq 0 ]; then
1028			return $ksft_skip
1029		fi
1030
1031		ip netns exec "$ns0" nft delete table ip stateless
1032		return $lret
1033	fi
1034
1035	local tmpfile=$(mktemp)
1036	dd if=/dev/urandom of=$tmpfile bs=4096 count=1 2>/dev/null
1037
1038	local outfile=$(mktemp)
1039	ip netns exec "$ns1" timeout 3 socat -u UDP4-RECV:4233 OPEN:$outfile < /dev/null &
1040	sc_r=$!
1041
1042	sleep 1
1043	# re-do with large ping -> ip fragmentation
1044	ip netns exec "$ns2" timeout 3 socat - UDP4-SENDTO:"10.0.1.99:4233" < "$tmpfile" > /dev/null
1045	if [ $? -ne 0 ] ; then
1046		echo "ERROR: failed to test udp $ns1 to $ns2 with stateless ip nat" 1>&2
1047		lret=1
1048	fi
1049
1050	wait
1051
1052	cmp "$tmpfile" "$outfile"
1053	if [ $? -ne 0 ]; then
1054		ls -l "$tmpfile" "$outfile"
1055		echo "ERROR: in and output file mismatch when checking udp with stateless nat" 1>&2
1056		lret=1
1057	fi
1058
1059	rm -f "$tmpfile" "$outfile"
1060
1061	# ns1 should have seen packets from 2.2, due to stateless rewrite.
1062	expect="packets 3 bytes 4164"
1063	cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect")
1064	if [ $? -ne 0 ]; then
1065		bad_counter "$ns1" ns0insl "$expect" "test_stateless 5"
1066		lret=1
1067	fi
1068
1069	ip netns exec "$ns0" nft delete table ip stateless
1070	if [ $? -ne 0 ]; then
1071		echo "ERROR: Could not delete table ip stateless" 1>&2
1072		lret=1
1073	fi
1074
1075	test $lret -eq 0 && echo "PASS: IP statless for $ns2"
1076
1077	return $lret
1078}
1079
1080# ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99
1081for i in 0 1 2; do
1082ip netns exec ns$i-$sfx nft -f /dev/stdin <<EOF
1083table inet filter {
1084	counter ns0in {}
1085	counter ns1in {}
1086	counter ns2in {}
1087
1088	counter ns0out {}
1089	counter ns1out {}
1090	counter ns2out {}
1091
1092	counter ns0in6 {}
1093	counter ns1in6 {}
1094	counter ns2in6 {}
1095
1096	counter ns0out6 {}
1097	counter ns1out6 {}
1098	counter ns2out6 {}
1099
1100	map nsincounter {
1101		type ipv4_addr : counter
1102		elements = { 10.0.1.1 : "ns0in",
1103			     10.0.2.1 : "ns0in",
1104			     10.0.1.99 : "ns1in",
1105			     10.0.2.99 : "ns2in" }
1106	}
1107
1108	map nsincounter6 {
1109		type ipv6_addr : counter
1110		elements = { dead:1::1 : "ns0in6",
1111			     dead:2::1 : "ns0in6",
1112			     dead:1::99 : "ns1in6",
1113			     dead:2::99 : "ns2in6" }
1114	}
1115
1116	map nsoutcounter {
1117		type ipv4_addr : counter
1118		elements = { 10.0.1.1 : "ns0out",
1119			     10.0.2.1 : "ns0out",
1120			     10.0.1.99: "ns1out",
1121			     10.0.2.99: "ns2out" }
1122	}
1123
1124	map nsoutcounter6 {
1125		type ipv6_addr : counter
1126		elements = { dead:1::1 : "ns0out6",
1127			     dead:2::1 : "ns0out6",
1128			     dead:1::99 : "ns1out6",
1129			     dead:2::99 : "ns2out6" }
1130	}
1131
1132	chain input {
1133		type filter hook input priority 0; policy accept;
1134		counter name ip saddr map @nsincounter
1135		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 saddr map @nsincounter6
1136	}
1137	chain output {
1138		type filter hook output priority 0; policy accept;
1139		counter name ip daddr map @nsoutcounter
1140		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 daddr map @nsoutcounter6
1141	}
1142}
1143EOF
1144done
1145
1146# special case for stateless nat check, counter needs to
1147# be done before (input) ip defragmentation
1148ip netns exec ns1-$sfx nft -f /dev/stdin <<EOF
1149table inet filter {
1150	counter ns0insl {}
1151
1152	chain pre {
1153		type filter hook prerouting priority -400; policy accept;
1154		ip saddr 10.0.2.2 counter name "ns0insl"
1155	}
1156}
1157EOF
1158
1159sleep 3
1160# test basic connectivity
1161for i in 1 2; do
1162  ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99 > /dev/null
1163  if [ $? -ne 0 ];then
1164  	echo "ERROR: Could not reach other namespace(s)" 1>&2
1165	ret=1
1166  fi
1167
1168  ip netns exec "$ns0" ping -c 1 -q dead:$i::99 > /dev/null
1169  if [ $? -ne 0 ];then
1170	echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2
1171	ret=1
1172  fi
1173  check_counters ns$i-$sfx
1174  if [ $? -ne 0 ]; then
1175	ret=1
1176  fi
1177
1178  check_ns0_counters ns$i
1179  if [ $? -ne 0 ]; then
1180	ret=1
1181  fi
1182  reset_counters
1183done
1184
1185if [ $ret -eq 0 ];then
1186	echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2"
1187fi
1188
1189reset_counters
1190test_local_dnat ip
1191test_local_dnat6 ip6
1192
1193reset_counters
1194test_local_dnat_portonly inet 10.0.1.99
1195
1196reset_counters
1197$test_inet_nat && test_local_dnat inet
1198$test_inet_nat && test_local_dnat6 inet
1199
1200for flags in "" "fully-random"; do
1201reset_counters
1202test_masquerade ip $flags
1203test_masquerade6 ip6 $flags
1204reset_counters
1205$test_inet_nat && test_masquerade inet $flags
1206$test_inet_nat && test_masquerade6 inet $flags
1207done
1208
1209reset_counters
1210test_redirect ip
1211test_redirect6 ip6
1212reset_counters
1213$test_inet_nat && test_redirect inet
1214$test_inet_nat && test_redirect6 inet
1215
1216test_port_shadowing
1217test_stateless_nat_ip
1218
1219if [ $ret -ne 0 ];then
1220	echo -n "FAIL: "
1221	nft --version
1222fi
1223
1224exit $ret
1225