Linux 6.9-rc1
[linux-2.6-microblaze.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         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
419 test_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
452 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
453 table $family nat {
454         chain postrouting {
455                 type nat hook postrouting priority 0; policy accept;
456                 meta oif veth0 masquerade $natflags
457         }
458 }
459 EOF
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
520 test_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
553 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
554 table $family nat {
555         chain postrouting {
556                 type nat hook postrouting priority 0; policy accept;
557                 meta oif veth0 masquerade $natflags
558         }
559 }
560 EOF
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
621 test_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
652 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
653 table $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 }
659 EOF
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
702 test_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
734 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
735 table $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 }
741 EOF
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).
794 test_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.
835 test_port_shadow_filter()
836 {
837         local family=$1
838
839 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
840 table $family filter {
841         chain forward {
842                 type filter hook forward priority 0; policy accept;
843                 meta iif veth1 udp sport 1405 drop
844         }
845 }
846 EOF
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.
853 test_port_shadow_notrack()
854 {
855         local family=$1
856
857 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
858 table $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 }
868 EOF
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.
875 test_port_shadow_pat()
876 {
877         local family=$1
878
879 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
880 table $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 }
886 EOF
887         test_port_shadow "pat" "ROUTER"
888
889         ip netns exec "$ns0" nft delete table $family pat
890 }
891
892 test_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
912 table $family nat {
913         chain postrouting {
914                 type nat hook postrouting priority 0; policy accept;
915                 meta oif veth0 masquerade
916         }
917 }
918 EOF
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
942 test_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
955 ip netns exec "$ns0" nft -f /dev/stdin <<EOF
956 table 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 }
976 EOF
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
1081 for i in 0 1 2; do
1082 ip netns exec ns$i-$sfx nft -f /dev/stdin <<EOF
1083 table 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 }
1143 EOF
1144 done
1145
1146 # special case for stateless nat check, counter needs to
1147 # be done before (input) ip defragmentation
1148 ip netns exec ns1-$sfx nft -f /dev/stdin <<EOF
1149 table 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 }
1157 EOF
1158
1159 sleep 3
1160 # test basic connectivity
1161 for 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
1183 done
1184
1185 if [ $ret -eq 0 ];then
1186         echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2"
1187 fi
1188
1189 reset_counters
1190 test_local_dnat ip
1191 test_local_dnat6 ip6
1192
1193 reset_counters
1194 test_local_dnat_portonly inet 10.0.1.99
1195
1196 reset_counters
1197 $test_inet_nat && test_local_dnat inet
1198 $test_inet_nat && test_local_dnat6 inet
1199
1200 for flags in "" "fully-random"; do
1201 reset_counters
1202 test_masquerade ip $flags
1203 test_masquerade6 ip6 $flags
1204 reset_counters
1205 $test_inet_nat && test_masquerade inet $flags
1206 $test_inet_nat && test_masquerade6 inet $flags
1207 done
1208
1209 reset_counters
1210 test_redirect ip
1211 test_redirect6 ip6
1212 reset_counters
1213 $test_inet_nat && test_redirect inet
1214 $test_inet_nat && test_redirect6 inet
1215
1216 test_port_shadowing
1217 test_stateless_nat_ip
1218
1219 if [ $ret -ne 0 ];then
1220         echo -n "FAIL: "
1221         nft --version
1222 fi
1223
1224 exit $ret