1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# This test is for checking devlink-trap functionality. It makes use of
5# netdevsim which implements the required callbacks.
6
7lib_dir=$(dirname $0)/../../../net/forwarding
8
9ALL_TESTS="
10	init_test
11	trap_action_test
12	trap_metadata_test
13	bad_trap_test
14	bad_trap_action_test
15	trap_stats_test
16	trap_group_action_test
17	bad_trap_group_test
18	trap_group_stats_test
19	trap_policer_test
20	trap_policer_bind_test
21	port_del_test
22	dev_del_test
23"
24NETDEVSIM_PATH=/sys/bus/netdevsim/
25DEV_ADDR=1337
26DEV=netdevsim${DEV_ADDR}
27DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/
28SLEEP_TIME=1
29NETDEV=""
30NUM_NETIFS=0
31source $lib_dir/lib.sh
32
33DEVLINK_DEV=
34source $lib_dir/devlink_lib.sh
35DEVLINK_DEV=netdevsim/${DEV}
36
37require_command udevadm
38
39modprobe netdevsim &> /dev/null
40if [ ! -d "$NETDEVSIM_PATH" ]; then
41	echo "SKIP: No netdevsim support"
42	exit 1
43fi
44
45if [ -d "${NETDEVSIM_PATH}/devices/netdevsim${DEV_ADDR}" ]; then
46	echo "SKIP: Device netdevsim${DEV_ADDR} already exists"
47	exit 1
48fi
49
50init_test()
51{
52	RET=0
53
54	test $(devlink_traps_num_get) -ne 0
55	check_err $? "No traps were registered"
56
57	log_test "Initialization"
58}
59
60trap_action_test()
61{
62	local orig_action
63	local trap_name
64	local action
65
66	RET=0
67
68	for trap_name in $(devlink_traps_get); do
69		# The action of non-drop traps cannot be changed.
70		if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
71			devlink_trap_action_set $trap_name "trap"
72			action=$(devlink_trap_action_get $trap_name)
73			if [ $action != "trap" ]; then
74				check_err 1 "Trap $trap_name did not change action to trap"
75			fi
76
77			devlink_trap_action_set $trap_name "drop"
78			action=$(devlink_trap_action_get $trap_name)
79			if [ $action != "drop" ]; then
80				check_err 1 "Trap $trap_name did not change action to drop"
81			fi
82		else
83			orig_action=$(devlink_trap_action_get $trap_name)
84
85			devlink_trap_action_set $trap_name "trap"
86			action=$(devlink_trap_action_get $trap_name)
87			if [ $action != $orig_action ]; then
88				check_err 1 "Trap $trap_name changed action when should not"
89			fi
90
91			devlink_trap_action_set $trap_name "drop"
92			action=$(devlink_trap_action_get $trap_name)
93			if [ $action != $orig_action ]; then
94				check_err 1 "Trap $trap_name changed action when should not"
95			fi
96		fi
97	done
98
99	log_test "Trap action"
100}
101
102trap_metadata_test()
103{
104	local trap_name
105
106	RET=0
107
108	for trap_name in $(devlink_traps_get); do
109		devlink_trap_metadata_test $trap_name "input_port"
110		check_err $? "Input port not reported as metadata of trap $trap_name"
111		if [ $trap_name == "ingress_flow_action_drop" ] ||
112		   [ $trap_name == "egress_flow_action_drop" ]; then
113			devlink_trap_metadata_test $trap_name "flow_action_cookie"
114			check_err $? "Flow action cookie not reported as metadata of trap $trap_name"
115		fi
116	done
117
118	log_test "Trap metadata"
119}
120
121bad_trap_test()
122{
123	RET=0
124
125	devlink_trap_action_set "made_up_trap" "drop"
126	check_fail $? "Did not get an error for non-existing trap"
127
128	log_test "Non-existing trap"
129}
130
131bad_trap_action_test()
132{
133	local traps_arr
134	local trap_name
135
136	RET=0
137
138	# Pick first trap.
139	traps_arr=($(devlink_traps_get))
140	trap_name=${traps_arr[0]}
141
142	devlink_trap_action_set $trap_name "made_up_action"
143	check_fail $? "Did not get an error for non-existing trap action"
144
145	log_test "Non-existing trap action"
146}
147
148trap_stats_test()
149{
150	local trap_name
151
152	RET=0
153
154	for trap_name in $(devlink_traps_get); do
155		devlink_trap_stats_idle_test $trap_name
156		check_err $? "Stats of trap $trap_name not idle when netdev down"
157
158		ip link set dev $NETDEV up
159
160		if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
161			devlink_trap_action_set $trap_name "trap"
162			devlink_trap_stats_idle_test $trap_name
163			check_fail $? "Stats of trap $trap_name idle when action is trap"
164
165			devlink_trap_action_set $trap_name "drop"
166			devlink_trap_stats_idle_test $trap_name
167			check_err $? "Stats of trap $trap_name not idle when action is drop"
168
169			echo "y"> $DEBUGFS_DIR/fail_trap_drop_counter_get
170			devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null
171			check_fail $? "Managed to read trap (hard dropped) statistics when should not"
172			echo "n"> $DEBUGFS_DIR/fail_trap_drop_counter_get
173			devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null
174			check_err $? "Did not manage to read trap (hard dropped) statistics when should"
175
176			devlink_trap_drop_stats_idle_test $trap_name
177			check_fail $? "Drop stats of trap $trap_name idle when should not"
178		else
179			devlink_trap_stats_idle_test $trap_name
180			check_fail $? "Stats of non-drop trap $trap_name idle when should not"
181		fi
182
183		ip link set dev $NETDEV down
184	done
185
186	log_test "Trap statistics"
187}
188
189trap_group_action_test()
190{
191	local curr_group group_name
192	local trap_name
193	local trap_type
194	local action
195
196	RET=0
197
198	for group_name in $(devlink_trap_groups_get); do
199		devlink_trap_group_action_set $group_name "trap"
200
201		for trap_name in $(devlink_traps_get); do
202			curr_group=$(devlink_trap_group_get $trap_name)
203			if [ $curr_group != $group_name ]; then
204				continue
205			fi
206
207			trap_type=$(devlink_trap_type_get $trap_name)
208			if [ $trap_type != "drop" ]; then
209				continue
210			fi
211
212			action=$(devlink_trap_action_get $trap_name)
213			if [ $action != "trap" ]; then
214				check_err 1 "Trap $trap_name did not change action to trap"
215			fi
216		done
217
218		devlink_trap_group_action_set $group_name "drop"
219
220		for trap_name in $(devlink_traps_get); do
221			curr_group=$(devlink_trap_group_get $trap_name)
222			if [ $curr_group != $group_name ]; then
223				continue
224			fi
225
226			trap_type=$(devlink_trap_type_get $trap_name)
227			if [ $trap_type != "drop" ]; then
228				continue
229			fi
230
231			action=$(devlink_trap_action_get $trap_name)
232			if [ $action != "drop" ]; then
233				check_err 1 "Trap $trap_name did not change action to drop"
234			fi
235		done
236	done
237
238	log_test "Trap group action"
239}
240
241bad_trap_group_test()
242{
243	RET=0
244
245	devlink_trap_group_action_set "made_up_trap_group" "drop"
246	check_fail $? "Did not get an error for non-existing trap group"
247
248	log_test "Non-existing trap group"
249}
250
251trap_group_stats_test()
252{
253	local group_name
254
255	RET=0
256
257	for group_name in $(devlink_trap_groups_get); do
258		devlink_trap_group_stats_idle_test $group_name
259		check_err $? "Stats of trap group $group_name not idle when netdev down"
260
261		ip link set dev $NETDEV up
262
263		devlink_trap_group_action_set $group_name "trap"
264		devlink_trap_group_stats_idle_test $group_name
265		check_fail $? "Stats of trap group $group_name idle when action is trap"
266
267		devlink_trap_group_action_set $group_name "drop"
268		ip link set dev $NETDEV down
269	done
270
271	log_test "Trap group statistics"
272}
273
274trap_policer_test()
275{
276	local packets_t0
277	local packets_t1
278
279	RET=0
280
281	if [ $(devlink_trap_policers_num_get) -eq 0 ]; then
282		check_err 1 "Failed to dump policers"
283	fi
284
285	devlink trap policer set $DEVLINK_DEV policer 1337 &> /dev/null
286	check_fail $? "Did not get an error for setting a non-existing policer"
287	devlink trap policer show $DEVLINK_DEV policer 1337 &> /dev/null
288	check_fail $? "Did not get an error for getting a non-existing policer"
289
290	devlink trap policer set $DEVLINK_DEV policer 1 rate 2000 burst 16
291	check_err $? "Failed to set valid parameters for a valid policer"
292	if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
293		check_err 1 "Policer rate was not changed"
294	fi
295	if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
296		check_err 1 "Policer burst size was not changed"
297	fi
298
299	devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null
300	check_fail $? "Policer rate was changed to rate lower than limit"
301	devlink trap policer set $DEVLINK_DEV policer 1 rate 9000 &> /dev/null
302	check_fail $? "Policer rate was changed to rate higher than limit"
303	devlink trap policer set $DEVLINK_DEV policer 1 burst 2 &> /dev/null
304	check_fail $? "Policer burst size was changed to burst size lower than limit"
305	devlink trap policer set $DEVLINK_DEV policer 1 rate 65537 &> /dev/null
306	check_fail $? "Policer burst size was changed to burst size higher than limit"
307	echo "y" > $DEBUGFS_DIR/fail_trap_policer_set
308	devlink trap policer set $DEVLINK_DEV policer 1 rate 3000 &> /dev/null
309	check_fail $? "Managed to set policer rate when should not"
310	echo "n" > $DEBUGFS_DIR/fail_trap_policer_set
311	if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
312		check_err 1 "Policer rate was changed to an invalid value"
313	fi
314	if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
315		check_err 1 "Policer burst size was changed to an invalid value"
316	fi
317
318	packets_t0=$(devlink_trap_policer_rx_dropped_get 1)
319	sleep .5
320	packets_t1=$(devlink_trap_policer_rx_dropped_get 1)
321	if [ ! $packets_t1 -gt $packets_t0 ]; then
322		check_err 1 "Policer drop counter was not incremented"
323	fi
324
325	echo "y"> $DEBUGFS_DIR/fail_trap_policer_counter_get
326	devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
327	check_fail $? "Managed to read policer drop counter when should not"
328	echo "n"> $DEBUGFS_DIR/fail_trap_policer_counter_get
329	devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
330	check_err $? "Did not manage to read policer drop counter when should"
331
332	log_test "Trap policer"
333}
334
335trap_group_check_policer()
336{
337	local group_name=$1; shift
338
339	devlink -j -p trap group show $DEVLINK_DEV group $group_name \
340		| jq -e '.[][][]["policer"]' &> /dev/null
341}
342
343trap_policer_bind_test()
344{
345	RET=0
346
347	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
348	check_err $? "Failed to bind a valid policer"
349	if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
350		check_err 1 "Bound policer was not changed"
351	fi
352
353	devlink trap group set $DEVLINK_DEV group l2_drops policer 1337 \
354		&> /dev/null
355	check_fail $? "Did not get an error for binding a non-existing policer"
356	if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
357		check_err 1 "Bound policer was changed when should not"
358	fi
359
360	devlink trap group set $DEVLINK_DEV group l2_drops policer 0
361	check_err $? "Failed to unbind a policer when using ID 0"
362	trap_group_check_policer "l2_drops"
363	check_fail $? "Trap group has a policer after unbinding with ID 0"
364
365	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
366	check_err $? "Failed to bind a valid policer"
367
368	devlink trap group set $DEVLINK_DEV group l2_drops nopolicer
369	check_err $? "Failed to unbind a policer when using 'nopolicer' keyword"
370	trap_group_check_policer "l2_drops"
371	check_fail $? "Trap group has a policer after unbinding with 'nopolicer' keyword"
372
373	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
374	check_err $? "Failed to bind a valid policer"
375
376	echo "y"> $DEBUGFS_DIR/fail_trap_group_set
377	devlink trap group set $DEVLINK_DEV group l2_drops policer 2 \
378		&> /dev/null
379	check_fail $? "Managed to bind a policer when should not"
380	echo "n"> $DEBUGFS_DIR/fail_trap_group_set
381	devlink trap group set $DEVLINK_DEV group l2_drops policer 2
382	check_err $? "Did not manage to bind a policer when should"
383
384	devlink trap group set $DEVLINK_DEV group l2_drops action drop \
385		policer 1337 &> /dev/null
386	check_fail $? "Did not get an error for partially modified trap group"
387
388	log_test "Trap policer binding"
389}
390
391port_del_test()
392{
393	local group_name
394	local i
395
396	# The test never fails. It is meant to exercise different code paths
397	# and make sure we properly dismantle a port while packets are
398	# in-flight.
399	RET=0
400
401	devlink_traps_enable_all
402
403	for i in $(seq 1 10); do
404		ip link set dev $NETDEV up
405
406		sleep $SLEEP_TIME
407
408		netdevsim_port_destroy
409		netdevsim_port_create
410		udevadm settle
411	done
412
413	devlink_traps_disable_all
414
415	log_test "Port delete"
416}
417
418dev_del_test()
419{
420	local group_name
421	local i
422
423	# The test never fails. It is meant to exercise different code paths
424	# and make sure we properly unregister traps while packets are
425	# in-flight.
426	RET=0
427
428	devlink_traps_enable_all
429
430	for i in $(seq 1 10); do
431		ip link set dev $NETDEV up
432
433		sleep $SLEEP_TIME
434
435		cleanup
436		setup_prepare
437	done
438
439	devlink_traps_disable_all
440
441	log_test "Device delete"
442}
443
444netdevsim_dev_create()
445{
446	echo "$DEV_ADDR 0" > ${NETDEVSIM_PATH}/new_device
447}
448
449netdevsim_dev_destroy()
450{
451	echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device
452}
453
454netdevsim_port_create()
455{
456	echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/new_port
457}
458
459netdevsim_port_destroy()
460{
461	echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/del_port
462}
463
464setup_prepare()
465{
466	local netdev
467
468	netdevsim_dev_create
469
470	if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}" ]; then
471		echo "Failed to create netdevsim device"
472		exit 1
473	fi
474
475	netdevsim_port_create
476
477	if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}/net/" ]; then
478		echo "Failed to create netdevsim port"
479		exit 1
480	fi
481
482	# Wait for udev to rename newly created netdev.
483	udevadm settle
484
485	NETDEV=$(ls ${NETDEVSIM_PATH}/devices/${DEV}/net/)
486}
487
488cleanup()
489{
490	pre_cleanup
491	netdevsim_port_destroy
492	netdevsim_dev_destroy
493}
494
495trap cleanup EXIT
496
497setup_prepare
498
499tests_run
500
501exit $EXIT_STATUS
502