Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: forwarding: Add initial testing framework

Add initial framework to test packet forwarding functionality. The tests
can run on actual devices using loop-backed cables or using veth pairs.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ido Schimmel and committed by
David S. Miller
73bae673 82308194

+465
+1
tools/testing/selftests/net/forwarding/.gitignore
··· 1 + forwarding.config
+56
tools/testing/selftests/net/forwarding/README
··· 1 + Motivation 2 + ========== 3 + 4 + One of the nice things about network namespaces is that they allow one 5 + to easily create and test complex environments. 6 + 7 + Unfortunately, these namespaces can not be used with actual switching 8 + ASICs, as their ports can not be migrated to other network namespaces 9 + (NETIF_F_NETNS_LOCAL) and most of them probably do not support the 10 + L1-separation provided by namespaces. 11 + 12 + However, a similar kind of flexibility can be achieved by using VRFs and 13 + by looping the switch ports together. For example: 14 + 15 + br0 16 + + 17 + vrf-h1 | vrf-h2 18 + + +---+----+ + 19 + | | | | 20 + 192.0.2.1/24 + + + + 192.0.2.2/24 21 + swp1 swp2 swp3 swp4 22 + + + + + 23 + | | | | 24 + +--------+ +--------+ 25 + 26 + The VRFs act as lightweight namespaces representing hosts connected to 27 + the switch. 28 + 29 + This approach for testing switch ASICs has several advantages over the 30 + traditional method that requires multiple physical machines, to name a 31 + few: 32 + 33 + 1. Only the device under test (DUT) is being tested without noise from 34 + other system. 35 + 36 + 2. Ability to easily provision complex topologies. Testing bridging 37 + between 4-ports LAGs or 8-way ECMP requires many physical links that are 38 + not always available. With the VRF-based approach one merely needs to 39 + loopback more ports. 40 + 41 + These tests are written with switch ASICs in mind, but they can be run 42 + on any Linux box using veth pairs to emulate physical loopbacks. 43 + 44 + Guidelines for Writing Tests 45 + ============================ 46 + 47 + o Where possible, reuse an existing topology for different tests instead 48 + of recreating the same topology. 49 + o Where possible, IPv6 and IPv4 addresses shall conform to RFC 3849 and 50 + RFC 5737, respectively. 51 + o Where possible, tests shall be written so that they can be reused by 52 + multiple topologies and added to lib.sh. 53 + o Checks shall be added to lib.sh for any external dependencies. 54 + o Code shall be checked using ShellCheck [1] prior to submission. 55 + 56 + 1. https://www.shellcheck.net/
+83
tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + NUM_NETIFS=4 5 + source lib.sh 6 + 7 + h1_create() 8 + { 9 + simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64 10 + } 11 + 12 + h1_destroy() 13 + { 14 + simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64 15 + } 16 + 17 + h2_create() 18 + { 19 + simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64 20 + } 21 + 22 + h2_destroy() 23 + { 24 + simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64 25 + } 26 + 27 + switch_create() 28 + { 29 + ip link add dev br0 type bridge vlan_filtering 1 mcast_snooping 0 30 + 31 + ip link set dev $swp1 master br0 32 + ip link set dev $swp2 master br0 33 + 34 + ip link set dev br0 up 35 + ip link set dev $swp1 up 36 + ip link set dev $swp2 up 37 + } 38 + 39 + switch_destroy() 40 + { 41 + ip link set dev $swp2 down 42 + ip link set dev $swp1 down 43 + 44 + ip link del dev br0 45 + } 46 + 47 + setup_prepare() 48 + { 49 + h1=${NETIFS[p1]} 50 + swp1=${NETIFS[p2]} 51 + 52 + swp2=${NETIFS[p3]} 53 + h2=${NETIFS[p4]} 54 + 55 + vrf_prepare 56 + 57 + h1_create 58 + h2_create 59 + 60 + switch_create 61 + } 62 + 63 + cleanup() 64 + { 65 + pre_cleanup 66 + 67 + switch_destroy 68 + 69 + h2_destroy 70 + h1_destroy 71 + 72 + vrf_cleanup 73 + } 74 + 75 + trap cleanup EXIT 76 + 77 + setup_prepare 78 + setup_wait 79 + 80 + ping_test $h1 192.0.2.2 81 + ping6_test $h1 2001:db8:1::2 82 + 83 + exit $EXIT_STATUS
+12
tools/testing/selftests/net/forwarding/config
··· 1 + CONFIG_BRIDGE=m 2 + CONFIG_VLAN_8021Q=m 3 + CONFIG_BRIDGE_VLAN_FILTERING=y 4 + CONFIG_NET_L3_MASTER_DEV=y 5 + CONFIG_IPV6_MULTIPLE_TABLES=y 6 + CONFIG_NET_VRF=m 7 + CONFIG_BPF_SYSCALL=y 8 + CONFIG_CGROUP_BPF=y 9 + CONFIG_NET_CLS_FLOWER=m 10 + CONFIG_NET_SCH_INGRESS=m 11 + CONFIG_NET_ACT_GACT=m 12 + CONFIG_VETH=m
+31
tools/testing/selftests/net/forwarding/forwarding.config.sample
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + ############################################################################## 5 + # Topology description. p1 looped back to p2, p3 to p4 and so on. 6 + declare -A NETIFS 7 + 8 + NETIFS[p1]=veth0 9 + NETIFS[p2]=veth1 10 + NETIFS[p3]=veth2 11 + NETIFS[p4]=veth3 12 + NETIFS[p5]=veth4 13 + NETIFS[p6]=veth5 14 + NETIFS[p7]=veth6 15 + NETIFS[p8]=veth7 16 + 17 + ############################################################################## 18 + # Defines 19 + 20 + # IPv4 ping utility name 21 + PING=ping 22 + # IPv6 ping utility name. Some distributions use 'ping' for IPv6. 23 + PING6=ping6 24 + # Packet generator. Some distributions use 'mz'. 25 + MZ=mausezahn 26 + # Time to wait after interfaces participating in the test are all UP 27 + WAIT_TIME=5 28 + # Whether to pause on failure or not. 29 + PAUSE_ON_FAIL=no 30 + # Whether to pause on cleanup or not. 31 + PAUSE_ON_CLEANUP=no
+282
tools/testing/selftests/net/forwarding/lib.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + ############################################################################## 5 + # Defines 6 + 7 + # Can be overridden by the configuration file. 8 + PING=${PING:=ping} 9 + PING6=${PING6:=ping6} 10 + WAIT_TIME=${WAIT_TIME:=5} 11 + PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 12 + PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} 13 + 14 + if [[ -f forwarding.config ]]; then 15 + source forwarding.config 16 + fi 17 + 18 + ############################################################################## 19 + # Sanity checks 20 + 21 + if [[ "$(id -u)" -ne 0 ]]; then 22 + echo "SKIP: need root privileges" 23 + exit 0 24 + fi 25 + 26 + tc -j &> /dev/null 27 + if [[ $? -ne 0 ]]; then 28 + echo "SKIP: iproute2 too old, missing JSON support" 29 + exit 0 30 + fi 31 + 32 + if [[ ! -x "$(command -v jq)" ]]; then 33 + echo "SKIP: jq not installed" 34 + exit 0 35 + fi 36 + 37 + if [[ ! -v NUM_NETIFS ]]; then 38 + echo "SKIP: importer does not define \"NUM_NETIFS\"" 39 + exit 0 40 + fi 41 + 42 + ############################################################################## 43 + # Network interfaces configuration 44 + 45 + for i in $(eval echo {1..$NUM_NETIFS}); do 46 + ip link show dev ${NETIFS[p$i]} &> /dev/null 47 + if [[ $? -ne 0 ]]; then 48 + echo "SKIP: could not find all required interfaces" 49 + exit 0 50 + fi 51 + done 52 + 53 + ############################################################################## 54 + # Helpers 55 + 56 + # Exit status to return at the end. Set in case one of the tests fails. 57 + EXIT_STATUS=0 58 + # Per-test return value. Clear at the beginning of each test. 59 + RET=0 60 + 61 + check_err() 62 + { 63 + local err=$1 64 + local msg=$2 65 + 66 + if [[ $RET -eq 0 && $err -ne 0 ]]; then 67 + RET=$err 68 + retmsg=$msg 69 + fi 70 + } 71 + 72 + check_fail() 73 + { 74 + local err=$1 75 + local msg=$2 76 + 77 + if [[ $RET -eq 0 && $err -eq 0 ]]; then 78 + RET=1 79 + retmsg=$msg 80 + fi 81 + } 82 + 83 + log_test() 84 + { 85 + local test_name=$1 86 + local opt_str=$2 87 + 88 + if [[ $# -eq 2 ]]; then 89 + opt_str="($opt_str)" 90 + fi 91 + 92 + if [[ $RET -ne 0 ]]; then 93 + EXIT_STATUS=1 94 + printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" 95 + if [[ ! -z "$retmsg" ]]; then 96 + printf "\t%s\n" "$retmsg" 97 + fi 98 + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 99 + echo "Hit enter to continue, 'q' to quit" 100 + read a 101 + [ "$a" = "q" ] && exit 1 102 + fi 103 + return 1 104 + fi 105 + 106 + printf "TEST: %-60s [PASS]\n" "$test_name $opt_str" 107 + return 0 108 + } 109 + 110 + setup_wait() 111 + { 112 + for i in $(eval echo {1..$NUM_NETIFS}); do 113 + while true; do 114 + ip link show dev ${NETIFS[p$i]} up \ 115 + | grep 'state UP' &> /dev/null 116 + if [[ $? -ne 0 ]]; then 117 + sleep 1 118 + else 119 + break 120 + fi 121 + done 122 + done 123 + 124 + # Make sure links are ready. 125 + sleep $WAIT_TIME 126 + } 127 + 128 + pre_cleanup() 129 + { 130 + if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 131 + echo "Pausing before cleanup, hit any key to continue" 132 + read 133 + fi 134 + } 135 + 136 + vrf_prepare() 137 + { 138 + ip -4 rule add pref 32765 table local 139 + ip -4 rule del pref 0 140 + ip -6 rule add pref 32765 table local 141 + ip -6 rule del pref 0 142 + } 143 + 144 + vrf_cleanup() 145 + { 146 + ip -6 rule add pref 0 table local 147 + ip -6 rule del pref 32765 148 + ip -4 rule add pref 0 table local 149 + ip -4 rule del pref 32765 150 + } 151 + 152 + __last_tb_id=0 153 + declare -A __TB_IDS 154 + 155 + __vrf_td_id_assign() 156 + { 157 + local vrf_name=$1 158 + 159 + __last_tb_id=$((__last_tb_id + 1)) 160 + __TB_IDS[$vrf_name]=$__last_tb_id 161 + return $__last_tb_id 162 + } 163 + 164 + __vrf_td_id_lookup() 165 + { 166 + local vrf_name=$1 167 + 168 + return ${__TB_IDS[$vrf_name]} 169 + } 170 + 171 + vrf_create() 172 + { 173 + local vrf_name=$1 174 + local tb_id 175 + 176 + __vrf_td_id_assign $vrf_name 177 + tb_id=$? 178 + 179 + ip link add dev $vrf_name type vrf table $tb_id 180 + ip -4 route add table $tb_id unreachable default metric 4278198272 181 + ip -6 route add table $tb_id unreachable default metric 4278198272 182 + } 183 + 184 + vrf_destroy() 185 + { 186 + local vrf_name=$1 187 + local tb_id 188 + 189 + __vrf_td_id_lookup $vrf_name 190 + tb_id=$? 191 + 192 + ip -6 route del table $tb_id unreachable default metric 4278198272 193 + ip -4 route del table $tb_id unreachable default metric 4278198272 194 + ip link del dev $vrf_name 195 + } 196 + 197 + __addr_add_del() 198 + { 199 + local if_name=$1 200 + local add_del=$2 201 + local array 202 + 203 + shift 204 + shift 205 + array=("${@}") 206 + 207 + for addrstr in "${array[@]}"; do 208 + ip address $add_del $addrstr dev $if_name 209 + done 210 + } 211 + 212 + simple_if_init() 213 + { 214 + local if_name=$1 215 + local vrf_name 216 + local array 217 + 218 + shift 219 + vrf_name=v$if_name 220 + array=("${@}") 221 + 222 + vrf_create $vrf_name 223 + ip link set dev $if_name master $vrf_name 224 + ip link set dev $vrf_name up 225 + ip link set dev $if_name up 226 + 227 + __addr_add_del $if_name add "${array[@]}" 228 + } 229 + 230 + simple_if_fini() 231 + { 232 + local if_name=$1 233 + local vrf_name 234 + local array 235 + 236 + shift 237 + vrf_name=v$if_name 238 + array=("${@}") 239 + 240 + __addr_add_del $if_name del "${array[@]}" 241 + 242 + ip link set dev $if_name down 243 + vrf_destroy $vrf_name 244 + } 245 + 246 + master_name_get() 247 + { 248 + local if_name=$1 249 + 250 + ip -j link show dev $if_name | jq -r '.[]["master"]' 251 + } 252 + 253 + ############################################################################## 254 + # Tests 255 + 256 + ping_test() 257 + { 258 + local if_name=$1 259 + local dip=$2 260 + local vrf_name 261 + 262 + RET=0 263 + 264 + vrf_name=$(master_name_get $if_name) 265 + ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null 266 + check_err $? 267 + log_test "ping" 268 + } 269 + 270 + ping6_test() 271 + { 272 + local if_name=$1 273 + local dip=$2 274 + local vrf_name 275 + 276 + RET=0 277 + 278 + vrf_name=$(master_name_get $if_name) 279 + ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null 280 + check_err $? 281 + log_test "ping6" 282 + }