Merge tag 'qcom-clk-for-6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom...
[linux-block.git] / tools / testing / selftests / netfilter / nft_nat.sh
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.
7 ksft_skip=4
8 ret=0
9 test_inet_nat=true
10
11 sfx=$(mktemp -u "XXXXXXXX")
12 ns0="ns0-$sfx"
13 ns1="ns1-$sfx"
14 ns2="ns2-$sfx"
15
16 cleanup()
17 {
18         for i in 0 1 2; do ip netns del ns$i-"$sfx";done
19 }
20
21 nft --version > /dev/null 2>&1
22 if [ $? -ne 0 ];then
23         echo "SKIP: Could not run test without nft tool"
24         exit $ksft_skip
25 fi
26
27 ip -Version > /dev/null 2>&1
28 if [ $? -ne 0 ];then
29         echo "SKIP: Could not run test without ip tool"
30         exit $ksft_skip
31 fi
32
33 ip netns add "$ns0"
34 if [ $? -ne 0 ];then
35         echo "SKIP: Could not create net namespace $ns0"
36         exit $ksft_skip
37 fi
38
39 trap cleanup EXIT
40
41 ip netns add "$ns1"
42 if [ $? -ne 0 ];then
43         echo "SKIP: Could not create net namespace $ns1"
44         exit $ksft_skip
45 fi
46
47 ip netns add "$ns2"
48 if [ $? -ne 0 ];then
49         echo "SKIP: Could not create net namespace $ns2"
50         exit $ksft_skip
51 fi
52
53 ip link add veth0 netns "$ns0" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1
54 if [ $? -ne 0 ];then
55     echo "SKIP: No virtual ethernet pair device support in kernel"
56     exit $ksft_skip
57 fi
58 ip link add veth1 netns "$ns0" type veth peer name eth0 netns "$ns2"
59
60 ip -net "$ns0" link set lo up
61 ip -net "$ns0" link set veth0 up
62 ip -net "$ns0" addr add 10.0.1.1/24 dev veth0
63 ip -net "$ns0" addr add dead:1::1/64 dev veth0
64
65 ip -net "$ns0" link set veth1 up
66 ip -net "$ns0" addr add 10.0.2.1/24 dev veth1
67 ip -net "$ns0" addr add dead:2::1/64 dev veth1
68
69 for 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
76 done
77
78 bad_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
89 check_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
120 check_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
167 reset_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
174 test_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
184 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
185 table $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 }
191 EOF
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
249 test_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
259 ip netns exec "$ns0" nft -f /dev/stdin <<EOF 2>/dev/null
260 table $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 }
266 EOF
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
377 test_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
385 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
386 table $family nat {
387         chain output {
388                 type nat hook output priority 0; policy accept;
389                 meta l4proto tcp dnat to :2000
390
391         }
392 }
393 EOF
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
417 test_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
450 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
451 table $family nat {
452         chain postrouting {
453                 type nat hook postrouting priority 0; policy accept;
454                 meta oif veth0 masquerade $natflags
455         }
456 }
457 EOF
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
518 test_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
551 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
552 table $family nat {
553         chain postrouting {
554                 type nat hook postrouting priority 0; policy accept;
555                 meta oif veth0 masquerade $natflags
556         }
557 }
558 EOF
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
619 test_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
650 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
651 table $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 }
657 EOF
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
700 test_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
732 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
733 table $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 }
739 EOF
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).
792 test_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.
833 test_port_shadow_filter()
834 {
835         local family=$1
836
837 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
838 table $family filter {
839         chain forward {
840                 type filter hook forward priority 0; policy accept;
841                 meta iif veth1 udp sport 1405 drop
842         }
843 }
844 EOF
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.
851 test_port_shadow_notrack()
852 {
853         local family=$1
854
855 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
856 table $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 }
866 EOF
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.
873 test_port_shadow_pat()
874 {
875         local family=$1
876
877 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
878 table $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 }
884 EOF
885         test_port_shadow "pat" "ROUTER"
886
887         ip netns exec "$ns0" nft delete table $family pat
888 }
889
890 test_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
910 table $family nat {
911         chain postrouting {
912                 type nat hook postrouting priority 0; policy accept;
913                 meta oif veth0 masquerade
914         }
915 }
916 EOF
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
940 test_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
953 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
954 table 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 }
974 EOF
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
1079 for i in 0 1 2; do
1080 ip netns exec ns$i-$sfx nft -f /dev/stdin <<EOF
1081 table 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 }
1141 EOF
1142 done
1143
1144 # special case for stateless nat check, counter needs to
1145 # be done before (input) ip defragmentation
1146 ip netns exec ns1-$sfx nft -f /dev/stdin <<EOF
1147 table 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 }
1155 EOF
1156
1157 sleep 3
1158 # test basic connectivity
1159 for 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
1181 done
1182
1183 if [ $ret -eq 0 ];then
1184         echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2"
1185 fi
1186
1187 reset_counters
1188 test_local_dnat ip
1189 test_local_dnat6 ip6
1190
1191 reset_counters
1192 test_local_dnat_portonly inet 10.0.1.99
1193
1194 reset_counters
1195 $test_inet_nat && test_local_dnat inet
1196 $test_inet_nat && test_local_dnat6 inet
1197
1198 for flags in "" "fully-random"; do
1199 reset_counters
1200 test_masquerade ip $flags
1201 test_masquerade6 ip6 $flags
1202 reset_counters
1203 $test_inet_nat && test_masquerade inet $flags
1204 $test_inet_nat && test_masquerade6 inet $flags
1205 done
1206
1207 reset_counters
1208 test_redirect ip
1209 test_redirect6 ip6
1210 reset_counters
1211 $test_inet_nat && test_redirect inet
1212 $test_inet_nat && test_redirect6 inet
1213
1214 test_port_shadowing
1215 test_stateless_nat_ip
1216
1217 if [ $ret -ne 0 ];then
1218         echo -n "FAIL: "
1219         nft --version
1220 fi
1221
1222 exit $ret