xref: /linux/tools/testing/selftests/net/forwarding/bridge_fdb_learning_limit.sh (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# ShellCheck incorrectly believes that most of the code here is unreachable
5# because it's invoked by variable name following ALL_TESTS.
6#
7# shellcheck disable=SC2317
8
9ALL_TESTS="check_accounting check_limit"
10NUM_NETIFS=6
11source lib.sh
12
13TEST_MAC_BASE=de:ad:be:ef:42:
14
15NUM_PKTS=16
16FDB_LIMIT=8
17
18FDB_TYPES=(
19	# name		is counted?	overrides learned?
20	'learned	1		0'
21	'static		0		1'
22	'user		0		1'
23	'extern_learn	0		1'
24	'local		0		1'
25)
26
27mac()
28{
29	printf "${TEST_MAC_BASE}%02x" "$1"
30}
31
32H1_DEFAULT_MAC=$(mac 42)
33
34switch_create()
35{
36	ip link add dev br0 type bridge
37
38	ip link set dev "$swp1" master br0
39	ip link set dev "$swp2" master br0
40	# swp3 is used to add local MACs, so do not add it to the bridge yet.
41
42	# swp2 is only used for replying when learning on swp1, its MAC should not be learned.
43	ip link set dev "$swp2" type bridge_slave learning off
44
45	ip link set dev br0 up
46
47	ip link set dev "$swp1" up
48	ip link set dev "$swp2" up
49	ip link set dev "$swp3" up
50}
51
52switch_destroy()
53{
54	ip link set dev "$swp3" down
55	ip link set dev "$swp2" down
56	ip link set dev "$swp1" down
57
58	ip link del dev br0
59}
60
61h_create()
62{
63	ip link set "$h1" addr "$H1_DEFAULT_MAC"
64
65	simple_if_init "$h1" 192.0.2.1/24
66	simple_if_init "$h2" 192.0.2.2/24
67}
68
69h_destroy()
70{
71	simple_if_fini "$h1" 192.0.2.1/24
72	simple_if_fini "$h2" 192.0.2.2/24
73}
74
75setup_prepare()
76{
77	h1=${NETIFS[p1]}
78	swp1=${NETIFS[p2]}
79
80	h2=${NETIFS[p3]}
81	swp2=${NETIFS[p4]}
82
83	swp3=${NETIFS[p6]}
84
85	vrf_prepare
86
87	h_create
88
89	switch_create
90}
91
92cleanup()
93{
94	pre_cleanup
95
96	switch_destroy
97
98	h_destroy
99
100	vrf_cleanup
101}
102
103fdb_get_n_learned()
104{
105	ip -d -j link show dev br0 type bridge | \
106		jq '.[]["linkinfo"]["info_data"]["fdb_n_learned"]'
107}
108
109fdb_get_n_mac()
110{
111	local mac=${1}
112
113	bridge -j fdb show br br0 | \
114		jq "map(select(.mac == \"${mac}\" and (has(\"vlan\") | not))) | length"
115}
116
117fdb_fill_learned()
118{
119	local i
120
121	for i in $(seq 1 "$NUM_PKTS"); do
122		fdb_add learned "$(mac "$i")"
123	done
124}
125
126fdb_reset()
127{
128	bridge fdb flush dev br0
129
130	# Keep the default MAC address of h1 in the table. We set it to a different one when
131	# testing dynamic learning.
132	bridge fdb add "$H1_DEFAULT_MAC" dev "$swp1" master static use
133}
134
135fdb_add()
136{
137	local type=$1 mac=$2
138
139	case "$type" in
140		learned)
141			ip link set "$h1" addr "$mac"
142			# Wait for a reply so we implicitly wait until after the forwarding
143			# code finished and the FDB entry was created.
144			PING_COUNT=1 ping_do "$h1" 192.0.2.2
145			check_err $? "Failed to ping another bridge port"
146			ip link set "$h1" addr "$H1_DEFAULT_MAC"
147			;;
148		local)
149			ip link set dev "$swp3" addr "$mac" && ip link set "$swp3" master br0
150			;;
151		static)
152			bridge fdb replace "$mac" dev "$swp1" master static
153			;;
154		user)
155			bridge fdb replace "$mac" dev "$swp1" master static use
156			;;
157		extern_learn)
158			bridge fdb replace "$mac" dev "$swp1" master extern_learn
159			;;
160	esac
161
162	check_err $? "Failed to add a FDB entry of type ${type}"
163}
164
165fdb_del()
166{
167	local type=$1 mac=$2
168
169	case "$type" in
170		local)
171			ip link set "$swp3" nomaster
172			;;
173		*)
174			bridge fdb del "$mac" dev "$swp1" master
175			;;
176	esac
177
178	check_err $? "Failed to remove a FDB entry of type ${type}"
179}
180
181check_fdb_n_learned_support()
182{
183	if ! ip link help bridge 2>&1 | grep -q "fdb_max_learned"; then
184		echo "SKIP: iproute2 too old, missing bridge max learned support"
185		exit $ksft_skip
186	fi
187
188	ip link add dev br0 type bridge
189	local learned=$(fdb_get_n_learned)
190	ip link del dev br0
191	if [ "$learned" == "null" ]; then
192		echo "SKIP: kernel too old; bridge fdb_n_learned feature not supported."
193		exit $ksft_skip
194	fi
195}
196
197check_accounting_one_type()
198{
199	local type=$1 is_counted=$2 overrides_learned=$3
200	shift 3
201	RET=0
202
203	fdb_reset
204	fdb_add "$type" "$(mac 0)"
205	learned=$(fdb_get_n_learned)
206	[ "$learned" -ne "$is_counted" ]
207	check_fail $? "Inserted FDB type ${type}: Expected the count ${is_counted}, but got ${learned}"
208
209	fdb_del "$type" "$(mac 0)"
210	learned=$(fdb_get_n_learned)
211	[ "$learned" -ne 0 ]
212	check_fail $? "Removed FDB type ${type}: Expected the count 0, but got ${learned}"
213
214	if [ "$overrides_learned" -eq 1 ]; then
215		fdb_reset
216		fdb_add learned "$(mac 0)"
217		fdb_add "$type" "$(mac 0)"
218		learned=$(fdb_get_n_learned)
219		[ "$learned" -ne "$is_counted" ]
220		check_fail $? "Set a learned entry to FDB type ${type}: Expected the count ${is_counted}, but got ${learned}"
221		fdb_del "$type" "$(mac 0)"
222	fi
223
224	log_test "FDB accounting interacting with FDB type ${type}"
225}
226
227check_accounting()
228{
229	local type_args learned
230	RET=0
231
232	fdb_reset
233	learned=$(fdb_get_n_learned)
234	[ "$learned" -ne 0 ]
235	check_fail $? "Flushed the FDB table: Expected the count 0, but got ${learned}"
236
237	fdb_fill_learned
238	sleep 1
239
240	learned=$(fdb_get_n_learned)
241	[ "$learned" -ne "$NUM_PKTS" ]
242	check_fail $? "Filled the FDB table: Expected the count ${NUM_PKTS}, but got ${learned}"
243
244	log_test "FDB accounting"
245
246	for type_args in "${FDB_TYPES[@]}"; do
247		# This is intentional use of word splitting.
248		# shellcheck disable=SC2086
249		check_accounting_one_type $type_args
250	done
251}
252
253check_limit_one_type()
254{
255	local type=$1 is_counted=$2
256	local n_mac expected=$((1 - is_counted))
257	RET=0
258
259	fdb_reset
260	fdb_fill_learned
261
262	fdb_add "$type" "$(mac 0)"
263	n_mac=$(fdb_get_n_mac "$(mac 0)")
264	[ "$n_mac" -ne "$expected" ]
265	check_fail $? "Inserted FDB type ${type} at limit: Expected the count ${expected}, but got ${n_mac}"
266
267	log_test "FDB limits interacting with FDB type ${type}"
268}
269
270check_limit()
271{
272	local learned
273	RET=0
274
275	ip link set br0 type bridge fdb_max_learned "$FDB_LIMIT"
276
277	fdb_reset
278	fdb_fill_learned
279
280	learned=$(fdb_get_n_learned)
281	[ "$learned" -ne "$FDB_LIMIT" ]
282	check_fail $? "Filled the limited FDB table: Expected the count ${FDB_LIMIT}, but got ${learned}"
283
284	log_test "FDB limits"
285
286	for type_args in "${FDB_TYPES[@]}"; do
287		# This is intentional use of word splitting.
288		# shellcheck disable=SC2086
289		check_limit_one_type $type_args
290	done
291}
292
293check_fdb_n_learned_support
294
295trap cleanup EXIT
296
297setup_prepare
298
299tests_run
300
301exit $EXIT_STATUS
302