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