diff options
author | Ido Schimmel <idosch@mellanox.com> | 2018-02-28 05:25:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-02-28 12:25:47 -0500 |
commit | 73bae6736b6b577b52fbba2d4b71a5a3e9920fb5 (patch) | |
tree | d43ffba823395681367b3db91d9e1aec0b9a2a7a | |
parent | 8230819494b3bf284ca7262ac5f877333147b937 (diff) |
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>
-rw-r--r-- | tools/testing/selftests/net/forwarding/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/net/forwarding/README | 56 | ||||
-rwxr-xr-x | tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh | 83 | ||||
-rw-r--r-- | tools/testing/selftests/net/forwarding/config | 12 | ||||
-rw-r--r-- | tools/testing/selftests/net/forwarding/forwarding.config.sample | 31 | ||||
-rw-r--r-- | tools/testing/selftests/net/forwarding/lib.sh | 282 |
6 files changed, 465 insertions, 0 deletions
diff --git a/tools/testing/selftests/net/forwarding/.gitignore b/tools/testing/selftests/net/forwarding/.gitignore new file mode 100644 index 000000000000..a793eef5b876 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/.gitignore | |||
@@ -0,0 +1 @@ | |||
forwarding.config | |||
diff --git a/tools/testing/selftests/net/forwarding/README b/tools/testing/selftests/net/forwarding/README new file mode 100644 index 000000000000..4a0964c42860 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/README | |||
@@ -0,0 +1,56 @@ | |||
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/ | ||
diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh new file mode 100755 index 000000000000..e6faa9548460 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh | |||
@@ -0,0 +1,83 @@ | |||
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 | ||
diff --git a/tools/testing/selftests/net/forwarding/config b/tools/testing/selftests/net/forwarding/config new file mode 100644 index 000000000000..5cd2aed97958 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/config | |||
@@ -0,0 +1,12 @@ | |||
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 | ||
diff --git a/tools/testing/selftests/net/forwarding/forwarding.config.sample b/tools/testing/selftests/net/forwarding/forwarding.config.sample new file mode 100644 index 000000000000..ab235c124f20 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/forwarding.config.sample | |||
@@ -0,0 +1,31 @@ | |||
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 | ||
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh new file mode 100644 index 000000000000..ff6550e9ddaf --- /dev/null +++ b/tools/testing/selftests/net/forwarding/lib.sh | |||
@@ -0,0 +1,282 @@ | |||
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 | } | ||