1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# nft_concat_range.sh - Tests for sets with concatenation of ranged fields 5# 6# Copyright (c) 2019 Red Hat GmbH 7# 8# Author: Stefano Brivio <sbrivio@redhat.com> 9# 10# shellcheck disable=SC2154,SC2034,SC2016,SC2030,SC2031,SC2317 11# ^ Configuration and templates sourced with eval, counters reused in subshells 12 13source lib.sh 14 15# Available test groups: 16# - reported_issues: check for issues that were reported in the past 17# - correctness: check that packets match given entries, and only those 18# - correctness_large: same but with additional non-matching entries 19# - concurrency: attempt races between insertion, deletion and lookup 20# - timeout: check that packets match entries until they expire 21# - performance: estimate matching rate, compare with rbtree and hash baselines 22TESTS="reported_issues correctness correctness_large concurrency timeout" 23 24[ -n "$NFT_CONCAT_RANGE_TESTS" ] && TESTS="${NFT_CONCAT_RANGE_TESTS}" 25 26# Set types, defined by TYPE_ variables below 27TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto 28 net_port_net net_mac mac_net net_mac_icmp net6_mac_icmp 29 net6_port_net6_port net_port_mac_proto_net" 30 31# Reported bugs, also described by TYPE_ variables below 32BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate 33 insert_overlap load_flush_load4 load_flush_load8" 34 35# List of possible paths to pktgen script from kernel tree for performance tests 36PKTGEN_SCRIPT_PATHS=" 37 ../../../../../samples/pktgen/pktgen_bench_xmit_mode_netif_receive.sh 38 pktgen/pktgen_bench_xmit_mode_netif_receive.sh" 39 40# Definition of set types: 41# display display text for test report 42# type_spec nftables set type specifier 43# chain_spec nftables type specifier for rules mapping to set 44# dst call sequence of format_*() functions for destination fields 45# src call sequence of format_*() functions for source fields 46# start initial integer used to generate addresses and ports 47# count count of entries to generate and match 48# src_delta number summed to destination generator for source fields 49# tools list of tools for correctness and timeout tests, any can be used 50# proto L4 protocol of test packets 51# 52# race_repeat race attempts per thread, 0 disables concurrency test for type 53# flood_tools list of tools for concurrency tests, any can be used 54# flood_proto L4 protocol of test packets for concurrency tests 55# flood_spec nftables type specifier for concurrency tests 56# 57# perf_duration duration of single pktgen injection test 58# perf_spec nftables type specifier for performance tests 59# perf_dst format_*() functions for destination fields in performance test 60# perf_src format_*() functions for source fields in performance test 61# perf_entries number of set entries for performance test 62# perf_proto L3 protocol of test packets 63TYPE_net_port=" 64display net,port 65type_spec ipv4_addr . inet_service 66chain_spec ip daddr . udp dport 67dst addr4 port 68src 69start 1 70count 5 71src_delta 2000 72tools sendip bash 73proto udp 74 75race_repeat 3 76flood_tools iperf3 iperf netperf 77flood_proto udp 78flood_spec ip daddr . udp dport 79 80perf_duration 5 81perf_spec ip daddr . udp dport 82perf_dst addr4 port 83perf_src 84perf_entries 1000 85perf_proto ipv4 86" 87 88TYPE_port_net=" 89display port,net 90type_spec inet_service . ipv4_addr 91chain_spec udp dport . ip daddr 92dst port addr4 93src 94start 1 95count 5 96src_delta 2000 97tools sendip socat bash 98proto udp 99 100race_repeat 3 101flood_tools iperf3 iperf netperf 102flood_proto udp 103flood_spec udp dport . ip daddr 104 105perf_duration 5 106perf_spec udp dport . ip daddr 107perf_dst port addr4 108perf_src 109perf_entries 100 110perf_proto ipv4 111" 112 113TYPE_net6_port=" 114display net6,port 115type_spec ipv6_addr . inet_service 116chain_spec ip6 daddr . udp dport 117dst addr6 port 118src 119start 10 120count 5 121src_delta 2000 122tools sendip socat bash 123proto udp6 124 125race_repeat 3 126flood_tools iperf3 iperf netperf 127flood_proto tcp6 128flood_spec ip6 daddr . udp dport 129 130perf_duration 5 131perf_spec ip6 daddr . udp dport 132perf_dst addr6 port 133perf_src 134perf_entries 1000 135perf_proto ipv6 136" 137 138TYPE_port_proto=" 139display port,proto 140type_spec inet_service . inet_proto 141chain_spec udp dport . meta l4proto 142dst port proto 143src 144start 1 145count 5 146src_delta 2000 147tools sendip socat bash 148proto udp 149 150race_repeat 0 151 152perf_duration 5 153perf_spec udp dport . meta l4proto 154perf_dst port proto 155perf_src 156perf_entries 30000 157perf_proto ipv4 158" 159 160TYPE_net6_port_mac=" 161display net6,port,mac 162type_spec ipv6_addr . inet_service . ether_addr 163chain_spec ip6 daddr . udp dport . ether saddr 164dst addr6 port 165src mac 166start 10 167count 5 168src_delta 2000 169tools sendip socat bash 170proto udp6 171 172race_repeat 0 173 174perf_duration 5 175perf_spec ip6 daddr . udp dport . ether daddr 176perf_dst addr6 port mac 177perf_src 178perf_entries 10 179perf_proto ipv6 180" 181 182TYPE_net6_port_mac_proto=" 183display net6,port,mac,proto 184type_spec ipv6_addr . inet_service . ether_addr . inet_proto 185chain_spec ip6 daddr . udp dport . ether saddr . meta l4proto 186dst addr6 port 187src mac proto 188start 10 189count 5 190src_delta 2000 191tools sendip socat bash 192proto udp6 193 194race_repeat 0 195 196perf_duration 5 197perf_spec ip6 daddr . udp dport . ether daddr . meta l4proto 198perf_dst addr6 port mac proto 199perf_src 200perf_entries 1000 201perf_proto ipv6 202" 203 204TYPE_net_port_net=" 205display net,port,net 206type_spec ipv4_addr . inet_service . ipv4_addr 207chain_spec ip daddr . udp dport . ip saddr 208dst addr4 port 209src addr4 210start 1 211count 5 212src_delta 2000 213tools sendip socat bash 214proto udp 215 216race_repeat 3 217flood_tools iperf3 iperf netperf 218flood_proto tcp 219flood_spec ip daddr . udp dport . ip saddr 220 221perf_duration 0 222" 223 224TYPE_net6_port_net6_port=" 225display net6,port,net6,port 226type_spec ipv6_addr . inet_service . ipv6_addr . inet_service 227chain_spec ip6 daddr . udp dport . ip6 saddr . udp sport 228dst addr6 port 229src addr6 port 230start 10 231count 5 232src_delta 2000 233tools sendip socat 234proto udp6 235 236race_repeat 3 237flood_tools iperf3 iperf netperf 238flood_proto tcp6 239flood_spec ip6 daddr . tcp dport . ip6 saddr . tcp sport 240 241perf_duration 0 242" 243 244TYPE_net_port_mac_proto_net=" 245display net,port,mac,proto,net 246type_spec ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr 247chain_spec ip daddr . udp dport . ether saddr . meta l4proto . ip saddr 248dst addr4 port 249src mac proto addr4 250start 1 251count 5 252src_delta 2000 253tools sendip socat bash 254proto udp 255 256race_repeat 0 257 258perf_duration 0 259" 260 261TYPE_net_mac=" 262display net,mac 263type_spec ipv4_addr . ether_addr 264chain_spec ip daddr . ether saddr 265dst addr4 266src mac 267start 1 268count 5 269src_delta 2000 270tools sendip socat bash 271proto udp 272 273race_repeat 0 274 275perf_duration 5 276perf_spec ip daddr . ether daddr 277perf_dst addr4 mac 278perf_src 279perf_entries 1000 280perf_proto ipv4 281" 282 283TYPE_mac_net=" 284display mac,net 285type_spec ether_addr . ipv4_addr 286chain_spec ether saddr . ip saddr 287dst 288src mac addr4 289start 1 290count 5 291src_delta 2000 292tools sendip socat bash 293proto udp 294 295race_repeat 0 296 297perf_duration 0 298" 299 300TYPE_net_mac_icmp=" 301display net,mac - ICMP 302type_spec ipv4_addr . ether_addr 303chain_spec ip daddr . ether saddr 304dst addr4 305src mac 306start 1 307count 5 308src_delta 2000 309tools ping 310proto icmp 311 312race_repeat 0 313 314perf_duration 0 315" 316 317TYPE_net6_mac_icmp=" 318display net6,mac - ICMPv6 319type_spec ipv6_addr . ether_addr 320chain_spec ip6 daddr . ether saddr 321dst addr6 322src mac 323start 10 324count 50 325src_delta 2000 326tools ping 327proto icmp6 328 329race_repeat 0 330 331perf_duration 0 332" 333 334TYPE_net_port_proto_net=" 335display net,port,proto,net 336type_spec ipv4_addr . inet_service . inet_proto . ipv4_addr 337chain_spec ip daddr . udp dport . meta l4proto . ip saddr 338dst addr4 port proto 339src addr4 340start 1 341count 5 342src_delta 2000 343tools sendip socat 344proto udp 345 346race_repeat 3 347flood_tools iperf3 iperf netperf 348flood_proto tcp 349flood_spec ip daddr . tcp dport . meta l4proto . ip saddr 350 351perf_duration 0 352" 353 354# Definition of tests for bugs reported in the past: 355# display display text for test report 356TYPE_flush_remove_add=" 357display Add two elements, flush, re-add 358" 359 360TYPE_reload=" 361display net,mac with reload 362type_spec ipv4_addr . ether_addr 363chain_spec ip daddr . ether saddr 364dst addr4 365src mac 366start 1 367count 1 368src_delta 2000 369tools sendip socat bash 370proto udp 371 372race_repeat 0 373 374perf_duration 0 375" 376 377TYPE_net_port_proto_match=" 378display net,port,proto 379type_spec ipv4_addr . inet_service . inet_proto 380chain_spec ip daddr . udp dport . meta l4proto 381dst addr4 port proto 382src 383start 1 384count 9 385src_delta 9 386tools sendip bash 387proto udp 388 389race_repeat 0 390 391perf_duration 0 392" 393 394TYPE_avx2_mismatch=" 395display avx2 false match 396type_spec inet_proto . ipv6_addr 397chain_spec meta l4proto . ip6 daddr 398dst proto addr6 399src 400start 1 401count 1 402src_delta 1 403tools ping 404proto icmp6 405 406race_repeat 0 407 408perf_duration 0 409" 410 411 412TYPE_doublecreate=" 413display cannot create same element twice 414type_spec ipv4_addr . ipv4_addr 415chain_spec ip saddr . ip daddr 416dst addr4 417proto icmp 418 419race_repeat 0 420 421perf_duration 0 422" 423 424TYPE_insert_overlap=" 425display reject overlapping range on add 426type_spec ipv4_addr . ipv4_addr 427chain_spec ip saddr . ip daddr 428dst addr4 429proto icmp 430 431race_repeat 0 432 433perf_duration 0 434" 435 436TYPE_load_flush_load4=" 437display reload with flush, 4bit groups 438type_spec ipv4_addr . ipv4_addr 439chain_spec ip saddr . ip daddr 440dst addr4 441proto icmp 442 443race_repeat 0 444 445perf_duration 0 446" 447 448TYPE_load_flush_load8=" 449display reload with flush, 8bit groups 450type_spec ipv4_addr . ipv4_addr 451chain_spec ip saddr . ip daddr 452dst addr4 453proto icmp 454 455race_repeat 0 456 457perf_duration 0 458" 459 460# Set template for all tests, types and rules are filled in depending on test 461set_template=' 462flush ruleset 463 464table inet filter { 465 counter test { 466 packets 0 bytes 0 467 } 468 469 set test { 470 type ${type_spec} 471 counter 472 flags interval,timeout 473 } 474 475 chain input { 476 type filter hook prerouting priority 0; policy accept; 477 ${chain_spec} @test counter name \"test\" 478 } 479} 480 481table netdev perf { 482 counter test { 483 packets 0 bytes 0 484 } 485 486 counter match { 487 packets 0 bytes 0 488 } 489 490 set test { 491 type ${type_spec} 492 flags interval 493 } 494 495 set norange { 496 type ${type_spec} 497 } 498 499 set noconcat { 500 type ${type_spec%% *} 501 flags interval 502 } 503 504 chain test { 505 type filter hook ingress device veth_a priority 0; 506 } 507} 508' 509 510err_buf= 511info_buf= 512 513# Append string to error buffer 514err() { 515 err_buf="${err_buf}${1} 516" 517} 518 519# Append string to information buffer 520info() { 521 info_buf="${info_buf}${1} 522" 523} 524 525# Flush error buffer to stdout 526err_flush() { 527 printf "%s" "${err_buf}" 528 err_buf= 529} 530 531# Flush information buffer to stdout 532info_flush() { 533 printf "%s" "${info_buf}" 534 info_buf= 535} 536 537# Setup veth pair: this namespace receives traffic, B generates it 538setup_veth() { 539 ip netns add B 540 ip link add veth_a type veth peer name veth_b || return 1 541 542 ip link set veth_a up 543 ip link set veth_b netns B 544 545 ip -n B link set veth_b up 546 547 ip addr add dev veth_a 10.0.0.1 548 ip route add default dev veth_a 549 550 ip -6 addr add fe80::1/64 dev veth_a nodad 551 ip -6 addr add 2001:db8::1/64 dev veth_a nodad 552 ip -6 route add default dev veth_a 553 554 ip -n B route add default dev veth_b 555 556 ip -6 -n B addr add fe80::2/64 dev veth_b nodad 557 ip -6 -n B addr add 2001:db8::2/64 dev veth_b nodad 558 ip -6 -n B route add default dev veth_b 559 560 B() { 561 ip netns exec B "$@" >/dev/null 2>&1 562 } 563} 564 565# Fill in set template and initialise set 566setup_set() { 567 eval "echo \"${set_template}\"" | nft -f - 568} 569 570# Check that at least one of the needed tools is available 571check_tools() { 572 [ -z "${tools}" ] && return 0 573 574 __tools= 575 for tool in ${tools}; do 576 __tools="${__tools} ${tool}" 577 578 command -v "${tool}" >/dev/null && return 0 579 done 580 err "need one of:${__tools}, skipping" && return 1 581} 582 583# Set up function to send ICMP packets 584setup_send_icmp() { 585 send_icmp() { 586 B ping -c1 -W1 "${dst_addr4}" >/dev/null 2>&1 587 } 588} 589 590# Set up function to send ICMPv6 packets 591setup_send_icmp6() { 592 if command -v ping6 >/dev/null; then 593 send_icmp6() { 594 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 595 2>/dev/null 596 B ping6 -q -c1 -W1 "${dst_addr6}" 597 } 598 else 599 send_icmp6() { 600 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 601 2>/dev/null 602 B ping -q -6 -c1 -W1 "${dst_addr6}" 603 } 604 fi 605} 606 607# Set up function to send single UDP packets on IPv4 608setup_send_udp() { 609 if command -v sendip >/dev/null; then 610 send_udp() { 611 [ -n "${src_port}" ] && src_port="-us ${src_port}" 612 [ -n "${dst_port}" ] && dst_port="-ud ${dst_port}" 613 [ -n "${src_addr4}" ] && src_addr4="-is ${src_addr4}" 614 615 # shellcheck disable=SC2086 # sendip needs split options 616 B sendip -p ipv4 -p udp ${src_addr4} ${src_port} \ 617 ${dst_port} "${dst_addr4}" 618 619 src_port= 620 dst_port= 621 src_addr4= 622 } 623 elif command -v socat -v >/dev/null; then 624 send_udp() { 625 if [ -n "${src_addr4}" ]; then 626 B ip addr add "${src_addr4}" dev veth_b 627 __socatbind=",bind=${src_addr4}" 628 if [ -n "${src_port}" ];then 629 __socatbind="${__socatbind}:${src_port}" 630 fi 631 fi 632 633 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 634 [ -z "${dst_port}" ] && dst_port=12345 635 636 echo "test4" | B socat -t 0.01 STDIN UDP4-DATAGRAM:"$dst_addr4":"$dst_port""${__socatbind}" 637 638 src_addr4= 639 src_port= 640 } 641 elif [ -z "$(bash -c 'type -p')" ]; then 642 send_udp() { 643 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 644 if [ -n "${src_addr4}" ]; then 645 B ip addr add "${src_addr4}/16" dev veth_b 646 B ip route add default dev veth_b 647 fi 648 649 B bash -c "echo > /dev/udp/${dst_addr4}/${dst_port}" 650 651 if [ -n "${src_addr4}" ]; then 652 B ip addr del "${src_addr4}/16" dev veth_b 653 fi 654 src_addr4= 655 } 656 else 657 return 1 658 fi 659} 660 661# Set up function to send single UDP packets on IPv6 662setup_send_udp6() { 663 if command -v sendip >/dev/null; then 664 send_udp6() { 665 [ -n "${src_port}" ] && src_port="-us ${src_port}" 666 [ -n "${dst_port}" ] && dst_port="-ud ${dst_port}" 667 if [ -n "${src_addr6}" ]; then 668 src_addr6="-6s ${src_addr6}" 669 else 670 src_addr6="-6s 2001:db8::2" 671 fi 672 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 673 2>/dev/null 674 675 # shellcheck disable=SC2086 # this needs split options 676 B sendip -p ipv6 -p udp ${src_addr6} ${src_port} \ 677 ${dst_port} "${dst_addr6}" 678 679 src_port= 680 dst_port= 681 src_addr6= 682 } 683 elif command -v socat -v >/dev/null; then 684 send_udp6() { 685 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 686 2>/dev/null 687 688 __socatbind6= 689 690 if [ -n "${src_addr6}" ]; then 691 B ip addr add "${src_addr6}" dev veth_b nodad 692 693 __socatbind6=",bind=[${src_addr6}]" 694 695 if [ -n "${src_port}" ] ;then 696 __socatbind6="${__socatbind6}:${src_port}" 697 fi 698 fi 699 700 echo "test6" | B socat -t 0.01 STDIN UDP6-DATAGRAM:["$dst_addr6"]:"$dst_port""${__socatbind6}" 701 } 702 elif [ -z "$(bash -c 'type -p')" ]; then 703 send_udp6() { 704 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 705 2>/dev/null 706 B ip addr add "${src_addr6}" dev veth_b nodad 707 B bash -c "echo > /dev/udp/${dst_addr6}/${dst_port}" 708 ip -6 addr del "${dst_addr6}" dev veth_a 2>/dev/null 709 } 710 else 711 return 1 712 fi 713} 714 715listener_ready() 716{ 717 port="$1" 718 ss -lnt -o "sport = :$port" | grep -q "$port" 719} 720 721# Set up function to send TCP traffic on IPv4 722setup_flood_tcp() { 723 if command -v iperf3 >/dev/null; then 724 flood_tcp() { 725 local n_port="${dst_port}" 726 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 727 if [ -n "${src_addr4}" ]; then 728 B ip addr add "${src_addr4}/16" dev veth_b 729 src_addr4="-B ${src_addr4}" 730 else 731 B ip addr add dev veth_b 10.0.0.2 732 src_addr4="-B 10.0.0.2" 733 fi 734 if [ -n "${src_port}" ]; then 735 src_port="--cport ${src_port}" 736 fi 737 B ip route add default dev veth_b 2>/dev/null 738 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 739 740 # shellcheck disable=SC2086 # this needs split options 741 iperf3 -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1 742 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 743 744 # shellcheck disable=SC2086 # this needs split options 745 B iperf3 -c "${dst_addr4}" ${dst_port} ${src_port} \ 746 ${src_addr4} -l16 -t 1000 747 748 src_addr4= 749 src_port= 750 dst_port= 751 } 752 elif command -v iperf >/dev/null; then 753 flood_tcp() { 754 local n_port="${dst_port}" 755 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 756 if [ -n "${src_addr4}" ]; then 757 B ip addr add "${src_addr4}/16" dev veth_b 758 src_addr4="-B ${src_addr4}" 759 else 760 B ip addr add dev veth_b 10.0.0.2 2>/dev/null 761 src_addr4="-B 10.0.0.2" 762 fi 763 if [ -n "${src_port}" ]; then 764 src_addr4="${src_addr4}:${src_port}" 765 fi 766 B ip route add default dev veth_b 767 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 768 769 # shellcheck disable=SC2086 # this needs split options 770 iperf -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1 771 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 772 773 # shellcheck disable=SC2086 # this needs split options 774 B iperf -c "${dst_addr4}" ${dst_port} ${src_addr4} \ 775 -l20 -t 1000 776 777 src_addr4= 778 src_port= 779 dst_port= 780 } 781 elif command -v netperf >/dev/null; then 782 flood_tcp() { 783 local n_port="${dst_port}" 784 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 785 if [ -n "${src_addr4}" ]; then 786 B ip addr add "${src_addr4}/16" dev veth_b 787 else 788 B ip addr add dev veth_b 10.0.0.2 789 src_addr4="10.0.0.2" 790 fi 791 if [ -n "${src_port}" ]; then 792 dst_port="${dst_port},${src_port}" 793 fi 794 B ip route add default dev veth_b 795 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 796 797 # shellcheck disable=SC2086 # this needs split options 798 netserver -4 ${dst_port} -L "${dst_addr4}" \ 799 >/dev/null 2>&1 800 busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}" 801 802 # shellcheck disable=SC2086 # this needs split options 803 B netperf -4 -H "${dst_addr4}" ${dst_port} \ 804 -L "${src_addr4}" -l 1000 -t TCP_STREAM 805 806 src_addr4= 807 src_port= 808 dst_port= 809 } 810 else 811 return 1 812 fi 813} 814 815# Set up function to send TCP traffic on IPv6 816setup_flood_tcp6() { 817 if command -v iperf3 >/dev/null; then 818 flood_tcp6() { 819 local n_port="${dst_port}" 820 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 821 if [ -n "${src_addr6}" ]; then 822 B ip addr add "${src_addr6}" dev veth_b nodad 823 src_addr6="-B ${src_addr6}" 824 else 825 src_addr6="-B 2001:db8::2" 826 fi 827 if [ -n "${src_port}" ]; then 828 src_port="--cport ${src_port}" 829 fi 830 B ip route add default dev veth_b 831 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 832 2>/dev/null 833 834 # shellcheck disable=SC2086 # this needs split options 835 iperf3 -s -DB "${dst_addr6}" ${dst_port} >/dev/null 2>&1 836 busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}" 837 838 # shellcheck disable=SC2086 # this needs split options 839 B iperf3 -c "${dst_addr6}" ${dst_port} \ 840 ${src_port} ${src_addr6} -l16 -t 1000 841 842 src_addr6= 843 src_port= 844 dst_port= 845 } 846 elif command -v iperf >/dev/null; then 847 flood_tcp6() { 848 local n_port="${dst_port}" 849 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 850 if [ -n "${src_addr6}" ]; then 851 B ip addr add "${src_addr6}" dev veth_b nodad 852 src_addr6="-B ${src_addr6}" 853 else 854 src_addr6="-B 2001:db8::2" 855 fi 856 if [ -n "${src_port}" ]; then 857 src_addr6="${src_addr6}:${src_port}" 858 fi 859 B ip route add default dev veth_b 860 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 861 2>/dev/null 862 863 # shellcheck disable=SC2086 # this needs split options 864 iperf -s -VDB "${dst_addr6}" ${dst_port} >/dev/null 2>&1 865 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 866 867 # shellcheck disable=SC2086 # this needs split options 868 B iperf -c "${dst_addr6}" -V ${dst_port} \ 869 ${src_addr6} -l1 -t 1000 870 871 src_addr6= 872 src_port= 873 dst_port= 874 } 875 elif command -v netperf >/dev/null; then 876 flood_tcp6() { 877 local n_port="${dst_port}" 878 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 879 if [ -n "${src_addr6}" ]; then 880 B ip addr add "${src_addr6}" dev veth_b nodad 881 else 882 src_addr6="2001:db8::2" 883 fi 884 if [ -n "${src_port}" ]; then 885 dst_port="${dst_port},${src_port}" 886 fi 887 B ip route add default dev veth_b 888 ip -6 addr add "${dst_addr6}" dev veth_a nodad \ 889 2>/dev/null 890 891 # shellcheck disable=SC2086 # this needs split options 892 netserver -6 ${dst_port} -L "${dst_addr6}" \ 893 >/dev/null 2>&1 894 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 895 896 # shellcheck disable=SC2086 # this needs split options 897 B netperf -6 -H "${dst_addr6}" ${dst_port} \ 898 -L "${src_addr6}" -l 1000 -t TCP_STREAM 899 900 src_addr6= 901 src_port= 902 dst_port= 903 } 904 else 905 return 1 906 fi 907} 908 909# Set up function to send UDP traffic on IPv4 910setup_flood_udp() { 911 if command -v iperf3 >/dev/null; then 912 flood_udp() { 913 local n_port="${dst_port}" 914 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 915 if [ -n "${src_addr4}" ]; then 916 B ip addr add "${src_addr4}/16" dev veth_b 917 src_addr4="-B ${src_addr4}" 918 else 919 B ip addr add dev veth_b 10.0.0.2 2>/dev/null 920 src_addr4="-B 10.0.0.2" 921 fi 922 if [ -n "${src_port}" ]; then 923 src_port="--cport ${src_port}" 924 fi 925 B ip route add default dev veth_b 926 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 927 928 # shellcheck disable=SC2086 # this needs split options 929 iperf3 -s -DB "${dst_addr4}" ${dst_port} 930 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 931 932 # shellcheck disable=SC2086 # this needs split options 933 B iperf3 -u -c "${dst_addr4}" -Z -b 100M -l16 -t1000 \ 934 ${dst_port} ${src_port} ${src_addr4} 935 936 src_addr4= 937 src_port= 938 dst_port= 939 } 940 elif command -v iperf >/dev/null; then 941 flood_udp() { 942 local n_port="${dst_port}" 943 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 944 if [ -n "${src_addr4}" ]; then 945 B ip addr add "${src_addr4}/16" dev veth_b 946 src_addr4="-B ${src_addr4}" 947 else 948 B ip addr add dev veth_b 10.0.0.2 949 src_addr4="-B 10.0.0.2" 950 fi 951 if [ -n "${src_port}" ]; then 952 src_addr4="${src_addr4}:${src_port}" 953 fi 954 B ip route add default dev veth_b 955 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 956 957 # shellcheck disable=SC2086 # this needs split options 958 iperf -u -sDB "${dst_addr4}" ${dst_port} >/dev/null 2>&1 959 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 960 961 # shellcheck disable=SC2086 # this needs split options 962 B iperf -u -c "${dst_addr4}" -b 100M -l1 -t1000 \ 963 ${dst_port} ${src_addr4} 964 965 src_addr4= 966 src_port= 967 dst_port= 968 } 969 elif command -v netperf >/dev/null; then 970 flood_udp() { 971 local n_port="${dst_port}" 972 [ -n "${dst_port}" ] && dst_port="-p ${dst_port}" 973 if [ -n "${src_addr4}" ]; then 974 B ip addr add "${src_addr4}/16" dev veth_b 975 else 976 B ip addr add dev veth_b 10.0.0.2 977 src_addr4="10.0.0.2" 978 fi 979 if [ -n "${src_port}" ]; then 980 dst_port="${dst_port},${src_port}" 981 fi 982 B ip route add default dev veth_b 983 ip addr add "${dst_addr4}" dev veth_a 2>/dev/null 984 985 # shellcheck disable=SC2086 # this needs split options 986 netserver -4 ${dst_port} -L "${dst_addr4}" \ 987 >/dev/null 2>&1 988 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port" 989 990 # shellcheck disable=SC2086 # this needs split options 991 B netperf -4 -H "${dst_addr4}" ${dst_port} \ 992 -L "${src_addr4}" -l 1000 -t UDP_STREAM 993 994 src_addr4= 995 src_port= 996 dst_port= 997 } 998 else 999 return 1 1000 fi 1001} 1002 1003# Find pktgen script and set up function to start pktgen injection 1004setup_perf() { 1005 for pktgen_script_path in ${PKTGEN_SCRIPT_PATHS} __notfound; do 1006 command -v "${pktgen_script_path}" >/dev/null && break 1007 done 1008 [ "${pktgen_script_path}" = "__notfound" ] && return 1 1009 1010 perf_ipv4() { 1011 ${pktgen_script_path} -s80 \ 1012 -i veth_a -d "${dst_addr4}" -p "${dst_port}" \ 1013 -m "${dst_mac}" \ 1014 -t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null & 1015 perf_pid=$! 1016 } 1017 perf_ipv6() { 1018 IP6=6 ${pktgen_script_path} -s100 \ 1019 -i veth_a -d "${dst_addr6}" -p "${dst_port}" \ 1020 -m "${dst_mac}" \ 1021 -t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null & 1022 perf_pid=$! 1023 } 1024} 1025 1026# Clean up before each test 1027cleanup() { 1028 nft reset counter inet filter test >/dev/null 2>&1 1029 nft flush ruleset >/dev/null 2>&1 1030 ip link del dummy0 2>/dev/null 1031 ip route del default 2>/dev/null 1032 ip -6 route del default 2>/dev/null 1033 ip netns pids B 2>/dev/null | xargs kill 2>/dev/null 1034 ip netns del B 2>/dev/null 1035 ip link del veth_a 2>/dev/null 1036 timeout= 1037 killall iperf3 2>/dev/null 1038 killall iperf 2>/dev/null 1039 killall netperf 2>/dev/null 1040 killall netserver 2>/dev/null 1041} 1042 1043cleanup_exit() { 1044 cleanup 1045 rm -f "$tmp" 1046} 1047 1048# Entry point for setup functions 1049setup() { 1050 if [ "$(id -u)" -ne 0 ]; then 1051 echo " need to run as root" 1052 exit ${ksft_skip} 1053 fi 1054 1055 cleanup 1056 check_tools || return 1 1057 for arg do 1058 if ! eval setup_"${arg}"; then 1059 err " ${arg} not supported" 1060 return 1 1061 fi 1062 done 1063} 1064 1065# Format integer into IPv4 address, summing 10.0.0.5 (arbitrary) to it 1066format_addr4() { 1067 a=$((${1} + 16777216 * 10 + 5)) 1068 printf "%i.%i.%i.%i" \ 1069 "$((a / 16777216))" "$((a % 16777216 / 65536))" \ 1070 "$((a % 65536 / 256))" "$((a % 256))" 1071} 1072 1073# Format integer into IPv6 address, summing 2001:db8:: to it 1074format_addr6() { 1075 printf "2001:db8::%04x:%04x" "$((${1} / 65536))" "$((${1} % 65536))" 1076} 1077 1078# Format integer into EUI-48 address, summing 00:01:00:00:00:00 to it 1079format_mac() { 1080 printf "00:01:%02x:%02x:%02x:%02x" \ 1081 "$((${1} / 16777216))" "$((${1} % 16777216 / 65536))" \ 1082 "$((${1} % 65536 / 256))" "$((${1} % 256))" 1083} 1084 1085# Format integer into port, avoid 0 port 1086format_port() { 1087 printf "%i" "$((${1} % 65534 + 1))" 1088} 1089 1090# Drop suffixed '6' from L4 protocol, if any 1091format_proto() { 1092 printf "%s" "${proto}" | tr -d 6 1093} 1094 1095# Format destination and source fields into nft concatenated type 1096format() { 1097 __start= 1098 __end= 1099 __expr="{ " 1100 1101 for f in ${dst}; do 1102 [ "${__expr}" != "{ " ] && __expr="${__expr} . " 1103 1104 __start="$(eval format_"${f}" "${start}")" 1105 __end="$(eval format_"${f}" "${end}")" 1106 1107 if [ "${f}" = "proto" ]; then 1108 __expr="${__expr}${__start}" 1109 else 1110 __expr="${__expr}${__start}-${__end}" 1111 fi 1112 done 1113 for f in ${src}; do 1114 [ "${__expr}" != "{ " ] && __expr="${__expr} . " 1115 1116 __start="$(eval format_"${f}" "${srcstart}")" 1117 __end="$(eval format_"${f}" "${srcend}")" 1118 1119 if [ "${f}" = "proto" ]; then 1120 __expr="${__expr}${__start}" 1121 else 1122 __expr="${__expr}${__start}-${__end}" 1123 fi 1124 done 1125 1126 if [ -n "${timeout}" ]; then 1127 echo "${__expr} timeout ${timeout}s }" 1128 else 1129 echo "${__expr} }" 1130 fi 1131} 1132 1133# Format destination and source fields into nft type, start element only 1134format_norange() { 1135 __expr="{ " 1136 1137 for f in ${dst}; do 1138 [ "${__expr}" != "{ " ] && __expr="${__expr} . " 1139 1140 __expr="${__expr}$(eval format_"${f}" "${start}")" 1141 done 1142 for f in ${src}; do 1143 __expr="${__expr} . $(eval format_"${f}" "${start}")" 1144 done 1145 1146 echo "${__expr} }" 1147} 1148 1149# Format first destination field into nft type 1150format_noconcat() { 1151 for f in ${dst}; do 1152 __start="$(eval format_"${f}" "${start}")" 1153 __end="$(eval format_"${f}" "${end}")" 1154 1155 if [ "${f}" = "proto" ]; then 1156 echo "{ ${__start} }" 1157 else 1158 echo "{ ${__start}-${__end} }" 1159 fi 1160 return 1161 done 1162} 1163 1164# Add single entry to 'test' set in 'inet filter' table 1165add() { 1166 if ! nft add element inet filter test "${1}"; then 1167 err "Failed to add ${1} given ruleset:" 1168 err "$(nft -a list ruleset)" 1169 return 1 1170 fi 1171} 1172 1173# Format and output entries for sets in 'netdev perf' table 1174add_perf() { 1175 if [ "${1}" = "test" ]; then 1176 echo "add element netdev perf test $(format)" 1177 elif [ "${1}" = "norange" ]; then 1178 echo "add element netdev perf norange $(format_norange)" 1179 elif [ "${1}" = "noconcat" ]; then 1180 echo "add element netdev perf noconcat $(format_noconcat)" 1181 fi 1182} 1183 1184# Add single entry to 'norange' set in 'netdev perf' table 1185add_perf_norange() { 1186 if ! nft add element netdev perf norange "${1}"; then 1187 err "Failed to add ${1} given ruleset:" 1188 err "$(nft -a list ruleset)" 1189 return 1 1190 fi 1191} 1192 1193# Add single entry to 'noconcat' set in 'netdev perf' table 1194add_perf_noconcat() { 1195 if ! nft add element netdev perf noconcat "${1}"; then 1196 err "Failed to add ${1} given ruleset:" 1197 err "$(nft -a list ruleset)" 1198 return 1 1199 fi 1200} 1201 1202# Delete single entry from set 1203del() { 1204 if ! nft delete element inet filter test "${1}"; then 1205 err "Failed to delete ${1} given ruleset:" 1206 err "$(nft -a list ruleset)" 1207 return 1 1208 fi 1209} 1210 1211# Return packet count for elem $1 from 'test' counter in 'inet filter' table 1212count_packets() { 1213 found=0 1214 for token in $(nft reset element inet filter test "${1}" ); do 1215 [ ${found} -eq 1 ] && echo "${token}" && return 1216 [ "${token}" = "packets" ] && found=1 1217 done 1218} 1219 1220# Return packet count from 'test' counter in 'inet filter' table 1221count_packets_nomatch() { 1222 found=0 1223 for token in $(nft list counter inet filter test); do 1224 [ ${found} -eq 1 ] && echo "${token}" && return 1225 [ "${token}" = "packets" ] && found=1 1226 done 1227} 1228 1229# Return packet count from 'test' counter in 'netdev perf' table 1230count_perf_packets() { 1231 found=0 1232 for token in $(nft list counter netdev perf test); do 1233 [ ${found} -eq 1 ] && echo "${token}" && return 1234 [ "${token}" = "packets" ] && found=1 1235 done 1236} 1237 1238# Set MAC addresses, send traffic according to specifier 1239flood() { 1240 ip link set veth_a address "$(format_mac "${1}")" 1241 ip -n B link set veth_b address "$(format_mac "${2}")" 1242 1243 for f in ${dst}; do 1244 eval dst_"$f"=\$\(format_\$f "${1}"\) 1245 done 1246 for f in ${src}; do 1247 eval src_"$f"=\$\(format_\$f "${2}"\) 1248 done 1249 eval flood_\$proto 1250} 1251 1252# Set MAC addresses, start pktgen injection 1253perf() { 1254 dst_mac="$(format_mac "${1}")" 1255 ip link set veth_a address "${dst_mac}" 1256 1257 for f in ${dst}; do 1258 eval dst_"$f"=\$\(format_\$f "${1}"\) 1259 done 1260 for f in ${src}; do 1261 eval src_"$f"=\$\(format_\$f "${2}"\) 1262 done 1263 eval perf_\$perf_proto 1264} 1265 1266# Set MAC addresses, send single packet, check that it matches, reset counter 1267send_match() { 1268 local elem="$1" 1269 1270 shift 1271 1272 ip link set veth_a address "$(format_mac "${1}")" 1273 ip -n B link set veth_b address "$(format_mac "${2}")" 1274 1275 for f in ${dst}; do 1276 eval dst_"$f"=\$\(format_\$f "${1}"\) 1277 done 1278 for f in ${src}; do 1279 eval src_"$f"=\$\(format_\$f "${2}"\) 1280 done 1281 eval send_\$proto 1282 if [ "$(count_packets "$elem")" != "1" ]; then 1283 err "${proto} packet to:" 1284 err " $(for f in ${dst}; do 1285 eval format_\$f "${1}"; printf ' '; done)" 1286 err "from:" 1287 err " $(for f in ${src}; do 1288 eval format_\$f "${2}"; printf ' '; done)" 1289 err "should have matched ruleset:" 1290 err "$(nft -a list ruleset)" 1291 return 1 1292 fi 1293 nft reset counter inet filter test >/dev/null 1294} 1295 1296# Set MAC addresses, send single packet, check that it doesn't match 1297send_nomatch() { 1298 ip link set veth_a address "$(format_mac "${1}")" 1299 ip -n B link set veth_b address "$(format_mac "${2}")" 1300 1301 for f in ${dst}; do 1302 eval dst_"$f"=\$\(format_\$f "${1}"\) 1303 done 1304 for f in ${src}; do 1305 eval src_"$f"=\$\(format_\$f "${2}"\) 1306 done 1307 eval send_\$proto 1308 if [ "$(count_packets_nomatch)" != "0" ]; then 1309 err "${proto} packet to:" 1310 err " $(for f in ${dst}; do 1311 eval format_\$f "${1}"; printf ' '; done)" 1312 err "from:" 1313 err " $(for f in ${src}; do 1314 eval format_\$f "${2}"; printf ' '; done)" 1315 err "should not have matched ruleset:" 1316 err "$(nft -a list ruleset)" 1317 return 1 1318 fi 1319} 1320 1321maybe_send_nomatch() { 1322 local elem="$1" 1323 local what="$4" 1324 1325 [ $((RANDOM%20)) -gt 0 ] && return 1326 1327 dst_addr4="$2" 1328 dst_port="$3" 1329 send_udp 1330 1331 if [ "$(count_packets_nomatch)" != "0" ]; then 1332 err "Packet to $dst_addr4:$dst_port did match $what" 1333 err "$(nft -a list ruleset)" 1334 return 1 1335 fi 1336} 1337 1338maybe_send_match() { 1339 local elem="$1" 1340 local what="$4" 1341 1342 [ $((RANDOM%20)) -gt 0 ] && return 1343 1344 dst_addr4="$2" 1345 dst_port="$3" 1346 send_udp 1347 1348 if [ "$(count_packets "{ $elem }")" != "1" ]; then 1349 err "Packet to $dst_addr4:$dst_port did not match $what" 1350 err "$(nft -a list ruleset)" 1351 return 1 1352 fi 1353 nft reset counter inet filter test >/dev/null 1354 nft reset element inet filter test "{ $elem }" >/dev/null 1355} 1356 1357# Correctness test template: 1358# - add ranged element, check that packets match it 1359# - check that packets outside range don't match it 1360# - remove some elements, check that packets don't match anymore 1361test_correctness_main() { 1362 range_size=1 1363 1364 send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1 1365 1366 for i in $(seq "${start}" $((start + count))); do 1367 local elem="" 1368 1369 end=$((start + range_size)) 1370 1371 # Avoid negative or zero-sized port ranges 1372 if [ $((end / 65534)) -gt $((start / 65534)) ]; then 1373 start=${end} 1374 end=$((end + 1)) 1375 fi 1376 srcstart=$((start + src_delta)) 1377 srcend=$((end + src_delta)) 1378 1379 elem="$(format)" 1380 add "$elem" || return 1 1381 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do 1382 send_match "$elem" "${j}" $((j + src_delta)) || return 1 1383 done 1384 send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1 1385 1386 # Delete elements now and then 1387 if [ $((i % 3)) -eq 0 ]; then 1388 del "$elem" || return 1 1389 for j in $(seq "$start" \ 1390 $((range_size / 2 + 1)) ${end}); do 1391 send_nomatch "${j}" $((j + src_delta)) \ 1392 || return 1 1393 done 1394 fi 1395 1396 range_size=$((range_size + 1)) 1397 start=$((end + range_size)) 1398 done 1399} 1400 1401test_correctness() { 1402 setup veth send_"${proto}" set || return ${ksft_skip} 1403 1404 test_correctness_main 1405} 1406 1407# Repeat the correctness tests, but add extra non-matching entries. 1408# This exercises the more compact '4 bit group' representation that 1409# gets picked when the default 8-bit representation exceed 1410# NFT_PIPAPO_LT_SIZE_HIGH bytes of memory. 1411# See usage of NFT_PIPAPO_LT_SIZE_HIGH in pipapo_lt_bits_adjust(). 1412# 1413# The format() helper is way too slow when generating lots of 1414# entries so its not used here. 1415test_correctness_large() { 1416 setup veth send_"${proto}" set || return ${ksft_skip} 1417 # number of dummy (filler) entries to add. 1418 local dcount=16385 1419 1420 ( 1421 echo -n "add element inet filter test { " 1422 1423 case "$type_spec" in 1424 "ether_addr . ipv4_addr") 1425 for i in $(seq 1 $dcount); do 1426 [ $i -gt 1 ] && echo ", " 1427 format_mac $((1000000 + i)) 1428 printf ". 172.%i.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) 1429 done 1430 ;; 1431 "inet_proto . ipv6_addr") 1432 for i in $(seq 1 $dcount); do 1433 [ $i -gt 1 ] && echo ", " 1434 printf "%i . " $((RANDOM%256)) 1435 format_addr6 $((1000000 + i)) 1436 done 1437 ;; 1438 "inet_service . inet_proto") 1439 # smaller key sizes, need more entries to hit the 1440 # 4-bit threshold. 1441 dcount=65536 1442 for i in $(seq 1 $dcount); do 1443 local proto=$((RANDOM%256)) 1444 1445 # Test uses UDP to match, as it also fails when matching 1446 # an entry that doesn't exist, so skip 'udp' entries 1447 # to not trigger a wrong failure. 1448 [ $proto -eq 17 ] && proto=18 1449 [ $i -gt 1 ] && echo ", " 1450 printf "%i . %i " $(((i%65534) + 1)) $((proto)) 1451 done 1452 ;; 1453 "inet_service . ipv4_addr") 1454 dcount=32768 1455 for i in $(seq 1 $dcount); do 1456 [ $i -gt 1 ] && echo ", " 1457 printf "%i . 172.%i.%i.%i " $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) $((i%256)) 1458 done 1459 ;; 1460 "ipv4_addr . ether_addr") 1461 for i in $(seq 1 $dcount); do 1462 [ $i -gt 1 ] && echo ", " 1463 printf "172.%i.%i.%i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) 1464 format_mac $((1000000 + i)) 1465 done 1466 ;; 1467 "ipv4_addr . inet_service") 1468 dcount=32768 1469 for i in $(seq 1 $dcount); do 1470 [ $i -gt 1 ] && echo ", " 1471 printf "172.%i.%i.%i . %i" $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) 1472 done 1473 ;; 1474 "ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr") 1475 dcount=65536 1476 for i in $(seq 1 $dcount); do 1477 [ $i -gt 1 ] && echo ", " 1478 printf "172.%i.%i.%i . %i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) 1479 format_mac $((1000000 + i)) 1480 printf ". %i . 192.168.%i.%i" $((RANDOM%256)) $((RANDOM%256)) $((i%256)) 1481 done 1482 ;; 1483 "ipv4_addr . inet_service . inet_proto") 1484 for i in $(seq 1 $dcount); do 1485 [ $i -gt 1 ] && echo ", " 1486 printf "172.%i.%i.%i . %i . %i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) 1487 done 1488 ;; 1489 "ipv4_addr . inet_service . inet_proto . ipv4_addr") 1490 for i in $(seq 1 $dcount); do 1491 [ $i -gt 1 ] && echo ", " 1492 printf "172.%i.%i.%i . %i . %i . 192.168.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) 1493 done 1494 ;; 1495 "ipv4_addr . inet_service . ipv4_addr") 1496 dcount=32768 1497 for i in $(seq 1 $dcount); do 1498 [ $i -gt 1 ] && echo ", " 1499 printf "172.%i.%i.%i . %i . 192.168.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) 1500 done 1501 ;; 1502 "ipv6_addr . ether_addr") 1503 for i in $(seq 1 $dcount); do 1504 [ $i -gt 1 ] && echo ", " 1505 format_addr6 $((i + 1000000)) 1506 echo -n " . " 1507 format_mac $((1000000 + i)) 1508 done 1509 ;; 1510 "ipv6_addr . inet_service") 1511 dcount=32768 1512 for i in $(seq 1 $dcount); do 1513 [ $i -gt 1 ] && echo ", " 1514 format_addr6 $((i + 1000000)) 1515 echo -n " . $(((RANDOM%65534) + 1))" 1516 done 1517 ;; 1518 "ipv6_addr . inet_service . ether_addr") 1519 dcount=32768 1520 for i in $(seq 1 $dcount); do 1521 [ $i -gt 1 ] && echo ", " 1522 format_addr6 $((i + 1000000)) 1523 echo -n " . $(((RANDOM%65534) + 1)) . " 1524 format_mac $((i + 1000000)) 1525 done 1526 ;; 1527 "ipv6_addr . inet_service . ether_addr . inet_proto") 1528 dcount=65536 1529 for i in $(seq 1 $dcount); do 1530 [ $i -gt 1 ] && echo ", " 1531 format_addr6 $((i + 1000000)) 1532 echo -n " . $(((RANDOM%65534) + 1)) . " 1533 format_mac $((i + 1000000)) 1534 echo -n " . $((RANDOM%256))" 1535 done 1536 ;; 1537 "ipv6_addr . inet_service . ipv6_addr . inet_service") 1538 dcount=32768 1539 for i in $(seq 1 $dcount); do 1540 [ $i -gt 1 ] && echo ", " 1541 format_addr6 $((i + 1000000)) 1542 echo -n " . $(((RANDOM%65534) + 1)) . " 1543 format_addr6 $((i + 2123456)) 1544 echo -n " . $((RANDOM%256))" 1545 done 1546 ;; 1547 *) 1548 "Unhandled $type_spec" 1549 return 1 1550 esac 1551 echo -n "}" 1552 1553 ) | nft -f - || return 1 1554 1555 test_correctness_main 1556} 1557 1558# Concurrency test template: 1559# - add all the elements 1560# - start a thread for each physical thread that: 1561# - adds all the elements 1562# - flushes the set 1563# - adds all the elements 1564# - flushes the entire ruleset 1565# - adds the set back 1566# - adds all the elements 1567# - delete all the elements 1568test_concurrency() { 1569 proto=${flood_proto} 1570 tools=${flood_tools} 1571 chain_spec=${flood_spec} 1572 setup veth flood_"${proto}" set || return ${ksft_skip} 1573 1574 range_size=1 1575 cstart=${start} 1576 flood_pids= 1577 for i in $(seq "$start" $((start + count))); do 1578 end=$((start + range_size)) 1579 srcstart=$((start + src_delta)) 1580 srcend=$((end + src_delta)) 1581 1582 add "$(format)" || return 1 1583 1584 flood "${i}" $((i + src_delta)) & flood_pids="${flood_pids} $!" 1585 1586 range_size=$((range_size + 1)) 1587 start=$((end + range_size)) 1588 done 1589 1590 sleep $((RANDOM%10)) 1591 1592 pids= 1593 for c in $(seq 1 "$(nproc)"); do ( 1594 for r in $(seq 1 "${race_repeat}"); do 1595 range_size=1 1596 1597 # $start needs to be local to this subshell 1598 # shellcheck disable=SC2030 1599 start=${cstart} 1600 for i in $(seq "$start" $((start + count))); do 1601 end=$((start + range_size)) 1602 srcstart=$((start + src_delta)) 1603 srcend=$((end + src_delta)) 1604 1605 add "$(format)" 2>/dev/null 1606 1607 range_size=$((range_size + 1)) 1608 start=$((end + range_size)) 1609 done 1610 1611 nft flush inet filter test 2>/dev/null 1612 1613 range_size=1 1614 start=${cstart} 1615 for i in $(seq "$start" $((start + count))); do 1616 end=$((start + range_size)) 1617 srcstart=$((start + src_delta)) 1618 srcend=$((end + src_delta)) 1619 1620 add "$(format)" 2>/dev/null 1621 1622 range_size=$((range_size + 1)) 1623 start=$((end + range_size)) 1624 done 1625 1626 nft flush ruleset 1627 setup set 2>/dev/null 1628 1629 range_size=1 1630 start=${cstart} 1631 for i in $(seq "$start" $((start + count))); do 1632 end=$((start + range_size)) 1633 srcstart=$((start + src_delta)) 1634 srcend=$((end + src_delta)) 1635 1636 add "$(format)" 2>/dev/null 1637 1638 range_size=$((range_size + 1)) 1639 start=$((end + range_size)) 1640 done 1641 1642 range_size=1 1643 start=${cstart} 1644 for i in $(seq "$start" $((start + count))); do 1645 end=$((start + range_size)) 1646 srcstart=$((start + src_delta)) 1647 srcend=$((end + src_delta)) 1648 1649 del "$(format)" 2>/dev/null 1650 1651 range_size=$((range_size + 1)) 1652 start=$((end + range_size)) 1653 done 1654 done 1655 ) & pids="${pids} $!" 1656 done 1657 1658 # shellcheck disable=SC2046,SC2086 # word splitting wanted here 1659 wait $(for pid in ${pids}; do echo ${pid}; done) 1660 # shellcheck disable=SC2046,SC2086 1661 kill $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null 1662 # shellcheck disable=SC2046,SC2086 1663 wait $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null 1664 1665 return 0 1666} 1667 1668# Timeout test template: 1669# - add all the elements with 3s timeout while checking that packets match 1670# - wait 3s after the last insertion, check that packets don't match any entry 1671test_timeout() { 1672 setup veth send_"${proto}" set || return ${ksft_skip} 1673 1674 timeout=3 1675 1676 [ "$KSFT_MACHINE_SLOW" = "yes" ] && timeout=8 1677 1678 range_size=1 1679 for i in $(seq "$start" $((start + count))); do 1680 local elem="" 1681 1682 end=$((start + range_size)) 1683 srcstart=$((start + src_delta)) 1684 srcend=$((end + src_delta)) 1685 1686 elem="$(format)" 1687 add "$elem" || return 1 1688 1689 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do 1690 send_match "$elem" "${j}" $((j + src_delta)) || return 1 1691 done 1692 1693 range_size=$((range_size + 1)) 1694 start=$((end + range_size)) 1695 done 1696 sleep $timeout 1697 for i in $(seq "$start" $((start + count))); do 1698 end=$((start + range_size)) 1699 srcstart=$((start + src_delta)) 1700 srcend=$((end + src_delta)) 1701 1702 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do 1703 send_nomatch "${j}" $((j + src_delta)) || return 1 1704 done 1705 1706 range_size=$((range_size + 1)) 1707 start=$((end + range_size)) 1708 done 1709} 1710 1711# Performance test template: 1712# - add concatenated ranged entries 1713# - add non-ranged concatenated entries (for hash set matching rate baseline) 1714# - add ranged entries with first field only (for rbhash baseline) 1715# - start pktgen injection directly on device rx path of this namespace 1716# - measure drop only rate, hash and rbtree baselines, then matching rate 1717test_performance() { 1718 chain_spec=${perf_spec} 1719 dst="${perf_dst}" 1720 src="${perf_src}" 1721 setup veth perf set || return ${ksft_skip} 1722 1723 first=${start} 1724 range_size=1 1725 for set in test norange noconcat; do 1726 start=${first} 1727 for i in $(seq "$start" $((start + perf_entries))); do 1728 end=$((start + range_size)) 1729 srcstart=$((start + src_delta)) 1730 srcend=$((end + src_delta)) 1731 1732 if [ $((end / 65534)) -gt $((start / 65534)) ]; then 1733 start=${end} 1734 end=$((end + 1)) 1735 elif [ "$start" -eq "$end" ]; then 1736 end=$((start + 1)) 1737 fi 1738 1739 add_perf ${set} 1740 1741 start=$((end + range_size)) 1742 done > "${tmp}" 1743 nft -f "${tmp}" 1744 done 1745 1746 perf $((end - 1)) "$srcstart" 1747 1748 sleep 2 1749 1750 nft add rule netdev perf test counter name \"test\" drop 1751 nft reset counter netdev perf test >/dev/null 2>&1 1752 sleep "${perf_duration}" 1753 pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))" 1754 info " baseline (drop from netdev hook): ${pps}pps" 1755 handle="$(nft -a list chain netdev perf test | grep counter)" 1756 handle="${handle##* }" 1757 nft delete rule netdev perf test handle "${handle}" 1758 1759 nft add rule "netdev perf test ${chain_spec} @norange \ 1760 counter name \"test\" drop" 1761 nft reset counter netdev perf test >/dev/null 2>&1 1762 sleep "${perf_duration}" 1763 pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))" 1764 info " baseline hash (non-ranged entries): ${pps}pps" 1765 handle="$(nft -a list chain netdev perf test | grep counter)" 1766 handle="${handle##* }" 1767 nft delete rule netdev perf test handle "${handle}" 1768 1769 nft add rule "netdev perf test ${chain_spec%%. *} @noconcat \ 1770 counter name \"test\" drop" 1771 nft reset counter netdev perf test >/dev/null 2>&1 1772 sleep "${perf_duration}" 1773 pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))" 1774 info " baseline rbtree (match on first field only): ${pps}pps" 1775 handle="$(nft -a list chain netdev perf test | grep counter)" 1776 handle="${handle##* }" 1777 nft delete rule netdev perf test handle "${handle}" 1778 1779 nft add rule "netdev perf test ${chain_spec} @test \ 1780 counter name \"test\" drop" 1781 nft reset counter netdev perf test >/dev/null 2>&1 1782 sleep "${perf_duration}" 1783 pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))" 1784 p5="$(printf %5s "${perf_entries}")" 1785 info " set with ${p5} full, ranged entries: ${pps}pps" 1786 kill "${perf_pid}" 1787} 1788 1789test_bug_flush_remove_add() { 1790 rounds=100 1791 [ "$KSFT_MACHINE_SLOW" = "yes" ] && rounds=10 1792 1793 set_cmd='{ set s { type ipv4_addr . inet_service; flags interval; }; }' 1794 elem1='{ 10.0.0.1 . 22-25, 10.0.0.1 . 10-20 }' 1795 elem2='{ 10.0.0.1 . 10-20, 10.0.0.1 . 22-25 }' 1796 for i in $(seq 1 $rounds); do 1797 nft add table t "$set_cmd" || return ${ksft_skip} 1798 nft add element t s "$elem1" 2>/dev/null || return 1 1799 nft flush set t s 2>/dev/null || return 1 1800 nft add element t s "$elem2" 2>/dev/null || return 1 1801 done 1802 nft flush ruleset 1803} 1804 1805# - add ranged element, check that packets match it 1806# - reload the set, check packets still match 1807test_bug_reload() { 1808 setup veth send_"${proto}" set || return ${ksft_skip} 1809 rstart=${start} 1810 1811 range_size=1 1812 for i in $(seq "${start}" $((start + count))); do 1813 end=$((start + range_size)) 1814 1815 # Avoid negative or zero-sized port ranges 1816 if [ $((end / 65534)) -gt $((start / 65534)) ]; then 1817 start=${end} 1818 end=$((end + 1)) 1819 fi 1820 srcstart=$((start + src_delta)) 1821 srcend=$((end + src_delta)) 1822 1823 add "$(format)" || return 1 1824 range_size=$((range_size + 1)) 1825 start=$((end + range_size)) 1826 done 1827 1828 # check kernel does allocate pcpu sctrach map 1829 # for reload with no elemet add/delete 1830 ( echo flush set inet filter test ; 1831 nft list set inet filter test ) | nft -f - 1832 1833 start=${rstart} 1834 range_size=1 1835 1836 for i in $(seq "${start}" $((start + count))); do 1837 end=$((start + range_size)) 1838 1839 # Avoid negative or zero-sized port ranges 1840 if [ $((end / 65534)) -gt $((start / 65534)) ]; then 1841 start=${end} 1842 end=$((end + 1)) 1843 fi 1844 srcstart=$((start + src_delta)) 1845 srcend=$((end + src_delta)) 1846 1847 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do 1848 send_match "$(format)" "${j}" $((j + src_delta)) || return 1 1849 done 1850 1851 range_size=$((range_size + 1)) 1852 start=$((end + range_size)) 1853 done 1854 1855 nft flush ruleset 1856} 1857 1858# - add ranged element, check that packets match it 1859# - delete element again, check it is gone 1860test_bug_net_port_proto_match() { 1861 setup veth send_"${proto}" set || return ${ksft_skip} 1862 rstart=${start} 1863 1864 range_size=1 1865 for i in $(seq 1 10); do 1866 for j in $(seq 1 20) ; do 1867 local dport=$j 1868 1869 elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") 1870 1871 # too slow, do not test all addresses 1872 maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "before add" || return 1 1873 1874 nft "add element inet filter test { $elem }" || return 1 1875 1876 maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "after add" || return 1 1877 1878 nft "get element inet filter test { $elem }" | grep -q "$elem" 1879 if [ $? -ne 0 ];then 1880 local got=$(nft "get element inet filter test { $elem }") 1881 err "post-add: should have returned $elem but got $got" 1882 return 1 1883 fi 1884 1885 maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "out-of-range" || return 1 1886 done 1887 done 1888 1889 # recheck after set was filled 1890 for i in $(seq 1 10); do 1891 for j in $(seq 1 20) ; do 1892 local dport=$j 1893 1894 elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") 1895 1896 nft "get element inet filter test { $elem }" | grep -q "$elem" 1897 if [ $? -ne 0 ];then 1898 local got=$(nft "get element inet filter test { $elem }") 1899 err "post-fill: should have returned $elem but got $got" 1900 return 1 1901 fi 1902 1903 maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "recheck" || return 1 1904 maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "recheck out-of-range" || return 1 1905 done 1906 done 1907 1908 # random del and re-fetch 1909 for i in $(seq 1 10); do 1910 for j in $(seq 1 20) ; do 1911 local rnd=$((RANDOM%10)) 1912 local dport=$j 1913 local got="" 1914 1915 elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") 1916 if [ $rnd -gt 0 ];then 1917 continue 1918 fi 1919 1920 nft "delete element inet filter test { $elem }" 1921 got=$(nft "get element inet filter test { $elem }" 2>/dev/null) 1922 if [ $? -eq 0 ];then 1923 err "post-delete: query for $elem returned $got instead of error." 1924 return 1 1925 fi 1926 1927 maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "match after deletion" || return 1 1928 done 1929 done 1930 1931 nft flush ruleset 1932} 1933 1934test_bug_avx2_mismatch() 1935{ 1936 setup veth send_"${proto}" set || return ${ksft_skip} 1937 1938 local a1="fe80:dead:01ff:0a02:0b03:6007:8009:a001" 1939 local a2="fe80:dead:01fe:0a02:0b03:6007:8009:a001" 1940 1941 nft "add element inet filter test { icmpv6 . $a1 }" 1942 1943 dst_addr6="$a2" 1944 send_icmp6 1945 1946 if [ "$(count_packets "{ icmpv6 . $a1 }")" -gt "0" ]; then 1947 err "False match for $a2" 1948 return 1 1949 fi 1950} 1951 1952test_bug_doublecreate() 1953{ 1954 local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4" 1955 local ret=1 1956 local i 1957 1958 setup veth send_"${proto}" set || return ${ksft_skip} 1959 1960 add "{ $elements }" || return 1 1961 # expected to work: 'add' on existing should be no-op. 1962 add "{ $elements }" || return 1 1963 1964 # 'create' should return an error. 1965 if nft create element inet filter test "{ $elements }" 2>/dev/null; then 1966 err "Could create an existing element" 1967 return 1 1968 fi 1969nft -f - <<EOF 2>/dev/null 1970flush set inet filter test 1971create element inet filter test { $elements } 1972create element inet filter test { $elements } 1973EOF 1974 ret=$? 1975 if [ $ret -eq 0 ]; then 1976 err "Could create element twice in one transaction" 1977 err "$(nft -a list ruleset)" 1978 return 1 1979 fi 1980 1981nft -f - <<EOF 2>/dev/null 1982flush set inet filter test 1983create element inet filter test { $elements } 1984EOF 1985 ret=$? 1986 if [ $ret -ne 0 ]; then 1987 err "Could not flush and re-create element in one transaction" 1988 return 1 1989 fi 1990 1991 return 0 1992} 1993 1994add_fail() 1995{ 1996 if nft add element inet filter test "$1" 2>/dev/null ; then 1997 err "Returned success for add ${1} given set:" 1998 err "$(nft -a list set inet filter test )" 1999 return 1 2000 fi 2001 2002 return 0 2003} 2004 2005test_bug_insert_overlap() 2006{ 2007 local elements="1.2.3.4 . 1.2.4.1" 2008 2009 setup veth send_"${proto}" set || return ${ksft_skip} 2010 2011 add "{ $elements }" || return 1 2012 2013 elements="1.2.3.0-1.2.3.4 . 1.2.4.1" 2014 add_fail "{ $elements }" || return 1 2015 2016 elements="1.2.3.0-1.2.3.4 . 1.2.4.2" 2017 add "{ $elements }" || return 1 2018 2019 elements="1.2.3.4 . 1.2.4.1-1.2.4.2" 2020 add_fail "{ $elements }" || return 1 2021 2022 return 0 2023} 2024 2025test_bug_load_flush_load4() 2026{ 2027 local i 2028 2029 setup veth send_"${proto}" set || return ${ksft_skip} 2030 2031 for i in $(seq 0 255); do 2032 local addelem="add element inet filter test" 2033 local j 2034 2035 for j in $(seq 0 20); do 2036 echo "$addelem { 10.$j.0.$i . 10.$j.1.$i }" 2037 echo "$addelem { 10.$j.0.$i . 10.$j.2.$i }" 2038 done 2039 done > "$tmp" 2040 2041 nft -f "$tmp" || return 1 2042 2043 ( echo "flush set inet filter test";cat "$tmp") | nft -f - 2044 [ $? -eq 0 ] || return 1 2045 2046 return 0 2047} 2048 2049test_bug_load_flush_load8() 2050{ 2051 local i 2052 2053 setup veth send_"${proto}" set || return ${ksft_skip} 2054 2055 for i in $(seq 1 100); do 2056 echo "add element inet filter test { 10.0.0.$i . 10.0.1.$i }" 2057 echo "add element inet filter test { 10.0.0.$i . 10.0.2.$i }" 2058 done > "$tmp" 2059 2060 nft -f "$tmp" || return 1 2061 2062 ( echo "flush set inet filter test";cat "$tmp") | nft -f - 2063 [ $? -eq 0 ] || return 1 2064 2065 return 0 2066} 2067 2068test_reported_issues() { 2069 eval test_bug_"${subtest}" 2070} 2071 2072# Run everything in a separate network namespace 2073[ "${1}" != "run" ] && { unshare -n "${0}" run; exit $?; } 2074tmp="$(mktemp)" 2075trap cleanup_exit EXIT 2076 2077# Entry point for test runs 2078passed=0 2079for name in ${TESTS}; do 2080 printf "TEST: %s\n" "$(echo "$name" | tr '_' ' ')" 2081 if [ "${name}" = "reported_issues" ]; then 2082 SUBTESTS="${BUGS}" 2083 else 2084 SUBTESTS="${TYPES}" 2085 fi 2086 2087 for subtest in ${SUBTESTS}; do 2088 eval desc=\$TYPE_"${subtest}" 2089 IFS=' 2090' 2091 for __line in ${desc}; do 2092 # shellcheck disable=SC2086 2093 eval ${__line%% *}=\"${__line##* }\"; 2094 done 2095 IFS=' 2096' 2097 2098 if [ "${name}" = "concurrency" ] && \ 2099 [ "${race_repeat}" = "0" ]; then 2100 continue 2101 fi 2102 if [ "${name}" = "performance" ] && \ 2103 [ "${perf_duration}" = "0" ]; then 2104 continue 2105 fi 2106 2107 [ "$KSFT_MACHINE_SLOW" = "yes" ] && count=1 2108 2109 printf " %-32s " "${display}" 2110 tthen=$(date +%s) 2111 eval test_"${name}" 2112 ret=$? 2113 2114 tnow=$(date +%s) 2115 printf "%5ds%-30s" $((tnow-tthen)) 2116 2117 if [ $ret -eq 0 ]; then 2118 printf "[ OK ]\n" 2119 info_flush 2120 passed=$((passed + 1)) 2121 elif [ $ret -eq 1 ]; then 2122 printf "[FAIL]\n" 2123 err_flush 2124 exit 1 2125 elif [ $ret -eq ${ksft_skip} ]; then 2126 printf "[SKIP]\n" 2127 err_flush 2128 fi 2129 done 2130done 2131 2132[ ${passed} -eq 0 ] && exit ${ksft_skip} || exit 0 2133