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.
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
Very nice one.
As a CCIE I was not aware of that one 🙂
Thank you very much for the explanation 😀