Classic STP (802.1D) Exception BPDU Generation

NetCraftsmen®

Recently, a friend of mine, currently pursuing the CCIE, was tackling the all important topic of spanning tree; specifically taking a deep dive into the inner workings of classic STP defined by 802.1D and comparing it to 802.1W (RSTP). After reviewing a couple of references, he was confused about how STP and RSTP respond to the reception of inferior BPDUs. This article illustrates the actual behavior in Cisco IOS focusing on the use of the “Exception BPDU”.

The working of STP BPDUs is well documented for a stable topology, essentially when a fully functional Root Bridge/Switch is present in the topology and is generating configuration BPDUs every HELLO_TIME seconds. The confusion usually occurs when one is discussing STP/RSTP during convergence/reconvergence i.e. the Root Bridge has, for whatever reason, experienced a failure and is no longer generating BPDUs; none that reach other switches in the network anyway. In such a case, a commonly used but not-exactly-correct summary states the behavior somewhat like: “STP switches ignore any inferior BPDUs they may receive on their designated ports till the stored BPDU expires whereas RSTP switches respond to inferior BPDUs with a proposal message using previously cached information”.

Sometimes it is a wording issue – for example, in this definitive article on STP and RSTP convergence, http://blog.ine.com/wp-content/uploads/2011/11/understanding-stp-rstp-convergence.pdf, quadruple CCIE Petr Lapukhov writes:

“Now for the case when upstream bridge loses all paths to the root bridge. In this case, the original root bridge information is expired (immediately or in up to Max_Age seconds) and the upstream declares itself as a new root. Immediately after this it starts sending inferior BPDUs, declaring itself the new root. The downstream bridge ignores this new information for the duration of the Max_Age – Message_Age, retaining information about the original root. After this timeout expires, there are two possible outcomes:”

The highlighted wording is seemingly at odds to what Radia Perlman describes in her “Interconnections” book. She writes:

“If the only time the Designated Bridge issued a configuration message were upon receipt of a configuration message from the direction of the root, there would be no reason to include the age field in the configuration message because upon transmission it would always be 0. However, there is another situation in which a Designated Bridge will issue a configuration message even if one has not been received recently from the root port. Suppose that B is the Designated Bridge on a particular port. Suppose, too, that B has not recently received a configuration message from the direction of the root, so that the stored configuration message from the direction of the root now has age X (B has been aging the message while holding it in memory). If another bridge, B2, were to come up, it would issue its own configuration message because it would not have heard B’s configuration message on that LAN. It would be dangerous for B to ignore this situation until B hears a new configuration message from the root because it would cause the new bridge to be very unsynchronized with respect to the rest of the network. If the new bridge does not hear B’s configuration message, the new bridge will assume that it should become designated on that port, causing extra connectivity, a loop. If B retransmitted its configuration message without the age field (or with age field 0), then it would slow down discovery of any failure of the root in that subtree because B’s retransmission of the configuration message would look like assurance that the root (and the path to the root) was still functioning at the time B retransmitted its configuration message”.

She is describing what we can call an “Exception BPDU”. Note: She does not name the behavior as an “Exception BPDU”, but Kennedy Clark does in “Cisco LAN Switching” to simplify the writing and the discussion of the behavior. What an “Exception BPDU” does is that it prevents the downstream switch from sending inferior BPDUs until the BPDU currently stored on the designated port is expired. In the quote from Petr above, he has simplified the description by omitting the behavior of “Exception BPDUs”, which for the purposes of his article is unimportant.

But for a student pursuing a CCIE, such a statement is very tormenting as the person is torn between who to believe in, a quadruple CCIE and CCDE or the mother of STP.

However, “Exception BPDUs” do happen in Cisco IOS. We can illustrate the “Exception BPDU” behavior in a simple lab with the following topology:

The links have been configured as trunk ports with VLAN 1 as the native VLAN.

Sw1 has been made the root for VLAN 1 with a bridge priority of 0.

Also, the Max-Age on Sw1 has been increased to a value of 40, the maximum value IOS allows. The reason for this will become apparent soon but mostly has to do with ease of verification.

