l2_tos_ttl_inherit.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. #!/bin/sh
  2. # SPDX-License-Identifier: GPL-2.0
  3. # Author: Matthias May <matthias.may@westermo.com>
  4. #
  5. # This script evaluates ip tunnels that are capable of carrying L2 traffic
  6. # if they inherit or set the inheritable fields.
  7. # Namely these tunnels are: 'gretap', 'vxlan' and 'geneve'.
  8. # Checked inheritable fields are: TOS and TTL.
  9. # The outer tunnel protocol of 'IPv4' or 'IPv6' is verified-
  10. # As payload frames of type 'IPv4', 'IPv6' and 'other'(ARP) are verified.
  11. # In addition this script also checks if forcing a specific field in the
  12. # outer header is working.
  13. # Return 4 by default (Kselftest SKIP code)
  14. ERR=4
  15. if [ "$(id -u)" != "0" ]; then
  16. echo "Please run as root."
  17. exit $ERR
  18. fi
  19. if ! which tcpdump > /dev/null 2>&1; then
  20. echo "No tcpdump found. Required for this test."
  21. exit $ERR
  22. fi
  23. expected_tos="0x00"
  24. expected_ttl="0"
  25. failed=false
  26. readonly NS0=$(mktemp -u ns0-XXXXXXXX)
  27. readonly NS1=$(mktemp -u ns1-XXXXXXXX)
  28. RUN_NS0="ip netns exec ${NS0}"
  29. get_random_tos() {
  30. # Get a random hex tos value between 0x00 and 0xfc, a multiple of 4
  31. echo "0x$(tr -dc '0-9a-f' < /dev/urandom | head -c 1)\
  32. $(tr -dc '048c' < /dev/urandom | head -c 1)"
  33. }
  34. get_random_ttl() {
  35. # Get a random dec value between 0 and 255
  36. printf "%d" "0x$(tr -dc '0-9a-f' < /dev/urandom | head -c 2)"
  37. }
  38. get_field() {
  39. # Expects to get the 'head -n 1' of a captured frame by tcpdump.
  40. # Parses this first line and returns the specified field.
  41. local field="$1"
  42. local input="$2"
  43. local found=false
  44. input="$(echo "$input" | tr -d '(),')"
  45. for input_field in $input; do
  46. if $found; then
  47. echo "$input_field"
  48. return
  49. fi
  50. # The next field that we iterate over is the looked for value
  51. if [ "$input_field" = "$field" ]; then
  52. found=true
  53. fi
  54. done
  55. echo "0"
  56. }
  57. setup() {
  58. local type="$1"
  59. local outer="$2"
  60. local inner="$3"
  61. local tos_ttl="$4"
  62. local vlan="$5"
  63. local test_tos="0x00"
  64. local test_ttl="0"
  65. # We don't want a test-tos of 0x00,
  66. # because this is the value that we get when no tos is set.
  67. expected_tos="$(get_random_tos)"
  68. while [ "$expected_tos" = "0x00" ]; do
  69. expected_tos="$(get_random_tos)"
  70. done
  71. if [ "$tos_ttl" = "random" ]; then
  72. test_tos="$expected_tos"
  73. tos="fixed $test_tos"
  74. elif [ "$tos_ttl" = "inherit" ]; then
  75. test_tos="$tos_ttl"
  76. tos="inherit $expected_tos"
  77. fi
  78. # We don't want a test-ttl of 64 or 0,
  79. # because 64 is when no ttl is set and 0 is not a valid ttl.
  80. expected_ttl="$(get_random_ttl)"
  81. while [ "$expected_ttl" = "64" ] || [ "$expected_ttl" = "0" ]; do
  82. expected_ttl="$(get_random_ttl)"
  83. done
  84. if [ "$tos_ttl" = "random" ]; then
  85. test_ttl="$expected_ttl"
  86. ttl="fixed $test_ttl"
  87. elif [ "$tos_ttl" = "inherit" ]; then
  88. test_ttl="$tos_ttl"
  89. ttl="inherit $expected_ttl"
  90. fi
  91. printf "│%7s │%6s │%6s │%13s │%13s │%6s │" \
  92. "$type" "$outer" "$inner" "$tos" "$ttl" "$vlan"
  93. # Create netns NS0 and NS1 and connect them with a veth pair
  94. ip netns add "${NS0}"
  95. ip netns add "${NS1}"
  96. ip link add name veth0 netns "${NS0}" type veth \
  97. peer name veth1 netns "${NS1}"
  98. ip -netns "${NS0}" link set dev veth0 up
  99. ip -netns "${NS1}" link set dev veth1 up
  100. ip -netns "${NS0}" address flush dev veth0
  101. ip -netns "${NS1}" address flush dev veth1
  102. local local_addr1=""
  103. local local_addr2=""
  104. if [ "$type" = "gre" ] || [ "$type" = "vxlan" ]; then
  105. if [ "$outer" = "4" ]; then
  106. local_addr1="local 198.18.0.1"
  107. local_addr2="local 198.18.0.2"
  108. elif [ "$outer" = "6" ]; then
  109. local_addr1="local fdd1:ced0:5d88:3fce::1"
  110. local_addr2="local fdd1:ced0:5d88:3fce::2"
  111. fi
  112. fi
  113. local vxlan=""
  114. if [ "$type" = "vxlan" ]; then
  115. vxlan="vni 100 dstport 4789"
  116. fi
  117. local geneve=""
  118. if [ "$type" = "geneve" ]; then
  119. geneve="vni 100"
  120. fi
  121. # Create tunnel and assign outer IPv4/IPv6 addresses
  122. if [ "$outer" = "4" ]; then
  123. if [ "$type" = "gre" ]; then
  124. type="gretap"
  125. fi
  126. ip -netns "${NS0}" address add 198.18.0.1/24 dev veth0
  127. ip -netns "${NS1}" address add 198.18.0.2/24 dev veth1
  128. ip -netns "${NS0}" link add name tep0 type $type $local_addr1 \
  129. remote 198.18.0.2 tos $test_tos ttl $test_ttl \
  130. $vxlan $geneve
  131. ip -netns "${NS1}" link add name tep1 type $type $local_addr2 \
  132. remote 198.18.0.1 tos $test_tos ttl $test_ttl \
  133. $vxlan $geneve
  134. elif [ "$outer" = "6" ]; then
  135. if [ "$type" = "gre" ]; then
  136. type="ip6gretap"
  137. fi
  138. ip -netns "${NS0}" address add fdd1:ced0:5d88:3fce::1/64 \
  139. dev veth0 nodad
  140. ip -netns "${NS1}" address add fdd1:ced0:5d88:3fce::2/64 \
  141. dev veth1 nodad
  142. ip -netns "${NS0}" link add name tep0 type $type $local_addr1 \
  143. remote fdd1:ced0:5d88:3fce::2 tos $test_tos \
  144. ttl $test_ttl $vxlan $geneve
  145. ip -netns "${NS1}" link add name tep1 type $type $local_addr2 \
  146. remote fdd1:ced0:5d88:3fce::1 tos $test_tos \
  147. ttl $test_ttl $vxlan $geneve
  148. fi
  149. # Bring L2-tunnel link up and create VLAN on top
  150. ip -netns "${NS0}" link set tep0 up
  151. ip -netns "${NS1}" link set tep1 up
  152. ip -netns "${NS0}" address flush dev tep0
  153. ip -netns "${NS1}" address flush dev tep1
  154. local parent
  155. if $vlan; then
  156. parent="vlan99-"
  157. ip -netns "${NS0}" link add link tep0 name ${parent}0 \
  158. type vlan id 99
  159. ip -netns "${NS1}" link add link tep1 name ${parent}1 \
  160. type vlan id 99
  161. ip -netns "${NS0}" link set dev ${parent}0 up
  162. ip -netns "${NS1}" link set dev ${parent}1 up
  163. ip -netns "${NS0}" address flush dev ${parent}0
  164. ip -netns "${NS1}" address flush dev ${parent}1
  165. else
  166. parent="tep"
  167. fi
  168. # Assign inner IPv4/IPv6 addresses
  169. if [ "$inner" = "4" ] || [ "$inner" = "other" ]; then
  170. ip -netns "${NS0}" address add 198.19.0.1/24 brd + dev ${parent}0
  171. ip -netns "${NS1}" address add 198.19.0.2/24 brd + dev ${parent}1
  172. elif [ "$inner" = "6" ]; then
  173. ip -netns "${NS0}" address add fdd4:96cf:4eae:443b::1/64 \
  174. dev ${parent}0 nodad
  175. ip -netns "${NS1}" address add fdd4:96cf:4eae:443b::2/64 \
  176. dev ${parent}1 nodad
  177. fi
  178. }
  179. verify() {
  180. local outer="$1"
  181. local inner="$2"
  182. local tos_ttl="$3"
  183. local vlan="$4"
  184. local ping_pid out captured_tos captured_ttl result
  185. local ping_dst
  186. if [ "$inner" = "4" ]; then
  187. ping_dst="198.19.0.2"
  188. elif [ "$inner" = "6" ]; then
  189. ping_dst="fdd4:96cf:4eae:443b::2"
  190. elif [ "$inner" = "other" ]; then
  191. ping_dst="198.19.0.3" # Generates ARPs which are not IPv4/IPv6
  192. fi
  193. if [ "$tos_ttl" = "inherit" ]; then
  194. ${RUN_NS0} ping -i 0.1 $ping_dst -Q "$expected_tos" \
  195. -t "$expected_ttl" 2>/dev/null 1>&2 & ping_pid="$!"
  196. else
  197. ${RUN_NS0} ping -i 0.1 $ping_dst 2>/dev/null 1>&2 & ping_pid="$!"
  198. fi
  199. local tunnel_type_offset tunnel_type_proto req_proto_offset req_offset
  200. if [ "$type" = "gre" ]; then
  201. tunnel_type_proto="0x2f"
  202. elif [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  203. tunnel_type_proto="0x11"
  204. fi
  205. if [ "$outer" = "4" ]; then
  206. tunnel_type_offset="9"
  207. if [ "$inner" = "4" ]; then
  208. req_proto_offset="47"
  209. req_offset="58"
  210. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  211. req_proto_offset="$((req_proto_offset + 12))"
  212. req_offset="$((req_offset + 12))"
  213. fi
  214. if $vlan; then
  215. req_proto_offset="$((req_proto_offset + 4))"
  216. req_offset="$((req_offset + 4))"
  217. fi
  218. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  219. -i veth0 -n \
  220. ip[$tunnel_type_offset] = $tunnel_type_proto and \
  221. ip[$req_proto_offset] = 0x01 and \
  222. ip[$req_offset] = 0x08 2>/dev/null \
  223. | head -n 1)"
  224. elif [ "$inner" = "6" ]; then
  225. req_proto_offset="44"
  226. req_offset="78"
  227. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  228. req_proto_offset="$((req_proto_offset + 12))"
  229. req_offset="$((req_offset + 12))"
  230. fi
  231. if $vlan; then
  232. req_proto_offset="$((req_proto_offset + 4))"
  233. req_offset="$((req_offset + 4))"
  234. fi
  235. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  236. -i veth0 -n \
  237. ip[$tunnel_type_offset] = $tunnel_type_proto and \
  238. ip[$req_proto_offset] = 0x3a and \
  239. ip[$req_offset] = 0x80 2>/dev/null \
  240. | head -n 1)"
  241. elif [ "$inner" = "other" ]; then
  242. req_proto_offset="36"
  243. req_offset="45"
  244. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  245. req_proto_offset="$((req_proto_offset + 12))"
  246. req_offset="$((req_offset + 12))"
  247. fi
  248. if $vlan; then
  249. req_proto_offset="$((req_proto_offset + 4))"
  250. req_offset="$((req_offset + 4))"
  251. fi
  252. if [ "$tos_ttl" = "inherit" ]; then
  253. expected_tos="0x00"
  254. expected_ttl="64"
  255. fi
  256. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  257. -i veth0 -n \
  258. ip[$tunnel_type_offset] = $tunnel_type_proto and \
  259. ip[$req_proto_offset] = 0x08 and \
  260. ip[$((req_proto_offset + 1))] = 0x06 and \
  261. ip[$req_offset] = 0x01 2>/dev/null \
  262. | head -n 1)"
  263. fi
  264. elif [ "$outer" = "6" ]; then
  265. if [ "$type" = "gre" ]; then
  266. tunnel_type_offset="40"
  267. elif [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  268. tunnel_type_offset="6"
  269. fi
  270. if [ "$inner" = "4" ]; then
  271. local req_proto_offset="75"
  272. local req_offset="86"
  273. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  274. req_proto_offset="$((req_proto_offset + 4))"
  275. req_offset="$((req_offset + 4))"
  276. fi
  277. if $vlan; then
  278. req_proto_offset="$((req_proto_offset + 4))"
  279. req_offset="$((req_offset + 4))"
  280. fi
  281. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  282. -i veth0 -n \
  283. ip6[$tunnel_type_offset] = $tunnel_type_proto and \
  284. ip6[$req_proto_offset] = 0x01 and \
  285. ip6[$req_offset] = 0x08 2>/dev/null \
  286. | head -n 1)"
  287. elif [ "$inner" = "6" ]; then
  288. local req_proto_offset="72"
  289. local req_offset="106"
  290. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  291. req_proto_offset="$((req_proto_offset + 4))"
  292. req_offset="$((req_offset + 4))"
  293. fi
  294. if $vlan; then
  295. req_proto_offset="$((req_proto_offset + 4))"
  296. req_offset="$((req_offset + 4))"
  297. fi
  298. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  299. -i veth0 -n \
  300. ip6[$tunnel_type_offset] = $tunnel_type_proto and \
  301. ip6[$req_proto_offset] = 0x3a and \
  302. ip6[$req_offset] = 0x80 2>/dev/null \
  303. | head -n 1)"
  304. elif [ "$inner" = "other" ]; then
  305. local req_proto_offset="64"
  306. local req_offset="73"
  307. if [ "$type" = "vxlan" ] || [ "$type" = "geneve" ]; then
  308. req_proto_offset="$((req_proto_offset + 4))"
  309. req_offset="$((req_offset + 4))"
  310. fi
  311. if $vlan; then
  312. req_proto_offset="$((req_proto_offset + 4))"
  313. req_offset="$((req_offset + 4))"
  314. fi
  315. if [ "$tos_ttl" = "inherit" ]; then
  316. expected_tos="0x00"
  317. expected_ttl="64"
  318. fi
  319. out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
  320. -i veth0 -n \
  321. ip6[$tunnel_type_offset] = $tunnel_type_proto and \
  322. ip6[$req_proto_offset] = 0x08 and \
  323. ip6[$((req_proto_offset + 1))] = 0x06 and \
  324. ip6[$req_offset] = 0x01 2>/dev/null \
  325. | head -n 1)"
  326. fi
  327. fi
  328. kill -9 $ping_pid
  329. wait $ping_pid 2>/dev/null || true
  330. result="FAIL"
  331. if [ "$outer" = "4" ]; then
  332. captured_ttl="$(get_field "ttl" "$out")"
  333. captured_tos="$(printf "0x%02x" "$(get_field "tos" "$out")")"
  334. if [ "$captured_tos" = "$expected_tos" ] &&
  335. [ "$captured_ttl" = "$expected_ttl" ]; then
  336. result="OK"
  337. fi
  338. elif [ "$outer" = "6" ]; then
  339. captured_ttl="$(get_field "hlim" "$out")"
  340. captured_tos="$(printf "0x%02x" "$(get_field "class" "$out")")"
  341. if [ "$captured_tos" = "$expected_tos" ] &&
  342. [ "$captured_ttl" = "$expected_ttl" ]; then
  343. result="OK"
  344. fi
  345. fi
  346. printf "%7s │\n" "$result"
  347. if [ "$result" = "FAIL" ]; then
  348. failed=true
  349. if [ "$captured_tos" != "$expected_tos" ]; then
  350. printf "│%43s%27s │\n" \
  351. "Expected TOS value: $expected_tos" \
  352. "Captured TOS value: $captured_tos"
  353. fi
  354. if [ "$captured_ttl" != "$expected_ttl" ]; then
  355. printf "│%43s%27s │\n" \
  356. "Expected TTL value: $expected_ttl" \
  357. "Captured TTL value: $captured_ttl"
  358. fi
  359. printf "│%71s│\n" " "
  360. fi
  361. }
  362. cleanup() {
  363. ip netns del "${NS0}" 2>/dev/null
  364. ip netns del "${NS1}" 2>/dev/null
  365. }
  366. exit_handler() {
  367. # Don't exit immediately if one of the intermediate commands fails.
  368. # We might be called at the end of the script, when the network
  369. # namespaces have already been deleted. So cleanup() may fail, but we
  370. # still need to run until 'exit $ERR' or the script won't return the
  371. # correct error code.
  372. set +e
  373. cleanup
  374. exit $ERR
  375. }
  376. # Restore the default SIGINT handler (just in case) and exit.
  377. # The exit handler will take care of cleaning everything up.
  378. interrupted() {
  379. trap - INT
  380. exit $ERR
  381. }
  382. set -e
  383. trap exit_handler EXIT
  384. trap interrupted INT
  385. printf "┌────────┬───────┬───────┬──────────────┬"
  386. printf "──────────────┬───────┬────────┐\n"
  387. for type in gre vxlan geneve; do
  388. if ! $(modprobe "$type" 2>/dev/null); then
  389. continue
  390. fi
  391. for outer in 4 6; do
  392. printf "├────────┼───────┼───────┼──────────────┼"
  393. printf "──────────────┼───────┼────────┤\n"
  394. printf "│ Type │ outer | inner │ tos │"
  395. printf " ttl │ vlan │ result │\n"
  396. for inner in 4 6 other; do
  397. printf "├────────┼───────┼───────┼──────────────┼"
  398. printf "──────────────┼───────┼────────┤\n"
  399. for tos_ttl in inherit random; do
  400. for vlan in false true; do
  401. setup "$type" "$outer" "$inner" \
  402. "$tos_ttl" "$vlan"
  403. verify "$outer" "$inner" "$tos_ttl" \
  404. "$vlan"
  405. cleanup
  406. done
  407. done
  408. done
  409. done
  410. done
  411. printf "└────────┴───────┴───────┴──────────────┴"
  412. printf "──────────────┴───────┴────────┘\n"
  413. # All tests done.
  414. # Set ERR appropriately: it will be returned by the exit handler.
  415. if $failed; then
  416. ERR=1
  417. else
  418. ERR=0
  419. fi