[Bridge] [bridge]: STP: no port in blocking state despite a loop when in a network namespace

Adel Belhouane bugs.a.b at free.fr
Mon Jun 14 16:42:25 UTC 2021


Hello,

I would like to simulate redundant bridge links in network namespaces
and protect against loops by using STP. I need the behavior to stay
consistent between the initial network namespace and a new network
namespace. This doesn't appear to be the case below.

I'm creating the simplest experiment possible: a loop between two
bridges with STP enabled, both linked directly with two pairs of
veth links.

In the first case, when run from initial network namespace, one bridge
port is put in blocking state to avoid the loop, as expected.

In the second case, when running the same experiment inside a new
network namespace, no port is put in blocking state. So every port goes
from listening -> learning -> forwarding. As I didn't disable for
example IPv6 auto-configuration, there's traffic starting to loop
between the two bridges. To be sure whatever happens there won't be too
much traffic and CPU use, I added a netem qdisc on all veth links.


Unique script loopbridgestp.sh below:

----

#!/bin/sh


cleanup () {
	for dev in lbr0 lbr1 lbr0p1 lbr0p2; do
		ip link del dev "$dev" 2>/dev/null || :
	done
}

cleanup

ip link add name lbr0 type bridge stp_state 1
ip link add name lbr1 type bridge stp_state 1
ip link add name lbr0p1 up master lbr0 type veth peer name lbr1p1
ip link set dev lbr1p1 up master lbr1
ip link add name lbr0p2 up master lbr0 type veth peer name lbr1p2
ip link set dev lbr1p2 up master lbr1

#optional, to protect host
tc qdisc add dev lbr0p1 root handle 1: netem rate 1gbit
tc qdisc add dev lbr0p2 root handle 1: netem rate 1gbit
tc qdisc add dev lbr1p1 root handle 1: netem rate 1gbit
tc qdisc add dev lbr1p2 root handle 1: netem rate 1gbit


ip link set lbr0 up
ip link set lbr1 up


----

First case (directly in initial network namespace in a VM):

    ./loopbridgestp.sh

First case results, waiting a bit for states to change, showing as
expected a port in blocking state:

    root at debian10:~# bridge link
    5: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state listening priority 32 cost 2 
    6: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state listening priority 32 cost 2 
    7: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state listening priority 32 cost 2 
    8: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state blocking priority 32 cost 2 
    root at debian10:~# bridge link
    5: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state learning priority 32 cost 2 
    6: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state learning priority 32 cost 2 
    7: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state learning priority 32 cost 2 
    8: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state blocking priority 32 cost 2 
    root at debian10:~# bridge link
    5: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state forwarding priority 32 cost 2 
    6: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state forwarding priority 32 cost 2 
    7: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state forwarding priority 32 cost 2 
    8: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state blocking priority 32 cost 2 


Second case, within a new network namespace:

    ip netns add experiment
    ip netns exec experiment bash

then:

    ./loopbridgestp.sh


Second case results in allowing bridge forwarding loops to happen
despite STP:

    root at debian10:~# bridge link
    4: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state listening priority 32 cost 2 
    5: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state listening priority 32 cost 2 
    6: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state listening priority 32 cost 2 
    7: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state listening priority 32 cost 2 
    root at debian10:~# bridge link
    4: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state learning priority 32 cost 2 
    5: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state learning priority 32 cost 2 
    6: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state learning priority 32 cost 2 
    7: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state learning priority 32 cost 2 
    root at debian10:~# bridge link
    4: lbr1p1 at lbr0p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state forwarding priority 32 cost 2 
    5: lbr0p1 at lbr1p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state forwarding priority 32 cost 2 
    6: lbr1p2 at lbr0p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr1 state forwarding priority 32 cost 2 
    7: lbr0p2 at lbr1p2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master lbr0 state forwarding priority 32 cost 2 

Tested with same results on:

* Debian kernel 4.19.0-16-amd64
* Linux vanilla kernel 5.10.43
* Linux vanilla kernel 5.12.10

and this behavior might date back from much earlier from what I can
remember.

tcpdump shows there is still STP traffic on the veth interfaces.

No firewall was in use: no ebtables/iptables/nftables kernel module
was loaded.

Relevant kernel configuration: 

CONFIG_BRIDGE=m
CONFIG_BRIDGE_IGMP_SNOOPING=y
CONFIG_BRIDGE_VLAN_FILTERING=y
# CONFIG_BRIDGE_MRP is not set
# CONFIG_BRIDGE_CFM is not set
CONFIG_STP=m
CONFIG_GARP=m
CONFIG_MRP=m

root at debian10:~# uname -a
Linux debian10 5.12.10 #1 SMP Sat Jun 12 18:22:17 UTC 2021 x86_64 GNU/Linux
root at debian10:~# lsmod | egrep 'br|stp|garp|mrp'
bridge                266240  0
stp                    16384  1 bridge
llc                    16384  2 bridge,stp

Is there something I missed in order to have STP select a bridge port to
be in blocking state in a new network namespace as is the case in the
initial network namespace?

Or is that a bug?

Regards,
Adel Belhouane.


More information about the Bridge mailing list