Spanning-Tree PortFast

Imagine the scenario: It’s Monday morning and you’re running a few minutes late getting into the office. You have an important meeting starting as you’re taking out your laptop, and letting it boot up while you plug in the charger and that cruddy old network cable coming out of the floor. As the network cable clicks into your laptop, you expect network access as you frantically try to join the meeting. Instead, your laptop gives you some annoying error about network connectivity. Blah.

What’s going on? You’re probably being slowed down by having to wait on Spanning Tree Protocol to start sending and receiving traffic on the port connected to your laptop. Fortunately, the PortFast feature can help speed things up.

Initial Port State Transition

Before you plug in your laptop, the switchport will be in a down state, this Spanning Tree Protocol is not actively running on the port. When you connect the cable, the laptop NIC and switchport will transition the link and line protocol to up. Once the switchport comes up, Spanning Tree has work to do. STP does not yet know if another STP enabled switch is connected, a laptop, or a network managed coffee pot.

Before the switchport can transition to the STP forwarding state (and let you join the network for your meeting), it must first go though the Listening and Learning STP port states. Each of these states will last for the duration of the forward delay timer, 15 seconds by default. That means 30 seconds before your laptop has a hope obtaining network connectivity!

To demonstrate this issue and a solution, I built the simple network below in Cisco Modeling Labs. ClientA is a router that is configured to obtain it’s IP address and default route next hop via DHCP. Router1 is acting as a DHCP server.

Initial Client Connectivity Without PortFast

On SW3 I have STP event debugging enabled, on ClientA I have DHCP debugging. With these debugs enabled, I will “bounce” the link between ClientA and SW3. This will simulate a client’s initial connection to the network. Because this is a virtual environment, I will have to admin down the ClientA and SW3 port. I will enable both ports as fast as possible.

SW3#show debugging 
Spanning Tree:
  Spanning Tree event debugging is on
ClientA#show debugging 
DHCPC:
  DHCP client activity debugging is on
SW3(config-if)#no shutdown 
SW3(config-if)#
*Jan 31 17:59:12.419: set portid: VLAN0001 Gi0/2: new port id 8003
*Jan 31 17:59:12.420: STP: VLAN0001 Gi0/2 -> listening
*Jan 31 17:59:14.242: %LINK-3-UPDOWN: Interface GigabitEthernet0/2, changed state to up
*Jan 31 17:59:15.243: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/2, changed state to up
*Jan 31 17:59:27.420: STP: VLAN0001 Gi0/2 -> learning
*Jan 31 17:59:42.425: STP[1]: Generating TC trap for port GigabitEthernet0/2
*Jan 31 17:59:42.427: STP: VLAN0001 sent Topology Change Notice on Gi0/0
*Jan 31 17:59:42.428: STP: VLAN0001 Gi0/2 -> forwarding

In the debug output above, we can see the STP port state transitions occurring. Listening and Learning states persisted for 15 seconds, the forward delay timer. The command below confirms the FWD delay timer.

SW3#show spanning-tree vlan 1 bridge forward-time 
VLAN0001         15

What was the client doing while STP did its thing? Attempt over and over to obtain IP addressing via DHCP. Cisco routers are persistent. Some other host may not be. DHCP failed until the switchport changed to the Forwarding state.

