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