MPLS QoS Basics

The EXP (Experimental) bits in the MPLS header are used for QoS. The EXP field is 3 bits long, which is the same length as the IPP field. (The IPP field is deprecated but the first three bits of the DSCP value map directly to an IPP value).

When an ingress PE does label imposition, it automatically copies the IP Precedence value in the IP header to the MPLS header EXP value. This is true for both IOS-XE and IOS-XR.

QoS for MPLS is very similar to regular QoS on IP traffic, except you match on the MPLS EXP value instead of DSCP, IPP, CoS, etc.

QoS Group

You should also be aware of the QoS Group feature, which is used to map an EXP value to a QoS Group, which is an internal “tag” put on the packet. This is used on the egress PE in order to apply policy to traffic without needing to classify IP traffic.

The problem that QoS group solves, is that the MPLS label has been popped off already when preforming shaping on the egress interface of the egress PE, so you can’t match the EXP value any longer. The solution is to map the EXP value to a QoS Group on ingress, and then shape the traffic on egress based on the QoS Group. The QoS Group is basically an internal placeholder. It is not a literal marking on the packet itself, but instead an internal marking associated with the packet and held in memory on the router. In the lab below we’ll use the QoS Group feature to solve the problem of shaping on the egress interface on the egress PE.

Lab

We’ll use a regular L3VPN topology, with an extra C router at each site (C1 and C2). Here are the startup configs:

#C1
hostname C1
!
line con 0
 logging sync
!
int Gi1
 ip address 10.1.1.10 255.255.255.0
 no shut
!
router ospf 1
 network 10.0.0.0 0.255.255.255 area 0
!
username cisco password cisco
!
enable password cisco
!
line vty 0 4
 login local
 transport input telnet

#CE1
hostname CE1
!
line con 0
 logging sync
!
int Gi1
 ip address 10.1.1.1 255.255.255.0
 no shut
!
int Gi2
 ip address 100.64.0.2 255.255.255.252
 no shut
!
router bgp 65000
 neighbor 100.64.0.1 remote-as 100
 network 10.1.1.0 mask 255.255.255.0
!
router ospf 1
 redistribute bgp 65000
 network 10.0.0.0 0.255.255.255 area 0

#C2
hostname C2
!
line con 0
 logging sync
!
int Gi1
 ip address 10.1.2.10 255.255.255.0
 no shut
!
router ospf 1
 network 10.0.0.0 0.255.255.255 area 0
!
username cisco password cisco
!
enable password cisco
!
line vty 0 4
 login local
 transport input telnet

#CE2
hostname CE2
!
line con 0
 logging sync
!
int Gi1
 ip address 10.1.2.1 255.255.255.0
 no shut
!
int Gi2
 ip address 100.64.0.6 255.255.255.252
 no shut
!
router bgp 65001
 neighbor 100.64.0.5 remote-as 100
 network 10.1.2.0 mask 255.255.255.0
!
router ospf 1
 redistribute bgp 65001
 network 10.0.0.0 0.255.255.255 area 0

#PE1
hostname PE1
!
line con 0
 logging sync
!
vrf definition CUSTOMER
 rd 100:1
 route-target both 100:1
 address-family ipv4 unicast
 exit
!
int Gi1
 vrf forwarding CUSTOMER
 ip address 100.64.0.1 255.255.255.252
 no shut
!
int Gi2
 ip address 10.1.3.1 255.255.255.0
 no shut
 ip router isis
 isis network point-to-point
 mpls ip
!
int lo0
 ip address 1.1.1.1 255.255.255.255
 ip router isis
!
router isis
 net 49.0001.0000.0000.0001.00
 is-type level-2-only
!
router bgp 100
 neighbor 2.2.2.2 remote-as 100
 neighbor 2.2.2.2 update-source lo0
 address-family vpnv4
  neighbor 2.2.2.2 activate
 address-family ipv4 unicast vrf CUSTOMER
  neighbor 100.64.0.2 remote-as 65000

#P3
hostname P3
!
line con 0
 logging sync
!
int Gi1
 ip address 10.1.3.3 255.255.255.0
 no shut
 ip router isis
 isis network point-to-point
 mpls ip
!
int Gi2
 ip address 10.3.4.3 255.255.255.0
 no shut
 ip router isis
 isis network point-to-point
 mpls ip
!
int lo0
 ip address 3.3.3.3 255.255.255.255
 ip router isis
!
router isis
 net 49.0001.0000.0000.0003.00
 is-type level-2-only

#P4
hostname P4
!
int Gi0/0/0/1
 ip address 10.3.4.4/24
 no shut
