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