Secure Spanning-tree on Cisco IOS switches

With 3 tier campus network design, layer 2 configurations, and spanning-tree are often implemented throughout the access and the disruption layer switches. In this article, using some Why? and How? questions, I will demonstrate securing the network against possible spanning-tree disruptions, such as rogue access point additions and the loss of stability to the root bridge by the addition of switches to the network. The improper addition of switches to the network can be either malicious or accidental. In either case, the network can be secured against such a disruption.

To best describe, I am using this simple topology of 4 Cisco IOS switches, running 15.4 IOS code and connected as shown in the above-mentioned diagram. Here is the initial configuration of all 4 switches.

SW1-4

conf t
 vlan 10
 vlan 20
exit
!
spanning-tree mode rapid-pvst
exit
!
interface range Ethernet1/0-2
 switchport trunk encapsulation dot1q
 switchport mode trunk 
 no shutdown
end
Spanning-tree Root Bridge placement
Why?

Spanning-tree elects the root bridge based on the priority and MAC address fields of the Bridge ID. The device with the lowest priority value is elected the root. If there is a tie in priority, the device with the lowest MAC address is elected root. It is advisable to manually configure the root bridge of spanning-tree to ensure optimized paths throughout the Layer 2 network.

How?

In this example, I will configure SW1 as spanning-tree root for VLAN 1 & 10, and SW2 as spanning-tree root for VLAN 20.

Configuration

SW1(config)#spanning-tree vlan 1,10 root primary
SW1(config)#spanning-tree vlan 20 root secondary
SW2(config)#spanning-tree vlan 1,10 root secondary
SW2(config)#spanning-tree vlan 20 root primary

Verification

SW1#show spanning-tree vlan 10

VLAN0010
Spanning tree enabled protocol rstp
Root ID Priority 24586
Address aabb.cc00.0100
This bridge is the root
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec

Bridge ID Priority 24586 (priority 24576 sys-id-ext 10)
Address aabb.cc00.0100
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300 sec

Interface Role Sts Cost Prio.Nbr Type
——————- —- — ——— ——– ——————————–
Et1/0 Desg FWD 100 128.5 Shr
Et1/1 Desg FWD 100 128.6 Shr
Et1/2 Desg FWD 100 128.7 Shr
SW2#show spanning-tree vlan 20

VLAN0020
Spanning tree enabled protocol rstp
Root ID Priority 24596
Address aabb.cc00.0200
This bridge is the root
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec

Bridge ID Priority 24596 (priority 24576 sys-id-ext 20)
Address aabb.cc00.0200
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300 sec

Interface Role Sts Cost Prio.Nbr Type
——————- —- — ——— ——– ——————————–
Et1/0 Desg FWD 100 128.5 Shr
Et1/1 Desg FWD 100 128.6 Shr
Et1/2 Desg FWD 100 128.7 Shr

Implement Root Guard
Why?

If a new switch is maliciously or accidentally added to the network, the STP topology could be changed if the new switch has
a lower bridge ID than the current root bridge. Root guard helps prevent this by putting a port that hears these BPDUs in the root-inconsistent state. In the root-inconsistent state, BPDUs are not sent outbound but accepted inbound, and all received frames are dropped by the switch-port. The switch automatically recovers the port from Root Inconsistent and starts negotiating the new port state and role, as soon as superior BPDUs are no longer received inbound.

How?

Root guard is enabled on a per-port basis with the spanning-tree guard root command. You should use root guard on switch ports where you would never expect to nd the root bridge for a VLAN.

In this example, I will enable root guard on SW1’s port Eth2/0 and Eth2/1.

Configuration

SW1(config)#interface range Ethernet2/0-1
SW1(config-if-range)# spanning-tree guard root

Verification

If the correct logging is ON, you will receive following log message indicating root guard implementation.

*Nov 6 14:40:56.065: %SPANTREE-2-ROOTGUARD_CONFIG_CHANGE: Root guard enabled on port Ethernet2/0.
*Nov 6 14:40:56.065: %SPANTREE-2-ROOTGUARD_CONFIG_CHANGE: Root guard enabled on port Ethernet2/1.

You can also verify inconsistent ports using this command. Right now, there is no interface shown because there is no violation occurred yet.

SW1#show spanning-tree inconsistentports

Name Interface Inconsistency
——————– ———————— ——————

Number of inconsistent ports (segments) in the system : 0

Implement spanning-tree portfast
Why?

A switchport configured with Portfast will immediately start transmitting data in the ‘forwarding’ state bypassing the other spanning-tree states rather than waiting for normal STP convergence time.