*Jan 31 17:59:14.187: DHCP: DHCP client process started: 10
*Jan 31 17:59:14.203: DHCP: Waiting for 5 seconds on interface GigabitEthernet0/0 to come up
*Jan 31 17:59:16.012: %LINK-3-UPDOWN: Interface GigabitEthernet0/0, changed state to up
*Jan 31 17:59:17.014: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/0, changed state to up
*Jan 31 17:59:19.203: RAC: Starting DHCP discover on GigabitEthernet0/0
*Jan 31 17:59:19.203: DHCP: Try 1 to acquire address for GigabitEthernet0/0
*Jan 31 17:59:19.267: DHCP: allocate request
*Jan 31 17:59:19.267: DHCP: zapping entry in DHC_PURGING state for Gi0/0
*Jan 31 17:59:19.268: DHCP: deleting entry F356AB8 0.0.0.0 from list
*Jan 31 17:59:19.268: DHCP: new entry. add to queue, interface GigabitEthernet0/0
*Jan 31 17:59:19.269: DHCP: SDiscover attempt # 1 for entry:
*Jan 31 17:59:19.269: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 17:59:19.270: DHCP: SDiscover 306 bytes 
*Jan 31 17:59:19.272:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 17:59:22.403: DHCP: SDiscover attempt # 2 for entry:
*Jan 31 17:59:22.403: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 17:59:22.404: DHCP: SDiscover 306 bytes 
*Jan 31 17:59:22.405:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 17:59:26.403: DHCP: SDiscover attempt # 3 for entry:
*Jan 31 17:59:26.404: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 17:59:26.404: DHCP: SDiscover 306 bytes 
*Jan 31 17:59:26.406:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0%Unknown DHCP problem.. No allocation possible
*Jan 31 17:59:39.457: DHCP: Waiting for 5 seconds on interface GigabitEthernet0/0
*Jan 31 17:59:44.459: DHCP: Try 2 to acquire address for GigabitEthernet0/0
*Jan 31 17:59:44.522: DHCP: allocate request
*Jan 31 17:59:44.523: DHCP: zapping entry in DHC_PURGING state for Gi0/0
*Jan 31 17:59:44.523: DHCP: deleting entry F356AB8 0.0.0.0 from list
*Jan 31 17:59:44.524: DHCP: new entry. add to queue, interface GigabitEthernet0/0
*Jan 31 17:59:44.524: DHCP: SDiscover attempt # 1 for entry:
*Jan 31 17:59:44.525: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 17:59:44.525: DHCP: SDiscover 306 bytes 
*Jan 31 17:59:44.527:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 17:59:48.403: DHCP: SDiscover attempt # 2 for entry:
*Jan 31 17:59:48.403: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 17:59:48.404: DHCP: SDiscover 306 bytes 
*Jan 31 17:59:48.406:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 17:59:48.419: DHCP: Received a BOOTREP pkt
*Jan 31 17:59:48.420: DHCP: offer received from 10.0.0.254
*Jan 31 17:59:48.420: DHCP: SRequest attempt # 1 for entry:
*Jan 31 17:59:48.421: DHCP: SRequest- Server ID option: 10.0.0.254
*Jan 31 17:59:48.421: DHCP: SRequest- Requested IP addr option: 10.0.0.1
*Jan 31 17:59:48.421: DHCP: SRequest: 318 bytes
*Jan 31 17:59:48.422: DHCP: SRequest: 318 bytes
*Jan 31 17:59:48.424:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 17:59:48.435: DHCP: Received a BOOTREP pkt
*Jan 31 17:59:52.515: DHCP: Sending notification of ASSIGNMENT:
*Jan 31 17:59:52.515:   Address 10.0.0.1 mask 255.255.255.0
*Jan 31 17:59:52.516: DHCP Client Pooling: ***Allocated IP address: 10.0.0.1
*Jan 31 17:59:52.546: Allocated IP address = 10.0.0.1  255.255.255.0

*Jan 31 17:59:52.547: %DHCP-6-ADDRESS_ASSIGN: Interface GigabitEthernet0/0 assigned DHCP address 10.0.0.1, mask 255.255.255.0, hostname ClientA

Enabling PortFast

As mentioned previously, the Spanning Tree PortFast feature can alleviate most of the delay that occurs when a client connects to the switch. There are different ways to configure PortFast, but for this article I will enable it in interface configuration. A warning message is printed when the feature is enabled. For additional loop protection on PortFast enabled ports, check out the BPDUGuard feature.

SW3#configure terminal 
Enter configuration commands, one per line.  End with CNTL/Z.
SW3(config)#interface gigabitEthernet0/2
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 GigabitEthernet0/2 but will only
 have effect when the interface is in a non-trunking mode.
SW3(config-if)#

So now that we have PortFast enabled, can the client connect faster and make it to the meeting? You bet! Let’s try again with the debugs enabled. The switchport goes to forwarding immediately.