The topology stabilizes for VLAN 1 as is shown below:

Sw1#sho span vlan 1
VLAN0001
  Spanning tree enabled protocol ieee
  Root ID    Priority    1
             Address     0012.0183.3b00
             This bridge is the root
             Hello Time   2 sec  Max Age 40 sec  Forward Delay 15 sec
  Bridge ID  Priority    1      (priority 0 sys-id-ext 1)
             Address     0012.0183.3b00
             Hello Time   2 sec  Max Age 40 sec  Forward Delay 15 sec
             Aging Time 300
Interface           Role Sts Cost      Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/23              Desg FWD 19        128.23   P2p 
-----------------------------------------------------------------------------------------------
Sw2#sho span vlan 1
VLAN0001
  Spanning tree enabled protocol ieee
  Root ID    Priority    1
             Address     0012.0183.3b00
             Cost        19
             Port        25 (FastEthernet0/23)
             Hello Time   2 sec  Max Age 40 sec  Forward Delay 15 sec
  Bridge ID  Priority    32769  (priority 32768 sys-id-ext 1)
             Address     0018.b9ff.c780
             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec
             Aging Time 300
Interface           Role Sts Cost      Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/19              Desg FWD 19        128.21   P2p 
Fa0/23              Root FWD 19        128.25   P2p 
-----------------------------------------------------------------------------------------------
Sw3#sho span vlan 1
VLAN0001
  Spanning tree enabled protocol ieee
  Root ID    Priority    1
             Address     0012.0183.3b00
             Cost        38
             Port        21 (FastEthernet0/19)
             Hello Time   2 sec  Max Age 40 sec  Forward Delay 15 sec
  Bridge ID  Priority    32769  (priority 32768 sys-id-ext 1)
             Address     0018.b974.3f80
             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec
             Aging Time 300
Interface           Role Sts Cost      Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/19              Root FWD 19        128.21   P2p

Now to set up our topology to test the scenario, we need a standalone switch that will produce an inferior BPDU during the time when Sw1 is apparently down but not out (its BPDUs still have not reached MESSAGE_AGE = MAX_AGE).

To facilitate this behavior, the following command is issued on Sw3:

int f0/19
  spann bpdufilter enable

As should be true, with a single uplink the is no longer processing any BPDUs, Sw3 believes itself to be the root:

Sw3#sho span vlan 1
VLAN0001
  Spanning tree enabled protocol ieee
  Root ID    Priority    32769
             Address     0018.b974.3f80
             This bridge is the root
             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec
  Bridge ID  Priority    32769  (priority 32768 sys-id-ext 1)
             Address     0018.b974.3f80
             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec
             Aging Time 300
Interface           Role Sts Cost      Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/19              Desg FWD 19        128.21   P2p

Note – it is not sending any BPDUs out F0/19 because of the filter.

Now, Sw3 can be forced to send an inferior BPDU to Sw2 on demand (removal of the BPDU filter).

Note that Sw1 right now is, and this is the normal behavior for 802.1D STP, the ticker of the topology. I.e. if it stops sending BPDUs, the switches in the rest of the topology stop relaying them. Specifically, Sw2 stops sending any BPDUs out on F0/19 towards Sw3.

Therefore, once the following command set is issued on Sw1 :

Sw1(config)#int f0/23
Sw1(config-if)#spanning-tree bpdufilter enable

Then Sw2 should stop receiving BPDUs and in turn should stop sending any BPDUs on its designated ports, namely F0/19.

Now, during this period when Sw1 and Sw2 are not producing any BPDUs but within 40 seconds of issuing the BPDU filter on Sw1 (the MAX_AGE was increased just so there is enough time to perform verification without going into “hurry up offense”), if the BPDU filter is removed on Sw3:

Sw3(config)#int f0/19
Sw3(config-if)#no spanning-tree bpdufilter

Then, Sw3, believing itself to be a Root Bridge, will send a BPDU on F0/19. This BPDU, when Sw2 compares it to the stored BPDU on F0/19, will be inferior thereby forcing Sw2 to send an exception BPDU in response.

