| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| |
| source lib.sh |
| |
| # shellcheck disable=SC2155 # prefer RO variable over return value from cmd |
| readonly CLI="$(dirname "$(readlink -f "$0")")/../../../net/ynl/pyynl/cli.py" |
| |
| readonly SRC=1 |
| readonly DST=2 |
| |
| readonly NET_V4=192.168.1. |
| readonly NET_V6=2001:db8:: |
| readonly OL1_NET_V4=172.16.1. |
| readonly OL1_NET_V6=2001:db8:1:: |
| readonly OL2_NET_V4=172.16.2. |
| readonly OL2_NET_V6=2001:db8:2:: |
| |
| trap cleanup_all_ns EXIT |
| |
| # shellcheck disable=SC2329 # can't figure out usage trough a variable |
| is_ipv6() { |
| if [[ $1 =~ .*:.* ]]; then |
| return 0 |
| fi |
| return 1 |
| } |
| |
| # shellcheck disable=SC2329 # can't figure out usage trough a variable |
| create_gnv_endpoint() { |
| local -r netns=$1 |
| local -r bm_rem_addr=$2 |
| local -r gnv_dev=$3 |
| local -r gnv_id=$4 |
| local opts=$5 |
| local gnv_json |
| local rem |
| |
| if is_ipv6 "$bm_rem_addr"; then |
| rem=remote6 |
| else |
| rem=remote |
| fi |
| |
| # add ynl opt separator, if needed |
| [ -n "$opts" ] && opts=", $opts" |
| |
| gnv_json="{ \"id\": $gnv_id, \"$rem\": \"$bm_rem_addr\"$opts }" |
| ip netns exec "$netns" "$CLI" --family rt-link --create --excl \ |
| --do newlink --json "{\"ifname\": \"$gnv_dev\", |
| \"linkinfo\": {\"kind\":\"geneve\", |
| \"data\": $gnv_json } }" > /dev/null |
| ip -n "$netns" link set dev "$gnv_dev" up |
| } |
| |
| # shellcheck disable=SC2329 # can't figure out usage trough a variable |
| create_vxlan_endpoint() { |
| local -r netns=$1 |
| local -r bm_rem_addr=$2 |
| local -r vxlan_dev=$3 |
| local -r vxlan_id=$4 |
| local -r opts_str=$5 |
| local oldifs |
| local -a opts |
| local opt |
| |
| # convert the arguments from yaml format |
| oldifs=$IFS |
| IFS=',' |
| for opt in $opts_str; do |
| local pattern='"port":' |
| |
| [ -n "$opt" ] || continue |
| |
| opts+=("${opt/$pattern*/dstport}" "${opt/$pattern/}") |
| done |
| IFS=$oldifs |
| [ ${#opts[@]} -gt 0 ] || opts+=("dstport" "4789") |
| |
| ip -n "$netns" link add "$vxlan_dev" type vxlan id "$vxlan_id" \ |
| remote "$bm_rem_addr" "${opts[@]}" |
| ip -n "$netns" link set dev "$vxlan_dev" up |
| } |
| |
| create_ns() { |
| local nested_opt='"port":6082' |
| local create_endpoint |
| local options="$1" |
| local feature |
| local dev |
| local id |
| local ns |
| |
| RET=0 |
| |
| # +-------------+ +-------------+ |
| # | NS_SRC | | NS_NST_DST | |
| # | | | | |
| # | gnv_nst1 | | gnv_nst2 | |
| # | + | | + | |
| # | | | | | | |
| # | + | | + | |
| # | gnv1 | | gnv2 | |
| # | + | | + | |
| # | | | | | | |
| # | + veth1 +--------+ veth2 + | |
| # | | | | |
| # +-------------+ +-------------+ |
| |
| setup_ns NS_SRC NS_DST |
| |
| # concatenate caller provided options and default one |
| [ -n "$2" ] && nested_opt="$nested_opt,$2" |
| |
| ip link add name "veth$SRC" netns "$NS_SRC" type veth \ |
| peer name "veth$DST" netns "$NS_DST" |
| case "$ENCAP" in |
| vxlan) |
| create_endpoint=create_vxlan_endpoint |
| dev=vx |
| ;; |
| geneve) |
| create_endpoint=create_gnv_endpoint |
| dev=gnv |
| ;; |
| esac |
| |
| id=1 |
| for ns in "${NS_LIST[@]}"; do |
| ip -n "$ns" link set dev "veth$id" up |
| |
| # ensure the sender can do large write just after 3whs |
| ip netns exec "$ns" \ |
| sysctl -qw net.ipv4.tcp_wmem="4096 4194304 4194304" |
| |
| # note that 3 - $SRC == $DST and 3 - $DST == $SRC |
| if [ $FAMILY = "4" ]; then |
| ip -n "$ns" addr add dev "veth$id" "$NET_V4$id/24" |
| $create_endpoint "$ns" "$NET_V4$((3 - id))" \ |
| "$dev$id" 4 "$options" |
| ip -n "$ns" addr add dev "$dev$id" "$OL1_NET_V4$id/24" |
| |
| # nested tunnel devices |
| # pmtu can't be propagated to upper layer devices; |
| # need manual adjust |
| $create_endpoint "$ns" "$OL1_NET_V4$((3 - id))" \ |
| "$dev"_nst"$id" 40 "$nested_opt" |
| ip -n "$ns" addr add dev "$dev"_nst"$id" \ |
| "$OL2_NET_V4$id/24" |
| ip -n "$ns" link set dev "$dev"_nst"$id" mtu 1392 |
| else |
| ip -n "$ns" addr add dev "veth$id" "$NET_V6$id/64" \ |
| nodad |
| $create_endpoint "$ns" "$NET_V6$((3 - id))" \ |
| "$dev"6"$id" 6 "$options" |
| ip -n "$ns" addr add dev "$dev"6"$id" \ |
| "$OL1_NET_V6$id/64" nodad |
| |
| $create_endpoint "$ns" "$OL1_NET_V6$((3 - id))" \ |
| "$dev"6_nst"$id" 60 "$nested_opt" |
| ip -n "$ns" addr add dev "$dev"6_nst"$id" \ |
| "$OL2_NET_V6$id/64" nodad |
| ip -n "$ns" link set dev "$dev"6_nst"$id" mtu 1352 |
| fi |
| id=$((id+1)) |
| done |
| |
| # enable GRO heuristic on the veth peer and ensure UDP L4 over tunnel is |
| # actually segmented |
| for feature in tso tx-udp_tnl-segmentation; do |
| ip netns exec "$NS_SRC" ethtool -K "veth$SRC" \ |
| "$feature" off 2>/dev/null |
| done |
| } |
| |
| create_ns_gso() { |
| local dev |
| |
| create_ns "$@" |
| if [ "$ENCAP" = "geneve" ]; then |
| dev=gnv |
| else |
| dev=vx |
| fi |
| [ "$FAMILY" = "6" ] && dev="$dev"6 |
| ip netns exec "$NS_SRC" ethtool -K "$dev$SRC" \ |
| tx-gso-partial on \ |
| tx-udp_tnl-segmentation on \ |
| tx-udp_tnl-csum-segmentation on |
| } |
| |
| create_ns_gso_gro() { |
| create_ns_gso "$@" |
| ip netns exec "$NS_DST" ethtool -K "veth$DST" gro on |
| ip netns exec "$NS_SRC" ethtool -K "veth$SRC" tx off >/dev/null 2>&1 |
| } |
| |
| run_test() { |
| local -r dst=$NET$DST |
| local -r msg=$1 |
| local -r total_size=$2 |
| local -r encappkts=$3 |
| local inner_proto_offset=0 |
| local inner_maclen=14 |
| local rx_family="-4" |
| local ipt=iptables |
| local bpf_filter |
| local -a rx_args |
| local wire_pkts |
| local rcvpkts |
| local encl=8 |
| local dport |
| local pkts |
| local snd |
| |
| if [ $FAMILY = "6" ]; then |
| ipt=ip6tables |
| else |
| # rx program does not support '-6' and implies ipv6 usage by |
| # default |
| rx_args=("$rx_family") |
| fi |
| |
| # The received can only check fixed size packet |
| pkts=$((total_size / GSO_SIZE)) |
| if [ -n "$4" ]; then |
| wire_pkts=$4 |
| elif [ $((total_size % GSO_SIZE)) -eq 0 ]; then |
| wire_pkts=1 |
| rx_args+=("-l" "$GSO_SIZE") |
| else |
| wire_pkts=2 |
| pkts=$((pkts + 1)) |
| fi |
| |
| if [ "$ENCAP" = "geneve" ]; then |
| dport=6081 |
| else |
| dport=4789 |
| fi |
| |
| # Either: |
| # - IPv4, nested tunnel carries UDP over IPv4, with dport 6082, |
| # innermost is TCP over IPv4 on port 8000 |
| # - IPv6, nested tunnel carries UDP over IPv6, with dport 6082, |
| # innermost is TCP over IPv6 on port 8000 |
| # The nested tunnel port is 6082 and the nested encap len is 8 |
| # regardless of the encap type (no geneve opts). |
| # In inherit protocol mode there is no nested mac hdr and the nested |
| # l3 protocol type field belongs to the geneve hdr. |
| [ "$USE_HINT" = true ] && encl=16 |
| [ "$INHERIT" = true ] && inner_maclen=0 |
| [ "$INHERIT" = true ] && inner_proto_offset=-4 |
| local inner=$((inner_maclen+encl)) |
| local proto=$((inner_maclen+encl+inner_proto_offset)) |
| bpf_filter=$(nfbpf_compile "(ip && |
| ip[$((40+encl))] == 0x08 && ip[$((41+encl))] == 0x00 && |
| ip[$((51+encl))] == 0x11 && |
| ip[$((64+encl))] == 0x17 && ip[$((65+encl))] == 0xc2 && |
| ip[$((76+proto))] == 0x08 && ip[$((77+proto))] == 0x00 && |
| ip[$((87+inner))] == 0x6 && |
| ip[$((100+inner))] == 0x1f && ip[$((101+inner))] == 0x40) || |
| (ip6 && |
| ip6[$((60+encl))] == 0x86 && ip6[$((61+encl))] == 0xdd && |
| ip6[$((68+encl))] == 0x11 && |
| ip6[$((104+encl))] == 0x17 && ip6[$((105+encl))] == 0xc2 && |
| ip6[$((116+proto))] == 0x86 && ip6[$((117+proto))] == 0xdd && |
| ip6[$((124+inner))] == 0x6 && |
| ip6[$((160+inner))] == 0x1f && ip6[$((161+inner))] == 0x40)") |
| |
| # ignore shorts packet, to avoid arp/mld induced noise |
| ip netns exec "$NS_SRC" "$ipt" -A OUTPUT -p udp --dport "$dport" \ |
| -m length --length 600:65535 -m bpf --bytecode "$bpf_filter" |
| ip netns exec "$NS_DST" "$ipt" -A INPUT -p udp --dport "$dport" \ |
| -m length --length 600:65535 -m bpf --bytecode "$bpf_filter" |
| ip netns exec "$NS_DST" ./udpgso_bench_rx -C 2000 -t -R 100 \ |
| -n "$pkts" "${rx_args[@]}" & |
| local pid=$! |
| wait_local_port_listen "$NS_DST" 8000 tcp |
| ip netns exec "$NS_SRC" ./udpgso_bench_tx -"$FAMILY" -t -M 1 \ |
| -s "$total_size" -D "$dst" |
| local ret=$? |
| check_err "$ret" "client failure exit code $ret" |
| wait "$pid" |
| ret=$? |
| check_err "$ret" "sever failure exit code $ret" |
| |
| snd=$(ip netns exec "$NS_SRC" "$ipt"-save -c | |
| grep "dport $dport" | sed -e 's/\[//' -e 's/:.*//') |
| |
| [ "$snd" = "$wire_pkts" ] |
| # shellcheck disable=SC2319 # known false positive |
| check_err $? "send $snd packets on the lowest link, expected $wire_pkts" |
| |
| rcvpkts=$(ip netns exec "$NS_DST" "$ipt"-save -c | \ |
| grep "dport $dport" | sed -e 's/\[//' -e 's/:.*//') |
| |
| [ "$rcvpkts" = "$encappkts" ] |
| check_err $? "received $rcvpkts $ENCAP packets, expected $encappkts" |
| log_test "$msg" |
| } |
| |
| run_tests() { |
| for FAMILY in 4 6; do |
| NET=$OL2_NET_V4 |
| GSO_SIZE=1340 # 1392 - 20 - 32 |
| |
| if [ $FAMILY = 6 ]; then |
| NET=$OL2_NET_V6 |
| GSO_SIZE=1280 # 1352 - 40 - 32 |
| fi |
| |
| echo "IPv$FAMILY" |
| |
| unset USE_HINT |
| unset INHERIT |
| |
| # "geneve" must be last encap in list, so that later |
| # test cases will run on it |
| for ENCAP in "vxlan" "geneve"; do |
| create_ns |
| run_test "No GSO - $ENCAP" $((GSO_SIZE * 4)) 4 4 |
| cleanup_all_ns |
| |
| create_ns_gso |
| run_test "GSO without GRO - $ENCAP" $((GSO_SIZE * 4)) \ |
| 4 1 |
| cleanup_all_ns |
| |
| # IPv4 only test |
| [ $FAMILY = "4" ] || continue |
| create_ns_gso |
| ip netns exec "$NS_SRC" \ |
| sysctl -qw net.ipv4.ip_no_pmtu_disc=1 |
| run_test "GSO disable due to no fixedid - $ENCAP" \ |
| $((GSO_SIZE * 4)) 4 4 |
| cleanup_all_ns |
| done |
| |
| # GRO tests imply/require geneve encap, the only one providing |
| # GRO hints |
| create_ns_gso_gro |
| run_test "double tunnel GRO, no hints" $((GSO_SIZE * 4)) 4 |
| cleanup_all_ns |
| |
| # hint option is expected for all the following tests in the RX |
| # path |
| USE_HINT=true |
| create_ns_gso_gro \ |
| '"gro-hint":1,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' \ |
| '"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' |
| run_test "double tunnel GRO" $((GSO_SIZE * 4)) 1 |
| cleanup_all_ns |
| |
| create_ns_gso_gro '"gro-hint":1,"udp-csum":1' '"udp-csum":1' |
| run_test "double tunnel GRO - csum complete" $((GSO_SIZE * 4))\ |
| 1 |
| cleanup_all_ns |
| |
| create_ns_gso_gro '"gro-hint":1' \ |
| '"udp-csum":0,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' |
| run_test "double tunnel GRO - no nested csum" \ |
| $((GSO_SIZE * 4)) 1 |
| cleanup_all_ns |
| |
| create_ns_gso_gro \ |
| '"gro-hint":1,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' \ |
| '"udp-csum":1' |
| run_test "double tunnel GRO - nested csum, outer 0-csum, skip"\ |
| $((GSO_SIZE * 4)) 4 |
| cleanup_all_ns |
| |
| INHERIT=true |
| create_ns_gso_gro '"gro-hint":1,"udp-csum":1' \ |
| '"udp-csum":1,"inner-proto-inherit":1' |
| run_test "double tunnel GRO - nested inherit proto" \ |
| $((GSO_SIZE * 4)) 1 |
| cleanup_all_ns |
| unset INHERIT |
| |
| create_ns_gso_gro '"gro-hint":1' |
| run_test "double tunnel GRO - short last pkt" \ |
| $((GSO_SIZE * 4 + GSO_SIZE / 2)) 2 |
| cleanup_all_ns |
| done |
| } |
| |
| require_command nfbpf_compile |
| require_command jq |
| |
| # tcp retransmisions will break the accounting |
| xfail_on_slow run_tests |
| exit "$EXIT_STATUS" |