There is also another great reason to configure Portfast on your client edge ports, that is not such widely known. Whenever a switchport participating in spanning-tree goes up or down the switch generates a TCN (Topology Change Notification) packet and sends this TCN packet to the root bridge, which starts SPT convergence. Now depending on the size of your layer 2 network this can waste a lot of resources on your switches which unnecessarily cause temporary traffic loss. Keep in mind also, that if CAM table entries start expiring this can cause unnecessary ARP traffic for additional information the switch already had.

Spanning-tree Portfast is certainly a great feature to have configured on your downstream ports connecting to your end-user workstation or your servers.

How?

Portfast can be configured using spanning-tree portfast command on an access port (interface subcommand) or by spanning-tree portfast trunk command on a trunk port (interface subcommand) or by spanning-tree portfast default(global command). The port must be an access port for this to work. If the port is configured as a trunk, the global portfast command will not convert the port to an edge port.

In this example, I will enable portfast on SW3 and SW4’s switchport Eth0/0.

Configuration

SW3(config)#interface Ethernet0/0
SW3(config-if)# switchport access vlan 10
SW3(config-if)# switchport mode access
SW3(config-if)# spanning-tree portfast
%Warning: portfast should only be enabled on ports connected to a single
host. Connecting hubs, concentrators, switches, bridges, etc… to this
interface when portfast is enabled, can cause temporary bridging loops.
Use with CAUTION

%Portfast has been configured on Ethernet0/0 but will only
have effect when the interface is in a non-trunking mode.
SW3(config-if)#end
SW4(config)#interface Ethernet0/0
SW4(config-if)# switchport trunk encapsulation dot1q
SW4(config-if)# switchport mode trunk
SW4(config-if)# switchport trunk allowed vlan add 10,20
SW4(config-if)# duplex auto
SW4(config-if)# spanning-tree portfast trunk
%Warning: portfast should only be enabled on ports connected to a single
host. Connecting hubs, concentrators, switches, bridges, etc… to this
interface when portfast is enabled, can cause temporary bridging loops.
Use with CAUTION

SW4(config-if)#exit

Verification

You can verify if the portfast is enabled or not using this command.

SW3#show spanning-tree interface ethernet 0/0 portfast
VLAN0010 enabled

SW4#show spanning-tree interface ethernet 0/0 portfast
VLAN0001 enabled
VLAN0010 enabled
VLAN0020 enabled
Or you can also verify the Edge status of switchport because of portfast configuration using this command.

SW4#show spanning-tree interface ethernet 0/0

Vlan Role Sts Cost Prio.Nbr Type
——————- —- — ——— ——– ——————————–
VLAN0001 Desg FWD 100 128.1 Shr Edge
VLAN0010 Desg FWD 100 128.1 Shr Edge
VLAN0020 Desg FWD 100 128.1 Shr Edge

NOTE:
Spanning-tree Loopguard has no effect on portfast enabled ports.
Implement spanning-tree BPDUGuard
Why?

BPDU Guard ensures that unauthorized switches cannot be plugged in to the network. BPDU guard protects ports from this type of situation by placing the interface in the error-disable state. With BPDU Guard, the port does not recover from the err-disabled state unless errdisable recovery related additional configuration is added. You can tell the switch to change from err-disabled state back to an up state after a certain amount of time.

How?

To enable BPDU guard on all PortFast-enabled ports, use the spanning-tree portfast bpduguard default global configuration command. Use an interface level command spanning-tree bpduguard enable to enable BPDU guard on a specific port.

In this example, I will enable BPDU guard globally on SW3 and use interface level command on Eth0/0 interface of SW4. I will also configure both switches to recover the switchports from the error-disable state after 120 sec.

Configuration

SW3(config)#spanning-tree portfast bpduguard default
SW3(config)#!
SW3(config)#errdisable recovery cause bpduguard
SW3(config)#errdisable recovery interval 120
SW4(config)#interface Ethernet0/0
SW4(config-if)# spanning-tree bpduguard enable
SW4(config-if)# exit
SW4(config)#errdisable recovery cause bpduguard
SW4(config)#errdisable recovery interval 120

Verification

SW3# show spanning-tree summary
Switch is in rapid-pvst mode
Root bridge for: none
EtherChannel misconfig guard is enabled
Extended system ID is enabled
Portfast Default is disabled
PortFast BPDU Guard Default is enabled
Portfast BPDU Filter Default is disabled
Loopguard Default is disabled
UplinkFast is disabled
BackboneFast is disabled
Configured Pathcost method used is short