SW3(config-if)#no shutdown 
SW3(config-if)#
*Jan 31 18:15:52.102: set portid: VLAN0001 Gi0/2: new port id 8003
*Jan 31 18:15:52.102: STP: VLAN0001 Gi0/2 ->jump to forwarding from blocking
*Jan 31 18:15:53.925: %LINK-3-UPDOWN: Interface GigabitEthernet0/2, changed state to up
*Jan 31 18:15:54.925: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/2, changed state to up



How’d the client do? Much better. DHCP was successful on the first attempt!

*Jan 31 18:15:55.264: DHCP: DHCP client process started: 10
*Jan 31 18:15:55.279: DHCP: Waiting for 5 seconds on interface GigabitEthernet0/0 to come up
*Jan 31 18:15:57.098: %LINK-3-UPDOWN: Interface GigabitEthernet0/0, changed state to up
*Jan 31 18:15:58.098: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/0, changed state to up
*Jan 31 18:16:00.280: RAC: Starting DHCP discover on GigabitEthernet0/0
*Jan 31 18:16:00.280: DHCP: Try 1 to acquire address for GigabitEthernet0/0
*Jan 31 18:16:00.335: DHCP: allocate request
*Jan 31 18:16:00.335: DHCP: new entry. add to queue, interface GigabitEthernet0/0
*Jan 31 18:16:00.336: DHCP: Client socket is opened
*Jan 31 18:16:00.337: DHCP: SDiscover attempt # 1 for entry:
*Jan 31 18:16:00.337: DHCP: SDiscover: sending 306 byte length DHCP packet
*Jan 31 18:16:00.337: DHCP: SDiscover 306 bytes 
*Jan 31 18:16:00.339:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 18:16:01.337: DHCP: Received a BOOTREP pkt
*Jan 31 18:16:01.337: DHCP: offer received from 10.0.0.254
*Jan 31 18:16:01.338: DHCP: SRequest attempt # 1 for entry:
*Jan 31 18:16:01.338: DHCP: SRequest- Server ID option: 10.0.0.254
*Jan 31 18:16:01.339: DHCP: SRequest- Requested IP addr option: 10.0.0.1
*Jan 31 18:16:01.339: DHCP: SRequest: 318 bytes
*Jan 31 18:16:01.339: DHCP: SRequest: 318 bytes
*Jan 31 18:16:01.341:             B'cast on GigabitEthernet0/0 interface from 0.0.0.0
*Jan 31 18:16:01.351: DHCP: Received a BOOTREP pkt
*Jan 31 18:16:05.432: DHCP: Sending notification of ASSIGNMENT:
*Jan 31 18:16:05.433:   Address 10.0.0.1 mask 255.255.255.0
*Jan 31 18:16:05.433: DHCP Client Pooling: ***Allocated IP address: 10.0.0.1
*Jan 31 18:16:05.441: Allocated IP address = 10.0.0.1  255.255.255.0

*Jan 31 18:16:05.441: %DHCP-6-ADDRESS_ASSIGN: Interface GigabitEthernet0/0 assigned DHCP address 10.0.0.1, mask 255.255.255.0, hostname ClientA

Danger!

When PortFast was enabled, that scary message was printed to the terminal, and for good reason. Telling STP to skip listening and learning means that bridging loops can be introduced. One way to protect against this is to enable BPDUGuard on PortFast enabled ports. BPDUGuard simply disables a PortFast interface if it receives a STP BPDU message. I recommend researching and utilizing BPDUGuard before configuring PortFast in your network.

Another Advantage of Portfast

It wasn’t included in the demonstrations but without Portfast enabled, edge ports changing state will trigger STP Topology Change Notifications(TCNs). When a TCN occurs, the bridge that experienced the port state change will send a TCP out of its root port. The TCN will be forwarded on until it reaches the root bridge. The root bridge will then set the TCN bit in it’s BPDUs that are forwarded throughout the STP domain. When bridges become aware of a topology change, they sent their MAC address aging timer to the value of the forward delay timer, 15 seconds by default. Reducing the MAC aging timer can mean some CAM table entries time out when they didn’t need to, leading to unknown unicast flooding. Portfast helps with this by disabling the initiation of STP TCNs when Portfast enabled ports go up or down.