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" 289