forked from mstpd/mstpd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbridge-stp.in
executable file
·252 lines (227 loc) · 8.85 KB
/
bridge-stp.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/sh
#
# Configuration is in @bridgestpconffile@
#
# `/sbin/bridge-stp <bridge> <start|stop>` is called by the kernel when STP is
# enabled/disabled on a bridge (via `brctl stp <bridge> <on|off>`). The kernel
# enables user_stp mode if that command returns 0, or enables kernel_stp mode if
# that command returns any other value.
#
# If called with the above arguments, this script determines whether MSTP should
# be used for the specified bridge (based on the "MSTP_BRIDGES" configuration
# value), starts/stops mstpd if necessary, and calls
# `mstpctl <addbridge|delbridge> <bridge>` to add/remove the bridge from mstpd
# (possibly in a background job after a delay; see the comments in the code
# below). No further configuration is performed automatically by this script at
# that time. Additional configuration is usually performed by
# @ifupdownfile@ (which calls `brctl stp <bridge> on`
# to trigger this script to start mstpd if necessary).
#
# This script is not intended to be called with the above arguments directly
# (not via the kernel). However, this script may be called directly as
# `mstpctl_restart_config` or `/sbin/bridge-stp restart_config` to reconfigure
# (using `@configbridgefile@ <bridge>` or an alternative command specified using
# a "config_cmd" configuration value) all existing bridges that are using mstpd,
# or called as `mstp_restart` or `/sbin/bridge-stp restart` to restart mstpd and
# then reconfigure all bridges that are using it.
#
# To avoid kernel deadlocks, this script (and any foreground processes it runs)
# must not make any changes (using brctl, ifconfig, ip, /sys/..., etc) to the
# bridge or any associated kernel network interfaces in any code paths that are
# used when this script is called by the kernel.
# Parse arguments.
CalledAs="$(basename "$0")"
if [ "$CalledAs" = 'mstpctl_restart_config' ]; then
action='restart_config'
elif [ $# -eq 1 ] && [ "$1" = 'restart_config' ]; then
action='restart_config'
elif [ "$CalledAs" = 'mstp_restart' ]; then
action='restart'
elif [ $# -eq 1 ] && [ "$1" = 'restart' ]; then
action='restart'
elif [ $# -eq 2 ] && [ "$2" = 'start' ]; then
action='start'
bridge="$1"
elif [ $# -eq 2 ] && [ "$2" = 'stop' ]; then
action='stop'
bridge="$1"
else
echo "Usage: $0 <bridge> {start|stop}" >&2
echo " or: $0 {restart|restart_config}" >&2
exit 1
fi
# Make sure this script is being run as root.
if [ "$(id -u)" != '0' ]; then
echo 'This script must be run as root' >&2
exit 1
fi
# Ensure that we have a sane umask.
umask 022
# Ensure that we have a sane PATH.
PATH='/sbin:/usr/sbin:/bin:/usr/bin'
export PATH
# Define some relevant paths.
mstpctl='@mstpctlfile@'
mstpd='@mstpdfile@'
config_cmd='@configbridgefile@'
pid_file='@mstpdpidfile@'
net_dir='/sys/class/net'
# Set default config values.
# If 'y', mstpd will be automatically started/stopped as needed.
MANAGE_MSTPD='y'
# Arguments to pass to mstpd when it is started.
MSTPD_ARGS=''
# A space-separated list of bridges for which MSTP should be used in place of
# the kernel's STP implementation. If empty, MSTP will be used for all bridges.
MSTP_BRIDGES=''
# Read the config.
if [ -e '@bridgestpconffile@' ]; then
. '@bridgestpconffile@'
fi
# Ensure that mstpctl and mstpd exist and are executable.
if [ -z "$mstpctl" ] || [ ! -x "$mstpctl" ]; then
echo "mstpctl binary does not exist or is not executable" >&2
exit 2
fi
if [ "$MANAGE_MSTPD" = 'y' ]; then
if [ -z "$mstpd" ] || [ ! -x "$mstpd" ]; then
echo "mstpd binary does not exist or is not executable" >&2
exit 2
fi
fi
# Determine whether mstpd should manage STP for the specified bridge.
# Returns 0 if mstpd should manage STP for the specified bridge, or 1 if mstpd
# should not manage STP for the specified bridge.
is_mstp_bridge()
{
if [ -z "$MSTP_BRIDGES" ]; then
return 0
fi
for b in $MSTP_BRIDGES; do
if [ "$1" = "$b" ]; then
return 0
fi
done
return 1
}
case "$action" in
start)
# Make sure the specified bridge is valid.
if [ ! -d "$net_dir/$bridge/bridge" ]; then
echo "'$bridge' is not a bridge" >&2
exit 1
fi
# Determine whether the specified bridge should use MSTP.
if ! is_mstp_bridge "$bridge"; then
echo "Ignoring bridge '$bridge' that is not listed in \$MSTP_BRIDGES"
exit 10
fi
# Start mstpd if necessary.
if ! pidof -c -s mstpd >/dev/null; then
if [ "$MANAGE_MSTPD" != 'y' ]; then
echo 'mstpd is not running' >&2
exit 3
fi
echo 'mstpd is not running'
echo 'Starting mstpd ...'
"$mstpd" $MSTPD_ARGS || exit 3
# Due to kernel locks, mstpd will not respond to mstpctl until after
# this script exits, so `mstpctl addbridge <bridge>` must be run as
# an asynchronous background process.
# On some systems (eg. OpenWrt), mstpctl will fail if it is called
# too soon after mstpd is started, so the call must also be delayed.
#
# To avoid race conditions, any scripts that configure the bridge
# immediately after calling `brctl stp <bridge> on` should
# explicitly call `mstpctl addbridge <bridge>` themselves before
# configuring the bridge. (It should not hurt to call
# `mstpctl addbridge <bridge>` multiple times.)
#
# If `mstpctl addbridge` fails, we could turn STP off and back on
# again to fall back to kernel STP mode. However, that could cause
# an infinite loop if mstpd is being started successfully but is
# then dying before or when mstpctl connects to it. To avoid that
# possibility, we instead simply turn STP off if `mstpctl addbridge`
# fails.
( sleep 1 ; "$mstpctl" addbridge "$bridge" || brctl stp "$bridge" off ) &
exit 0
fi
# Add bridge to mstpd.
"$mstpctl" addbridge "$bridge" || exit 3
;;
stop)
# Remove bridge from mstpd.
"$mstpctl" delbridge "$bridge" || exit 3
# Exit if mstpd should not be stopped when it is no longer used.
if [ "$MANAGE_MSTPD" != 'y' ]; then
exit 0
fi
# Exit if any other bridges are using mstpd.
for bridge in $(ls "$net_dir"); do
# Ignore interfaces that are not bridges.
if [ ! -e "$net_dir/$bridge/bridge/stp_state" ]; then
continue
fi
# Ignore bridges that should not use MSTP.
if ! is_mstp_bridge "$bridge"; then
continue
fi
# If bridge is in user_stp mode, then it is probably using MSTP.
read State < "$net_dir/$bridge/bridge/stp_state"
if [ "$State" = '2' ]; then
exit 0
fi
done
# Kill mstpd, since no bridges are currently using it.
kill $(pidof -c mstpd)
;;
restart|restart_config)
if [ "$action" = 'restart' ]; then
# Kill mstpd.
pids="$(pidof -c mstpd)" ; Err=$?
if [ $Err -eq 0 ]; then
echo 'Stopping mstpd ...'
kill $pids
fi
# Start mstpd.
echo 'Starting mstpd ...'
"$mstpd" $MSTPD_ARGS || exit 3
fi
# Reconfigure bridges.
for bridge in $(ls "$net_dir"); do
# Ignore interfaces that are not bridges.
if [ ! -e "$net_dir/$bridge/bridge/stp_state" ]; then
continue
fi
# Ignore bridges that should not use MSTP.
if ! is_mstp_bridge "$bridge"; then
continue
fi
# Skip bridges that have STP disabled.
read State < "$net_dir/$bridge/bridge/stp_state"
if [ "$State" = '0' ]; then
echo
echo "Skipping bridge '$bridge' that has STP disabled."
echo "To configure this bridge to use MSTP, run:"
echo "brctl stp '$bridge' on"
continue
fi
# Skip bridges that are not in user_stp mode.
if [ "$State" != '2' ]; then
echo
echo "Skipping bridge '$bridge' that is not in user_stp mode."
echo "To reconfigure this bridge to use MSTP, run:"
echo "brctl stp '$bridge' off ; brctl stp '$bridge' on"
continue
fi
# Add bridge to mstpd and configure.
echo "Adding/configuring bridge '$bridge' ..."
"$mstpctl" addbridge "$bridge" || continue
if [ -x "$config_cmd" ] || type "$config_cmd" 2>/dev/null >/dev/null ; then
"$config_cmd" "$bridge"
fi
done
echo
echo 'Done'
;;
esac