The above two commands, on Sw1 first and Sw3 second, will be issued about 15 to 20 seconds apart. What does this do? This enables the check of “before and after” on Sw3. The first round of verification is simple, with show commands. The message age will be shown as increasing towards 40 on Sw2 for BPDUs on F0/23 and then same age will be shown to have instantaneously reflected on Sw3 after the removal of the BPDU filter.

On Sw3, this is its idea of the stabilized STP topology :

Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 1, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 394, received 890
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 394, received 890
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 1, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 394, received 890

Notice the BPDUs are stable, courtesy of the BPDU Filter. Keep these numbers in memory.

After issuing the BPDU filter on Sw1, a few snapshots are taken at Sw2 –

Sw2#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 2131, received 394
 Port 25 (FastEthernet0/23) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.25.
   Timers: message age 9, forward delay 0, hold 0
   BPDU: sent 2131, received 394
Sw2#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 2131, received 394
 Port 25 (FastEthernet0/23) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.25.
   Timers: message age 11, forward delay 0, hold 0
   BPDU: sent 2131, received 394
Sw2#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 2131, received 394
 Port 25 (FastEthernet0/23) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.25.
   Timers: message age 13, forward delay 0, hold 0
   BPDU: sent 2131, received 394
Sw2#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 2131, received 394
 Port 25 (FastEthernet0/23) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.25.
   Timers: message age 14, forward delay 0, hold 0
   BPDU: sent 2131, received 394

As can be seen, the message age on the Root port BPDU is incrementing towards 40 and BPDU transmission has ceased in both directions.

Now on Sw3, the filter command will be removed. Pay attention to the Message-age and BPDU numbers before and after. Note that the commands below were issued in quick succession, no smoke and mirrors (Empty lines have been placed to increase visibility after the fact).

Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 1, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is designated forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 0, forward delay 0, hold 0
   BPDU: sent 394, received 890
Sw3#conf t
!
Enter configuration commands, one per line.  End with CNTL/Z.
Sw3(config)#int f0/19
Sw3(config-if)#no spanning-tree bpdufilter 
Sw3(config-if)#end
!
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 30, forward delay 0, hold 0
   BPDU: sent 395, received 891
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 32, forward delay 0, hold 0
   BPDU: sent 395, received 891
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 33, forward delay 0, hold 0
   BPDU: sent 395, received 891
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 35, forward delay 0, hold 0
   BPDU: sent 395, received 891
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 37, forward delay 0, hold 0
   BPDU: sent 395, received 891
Sw3#sh span vlan 1 det | in Port|Timers|BPDU
  Timers: hello 0, topology change 0, notification 0, aging 300
 Port 21 (FastEthernet0/19) of VLAN0001 is root forwarding 
   Port path cost 19, Port priority 128, Port Identifier 128.21.
   Timers: message age 38, forward delay 0, hold 0
   BPDU: sent 395, received 891

Conclusion –

Study the very first show output and compare it to the one directly after removing the BPDUfilter.

Two important things – Message age of the BPDU jumps to 30 and send and received BPUD counts increase by 1.

Analysis – As soon as the filter is removed, Sw3 sends an inferior BPDU to Sw2. Sw2 sees this and relays the stored BPDU from its root port. This explains the increase of 1 in the BPDU counts and the message age on Sw3 jumping from 0 to 30, impossible under normal circumstances.

In a short follow-up post, the same verification will be performed using IOS debugs.

2 responses to “Classic STP (802.1D) Exception BPDU Generation

  1. the usage of deductive reasoning to extrapolate the existence of exception BPDU processing in IOS is pure genius. I came to the complete opposite conclusion while doing my studies accepting the regular BPDU rx and tx debugs as asbofacto while failing to pay attention to the details of the debugs, and not designing a work upon plan of attack to see the solution (like it ws done here with the bpdufilter).

    bar none the smartest most original thing you have done yet (other than the 2 CCIEs!)

    J

  2. Very nice one.
    As a CCIE I was not aware of that one 🙂
    Thank you very much for the explanation 😀

Leave a Reply