Name Blocking Listening Learning Forwarding STP Active
———————- ——– ——— ——– ———- ———-
VLAN0001 1 0 0 14 15
VLAN0010 1 0 0 3 4
VLAN0020 1 0 0 2 3
———————- ——– ——— ——– ———- ———-
3 vlans 3 0 0 19 22
SW4# show errdisable recovery | i Time|bpdu
ErrDisable Reason Timer Status
bpduguard Enabled
Timer interval: 120 seconds

NOTE:
In some corner cases, BPDU filtering is more recommended than BPDU guard. When BPDU filtering is enabled on an individual port, it prevents unnecessary BPDUs from being transmitted to & from the host device. BPDU filtering doesn’t error-disable the switchport like BPDU guard.
Implement Strom-Control
Why?

Strom control can be handy to protect against broadcast storms resulting from spanning tree misconfiguration, or even unicast storms created by malfunction host NICs.

How?

Configuring storm control on an interface is simple. At a minimum you’ll need to specify a traffic type (unicast, multicast, or broadcast) and a rising threshold:

In this example, I will configure SW3 and SW4’s switchport Eth1/0 to restrict broadcast storm to 50% of interface capacity and switchport Eth0/0 to only allow 100 unicast packets per seconds.

Configuration

SW3(config)#interface Ethernet0/0
SW3(config-if)# storm-control unicast level pps 100

SW3(config)#interface Ethernet1/0
SW3(config-if)# storm-control broadcast level 50
SW4(config)#interface Ethernet0/0
SW4(config-if)# storm-control unicast level pps 100

SW4(config)#interface Ethernet1/0
SW4(config-if)# storm-control broadcast level 50

Verification

SW3#show storm-control
Interface Filter State Upper Lower Current
——— ————- ———– ———– ———-
Eth0/0 Forwarding 100 pps 100 pps 0 pps
Eth0/1 Forwarding 50.00% 50.00% 0.00%

Implement UDLD
Why?

Unidirectional Link Detection (UDLD) is a Cisco-proprietary protocol, detects the existence of unidirectional links. In other words, detects the loss of bidirectional communication on a link. A unidirectional link occurs when traffic is transmitted between neighbors in one direction only. Unidirectional links can cause spanning-tree topology loops. UDLD allows devices to detect when a unidirectional link exists and shut down the affected interface. UDLD can be run on both fiber-optic and twisted-pair copper links.

How?

You can configure UDLD on a per-port basis using udld enable [aggressive] command or globally for all fiber-optic gigabit interfaces using udld { enable | aggressive } command . The aggressive keyword places the port in the error-disable state when a violation occurs on the port.

In this example, I will enable UDLD on interface Eth1/0-2 of all 4 switches.

Configuration

SW1(config)# interface range Ethenet1/0-2
SW1(config-if)# udld port aggressive

SW2(config)# interface range Ethenet1/0-2
SW2(config-if)# udld port aggressive

SW3(config)# interface range Ethenet1/0-2
SW3(config-if)# udld port aggressive

SW4(config)# interface range Ethenet1/0-2
SW4(config-if)# udld port aggressive

Verification

SW3# show udld Eth1/0 | i Current
Current bidirectional state: Bidirectional
Current operational state: Advertisement – Single neighbor detected
Current neighbor state: Bidirectional

NOTE: 
UDLD can only detect unidirectional links when UDLD is enabled on both ends of a link.
Thoughts on Loop Guard

Although not configured in this lab, loop guard can be configured as an alternative or in addition to UDLD. UDLD and Loop Guard features prevent network device failures that are due to faulty hardware or software errors. Problems such as link duplex mismatch, unidirectional link failure, frame corruption, resource errors, and misconfigurations can disrupt the spanning tree, which in turn disrupts network traffic.

Here are some of the limitations with Loop Guard interoperability.

  • The root guard is mutually exclusive with the loop guard. The root guard is used on designated ports, and it does not allow the port to become non-designated. The loop guard works on non-designated ports and does not allow the port to become designated through the expiration of max_age. The root guard cannot be enabled on the same port as the loop guard. When the loop guard is configured on the port, it disables the root guard configured on the same port. NOTE: you can use UDLD and root guard on same port to achieve the same results.
  • Both uplink fast and backbone fast are transparent to the loop guard feature.
  • Loop guard cannot be enabled for ports on which portfast is enabled. Since BPDU Guard works on portfast-enabled ports, some restrictions apply to BPDU Guard.
  • Loop guard should not be enabled on shared links. If you enable loop guard on shared links, traffic from hosts connected to shared segments might be blocked.
  • Loop guard functions correctly in the MST environment. When the switch is operating in MST mode, BPDUs are not sent on non-boundary ports only if the interface is blocked by loop guard in all MST instances. On a boundary port, loop guard blocks the interface in all MST instances.

I hope you enjoyed reading this article. Please feel free to leave any comment or feedback.