QoS Pre-Classify on Cisco IOS

In this lesson you will learn about the QoS Pre-classify feature. When you use tunnelling, your Cisco IOS router will do classification based on the outer (post) header, not the inner (pre) header. This can cause issues with QoS policies that are applied to the physical interfaces. I will explain the issue and we will take a look how we can fix it. Here’s the topology that we will use:

R1 R2 R3 GRE tunnel loopback interfaces

Below is the tunnel configuration, I’m using a static route so that R1 and R3 can reach each other’s loopback interfaces through the tunnel:

R1(config)#interface Tunnel 0
R1(config-if)#tunnel source
R1(config-if)#tunnel destination
R1(config-if)#ip address
R1(config)#ip route

The configuration on R3 is similar:

R3(config)#interface Tunnel 0
R3(config-if)#tunnel source
R3(config-if)#tunnel destination
R3(config-if)#ip address
R3(config)#ip route

The tunnel is up and running, before we play with classification and service policies, let’s take a look at the default classification behaviour of Cisco IOS when it comes to tunnelling…

Default Classification Behaviour

IOS will copy the information in the TOS (Type of Service) byte from the inner IP header to the outer IP header by default. We can demonstrate this with a simple ping, here’s how:

Protocol [ip]: 
Target IP address:
Repeat count [5]: 
Datagram size [100]: 
Timeout in seconds [2]: 
Extended commands [n]: y
Source address or interface:
Type of service [0]: 160
Set DF bit in IP header? [no]: 
Validate reply data? [no]: 
Data pattern [0xABCD]: 
Loose, Strict, Record, Timestamp, Verbose[none]: 
Sweep range of sizes [n]: 
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to, timeout is 2 seconds:
Packet sent with a source address of 
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/4 ms

This ping between and will go through the tunnel and I marked the TOS byte of this IP packet with 160 (decimal). 160 in binary is 10100000, remove the last two bits and you have our 6 DSCP bits. 101000 in binary is 40 in decimal which is the same as the CS5.

Below you can see a wireshark capture of this ping:

GRE Tunnel TOS marking inner outer

As you can see, Cisco IOS automatically copied the TOS byte from the inner IP header to the outer IP header. This is a good thing, I’m using GRE in my example so we can see both headers but if this was an encrypted IPSEC tunnel then we (and any device in between) could only see the outer header.

When you have QoS policies based on the TOS byte then you will have no problems at all because the TOS byte is copied from the inner to the outer header. You will run into issues when you have policies based on access-lists that match on source / destination addresses and/or port numbers. Let me give you an example…

Post Header Classification

I’m going to create two class-maps, one for telnet traffic and another one for GRE traffic. Both class-maps will use an access-list to classify traffic:

R1(config)#ip access-list extended TELNET
R1(config-ext-nacl)#permit tcp any any eq telnet

R1(config)#class-map TELNET
R1(config-cmap)#match access-group name TELNET
R1(config)#ip access-list extended GRE
R1(config-ext-nacl)#permit gre any any

R1(config)#class-map GRE
R1(config-cmap)#match access-group name GRE

The two class-maps will be used in a policy-map:

R1(config)#policy-map POLICE
R1(config-pmap)#class TELNET
R1(config-pmap-c)#police 128000
R1(config-pmap)#class GRE

I’ve added policing for telnet traffic and nothing for GRE. It doesn’t matter what “actions” we configure here, even without an action the traffic will still be classified and it will show up in the policy-map. Let’s activate it on the physical interface:

R1(config)#interface FastEthernet 0/0
R1(config-if)#service-policy output POLICE

Something to keep in mind is that when you enable a policy on the physical interface, it will be applied to all tunnel interfaces. Let’s generate some telnet traffic between R1 and R3 so it goes through the tunnel:

R1#telnet /source-interface loopback 0
Trying ... Open

Now take a look at the policy-map:

R1#show policy-map interface FastEthernet 0/0

  Service-policy output: POLICE

    Class-map: TELNET (match-all)
      0 packets, 0 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: access-group name TELNET
          cir 128000 bps, bc 4000 bytes
        conformed 0 packets, 0 bytes; actions:
        exceeded 0 packets, 0 bytes; actions:
        conformed 0 bps, exceed 0 bps

    Class-map: GRE (match-all)
      11 packets, 735 bytes
      5 minute offered rate 0 bps
      Match: access-group name GRE

    Class-map: class-default (match-any)
      2 packets, 120 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: any

See how it only matches the GRE traffic? We don’t have any matches for the telnet traffic. If this was a real network, it means that telnet traffic will never get policed (or any other action you configured). The reason that we don’t see any matches is because Cisco IOS first encapsulates the IP packet and then applies the policy to the GRE traffic. Let me illustrate this:

IP Packet GRE encapsulation

The blue IP header on top is our original IP packet with telnet traffic, this is encapsulated and the router adds a GRE header and a new IP header (the red one). The policy-map is then applied to this outer IP header.

How do we fix this? There are a couple of options…let’s look at the first one!

We're Sorry, Full Content Access is for Members Only...

If you like to keep on reading, Become a Member Now! Here is why:

  • Learn any CCNA, CCNP and CCIE R&S Topic. Explained As Simple As Possible.
  • Try for Just $1. The Best Dollar You’ve Ever Spent on Your Cisco Career!
  • Full Access to our 735 Lessons. More Lessons Added Every Week!
  • Content created by Rene Molenaar (CCIE #41726)

512 Sign Ups in the last 30 days

100% Satisfaction Guaranteed!
You may cancel your monthly membership at any time.
No Questions Asked!


Forum Replies

  1. Very clear and concise explanation, thank you

  2. Thanks a lot for your excelent explanation…

  3. Hello Rene,

    I was very happy to find you had a lab on this subject. I went through this myself and everything worked as described, except one area. When I moved the service policy off the FA0/0 interface and put it on the Tunnel 0 interface (and removed the qos pre-qualify from the tunnel), the encapsulated IP header had the CS5 markings, but the outer header did not (as seen below)


    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))


    Differentiated Services Field: 0xa0 (DSCP 0x28: Class Select

    ... Continue reading in our forum

  4. Hi None N
    I tested this scenario again (GNS3, IOS 15.1), and confirmed my prior results. It sure looks like your configuration is the same as mine. When you did your extended ping, you chose TOS of 96, right?

    Since all the config happens on R1, here’s the full config. If you paste this in, and still get different results, try again using an IOS 15 version for R1 (mine is c7200-adventerprisek9-mz.152-4.M6)

    service timestamps debug datetime msec
    service timestamps log datetime msec
    no service password-encryption
    hostname R1
    ... Continue reading in our forum

  5. Hello

    I used the example above and everything works until I apply encryption and now I noticed that classification no longer works. Here are the configs from R1.
    The only thing I changed was the routing protocol over the tunnel and added some VTY password and now Im using VTI.
    When I classify based on the ESP header it works as I see matches in my policy-map but thats not very useful when you want to match specific traffic within the inner packet as in this case, Telnet and police that traffic.

    R1#sh running-config
    Building configuration...
    Current configurat
    ... Continue reading in our forum

6 more replies! Ask a question or join the discussion by visiting our Community Forum