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		else
169			devlink_trap_stats_idle_test $trap_name
170			check_fail $? "Stats of non-drop trap $trap_name idle when should not"
171		fi
172
173		ip link set dev $NETDEV down
174	done
175
176	log_test "Trap statistics"
177}
178
179trap_group_action_test()
180{
181	local curr_group group_name
182	local trap_name
183	local trap_type
184	local action
185
186	RET=0
187
188	for group_name in $(devlink_trap_groups_get); do
189		devlink_trap_group_action_set $group_name "trap"
190
191		for trap_name in $(devlink_traps_get); do
192			curr_group=$(devlink_trap_group_get $trap_name)
193			if [ $curr_group != $group_name ]; then
194				continue
195			fi
196
197			trap_type=$(devlink_trap_type_get $trap_name)
198			if [ $trap_type != "drop" ]; then
199				continue
200			fi
201
202			action=$(devlink_trap_action_get $trap_name)
203			if [ $action != "trap" ]; then
204				check_err 1 "Trap $trap_name did not change action to trap"
205			fi
206		done
207
208		devlink_trap_group_action_set $group_name "drop"
209
210		for trap_name in $(devlink_traps_get); do
211			curr_group=$(devlink_trap_group_get $trap_name)
212			if [ $curr_group != $group_name ]; then
213				continue
214			fi
215
216			trap_type=$(devlink_trap_type_get $trap_name)
217			if [ $trap_type != "drop" ]; then
218				continue
219			fi
220
221			action=$(devlink_trap_action_get $trap_name)
222			if [ $action != "drop" ]; then
223				check_err 1 "Trap $trap_name did not change action to drop"
224			fi
225		done
226	done
227
228	log_test "Trap group action"
229}
230
231bad_trap_group_test()
232{
233	RET=0
234
235	devlink_trap_group_action_set "made_up_trap_group" "drop"
236	check_fail $? "Did not get an error for non-existing trap group"
237
238	log_test "Non-existing trap group"
239}
240
241trap_group_stats_test()
242{
243	local group_name
244
245	RET=0
246
247	for group_name in $(devlink_trap_groups_get); do
248		devlink_trap_group_stats_idle_test $group_name
249		check_err $? "Stats of trap group $group_name not idle when netdev down"
250
251		ip link set dev $NETDEV up
252
253		devlink_trap_group_action_set $group_name "trap"
254		devlink_trap_group_stats_idle_test $group_name
255		check_fail $? "Stats of trap group $group_name idle when action is trap"
256
257		devlink_trap_group_action_set $group_name "drop"
258		ip link set dev $NETDEV down
259	done
260
261	log_test "Trap group statistics"
262}
263
264trap_policer_test()
265{
266	local packets_t0
267	local packets_t1
268
269	RET=0
270
271	if [ $(devlink_trap_policers_num_get) -eq 0 ]; then
272		check_err 1 "Failed to dump policers"
273	fi
274
275	devlink trap policer set $DEVLINK_DEV policer 1337 &> /dev/null
276	check_fail $? "Did not get an error for setting a non-existing policer"
277	devlink trap policer show $DEVLINK_DEV policer 1337 &> /dev/null
278	check_fail $? "Did not get an error for getting a non-existing policer"
279
280	devlink trap policer set $DEVLINK_DEV policer 1 rate 2000 burst 16
281	check_err $? "Failed to set valid parameters for a valid policer"
282	if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
283		check_err 1 "Policer rate was not changed"
284	fi
285	if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
286		check_err 1 "Policer burst size was not changed"
287	fi
288
289	devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null
290	check_fail $? "Policer rate was changed to rate lower than limit"
291	devlink trap policer set $DEVLINK_DEV policer 1 rate 9000 &> /dev/null
292	check_fail $? "Policer rate was changed to rate higher than limit"
293	devlink trap policer set $DEVLINK_DEV policer 1 burst 2 &> /dev/null
294	check_fail $? "Policer burst size was changed to burst size lower than limit"
295	devlink trap policer set $DEVLINK_DEV policer 1 rate 65537 &> /dev/null
296	check_fail $? "Policer burst size was changed to burst size higher than limit"
297	echo "y" > $DEBUGFS_DIR/fail_trap_policer_set
298	devlink trap policer set $DEVLINK_DEV policer 1 rate 3000 &> /dev/null
299	check_fail $? "Managed to set policer rate when should not"
300	echo "n" > $DEBUGFS_DIR/fail_trap_policer_set
301	if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
302		check_err 1 "Policer rate was changed to an invalid value"
303	fi
304	if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
305		check_err 1 "Policer burst size was changed to an invalid value"
306	fi
307
308	packets_t0=$(devlink_trap_policer_rx_dropped_get 1)
309	sleep .5
310	packets_t1=$(devlink_trap_policer_rx_dropped_get 1)
311	if [ ! $packets_t1 -gt $packets_t0 ]; then
312		check_err 1 "Policer drop counter was not incremented"
313	fi
314
315	echo "y"> $DEBUGFS_DIR/fail_trap_policer_counter_get
316	devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
317	check_fail $? "Managed to read policer drop counter when should not"
318	echo "n"> $DEBUGFS_DIR/fail_trap_policer_counter_get
319	devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
320	check_err $? "Did not manage to read policer drop counter when should"
321
322	log_test "Trap policer"
323}
324
325trap_group_check_policer()
326{
327	local group_name=$1; shift
328
329	devlink -j -p trap group show $DEVLINK_DEV group $group_name \
330		| jq -e '.[][][]["policer"]' &> /dev/null
331}
332
333trap_policer_bind_test()
334{
335	RET=0
336
337	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
338	check_err $? "Failed to bind a valid policer"
339	if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
340		check_err 1 "Bound policer was not changed"
341	fi
342
343	devlink trap group set $DEVLINK_DEV group l2_drops policer 1337 \
344		&> /dev/null
345	check_fail $? "Did not get an error for binding a non-existing policer"
346	if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
347		check_err 1 "Bound policer was changed when should not"
348	fi
349
350	devlink trap group set $DEVLINK_DEV group l2_drops policer 0
351	check_err $? "Failed to unbind a policer when using ID 0"
352	trap_group_check_policer "l2_drops"
353	check_fail $? "Trap group has a policer after unbinding with ID 0"
354
355	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
356	check_err $? "Failed to bind a valid policer"
357
358	devlink trap group set $DEVLINK_DEV group l2_drops nopolicer
359	check_err $? "Failed to unbind a policer when using 'nopolicer' keyword"
360	trap_group_check_policer "l2_drops"
361	check_fail $? "Trap group has a policer after unbinding with 'nopolicer' keyword"
362
363	devlink trap group set $DEVLINK_DEV group l2_drops policer 1
364	check_err $? "Failed to bind a valid policer"
365
366	echo "y"> $DEBUGFS_DIR/fail_trap_group_set
367	devlink trap group set $DEVLINK_DEV group l2_drops policer 2 \
368		&> /dev/null
369	check_fail $? "Managed to bind a policer when should not"
370	echo "n"> $DEBUGFS_DIR/fail_trap_group_set
371	devlink trap group set $DEVLINK_DEV group l2_drops policer 2
372	check_err $? "Did not manage to bind a policer when should"
373
374	devlink trap group set $DEVLINK_DEV group l2_drops action drop \
375		policer 1337 &> /dev/null
376	check_fail $? "Did not get an error for partially modified trap group"
377
378	log_test "Trap policer binding"
379}
380
381port_del_test()
382{
383	local group_name
384	local i
385
386	# The test never fails. It is meant to exercise different code paths
387	# and make sure we properly dismantle a port while packets are
388	# in-flight.
389	RET=0
390
391	devlink_traps_enable_all
392
393	for i in $(seq 1 10); do
394		ip link set dev $NETDEV up
395
396		sleep $SLEEP_TIME
397
398		netdevsim_port_destroy
399		netdevsim_port_create
400		udevadm settle
401	done
402
403	devlink_traps_disable_all
404
405	log_test "Port delete"
406}
407
408dev_del_test()
409{
410	local group_name
411	local i
412
413	# The test never fails. It is meant to exercise different code paths
414	# and make sure we properly unregister traps while packets are
415	# in-flight.
416	RET=0
417
418	devlink_traps_enable_all
419
420	for i in $(seq 1 10); do
421		ip link set dev $NETDEV up
422
423		sleep $SLEEP_TIME
424
425		cleanup
426		setup_prepare
427	done
428
429	devlink_traps_disable_all
430
431	log_test "Device delete"
432}
433
434netdevsim_dev_create()
435{
436	echo "$DEV_ADDR 0" > ${NETDEVSIM_PATH}/new_device
437}
438
439netdevsim_dev_destroy()
440{
441	echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device
442}
443
444netdevsim_port_create()
445{
446	echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/new_port
447}
448
449netdevsim_port_destroy()
450{
451	echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/del_port
452}
453
454setup_prepare()
455{
456	local netdev
457
458	netdevsim_dev_create
459
460	if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}" ]; then
461		echo "Failed to create netdevsim device"
462		exit 1
463	fi
464
465	netdevsim_port_create
466
467	if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}/net/" ]; then
468		echo "Failed to create netdevsim port"
469		exit 1
470	fi
471
472	# Wait for udev to rename newly created netdev.
473	udevadm settle
474
475	NETDEV=$(ls ${NETDEVSIM_PATH}/devices/${DEV}/net/)
476}
477
478cleanup()
479{
480	pre_cleanup
481	netdevsim_port_destroy
482	netdevsim_dev_destroy
483}
484
485trap cleanup EXIT
486
487setup_prepare
488
489tests_run
490
491exit $EXIT_STATUS
492