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