Sunday, May 27, 2012

Configuring QoS in Cisco IOS

I'm back! Today I'm posting a how-to on QoS (Quality of Service) configuration on Cisco routers and switches.

QoS is a valuable tool whenever you have different types of traffic arriving at a router where they'll need to be aggregated and sent out over a single link like a WAN connection. Some types of data and applications require traffic to be handled with as little latency and jitter as possible; VOIP is a prime example of this, as well as Citrix, RDP, and anything else that involves streaming audio and video.

The reason this is important is because as a traffic arrives in a router, it gets sent into a processing queue in the router based upon the order in which the packet was received. After routing decisions are made, this traffic is sent into an input queue for the WAN interface in preparation of being sent out over the WAN link.

Traffic passes from this input queue into the transmit ring, a hardware output queue. If this queue fills up during a period of high traffic congestion, additional traffic arriving to the interface is queued in the software output queue, where it waits on room in the transmit ring to become available. If the software output queue (usually just called the output queue) gets filled up and traffic is still arriving at the interface, the interface has to start dropping packets. These are recorded as "Output drops" when you do a show interface command.

If this happens, critical data can be dropped, or voice traffic can be delayed while it sits in the output queue with less important traffic in front of it. Even if the voice traffic is successfully delivered, the delays can cause stuttering and poor quality on the call.

QoS prevents problems like these by guaranteeing bandwidth to critical data and preventing that traffic from being dropped if the circuit becomes congested. It can also be configured to prioritize certain types of traffic over others in terms of the order they are transmitted, to ensure time-sensitive traffic is transmitted as quickly as possible.

Hit the jump and we'll look at this in detail!

So QoS works in a Cisco router in a few pieces. First, you need something to define the different classes of traffic you would like to have. Thus is born the class map:
Code:
class-map match-any QOS_REAL-TIME
 match ip dscp 46 (alternatively match ip dscp ef)
class-map match-any QOS_CRITICAL-DATA
 match ip dscp af41 (alternatively match ip dscp 34)
 match access-group name IMPORTANT-SHIT
Now you can see the first class-map for real-time traffic is set to match packets tagged with DSCP value 46, aka EF (Expedited Forwarding). Usually this is voice traffic, and often the tagging is done by the voice equipment, or IP PBX.

The second for critical data matches anything arriving pre-tagged as DSCP value 34, aka AF41. It also matches anything that makes it through the access-group IMPORTANT-SHIT.
Code:
ip access-list extended IMPORTANT-SHIT
 permit ip any any eq citrix-ica
 permit udp any any range 6112 6119 (StarCraft is serious business!)
So now we see that the access-list IMPORTANT-SHIT is permitting traffic that is coming through as Citrix traffic, or UDP on Battle.Net ports. We don't want the network admin's StarCraft sessions lagging, now do we?

Okay so now we need something that will actually govern these classes of traffic we've defined. Here comes the infamous policy-map.
Code:
policy-map 75r_24c
 class QOS_REAL-TIME
  bandwidth percent 75
 class QOS_CRITICAL-DATA
  bandwidth percent 24
  set ip dscp 34
 class class-default
  shape average 1000000000 (One Beeeellion... bits!)
Alright, now we've got the meat of it put together. Standard practice at work is to name the map based upon the bandwidth percentages used but you can do whatever. So we define each class, and underneath we allocate a percentage of the bandwidth we want to guarantee that traffic.

I've also got a set ip dscp 34 statement under my CRITICAL-DATA class. I'm tagging that traffic because while I was matching AF41 (DSCP 34) traffic, that Citrix and StarCraft traffic wasn't coming to me pre-tagged. I'm tagging it now so that when I hand it off to my provider, they can see it come across tagged and match it (of course, this is assuming I've called them and had them set up QoS on their side). This way they don't need to build any ACLs. Besides, they don't need to know that I've got StarCraft in that priority queue.

At the bottom I've got my shaper, shaping traffic heading out of the interface to a mere 1 Billion bits (though I'm guessing a normal Cisco router would give me a hard time for this when I try to apply it to a FastEthernet port at 100 Mbps!) Also note that you can set up a separate shape-policy and apply it instead of the shape-average statement I used above. The shaper isn't necessary on Serial links since the router already knows the max bandwidth of the circuit.

With a serial interface the T1 knows the bandwidth capacity of the link, but by default only allows 75% of it to be used by a QoS policy. To change this, you use a max-reserved-bandwidth 100 applied to the serial interface to allow the QoS policy to use all of the available bandwidth. It's not unusual for me to find QoS policies set up and applied but completely inactive within a router because the QoS policy allows for more bandwidth to be used by the policy than the 75% default can provide. A show policy-map int Serialx/x will usually reveal this. In addition, standard practice at my workplace is to only allow the QoS queues to add up to 99%, not 100. The reason for this is to prevent the QoS policy from smothering traffic like keepalives or route advertisements.

IMPORTANT NOTE: Instead of using the bandwidth statement you can use a priority statement (ie priority percent 75). In addition to guaranteeing bandwidth, priority actually delays other traffic to allow the priority traffic to the front of the line heading into the transmit ring (buffer) of the physical interface the policy is applied to. This is important when handling voice and traffic VERY sensitive to latency or jitter. However, while with the bandwidth statement, any available bandwidth by a queue with spare bandwidth gets allocated to other queues that need bandwidth, priority does not do this. So if my CRITICAL-DATA queue has spare bandwidth and my REAL-TIME queue does not, with the bandwidth statement the REAL-TIME queue will be allocated some of CRITICAL-DATA's free bandwidth. With priority statements this will not happen; 75 percent is all REAL-TIME will ever get when the circuit is congested.

We're not done though! We have to tell the router which interface QoS applies to. Generally, QoS is applied outbound on an interface.
Code:
interface FastEthernet0/0
 description Connection to a Series of Tubes
 ip address 123.123.123.123 255.255.255.0
 service-policy output 75r_24c
...aaannnd there we have it! QoS is now configured for this router.

A couple other quick points in closing:

In an ADTRAN, QoS is set up almost exactly the same with a few minor changes in syntax, and the fact that the class-map is actually merged within the policy map (which is called a "qos map").

Additionally, in Ciscos, policy maps can actually be nested, much as how I described a shaper policy can be nested within class-default under the main QoS policy. This is often done when applying QoS to traffic from multiple VLANs.

Another thing worth mentioning is the random-detect statement you may sometimes see under class-default in QoS policies. This causes the best effort queue to drop random packets when congestion occurs, which can cause TCP retransmits and thus lowered TCP window sizes which effectively throttles down TCP traffic to reduce bandwidth maxing. Random-detect drops can be weighted based upon DSCP values and the drop rate can be adjusted as well. My company doesn't use this statement as we rely upon shapers to throttle traffic and our customers to decide if they want to enforce limitations upon their TCP streams.

I hope this proves helpful to some folks out there looking to get QoS configured on their router or switch. If anyone has any suggestions, comments, or corrections, please post below, I appreciate the feedback!

1 comment: