Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# shellcheck disable=SC2034,SC2154,SC2317,SC2329
4#
5# Test for bridge STP mode selection (IFLA_BR_STP_MODE).
6#
7# Verifies that:
8# - stp_mode defaults to auto on new bridges
9# - stp_mode can be toggled between user, kernel, and auto
10# - stp_mode change is rejected while STP is active (-EBUSY)
11# - stp_mode user in a netns yields userspace STP (stp_state=2)
12# - stp_mode kernel forces kernel STP (stp_state=1)
13# - stp_mode auto preserves traditional fallback to kernel STP
14# - stp_mode and stp_state can be set atomically in one message
15# - stp_mode persists across STP disable/enable cycles
16
17source lib.sh
18
19require_command jq
20
21ALL_TESTS="
22 test_default_auto
23 test_set_modes
24 test_reject_change_while_stp_active
25 test_idempotent_mode_while_stp_active
26 test_user_mode_in_netns
27 test_kernel_mode
28 test_auto_mode
29 test_atomic_mode_and_state
30 test_mode_persistence
31"
32
33bridge_info_get()
34{
35 ip -n "$NS1" -d -j link show "$1" | \
36 jq -r ".[0].linkinfo.info_data.$2"
37}
38
39check_stp_mode()
40{
41 local br=$1; shift
42 local expected=$1; shift
43 local msg=$1; shift
44 local val
45
46 val=$(bridge_info_get "$br" stp_mode)
47 [ "$val" = "$expected" ]
48 check_err $? "$msg: expected $expected, got $val"
49}
50
51check_stp_state()
52{
53 local br=$1; shift
54 local expected=$1; shift
55 local msg=$1; shift
56 local val
57
58 val=$(bridge_info_get "$br" stp_state)
59 [ "$val" = "$expected" ]
60 check_err $? "$msg: expected $expected, got $val"
61}
62
63# Create a bridge in NS1, bring it up, and defer its deletion.
64bridge_create()
65{
66 ip -n "$NS1" link add "$1" type bridge
67 ip -n "$NS1" link set "$1" up
68 defer ip -n "$NS1" link del "$1"
69}
70
71setup_prepare()
72{
73 setup_ns NS1
74}
75
76cleanup()
77{
78 defer_scopes_cleanup
79 cleanup_all_ns
80}
81
82# Check that stp_mode defaults to auto when creating a bridge.
83test_default_auto()
84{
85 RET=0
86
87 ip -n "$NS1" link add br-test type bridge
88 defer ip -n "$NS1" link del br-test
89
90 check_stp_mode br-test auto "stp_mode default"
91
92 log_test "stp_mode defaults to auto"
93}
94
95# Test setting stp_mode to user, kernel, and back to auto.
96test_set_modes()
97{
98 RET=0
99
100 ip -n "$NS1" link add br-test type bridge
101 defer ip -n "$NS1" link del br-test
102
103 ip -n "$NS1" link set dev br-test type bridge stp_mode user
104 check_err $? "Failed to set stp_mode to user"
105 check_stp_mode br-test user "after set user"
106
107 ip -n "$NS1" link set dev br-test type bridge stp_mode kernel
108 check_err $? "Failed to set stp_mode to kernel"
109 check_stp_mode br-test kernel "after set kernel"
110
111 ip -n "$NS1" link set dev br-test type bridge stp_mode auto
112 check_err $? "Failed to set stp_mode to auto"
113 check_stp_mode br-test auto "after set auto"
114
115 log_test "stp_mode set user/kernel/auto"
116}
117
118# Verify that stp_mode cannot be changed while STP is active.
119test_reject_change_while_stp_active()
120{
121 RET=0
122
123 bridge_create br-test
124
125 ip -n "$NS1" link set dev br-test type bridge stp_mode kernel
126 check_err $? "Failed to set stp_mode to kernel"
127
128 ip -n "$NS1" link set dev br-test type bridge stp_state 1
129 check_err $? "Failed to enable STP"
130
131 # Changing stp_mode while STP is active should fail.
132 ip -n "$NS1" link set dev br-test type bridge stp_mode auto 2>/dev/null
133 check_fail $? "Changing stp_mode should fail while STP is active"
134
135 check_stp_mode br-test kernel "mode unchanged after rejected change"
136
137 # Disable STP, then change should succeed.
138 ip -n "$NS1" link set dev br-test type bridge stp_state 0
139 check_err $? "Failed to disable STP"
140
141 ip -n "$NS1" link set dev br-test type bridge stp_mode auto
142 check_err $? "Changing stp_mode should succeed after STP is disabled"
143
144 log_test "reject stp_mode change while STP is active"
145}
146
147# Verify that re-setting the same stp_mode while STP is active succeeds.
148test_idempotent_mode_while_stp_active()
149{
150 RET=0
151
152 bridge_create br-test
153
154 ip -n "$NS1" link set dev br-test type bridge stp_mode user stp_state 1
155 check_err $? "Failed to enable STP with user mode"
156
157 # Re-setting the same mode while STP is active should succeed.
158 ip -n "$NS1" link set dev br-test type bridge stp_mode user
159 check_err $? "Idempotent stp_mode set should succeed while STP is active"
160
161 check_stp_state br-test 2 "stp_state after idempotent set"
162
163 # Changing mode while disabling STP in the same message should succeed.
164 ip -n "$NS1" link set dev br-test type bridge stp_mode auto stp_state 0
165 check_err $? "Mode change with simultaneous STP disable should succeed"
166
167 check_stp_mode br-test auto "mode changed after disable+change"
168 check_stp_state br-test 0 "stp_state after disable+change"
169
170 log_test "idempotent and simultaneous mode change while STP active"
171}
172
173# Test that stp_mode user in a non-init netns yields userspace STP
174# (stp_state == 2). This is the key use case: userspace STP without
175# needing /sbin/bridge-stp or being in init_net.
176test_user_mode_in_netns()
177{
178 RET=0
179
180 bridge_create br-test
181
182 ip -n "$NS1" link set dev br-test type bridge stp_mode user
183 check_err $? "Failed to set stp_mode to user"
184
185 ip -n "$NS1" link set dev br-test type bridge stp_state 1
186 check_err $? "Failed to enable STP"
187
188 check_stp_state br-test 2 "stp_state with user mode"
189
190 log_test "stp_mode user in netns yields userspace STP"
191}
192
193# Test that stp_mode kernel forces kernel STP (stp_state == 1)
194# regardless of whether /sbin/bridge-stp exists.
195test_kernel_mode()
196{
197 RET=0
198
199 bridge_create br-test
200
201 ip -n "$NS1" link set dev br-test type bridge stp_mode kernel
202 check_err $? "Failed to set stp_mode to kernel"
203
204 ip -n "$NS1" link set dev br-test type bridge stp_state 1
205 check_err $? "Failed to enable STP"
206
207 check_stp_state br-test 1 "stp_state with kernel mode"
208
209 log_test "stp_mode kernel forces kernel STP"
210}
211
212# Test that stp_mode auto preserves traditional behavior: in a netns
213# (non-init_net), bridge-stp is not called and STP falls back to
214# kernel mode (stp_state == 1).
215test_auto_mode()
216{
217 RET=0
218
219 bridge_create br-test
220
221 # Auto mode is the default; enable STP in a netns.
222 ip -n "$NS1" link set dev br-test type bridge stp_state 1
223 check_err $? "Failed to enable STP"
224
225 # In a netns with auto mode, bridge-stp is skipped (init_net only),
226 # so STP should fall back to kernel mode (stp_state == 1).
227 check_stp_state br-test 1 "stp_state with auto mode in netns"
228
229 log_test "stp_mode auto preserves traditional behavior"
230}
231
232# Test that stp_mode and stp_state can be set in a single netlink
233# message. This is the intended atomic usage pattern.
234test_atomic_mode_and_state()
235{
236 RET=0
237
238 bridge_create br-test
239
240 # Set both stp_mode and stp_state in one command.
241 ip -n "$NS1" link set dev br-test type bridge stp_mode user stp_state 1
242 check_err $? "Failed to set stp_mode user and stp_state 1 atomically"
243
244 check_stp_state br-test 2 "stp_state after atomic set"
245
246 log_test "atomic stp_mode user + stp_state 1 in single message"
247}
248
249# Test that stp_mode persists across STP disable/enable cycles.
250test_mode_persistence()
251{
252 RET=0
253
254 bridge_create br-test
255
256 # Set user mode and enable STP.
257 ip -n "$NS1" link set dev br-test type bridge stp_mode user
258 ip -n "$NS1" link set dev br-test type bridge stp_state 1
259 check_err $? "Failed to enable STP with user mode"
260
261 # Disable STP.
262 ip -n "$NS1" link set dev br-test type bridge stp_state 0
263 check_err $? "Failed to disable STP"
264
265 # Verify mode is still user.
266 check_stp_mode br-test user "stp_mode after STP disable"
267
268 # Re-enable STP -- should use user mode again.
269 ip -n "$NS1" link set dev br-test type bridge stp_state 1
270 check_err $? "Failed to re-enable STP"
271
272 check_stp_state br-test 2 "stp_state after re-enable"
273
274 log_test "stp_mode persists across STP disable/enable cycles"
275}
276
277# Check iproute2 support before setting up resources.
278if ! ip link add type bridge help 2>&1 | grep -q "stp_mode"; then
279 echo "SKIP: iproute2 too old, missing stp_mode support"
280 exit "$ksft_skip"
281fi
282
283trap cleanup EXIT
284
285setup_prepare
286tests_run
287
288exit "$EXIT_STATUS"