!
int Gi0/0/0/0
 ip address 10.2.4.4/24
 no shut
!
int lo0
 ip address 4.4.4.4/32
!
router isis 1
 net 49.0001.0000.0000.0004.00
 is-type level-2-only
 interface Gi0/0/0/0
  point-to-point
  address-family ipv4 unicast
 int Gi0/0/0/1
  point-to-point
  address-family ipv4 unicast
 int Lo0
  address-family ipv4 unicast
!
mpls ldp
 int Gi0/0/0/0
 int Gi0/0/0/1

#PE2
hostname PE2
!
vrf CUSTOMER
 address-family ipv4 unicast
  import route-target 100:1
  export route-target 100:1
!
int Gi0/0/0/1
 ip address 10.2.4.2/24
 no shut
!
int Gi0/0/0/0
 vrf CUSTOMER
 ip address 100.64.0.5/30
 no shut
!
int lo0
 ip address 2.2.2.2/32
!
router isis 1
 net 49.0001.0000.0000.0002.00
 is-type level-2-only
 int Gi0/0/0/1
  point-to-point
  address-family ipv4 unicast
 int Lo0
  address-family ipv4 unicast
!
router bgp 100
 address-family ipv4 unicast
 address-family vpnv4 unicast
 neighbor 1.1.1.1
  remote-as 100
  update-source lo0
  address-family vpnv4 unicast
 vrf CUSTOMER
  rd 100:1
  address-family ipv4 unicast
  neighbor 100.64.0.6
   remote-as 65001
   address-family ipv4 unicast
    route-policy PASS in
    route-policy PASS out
!
mpls ldp
 int Gi0/0/0/1
!
route-policy PASS
 pass
end-policy

If you’ve setup the inital lab correctly, you should be able to telnet from C1 to C2.

The customer has purchased QoS service from the SP. The SLA gaurantees that voice traffic will have 5mb and low latency, while data traffic will have 20mb and no latency gaurantee. The SP tells the customer that the voice traffic must be marked with IPP 5 or DSCP 46 (EF) in order to receive this treatment.

For this lab, we’ll use icmp as a substitute for voice traffic, and telnet as a substitute for general data traffic.

On the C routers, we’ll create an input policy that simply lets us count the IP Precedence markings on received traffic. We’ll use this to validate the markings on received traffic.

#C1, C2
class-map IPP_1
 match ip precedence 1
class-map IPP_2
 match ip precedence 2
class-map IPP_3
 match ip precedence 3
class-map IPP_4
 match ip precedence 4
class-map IPP_5
 match ip precedence 5
class-map IPP_6
 match ip precedence 6
class-map IPP_7
 match ip precedence 7
class-map IPP_0
 match ip precedence 0
!
policy-map COUNT_IPP
 class IPP_0
 class IPP_1
 class IPP_2
 class IPP_3
 class IPP_4
 class IPP_5
 class IPP_6
 class IPP_7
!
int Gi1
 service-policy input COUNT_IPP

From C1, ping C2 and telnet to C2. Then check the counters on the class maps on the policy-map applied to Gi1.

C1#show policy-map interface gi1
 GigabitEthernet1 

  Service-policy input: COUNT_IPP

    Class-map: IPP_0 (match-all)  
      5 packets, 570 bytes
      5 minute offered rate 0000 bps
      Match: ip precedence 0 
<snip>

    Class-map: IPP_6 (match-all)  
      106 packets, 11174 bytes
      5 minute offered rate 0000 bps
      Match: ip precedence 6 

Telnet automatically uses IP Precedence 6. This is because telnet is considered as network traffic. Network traffic is given the highest preference, IPP 6 and 7. We’ll need to re-mark this on the CEs in order to substitute telnet as voice traffic in our lab.

On CE1 and CE2, create an input service policy which matches IPP 6 and re-marks it as IPP 5.

#CE1, CE2
class-map IPP_6
 match ip precedence 6
!
policy-map REMARK_TELNET
 class IPP_6
  set ip precedence 5
!
int Gi1
 service-policy input REMARK_TELNET

Telnet from C1 to C2 again, and this time you should see the IPP_5 counters climb.

C2#show policy-map int gi1 | sec IPP_5
    Class-map: IPP_5 (match-all)  
      503 packets, 27162 bytes
      5 minute offered rate 2000 bps
      Match: ip precedence 5

If we take a pcap at any point in the SP network, we can see that the IP Precedence is automatically mapped to the EXP field in all MPLS labels. Both the transport and service label reflect the IP Precedence value.

