| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- #!/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"
|