xref: /linux/tools/testing/selftests/net/hsr/link_faults.sh (revision 8908c3c8cef437d8d2ad41f9b23f4305029d1782)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# shellcheck disable=SC2329
4
5source ../lib.sh
6
7ALL_TESTS="
8	test_clean_hsrv0
9	test_cut_link_hsrv0
10	test_clean_hsrv1
11	test_cut_link_hsrv1
12	test_clean_prp
13	test_cut_link_prp
14	test_packet_loss_prp
15	test_high_packet_loss_prp
16	test_reordering_prp
17"
18
19# The tests are running ping for 5sec with a relatively short interval in
20# different scenarios with faulty links (cut links, packet loss, delay,
21# reordering) that should be recoverable by HSR/PRP. The ping interval (10ms)
22# is short enough that the base delay (50ms) leads to a queue in the netem
23# qdiscs which is needed for reordering.
24
25setup_hsr_topo()
26{
27	# Three HSR nodes in a ring, every node has a LAN A interface connected
28	# to the LAN B interface of the next node.
29	#
30	#    node1            node2
31	#
32	#     vethA -------- vethB
33	#   hsr1                 hsr2
34	#     vethB          vethA
35	#         \          /
36	#         vethA  vethB
37	#             hsr3
38	#
39	#            node3
40
41	local ver="$1"
42
43	setup_ns node1 node2 node3
44
45	# veth links
46	# shellcheck disable=SC2154 # variables assigned by setup_ns
47	ip link add vethA netns "$node1" type veth peer name vethB netns "$node2"
48	# shellcheck disable=SC2154 # variables assigned by setup_ns
49	ip link add vethA netns "$node2" type veth peer name vethB netns "$node3"
50	ip link add vethA netns "$node3" type veth peer name vethB netns "$node1"
51
52	# MAC addresses (not needed for HSR operation, but helps with debugging)
53	ip -net "$node1" link set address 00:11:22:00:01:01 dev vethA
54	ip -net "$node1" link set address 00:11:22:00:01:02 dev vethB
55
56	ip -net "$node2" link set address 00:11:22:00:02:01 dev vethA
57	ip -net "$node2" link set address 00:11:22:00:02:02 dev vethB
58
59	ip -net "$node3" link set address 00:11:22:00:03:01 dev vethA
60	ip -net "$node3" link set address 00:11:22:00:03:02 dev vethB
61
62	# HSR interfaces
63	ip -net "$node1" link add name hsr1 type hsr proto 0 version "$ver" \
64		slave1 vethA slave2 vethB supervision 45
65	ip -net "$node2" link add name hsr2 type hsr proto 0 version "$ver" \
66		slave1 vethA slave2 vethB supervision 45
67	ip -net "$node3" link add name hsr3 type hsr proto 0 version "$ver" \
68		slave1 vethA slave2 vethB supervision 45
69
70	# IP addresses
71	ip -net "$node1" addr add 100.64.0.1/24 dev hsr1
72	ip -net "$node2" addr add 100.64.0.2/24 dev hsr2
73	ip -net "$node3" addr add 100.64.0.3/24 dev hsr3
74
75	# Set all links up
76	ip -net "$node1" link set vethA up
77	ip -net "$node1" link set vethB up
78	ip -net "$node1" link set hsr1 up
79
80	ip -net "$node2" link set vethA up
81	ip -net "$node2" link set vethB up
82	ip -net "$node2" link set hsr2 up
83
84	ip -net "$node3" link set vethA up
85	ip -net "$node3" link set vethB up
86	ip -net "$node3" link set hsr3 up
87}
88
89setup_prp_topo()
90{
91	# Two PRP nodes, connected by two links (treated as LAN A and LAN B).
92	#
93	#       vethA ----- vethA
94	#     prp1             prp2
95	#       vethB ----- vethB
96	#
97	#     node1           node2
98
99	setup_ns node1 node2
100
101	# veth links
102	ip link add vethA netns "$node1" type veth peer name vethA netns "$node2"
103	ip link add vethB netns "$node1" type veth peer name vethB netns "$node2"
104
105	# MAC addresses will be copied from LAN A interface
106	ip -net "$node1" link set address 00:11:22:00:00:01 dev vethA
107	ip -net "$node2" link set address 00:11:22:00:00:02 dev vethA
108
109	# PRP interfaces
110	ip -net "$node1" link add name prp1 type hsr \
111		slave1 vethA slave2 vethB supervision 45 proto 1
112	ip -net "$node2" link add name prp2 type hsr \
113		slave1 vethA slave2 vethB supervision 45 proto 1
114
115	# IP addresses
116	ip -net "$node1" addr add 100.64.0.1/24 dev prp1
117	ip -net "$node2" addr add 100.64.0.2/24 dev prp2
118
119	# All links up
120	ip -net "$node1" link set vethA up
121	ip -net "$node1" link set vethB up
122	ip -net "$node1" link set prp1 up
123
124	ip -net "$node2" link set vethA up
125	ip -net "$node2" link set vethB up
126	ip -net "$node2" link set prp2 up
127}
128
129wait_for_hsr_node_table()
130{
131	log_info "Wait for node table entries to be merged."
132	WAIT=5
133	while [ "${WAIT}" -gt 0 ]; do
134		nts=$(cat /sys/kernel/debug/hsr/hsr*/node_table)
135
136		# We need entries in the node tables, and they need to be merged
137		if (echo "$nts" | grep -qE "^([0-9a-f]{2}:){5}") && \
138		    ! (echo "$nts" | grep -q "00:00:00:00:00:00"); then
139			return
140		fi
141
142		sleep 1
143		((WAIT--))
144	done
145	check_err 1 "Failed to wait for merged node table entries"
146}
147
148setup_topo()
149{
150	local proto="$1"
151
152	if [ "$proto" = "HSRv0" ]; then
153		setup_hsr_topo 0
154		wait_for_hsr_node_table
155	elif [ "$proto" = "HSRv1" ]; then
156		setup_hsr_topo 1
157		wait_for_hsr_node_table
158	elif [ "$proto" = "PRP" ]; then
159		setup_prp_topo
160	else
161		check_err 1 "Unknown protocol (${proto})"
162	fi
163}
164
165check_ping()
166{
167	local node="$1"
168	local dst="$2"
169	local accepted_dups="$3"
170	local ping_args="-q -i 0.01 -c 400"
171
172	log_info "Running ping $node -> $dst"
173	# shellcheck disable=SC2086
174	output=$(ip netns exec "$node" ping $ping_args "$dst" | \
175		grep "packets transmitted")
176	log_info "$output"
177
178	dups=0
179	loss=0
180
181	if [[ "$output" =~ \+([0-9]+)" duplicates" ]]; then
182		dups="${BASH_REMATCH[1]}"
183	fi
184	if [[ "$output" =~ ([0-9\.]+\%)" packet loss" ]]; then
185		loss="${BASH_REMATCH[1]}"
186	fi
187
188	if [ "$dups" -gt "$accepted_dups" ]; then
189		check_err 1 "Unexpected duplicate packets (${dups})"
190	fi
191	if [ "$loss" != "0%" ]; then
192		check_err 1 "Unexpected packet loss (${loss})"
193	fi
194}
195
196test_clean()
197{
198	local proto="$1"
199
200	RET=0
201	tname="${FUNCNAME[0]} - ${proto}"
202
203	setup_topo "$proto"
204	if ((RET != ksft_pass)); then
205		log_test "${tname} setup"
206		return
207	fi
208
209	check_ping "$node1" "100.64.0.2" 0
210
211	log_test "${tname}"
212}
213
214test_clean_hsrv0()
215{
216	test_clean "HSRv0"
217}
218
219test_clean_hsrv1()
220{
221	test_clean "HSRv1"
222}
223
224test_clean_prp()
225{
226	test_clean "PRP"
227}
228
229test_cut_link()
230{
231	local proto="$1"
232
233	RET=0
234	tname="${FUNCNAME[0]} - ${proto}"
235
236	setup_topo "$proto"
237	if ((RET != ksft_pass)); then
238		log_test "${tname} setup"
239		return
240	fi
241
242	# Cutting link from subshell, so check_ping can run in the normal shell
243	# with access to global variables from the test harness.
244	(
245		sleep 2
246		log_info "Cutting link"
247		ip -net "$node1" link set vethB down
248	) &
249	check_ping "$node1" "100.64.0.2" 0
250
251	wait
252	log_test "${tname}"
253}
254
255
256test_cut_link_hsrv0()
257{
258	test_cut_link "HSRv0"
259}
260
261test_cut_link_hsrv1()
262{
263	test_cut_link "HSRv1"
264}
265
266test_cut_link_prp()
267{
268	test_cut_link "PRP"
269}
270
271test_packet_loss()
272{
273	local proto="$1"
274	local loss="$2"
275
276	RET=0
277	tname="${FUNCNAME[0]} - ${proto}, ${loss}"
278
279	setup_topo "$proto"
280	if ((RET != ksft_pass)); then
281		log_test "${tname} setup"
282		return
283	fi
284
285	# Packet loss with lower delay makes sure the packets on the lossy link
286	# arrive first.
287	tc -net "$node1" qdisc add dev vethA root netem delay 50ms
288	tc -net "$node1" qdisc add dev vethB root netem delay 20ms loss "$loss"
289
290	check_ping "$node1" "100.64.0.2" 40
291
292	log_test "${tname}"
293}
294
295test_packet_loss_prp()
296{
297	test_packet_loss "PRP" "20%"
298}
299
300test_high_packet_loss_prp()
301{
302	test_packet_loss "PRP" "80%"
303}
304
305test_reordering()
306{
307	local proto="$1"
308
309	RET=0
310	tname="${FUNCNAME[0]} - ${proto}"
311
312	setup_topo "$proto"
313	if ((RET != ksft_pass)); then
314		log_test "${tname} setup"
315		return
316	fi
317
318	tc -net "$node1" qdisc add dev vethA root netem delay 50ms
319	tc -net "$node1" qdisc add dev vethB root netem delay 50ms reorder 20%
320
321	check_ping "$node1" "100.64.0.2" 40
322
323	log_test "${tname}"
324}
325
326test_reordering_prp()
327{
328	test_reordering "PRP"
329}
330
331cleanup()
332{
333	cleanup_all_ns
334}
335
336trap cleanup EXIT
337
338tests_run
339
340exit $EXIT_STATUS
341