We’ll now configure the SP QoS policy which will reserve bandwidth and minimize delay for voice traffic.

#PE1
class-map VOICE
 match mpls experimental topmost 5
!
policy-map VOICE_POLICY
 class VOICE
  priority 5000
 class class-default
  bandwidth 20000
!
int Gi2
 service-policy output VOICE_POLICY

#P3
class-map VOICE
 match mpls experimental topmost 5
!
policy-map VOICE_POLICY
 class VOICE
  priority 5000
 class class-default
  bandwidth 20000
!
int range Gi1-2
 service-policy output VOICE_POLICY

#P4
class-map VOICE
 match mpls experimental topmost 5
!
policy-map VOICE_POLICY
 class VOICE
  priority level 1
 class class-default
  bandwidth 20000
!
int Gi0/0/0/0
 service-policy output VOICE_POLICY
!
int Gi0/0/0/1
 service-policy output VOICE_POLICY

#PE2
class-map VOICE
 match mpls experimental topmost 5
!
policy-map VOICE_POLICY
 class VOICE
  priority level 1
 class class-default
  bandwidth 20000
!
int Gi0/0/0/1
 service-policy output VOICE_POLICY

Initiate telnet traffic between the C routers again. If you check any core routers, you should see the VOICE class counters incrementing. (XRv does not appear to work - it tells me that the service policy is not installed. I believe this is just a limitation of the virtual image. You can apply the policy in the config but it doesn’t actually take effect.)

P3#show policy-map interface Gi1 | sec VOICE
  Service-policy output: VOICE_POLICY
    Class-map: VOICE (match-all)  
      27 packets, 1776 bytes
      5 minute offered rate 1000 bps, drop rate 0000 bps
      Match: mpls experimental topmost 5 
      Priority: 5000 kbps, burst bytes 125000, b/w exceed drops: 0

The problem right now is that we are not applying the policy to the egress interfaces on PE1 and PE2 when they act as the egress PE. The router cannot look up the MPLS EXP field in this case, because the MPLS labels are already gone. Instead of classifying the traffic on egress, we can use the qos-group to classify the traffic on ingress and map it to a qos-group. We can then use the qos-group in the policy that is applied to the egress interface facing the CE.

#PE1
policy-map EXP_TO_QOSGROUP
 class VOICE
  set qos-group 5
!
class-map QOS_GROUP_5
 match qos-group 5
!
policy-map EGRESS_VOICE_POLICY
 class QOS_GROUP_5
  priority 5000
 class class-default
  bandwidth 20000
!
int Gi2
 service-policy input EXP_TO_QOSGROUP
int Gi1
 service-policy output EGRESS_VOICE_POLICY

#PE2
policy-map EXP_TO_QOSGROUP
 class VOICE
  set qos-group 5
!
class-map QOS_GROUP_5
 match qos-group 5
!
policy-map EGRESS_VOICE_POLICY
 class QOS_GROUP_5
  priority level 1
 class class-default
  bandwidth 20000
!
int Gi0/0/0/1
 service-policy input EXP_TO_QOSGROUP
int Gi0/0/0/0
 service-policy output EGRESS_VOICE_POLICY

The commit fails for me on XRv, however we can verify that this policy is working on PE1. Initiate the telnet traffic again and check the counters on both the input policy on Gi2 and output policy on Gi1.

PE1#show policy-map int Gi2 input | sec VOICE     
    Class-map: VOICE (match-all)  
      15 packets, 1099 bytes
      5 minute offered rate 0000 bps, drop rate 0000 bps
      Match: mpls experimental topmost 5 
      QoS Set
        qos-group 5
          Marker statistics: Disabled

PE1#show policy-map int Gi1 output | sec QOS_GROUP
    Class-map: QOS_GROUP_5 (match-all)  
      15 packets, 1004 bytes
      5 minute offered rate 0000 bps, drop rate 0000 bps
      Match: qos-group 5
      Priority: 5000 kbps, burst bytes 125000, b/w exceed drops: 0

Conclusion

This was a simple lab exercise to get some familiarity with how QoS works in a service provider’s MPLS network. We see that we can only have a maximum of 8 different queues or classes in the MPLS network, as the EXP field is only 3 bits long. By default the IP Precedence value on an IP packet is mapped to the EXP field when MPLS labels are imposed on an IP packet.

We can also use the QoS-Group feature to mark received packets which will have their labels popped off by the time they egress the other interface. This prevents us from having to classify the IP traffic on egress.

As you can see, the QoS tools we use for MPLS on IOS-XE and IOS-XR are extremely similar to QoS in the enterprise world.

Last updated