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