double_udp_encap.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. #!/bin/bash
  2. # SPDX-License-Identifier: GPL-2.0
  3. source lib.sh
  4. # shellcheck disable=SC2155 # prefer RO variable over return value from cmd
  5. readonly CLI="$(dirname "$(readlink -f "$0")")/../../../net/ynl/pyynl/cli.py"
  6. readonly SRC=1
  7. readonly DST=2
  8. readonly NET_V4=192.168.1.
  9. readonly NET_V6=2001:db8::
  10. readonly OL1_NET_V4=172.16.1.
  11. readonly OL1_NET_V6=2001:db8:1::
  12. readonly OL2_NET_V4=172.16.2.
  13. readonly OL2_NET_V6=2001:db8:2::
  14. trap cleanup_all_ns EXIT
  15. # shellcheck disable=SC2329 # can't figure out usage trough a variable
  16. is_ipv6() {
  17. if [[ $1 =~ .*:.* ]]; then
  18. return 0
  19. fi
  20. return 1
  21. }
  22. # shellcheck disable=SC2329 # can't figure out usage trough a variable
  23. create_gnv_endpoint() {
  24. local -r netns=$1
  25. local -r bm_rem_addr=$2
  26. local -r gnv_dev=$3
  27. local -r gnv_id=$4
  28. local opts=$5
  29. local gnv_json
  30. local rem
  31. if is_ipv6 "$bm_rem_addr"; then
  32. rem=remote6
  33. else
  34. rem=remote
  35. fi
  36. # add ynl opt separator, if needed
  37. [ -n "$opts" ] && opts=", $opts"
  38. gnv_json="{ \"id\": $gnv_id, \"$rem\": \"$bm_rem_addr\"$opts }"
  39. ip netns exec "$netns" "$CLI" --family rt-link --create --excl \
  40. --do newlink --json "{\"ifname\": \"$gnv_dev\",
  41. \"linkinfo\": {\"kind\":\"geneve\",
  42. \"data\": $gnv_json } }" > /dev/null
  43. ip -n "$netns" link set dev "$gnv_dev" up
  44. }
  45. # shellcheck disable=SC2329 # can't figure out usage trough a variable
  46. create_vxlan_endpoint() {
  47. local -r netns=$1
  48. local -r bm_rem_addr=$2
  49. local -r vxlan_dev=$3
  50. local -r vxlan_id=$4
  51. local -r opts_str=$5
  52. local oldifs
  53. local -a opts
  54. local opt
  55. # convert the arguments from yaml format
  56. oldifs=$IFS
  57. IFS=','
  58. for opt in $opts_str; do
  59. local pattern='"port":'
  60. [ -n "$opt" ] || continue
  61. opts+=("${opt/$pattern*/dstport}" "${opt/$pattern/}")
  62. done
  63. IFS=$oldifs
  64. [ ${#opts[@]} -gt 0 ] || opts+=("dstport" "4789")
  65. ip -n "$netns" link add "$vxlan_dev" type vxlan id "$vxlan_id" \
  66. remote "$bm_rem_addr" "${opts[@]}"
  67. ip -n "$netns" link set dev "$vxlan_dev" up
  68. }
  69. create_ns() {
  70. local nested_opt='"port":6082'
  71. local create_endpoint
  72. local options="$1"
  73. local feature
  74. local dev
  75. local id
  76. local ns
  77. RET=0
  78. # +-------------+ +-------------+
  79. # | NS_SRC | | NS_NST_DST |
  80. # | | | |
  81. # | gnv_nst1 | | gnv_nst2 |
  82. # | + | | + |
  83. # | | | | | |
  84. # | + | | + |
  85. # | gnv1 | | gnv2 |
  86. # | + | | + |
  87. # | | | | | |
  88. # | + veth1 +--------+ veth2 + |
  89. # | | | |
  90. # +-------------+ +-------------+
  91. setup_ns NS_SRC NS_DST
  92. # concatenate caller provided options and default one
  93. [ -n "$2" ] && nested_opt="$nested_opt,$2"
  94. ip link add name "veth$SRC" netns "$NS_SRC" type veth \
  95. peer name "veth$DST" netns "$NS_DST"
  96. case "$ENCAP" in
  97. vxlan)
  98. create_endpoint=create_vxlan_endpoint
  99. dev=vx
  100. ;;
  101. geneve)
  102. create_endpoint=create_gnv_endpoint
  103. dev=gnv
  104. ;;
  105. esac
  106. id=1
  107. for ns in "${NS_LIST[@]}"; do
  108. ip -n "$ns" link set dev "veth$id" up
  109. # ensure the sender can do large write just after 3whs
  110. ip netns exec "$ns" \
  111. sysctl -qw net.ipv4.tcp_wmem="4096 4194304 4194304"
  112. # note that 3 - $SRC == $DST and 3 - $DST == $SRC
  113. if [ $FAMILY = "4" ]; then
  114. ip -n "$ns" addr add dev "veth$id" "$NET_V4$id/24"
  115. $create_endpoint "$ns" "$NET_V4$((3 - id))" \
  116. "$dev$id" 4 "$options"
  117. ip -n "$ns" addr add dev "$dev$id" "$OL1_NET_V4$id/24"
  118. # nested tunnel devices
  119. # pmtu can't be propagated to upper layer devices;
  120. # need manual adjust
  121. $create_endpoint "$ns" "$OL1_NET_V4$((3 - id))" \
  122. "$dev"_nst"$id" 40 "$nested_opt"
  123. ip -n "$ns" addr add dev "$dev"_nst"$id" \
  124. "$OL2_NET_V4$id/24"
  125. ip -n "$ns" link set dev "$dev"_nst"$id" mtu 1392
  126. else
  127. ip -n "$ns" addr add dev "veth$id" "$NET_V6$id/64" \
  128. nodad
  129. $create_endpoint "$ns" "$NET_V6$((3 - id))" \
  130. "$dev"6"$id" 6 "$options"
  131. ip -n "$ns" addr add dev "$dev"6"$id" \
  132. "$OL1_NET_V6$id/64" nodad
  133. $create_endpoint "$ns" "$OL1_NET_V6$((3 - id))" \
  134. "$dev"6_nst"$id" 60 "$nested_opt"
  135. ip -n "$ns" addr add dev "$dev"6_nst"$id" \
  136. "$OL2_NET_V6$id/64" nodad
  137. ip -n "$ns" link set dev "$dev"6_nst"$id" mtu 1352
  138. fi
  139. id=$((id+1))
  140. done
  141. # enable GRO heuristic on the veth peer and ensure UDP L4 over tunnel is
  142. # actually segmented
  143. for feature in tso tx-udp_tnl-segmentation; do
  144. ip netns exec "$NS_SRC" ethtool -K "veth$SRC" \
  145. "$feature" off 2>/dev/null
  146. done
  147. }
  148. create_ns_gso() {
  149. local dev
  150. create_ns "$@"
  151. if [ "$ENCAP" = "geneve" ]; then
  152. dev=gnv
  153. else
  154. dev=vx
  155. fi
  156. [ "$FAMILY" = "6" ] && dev="$dev"6
  157. ip netns exec "$NS_SRC" ethtool -K "$dev$SRC" \
  158. tx-gso-partial on \
  159. tx-udp_tnl-segmentation on \
  160. tx-udp_tnl-csum-segmentation on
  161. }
  162. create_ns_gso_gro() {
  163. create_ns_gso "$@"
  164. ip netns exec "$NS_DST" ethtool -K "veth$DST" gro on
  165. ip netns exec "$NS_SRC" ethtool -K "veth$SRC" tx off >/dev/null 2>&1
  166. }
  167. run_test() {
  168. local -r dst=$NET$DST
  169. local -r msg=$1
  170. local -r total_size=$2
  171. local -r encappkts=$3
  172. local inner_proto_offset=0
  173. local inner_maclen=14
  174. local rx_family="-4"
  175. local ipt=iptables
  176. local bpf_filter
  177. local -a rx_args
  178. local wire_pkts
  179. local rcvpkts
  180. local encl=8
  181. local dport
  182. local pkts
  183. local snd
  184. if [ $FAMILY = "6" ]; then
  185. ipt=ip6tables
  186. else
  187. # rx program does not support '-6' and implies ipv6 usage by
  188. # default
  189. rx_args=("$rx_family")
  190. fi
  191. # The received can only check fixed size packet
  192. pkts=$((total_size / GSO_SIZE))
  193. if [ -n "$4" ]; then
  194. wire_pkts=$4
  195. elif [ $((total_size % GSO_SIZE)) -eq 0 ]; then
  196. wire_pkts=1
  197. rx_args+=("-l" "$GSO_SIZE")
  198. else
  199. wire_pkts=2
  200. pkts=$((pkts + 1))
  201. fi
  202. if [ "$ENCAP" = "geneve" ]; then
  203. dport=6081
  204. else
  205. dport=4789
  206. fi
  207. # Either:
  208. # - IPv4, nested tunnel carries UDP over IPv4, with dport 6082,
  209. # innermost is TCP over IPv4 on port 8000
  210. # - IPv6, nested tunnel carries UDP over IPv6, with dport 6082,
  211. # innermost is TCP over IPv6 on port 8000
  212. # The nested tunnel port is 6082 and the nested encap len is 8
  213. # regardless of the encap type (no geneve opts).
  214. # In inherit protocol mode there is no nested mac hdr and the nested
  215. # l3 protocol type field belongs to the geneve hdr.
  216. [ "$USE_HINT" = true ] && encl=16
  217. [ "$INHERIT" = true ] && inner_maclen=0
  218. [ "$INHERIT" = true ] && inner_proto_offset=-4
  219. local inner=$((inner_maclen+encl))
  220. local proto=$((inner_maclen+encl+inner_proto_offset))
  221. bpf_filter=$(nfbpf_compile "(ip &&
  222. ip[$((40+encl))] == 0x08 && ip[$((41+encl))] == 0x00 &&
  223. ip[$((51+encl))] == 0x11 &&
  224. ip[$((64+encl))] == 0x17 && ip[$((65+encl))] == 0xc2 &&
  225. ip[$((76+proto))] == 0x08 && ip[$((77+proto))] == 0x00 &&
  226. ip[$((87+inner))] == 0x6 &&
  227. ip[$((100+inner))] == 0x1f && ip[$((101+inner))] == 0x40) ||
  228. (ip6 &&
  229. ip6[$((60+encl))] == 0x86 && ip6[$((61+encl))] == 0xdd &&
  230. ip6[$((68+encl))] == 0x11 &&
  231. ip6[$((104+encl))] == 0x17 && ip6[$((105+encl))] == 0xc2 &&
  232. ip6[$((116+proto))] == 0x86 && ip6[$((117+proto))] == 0xdd &&
  233. ip6[$((124+inner))] == 0x6 &&
  234. ip6[$((160+inner))] == 0x1f && ip6[$((161+inner))] == 0x40)")
  235. # ignore shorts packet, to avoid arp/mld induced noise
  236. ip netns exec "$NS_SRC" "$ipt" -A OUTPUT -p udp --dport "$dport" \
  237. -m length --length 600:65535 -m bpf --bytecode "$bpf_filter"
  238. ip netns exec "$NS_DST" "$ipt" -A INPUT -p udp --dport "$dport" \
  239. -m length --length 600:65535 -m bpf --bytecode "$bpf_filter"
  240. ip netns exec "$NS_DST" ./udpgso_bench_rx -C 2000 -t -R 100 \
  241. -n "$pkts" "${rx_args[@]}" &
  242. local pid=$!
  243. wait_local_port_listen "$NS_DST" 8000 tcp
  244. ip netns exec "$NS_SRC" ./udpgso_bench_tx -"$FAMILY" -t -M 1 \
  245. -s "$total_size" -D "$dst"
  246. local ret=$?
  247. check_err "$ret" "client failure exit code $ret"
  248. wait "$pid"
  249. ret=$?
  250. check_err "$ret" "sever failure exit code $ret"
  251. snd=$(ip netns exec "$NS_SRC" "$ipt"-save -c |
  252. grep "dport $dport" | sed -e 's/\[//' -e 's/:.*//')
  253. [ "$snd" = "$wire_pkts" ]
  254. # shellcheck disable=SC2319 # known false positive
  255. check_err $? "send $snd packets on the lowest link, expected $wire_pkts"
  256. rcvpkts=$(ip netns exec "$NS_DST" "$ipt"-save -c | \
  257. grep "dport $dport" | sed -e 's/\[//' -e 's/:.*//')
  258. [ "$rcvpkts" = "$encappkts" ]
  259. check_err $? "received $rcvpkts $ENCAP packets, expected $encappkts"
  260. log_test "$msg"
  261. }
  262. run_tests() {
  263. for FAMILY in 4 6; do
  264. NET=$OL2_NET_V4
  265. GSO_SIZE=1340 # 1392 - 20 - 32
  266. if [ $FAMILY = 6 ]; then
  267. NET=$OL2_NET_V6
  268. GSO_SIZE=1280 # 1352 - 40 - 32
  269. fi
  270. echo "IPv$FAMILY"
  271. unset USE_HINT
  272. unset INHERIT
  273. # "geneve" must be last encap in list, so that later
  274. # test cases will run on it
  275. for ENCAP in "vxlan" "geneve"; do
  276. create_ns
  277. run_test "No GSO - $ENCAP" $((GSO_SIZE * 4)) 4 4
  278. cleanup_all_ns
  279. create_ns_gso
  280. run_test "GSO without GRO - $ENCAP" $((GSO_SIZE * 4)) \
  281. 4 1
  282. cleanup_all_ns
  283. # IPv4 only test
  284. [ $FAMILY = "4" ] || continue
  285. create_ns_gso
  286. ip netns exec "$NS_SRC" \
  287. sysctl -qw net.ipv4.ip_no_pmtu_disc=1
  288. run_test "GSO disable due to no fixedid - $ENCAP" \
  289. $((GSO_SIZE * 4)) 4 4
  290. cleanup_all_ns
  291. done
  292. # GRO tests imply/require geneve encap, the only one providing
  293. # GRO hints
  294. create_ns_gso_gro
  295. run_test "double tunnel GRO, no hints" $((GSO_SIZE * 4)) 4
  296. cleanup_all_ns
  297. # hint option is expected for all the following tests in the RX
  298. # path
  299. USE_HINT=true
  300. create_ns_gso_gro \
  301. '"gro-hint":1,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' \
  302. '"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1'
  303. run_test "double tunnel GRO" $((GSO_SIZE * 4)) 1
  304. cleanup_all_ns
  305. create_ns_gso_gro '"gro-hint":1,"udp-csum":1' '"udp-csum":1'
  306. run_test "double tunnel GRO - csum complete" $((GSO_SIZE * 4))\
  307. 1
  308. cleanup_all_ns
  309. create_ns_gso_gro '"gro-hint":1' \
  310. '"udp-csum":0,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1'
  311. run_test "double tunnel GRO - no nested csum" \
  312. $((GSO_SIZE * 4)) 1
  313. cleanup_all_ns
  314. create_ns_gso_gro \
  315. '"gro-hint":1,"udp-zero-csum6-tx":1,"udp-zero-csum6-rx":1' \
  316. '"udp-csum":1'
  317. run_test "double tunnel GRO - nested csum, outer 0-csum, skip"\
  318. $((GSO_SIZE * 4)) 4
  319. cleanup_all_ns
  320. INHERIT=true
  321. create_ns_gso_gro '"gro-hint":1,"udp-csum":1' \
  322. '"udp-csum":1,"inner-proto-inherit":1'
  323. run_test "double tunnel GRO - nested inherit proto" \
  324. $((GSO_SIZE * 4)) 1
  325. cleanup_all_ns
  326. unset INHERIT
  327. create_ns_gso_gro '"gro-hint":1'
  328. run_test "double tunnel GRO - short last pkt" \
  329. $((GSO_SIZE * 4 + GSO_SIZE / 2)) 2
  330. cleanup_all_ns
  331. done
  332. }
  333. require_command nfbpf_compile
  334. require_command jq
  335. # tcp retransmisions will break the accounting
  336. xfail_on_slow run_tests
  337. exit "$EXIT_STATUS"