[Record] Multiple Spanning Tree Protocol Daemon Review
===
###### tags: `record`, `protocol`, [mstpd](https://github.com/mstpd/mstpd), `stp`, `rstp`, `netwrok redundancy`, `IEEE 802.1D`, `IEEE 802.1W`, `IEEE 802.1S`
[TOC]
# Overview
This is a third-party user space daemom. I am going to study it because we may migrate it to our system in the future. Therefore, let's take a look what are provided in mstpd.
# Tree-structured directory
+ /sbin
+ bridge-stp [Check STP state before terminating MSTPd]
+ mstpd
+ mstpctl
+ /lib/mstpctl-utils
+ ifquery
+ ifupdown.sh [Disable STP processing on a specific port]
+ mstp_config_bridge [Fix new shellcheck warnings]
+ mstpctl_restart_config
+ mstpctl-utils-functions.sh
+ /opt/mips-buildroot-linux-uclibc_sdk-gcc7.3/etc/bridge-stp.conf [automatically start/stop mstpd as needed]
---
## 1. /sbin/bridge-stp
``` shell=
#!/bin/sh
#
# Configuration is in /opt/mips-buildroot-linux-uclibc_sdk-gcc7.3/etc/bridge-stp.conf
#
# `/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
# /lib/mstpctl-utils/ifupdown.sh (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 `/lib/mstpctl-utils/mstp_config_bridge <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='/sbin/mstpctl'
mstpd='/sbin/mstpd'
config_cmd='/lib/mstpctl-utils/mstp_config_bridge'
pid_file='@mstpdpidfile@' #/var/run/mstpd.pid
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 '/opt/mips-buildroot-linux-uclibc_sdk-gcc7.3/etc/bridge-stp.conf' ]; then
. '/opt/mips-buildroot-linux-uclibc_sdk-gcc7.3/etc/bridge-stp.conf'
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.
# /sys/class/net/$bridge/bridge
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.
# /sys/class/net/$bridge/bridge/stp_state
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
```
## 2. /opt/mips-buildroot-linux-uclibc_sdk-gcc7.3/etc/bridge-stp.conf
``` shell=
# If 'y' or commented out, mstpd will be automatically started/stopped as
# needed. If 'n', mstpd will not be automatically started/stopped by the
# bridge-stp script.
#MANAGE_MSTPD='n'
# Arguments to be passed to mstpd when it is started.
# -v <0-4> : Adjust log level (default is 2)
#MSTPD_ARGS='-v 2'
# A space-separated list of bridges for which MSTP should be used in place of
# the kernel's STP implementation. If empty or commented out, MSTP will be used
# for all bridges.
#MSTP_BRIDGES='br0 br1'
~
~
```
## 3. /lib/mstpctl-utils/ifupdown.sh
``` shell=
#!/bin/sh
# ifup -av
# un-parts /etc/network/if-pre-up.d
# rip addr add 192.168.1.1/24 dev eth0 label eth0
# rip link set eth0 up
# rip route add default via 192.168.1.254 dev eth0
# run-parts /etc/network/if-up.d
#
# This script should be symlinked into /etc/network/if-pre-up.d/ and
# /etc/network/if-post-down.d/ on Debian/Ubuntu systems so that it will be run
# by ifupdown when configuring network interfaces.
#
# This script is also used by `mstp_config_bridge`.
# `mstp_config_bridge` is used by `/sbin/bridge-stp` when
# `mstpctl_restart_config` or `mstp_restart` or
# `/sbin/bridge-stp restart_config` or `/sbin/bridge-stp restart` are called to
# automatically reconfigure bridges.
# `mstp_config_bridge` may also be used and to configure bridges on systems
# without ifupdown (on systems that are not based on Debian or Ubuntu).
#
# Have a look at /usr/share/doc/bridge-utils/README.Debian if you want
# more info about the way in which bridges are set up on Debian.
#
# Author: Satish Ashok, <sashok@cumulusnetworks.com>
if [ "$IF_MSTPCTL_STP" != 'on' ]; then
exit 0
fi
mstpctl='/sbin/mstpctl'
if [ ! -x "$mstpctl" ]; then
exit 0
fi
brctl="$(command -v brctl)"
if [ -z "$brctl" ] || [ ! -x "$brctl" ]; then
echo "'brctl' binary does not exist or is not executable" >&2
exit 2
fi
ip="$(command -v ip)"
if [ -z "$ip" ] || [ ! -x "$ip" ]; then
echo "'ip' binary does not exist or is not executable" >&2
exit 2
fi
# shellcheck disable=SC1091
. '/lib/mstpctl-utils/mstpctl-utils-functions.sh' || exit 2
case "$IF_MSTPCTL_PORTS" in
'')
exit 0
;;
none)
INTERFACES=''
;;
*)
INTERFACES="$IF_MSTPCTL_PORTS"
;;
esac
# Previous work (create the interface)
if [ "$MODE" = 'start' ] && [ ! -d "/sys/class/net/$IFACE" ]; then
"$brctl" addbr "$IFACE" || exit 2
# Previous work (stop the interface)
elif [ "$MODE" = 'stop' ]; then
"$ip" link set dev "$IFACE" down || exit 2
fi
# shellcheck disable=SC2086
unset all_interfaces &&
# $INTERFACES should be word split into multiple arguments to
# mstpctl_parse_ports. Therefore it should not be quoted here.
mstpctl_parse_ports $INTERFACES | while read -r i; do
for port in $i; do
# We attach and configure each port of the bridge
if [ "$MODE" = 'start' ] && [ ! -d "/sys/class/net/$IFACE/brif/$port" ]; then
if [ -x /etc/network/if-pre-up.d/vlan ]; then
IFACE="$port" /etc/network/if-pre-up.d/vlan
fi
if [ "$IF_BRIDGE_HW" ]; then
"$ip" link set dev "$port" down; "$ip" link set dev "$port" address "$IF_BRIDGE_HW"
fi
if [ -f "/proc/sys/net/ipv6/conf/$port/disable_ipv6" ]; then
echo 1 > "/proc/sys/net/ipv6/conf/$port/disable_ipv6"
fi
"$brctl" addif "$IFACE" "$port" && "$ip" link set dev "$port" up
# We detach each port of the bridge
elif [ "$MODE" = 'stop' ] && [ -d "/sys/class/net/$IFACE/brif/$port" ]; then
"$ip" link set dev "$port" down && "$brctl" delif "$IFACE" "$port" && \
if [ -x /etc/network/if-post-down.d/vlan ]; then
IFACE="$port" /etc/network/if-post-down.d/vlan
fi
if [ -f "/proc/sys/net/ipv6/conf/$port/disable_ipv6" ]; then
echo 0 > "/proc/sys/net/ipv6/conf/$port/disable_ipv6"
fi
fi
done
done
# We finish setting up the bridge
if [ "$MODE" = 'start' ]; then
# This triggers the kernel to run `/sbin/bridge-stp start $IFACE`
"$brctl" stp "$IFACE" on
# `mstpctl addbridge $IFACE` must be called before this script continues.
# If mstpd is already running then /sbin/bridge-stp will call
# `mstpctl addbridge $IFACE` before `brctl stp $IFACE on` returns.
# If mstpd is not already running then /sbin/bridge-stp will start it and call
# `mstpctl addbridge $IFACE` as a delayed background process, in which case it
# may not run before `brctl stp $IFACE on` returns.
# It should not hurt to call `mstpctl addbridge $IFACE` twice, so call it
# again to ensure that it has been called before continuing.
# See the code and comments in /sbin/bridge-stp for more details.
"$mstpctl" addbridge "$IFACE"
if [ "$IF_MSTPCTL_MAXAGE" ]; then
"$mstpctl" setmaxage "$IFACE" "$IF_MSTPCTL_MAXAGE"
fi
if [ "$IF_MSTPCTL_FDELAY" ]; then
"$mstpctl" setfdelay "$IFACE" "$IF_MSTPCTL_FDELAY"
fi
if [ "$IF_MSTPCTL_MAXHOPS" ]; then
"$mstpctl" setmaxhops "$IFACE" "$IF_MSTPCTL_MAXHOPS"
fi
if [ "$IF_MSTPCTL_TXHOLDCOUNT" ]; then
"$mstpctl" settxholdcount "$IFACE" "$IF_MSTPCTL_TXHOLDCOUNT"
fi
if [ "$IF_MSTPCTL_FORCEVERS" ]; then
"$mstpctl" setforcevers "$IFACE" "$IF_MSTPCTL_FORCEVERS"
fi
if [ "$IF_MSTPCTL_TREEPRIO" ]; then
"$mstpctl" settreeprio "$IFACE" 0 "$IF_MSTPCTL_TREEPRIO"
fi
if [ "$IF_MSTPCTL_PORTPATHCOST" ]; then
portpathcosts="$(echo "$IF_MSTPCTL_PORTPATHCOST" | tr '\n' ' ' | tr -s ' ')"
for portpathcost in $portpathcosts; do
port="$(echo "$portpathcost" | cut -d '=' -f1)"
pathcost="$(echo "$portpathcost" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$pathcost" ]; then
"$mstpctl" setportpathcost "$IFACE" "$port" "$pathcost"
fi
done
fi
if [ "$IF_MSTPCTL_PORTADMINEDGE" ]; then
portadminedges="$(echo "$IF_MSTPCTL_PORTADMINEDGE" | tr '\n' ' ' | tr -s ' ')"
for portadminedge in $portadminedges; do
port="$(echo "$portadminedge" | cut -d '=' -f1)"
adminedge="$(echo "$portadminedge" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$adminedge" ]; then
"$mstpctl" setportadminedge "$IFACE" "$port" "$adminedge"
fi
done
fi
if [ "$IF_MSTPCTL_PORTAUTOEDGE" ]; then
portautoedges="$(echo "$IF_MSTPCTL_PORTAUTOEDGE" | tr '\n' ' ' | tr -s ' ')"
for portautoedge in $portautoedges; do
port="$(echo "$portautoedge" | cut -d '=' -f1)"
autoedge="$(echo "$portautoedge" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$autoedge" ]; then
"$mstpctl" setportautoedge "$IFACE" "$port" "$autoedge"
fi
done
fi
if [ "$IF_MSTPCTL_PORTP2P" ]; then
portp2ps="$(echo "$IF_MSTPCTL_PORTP2P" | tr '\n' ' ' | tr -s ' ')"
for portp2p in $portp2ps; do
port="$(echo "$portp2p" | cut -d '=' -f1)"
p2p="$(echo "$portp2p" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$p2p" ]; then
"$mstpctl" setportp2p "$IFACE" "$port" "$p2p"
fi
done
fi
if [ "$IF_MSTPCTL_PORTRESTRROLE" ]; then
portrestrroles="$(echo "$IF_MSTPCTL_PORTRESTRROLE" | tr '\n' ' ' | tr -s ' ')"
for portrestrrole in $portrestrroles; do
port="$(echo "$portrestrrole" | cut -d '=' -f1)"
restrrole="$(echo "$portrestrrole" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$restrrole" ]; then
"$mstpctl" setportrestrrole "$IFACE" "$port" "$restrrole"
fi
done
fi
if [ "$IF_MSTPCTL_PORTRESTRTCN" ]; then
portrestrtcns="$(echo "$IF_MSTPCTL_PORTRESTRTCN" | tr '\n' ' ' | tr -s ' ')"
for portrestrtcn in $portrestrtcns; do
port="$(echo "$portrestrtcn" | cut -d '=' -f1)"
restrtcn="$(echo "$portrestrtcn" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$restrtcn" ]; then
"$mstpctl" setportrestrtcn "$IFACE" "$port" "$restrtcn"
fi
done
fi
if [ "$IF_MSTPCTL_BPDUGUARD" ]; then
portbpduguards="$(echo "$IF_MSTPCTL_BPDUGUARD" | tr '\n' ' ' | tr -s ' ')"
for portbpduguard in $portbpduguards; do
port="$(echo "$portbpduguard" | cut -d '=' -f1)"
bpduguard="$(echo "$portbpduguard" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$bpduguard" ]; then
"$mstpctl" setbpduguard "$IFACE" "$port" "$bpduguard"
fi
done
fi
if [ "$IF_MSTPCTL_PORTBPDUFILTER" ]; then
portbpdufilters="$(echo "$IF_MSTPCTL_PORTBPDUFILTER" | tr '\n' ' ' | tr -s ' ')"
for portbpdufilter in $portbpdufilters; do
port="$(echo "$portbpdufilter" | cut -d '=' -f1)"
bpdufilter="$(echo "$portbpdufilter" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$bpdufilter" ]; then
"$mstpctl" setportbpdufilter "$IFACE" "$port" "$bpdufilter"
fi
done
fi
if [ "$IF_MSTPCTL_TREEPORTPRIO" ]; then
treeportprios="$(echo "$IF_MSTPCTL_TREEPORTPRIO" | tr '\n' ' ' | tr -s ' ')"
for treeportprio in $treeportprios; do
treeport="$(echo "$treeportprio" | cut -d '=' -f1)"
prio="$(echo "$treeportprio" | cut -d '=' -f2)"
if [ -n "$treeport" ] && [ -n "$prio" ]; then
"$mstpctl" settreeportprio "$IFACE" "$treeport" 0 "$prio"
fi
done
fi
if [ "$IF_MSTPCTL_TREEPORTCOST" ]; then
treeportcosts="$(echo "$IF_MSTPCTL_TREEPORTCOST" | tr '\n' ' ' | tr -s ' ')"
for treeportcost in $treeportcosts; do
treeport="$(echo "$treeportcost" | cut -d '=' -f1)"
cost="$(echo "$treeportcost" | cut -d '=' -f2)"
if [ -n "$treeport" ] && [ -n "$cost" ]; then
"$mstpctl" settreeportcost "$IFACE" "$treeport" 0 "$cost"
fi
done
fi
if [ "$IF_MSTPCTL_HELLO" ]; then
"$mstpctl" sethello "$IFACE" "$IF_MSTPCTL_HELLO"
fi
if [ "$IF_MSTPCTL_AGEING" ]; then
"$mstpctl" setageing "$IFACE" "$IF_MSTPCTL_AGEING"
fi
if [ "$IF_MSTPCTL_PORTNETWORK" ]; then
portnetworks="$(echo "$IF_MSTPCTL_PORTNETWORK" | tr '\n' ' ' | tr -s ' ')"
for portnetwork in $portnetworks; do
port="$(echo "$portnetwork" | cut -d '=' -f1)"
network="$(echo "$portnetwork" | cut -d '=' -f2)"
if [ -n "$port" ] && [ -n "$network" ]; then
"$mstpctl" setportnetwork "$IFACE" "$port" "$network"
fi
done
fi
# We activate the bridge
"$ip" link set dev "$IFACE" up
# Calculate the maximum time to wait for STP to converge
maxwait=''
if [ -n "$IF_MSTPCTL_MAXWAIT" ] && [ "$IF_MSTPCTL_MAXWAIT" != 'auto' ]; then
# if [ "$IF_MSTPCTL_MAXWAIT" is a number ]; then
if [ "$IF_MSTPCTL_MAXWAIT" = "${IF_MSTPCTL_MAXWAIT%[!0-9]*}" ]; then
maxwait="$IF_MSTPCTL_MAXWAIT"
else
echo "Ignoring invalid mstpctl_maxwait value '$maxwait'" >&2
fi
fi
if [ -z "$maxwait" ]; then
root_forward_delay="$("$mstpctl" showbridge "$IFACE" forward-delay)"
if [ -z "$root_forward_delay" ] || [ "$root_forward_delay" != "${root_forward_delay%[!0-9]*}" ]; then
root_forward_delay=0
fi
bridge_forward_delay="$("$mstpctl" showbridge "$IFACE" bridge-forward-delay)"
if [ -z "$bridge_forward_delay" ] || [ "$root_forward_delay" != "${bridge_forward_delay%[!0-9]*}" ]; then
bridge_forward_delay=0
fi
if [ $root_forward_delay -gt $bridge_forward_delay ]; then
maxwait="$root_forward_delay"
else
maxwait="$bridge_forward_delay"
fi
maxwait=$((maxwait*2+1))
fi
# Wait for STP to converge
if [ $maxwait -ne 0 ]; then
echo
echo "Waiting for STP on bridge '$IFACE' to converge (mstpctl_maxwait is $maxwait seconds)."
# Use 0.1 delay if available
sleep 0.1 2>/dev/null && maxwait=$((maxwait*10))
count=0 ; transitioned='' ; converged=''
while [ -z "$converged" ] && [ $count -lt $maxwait ]; do
sleep 0.1 2>/dev/null || sleep 1
count=$((count+1))
# Converged if all ports are either 'forwarding', 'blocking', or
# 'disabled', except if all ports are 'disabled' and we have yet to see
# any port transition to any other valid state.
converged=true
for i in $("$brctl" showstp "$IFACE" | sed -n 's/^.*port id.*state[ \t]*\(.*\)$/\1/p'); do
if [ "$i" = 'listening' ] || [ "$i" = 'learning' ]; then
transitioned=true
converged=''
break
elif [ "$i" = 'forwarding' ] || [ "$i" = 'blocking' ]; then
transitioned=true
elif [ "$i" != 'disabled' ] || [ -z "$transitioned" ]; then
converged=''
fi
done
done
if [ -z "$converged" ]; then
echo "Timed out waiting for STP on bridge '$IFACE' to converge" >&2
fi
fi
# Finally we destroy the interface
elif [ "$MODE" = 'stop' ]; then
"$brctl" delbr "$IFACE"
fi
```
## 4. /lib/mstpctl-utils/mstpctl_restart_config = 1. /sbin/bridge-stp
## 5. /lib/mstpctl-utils/mstp_config_bridge
``` shell=
#!/bin/sh
#
# This script uses `ifquery` to read the config in
# /etc/network/interfaces and then runs the mstpctl-utils
# `ifupdown.sh` script to configure the specified bridge in mstpd.
#
# This is used by the `/sbin/bridge-stp` script when called as
# `mstpctl_restart_config` or `mstp_restart` or
# `/sbin/bridge-stp restart_config` or `/sbin/bridge-stp restart` to
# automatically reconfigure bridges.
#
# This may also be used to configure mstpd on systems without ifupdown (on
# systems that are not based on Debian or Ubuntu).
# Parse arguments.
if [ $# -ne 1 ]; then
echo "Usage: $0 <bridge>" >&2
exit 1
fi
Bridge="$1"
# 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
# The mstp ifupdown.sh script will not work properly unless mstpctl, brctl, and
# ip exist and are executable.
mstpctl='/sbin/mstpctl'
if [ ! -x "$mstpctl" ]; then
echo "'mstpctl' binary does not exist or is not executable" >&2
exit 2
fi
brctl="$(command -v brctl)"
if [ -z "$brctl" ] || [ ! -x "$brctl" ]; then
echo "'brctl' binary does not exist or is not executable" >&2
exit 2
fi
ip="$(command -v ip)"
if [ -z "$ip" ] || [ ! -x "$ip" ]; then
echo "'ip' binary does not exist or is not executable" >&2
exit 2
fi
# Find ifquery.
ifquery="$(command -v ifquery 2>/dev/null)"
if [ -z "$ifquery" ]; then
# If the real ifquery is not installed, use our emulator.
ifquery='/lib/mstpctl-utils/ifquery'
fi
if [ ! -x "$ifquery" ]; then
echo "'ifquery' binary does not exist or is not executable" >&2
exit 2
fi
# Run ifquery.
# For example,
# ifquery eth0
# Out=
# address: 192.168.1.1
# netmask: 255.255.255.0
# gateway: 192.168.1.254
# broadcast: 192.168.1.255
Out="$("$ifquery" "$Bridge" 2>/dev/null)" ; Err=$?
if [ $Err -ne 0 ]; then
echo "Skipping configuration of bridge '$Bridge' that is not configured in"
echo '/etc/network/interfaces'
exit 0
fi
# Read the interface config and generate the environment variables that are
# typically generated by ifupdown.
export IFACE="$Bridge"
export LOGICAL="$Bridge"
export ADDRFAM='inet'
export METHOD='manual'
export MODE='start'
export PHASE='post-up'
export VERBOSITY=0
eval "$(
IFS= ; echo "$Out" | \
while read -r Line; do
OptName="${Line%%: *}" # Strip everything after the first ': '
OptName="IF_$(echo "$OptName" | tr '[:lower:]' '[:upper:]')" # Uppercase
OptVal="${Line#*: }" # Strip everything before the first ': '
echo "export $OptName=\"$OptVal\""
done
)"
# Call the mstpctl-utils ifupdown.sh script.
'/lib/mstpctl-utils/ifupdown.sh'
```
## 6. /lib/mstpctl-utils/ifquery
``` python=
#!/usr/bin/python
# Crudely emulate ifquery on systems that don't have it.
import sys
import glob
def parse_config(interfaces_file, interface):
f = open(interfaces_file)
intf = ''
for line in f:
str = line.strip().split(' ')
len_str = len(str)
if 2 > len_str :
continue
if str[0].startswith('#') :
continue
# Check if the source <file> keyword is present and extract the file
if str[0] == 'source' :
for sfile in glob.glob(str[1]):
parse_config(sfile, interface)
if str[0] == 'iface' :
intf = str[1]
continue
# Check if this is the relevant interface.
if intf != interface :
continue
# Print configuration.
print(str[0]+": "+" ".join(str[1:]))
f.close()
return
parse_config('/etc/network/interfaces', sys.argv[1])
```
## 7. /lib/mstpctl-utils/mstpctl-utils-functions.sh
``` shell=
#!/bin/sh
mstpctl_parse_ports()
{
while [ x"${1+set}" = xset ]
do
# For compatibility: the 'all' option.
case $1 in
all)
shift &&
set -- regex eth.\* em.\* 'p[0-9].*' noregex "$@"
;;
esac
# Primitive state machine...
case $1-$(uname -s) in
regex-Linux)
all_interfaces=$(sed -n 's%^[\ ]*\([^:]*\):.*$%\1%p' < /proc/net/dev)
shift
;;
regex-*)
echo "$0 needs to be ported for your $(uname -s) system. " \
"Trying to continue nevertheless." >&2
shift
;;
noregex-*)
unset all_interfaces
shift
;;
esac
case ${all_interfaces+regex}-${1+set} in
regex-set)
# The following interface specification are to be parsed as regular
# expressions against all interfaces the system provides.
i=$(grep -E "^$1$" << EOAI
$all_interfaces
EOAI
)
shift
;;
*-set)
# Literal interfaces.
i=$1
shift
;;
*)
# No interface specification is following.
i=
;;
esac
echo $i
done
}
```
# Reference
https://github.com/mstpd/mstpd