Adel Belhouane
2021-Jun-14 16:42 UTC
[Bridge] [bridge]: STP: no port in blocking state despite a loop when in a network namespace
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.