Tuesday, July 5, 2016

Cisco Multicast Lesson 1: Introduction to Multicast through an Easy Example

In a lot of my QoS testing, I have used Gnome M-Player to play an MP4 file, served off of a web server. The cool thing about this approach is that I can easily increase the bandwidth coming across the network by connecting additional clients and using M-Player to download new streams.

In the real world, however, you typically want to conserve bandwidth, and so having multiple clients downloading separate instances of the same data is usually a bad thing. Consider the following network as an example:



In this very simple example, we have a CentOS 6 server (Which will be streaming the file), and three Knoppix clients (which will be playing the file with M-Player). When I start up one of the Knoppix clients and begin a unicast stream, I see 270Kbps through Fa2/0:
R1#sho int fa2/0
...
5 minute output rate 270000 bits/sec, 32 packets/sec
...

(yes, I waited 5 minutes after beginning the stream).

What happens if we clear the interface counters and start up a second video stream from another client (and wait 5 minutes for the average to stabilize)? Will we see 540Kbps (2 streams x 270Kbps per stream)?
R1#sho int fa2/0
...
5 minute output rate 650000 bits/sec, 63 packets/sec
...

Huh...a little over the estimate. What if we fire up a third client downloading the same file?

R1#sho int fa2/0
...
5 minute output rate 907000 bits/sec, 92 packets/sec
...

Again, pretty close to what we would expect (3 x 270Kbps = 810Kbps). I'll chalk up the additional bandwidth to OSPF routing updates, CDP, and other network chatter, but the important thing is that this method of streaming data across a large network doesn't scale well.

Fortunately, there is a solution for this problem. What if there were a way to reduce the number of data streams that each router had to propagate? In a unicast stream, the router opens up a stream of data for each host that connects to the CentOS server. However, with multicast streams, the router only opens up one channel per outbound path. For example, the route from the CentOS server to Knoppix and Knoppix Clone 2 is identical up to R3 (and if Fa1/0 on R1 is shut down, as it was during my testing, it's identical to Knoppix Clone 1, as well). With multicast, R1 would forward a single datastream to R3; R3 would then split that data stream to R5 and to R4. With Fa1/0 shut down on R1, R4 would split its incoming stream also, sending one stream to Knoppix Clone 2 and one stream to R6. Now, suppose that instead of one client on R4, R5 and R6, suppose there were ten...it's not hard to see how multicast can quickly reduce bandwidth consumption on a large network.

So...how do you configure multicast on a Cisco router?

This is actually very easy to do. First, you globally enable multicast on your router:
R1(config)#ip multicast-routing
R1(config)#

Then, you enable PIM on the interfaces that will be participating in multicast streams:
R1(config)#int fa1/0
R1(config-if)#ip pim sparse-dense-mode
R1(config-if)#int fa2/0
R1(config-if)#ip pim sparse-dense-mode
R1(config-if)#int vlan10
R1(config-if)#ip pim sparse-dense-mode

There's a lot more to multicast -- which we'll get to in later labs -- but for a bare-bones configuration, that's it! Pretty easy, isn't it?

At this point, I'm going to chase down a rabbit trail for a few minutes. This is primarily intended as a Cisco lab, but unlike most of the other labs on this blog, this one requires some additional work on the server side in order to test. I started by installing VLC on the CentOS host. For CentOS6, you can install a binary package like this:
# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# rpm -Uvh http://li.nux.ro/download/nux/dextop/el6/i386/nux-dextop-release-0-3.el6.nux.noarch.rpm
# yum install vlc

For CentOS7, the install goes like this:
# rpm -Uvh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
# rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
# yum install vlc

Once VLC is installed, you will need to start the multicast stream:
$ cvlc -vvv /var/www/localhost/htdocs/ZNKR_Iaido.mp4 --sout '#rtp{mux=ts,dst=239.255.255.1}' --ttl 12

Then on the Knoppix clients, start up Gnome M-Player (the command line version would probably work, but I used the GUI), click "File," click "Open Location" and in the text window, type:
rtp://@239.255.255.1:5004/

So...what did we just do?

On the CentOS server, we added the repositories that allow you to install VLC through the CentOS package manager. Then, we installed VLC. As a NON-ROOT user (VLC will not run as root), start the command-line version of VLC, telling it to use the RTP protocol, and use the multi-cast address of 239.255.255.1 (a full discussion of multicast addressing is outside the scope of this lab, but for now, feel free to substitute any address in the subnet 239.255.0.0/16).

On the client side, we are again saying to use the RTP protocol, we are referencing the same multicast address, and specifying the port 5004 in the URL.

</rabbit-trail>Back to networking...

Now that we have multicast running, what does that do to our bandwidth usage? Let's find out! Start up two Knoppix clients, start the multicast stream on the CentOS server, clear the counters on FA2/0 on R1, and let's see:

R1#sho int fa2/0
...
5 minute output rate 449000 bits/sec, 43 packets/sec
...

That's much better! There's a little overhead for the multicast protocol itself, but it's still around 30% less than the unicast bandwidth utilization with two clients.

One interesting aspect of multicast is that, from the client's perspective, multicast is much more like broadcast television than video-on-demand. That is, with a unicast stream, when you request a resource, you start at the beginning of the video (or audio or...) stream. With multicast, you are joining a stream already in progress, which means you will be watching (or listening or...) to the exact same portion of the stream as other users.

Look at the two following screenshots. In the first screenshot, the videos are obviously playing at different locations in the video stream:


However, in the second screenshot, you can see that the stream on the right has been playing for 13 minutes and 45 seconds and the one on the left has been playing for only 7 minutes and 50 seconds...but both are playing the exact same point in the video stream:


With that, we'll close out this lab, even though we've only scratched the surface of multicast. More to come!

Tuesday, June 7, 2016

BGP Route Filtering

Consider the network in the following diagram...:

If you apply a simple BGP configuration to each router in this network, something along the lines of...:
#R5 BGP Configuration:
router bgp 500
no synchronization
bgp router-id 10.254.254.5
bgp log-neighbor-changes
network 10.254.254.5 mask 255.255.255.255
network 172.16.0.0 mask 255.255.255.252
network 172.16.0.16 mask 255.255.255.252
neighbor 172.16.0.2 remote-as 100
neighbor 172.16.0.17 remote-as 400
no auto-summary

...BGP will find the "best" route around the network. For example, for R1 to ping R4's loopback address, it would take the route R1, R5, R4, and to ping R3's loopback address, R1 would take the route R1, R2, R3:
R1#sho ip route
<---snip--->
10.0.0.0/32 is subnetted, 5 subnets
B 10.254.254.2 [20/0] via 172.16.0.6, 00:00:10
B 10.254.254.3 [20/0] via 172.16.0.6, 00:00:10
C 10.254.254.1 is directly connected, Loopback0
B 10.254.254.4 [20/0] via 172.16.0.1, 00:00:09
B 10.254.254.5 [20/0] via 172.16.0.1, 00:00:15
R1#

Exactly as you would expect, right?

It could happen, however, that there is some factor of which BGP is unaware that makes this routing less than optimal. Suppose, for example, that you are the administrator of router R5, and while you would like to multi-home your router, you don't want to allow other networks to transit your router. To prevent BGP from advertising routes to other networks, it is possible to filter outbound routes from your router.

Step 1: Create an access list to define the networks you want to allow outbound.
R5(config)#access-list 1 permit 10.254.254.5 0.0.0.0
R5(config)#access-list 1 permit 172.16.0.0 0.0.0.3
R5(config)#access-list 1 permit 172.16.0.16 0.0.0.3

Step 2: Edit the BGP config to filter the routes as defined in your access list.
R5(config)#router bgp 500
R5(config-router)# neighbor 172.16.0.2 distribute-list 1 out
R5(config-router)# neighbor 172.16.0.17 distribute-list 1 out

Step 3: Clear the BGP sessions to reset the routes.
R5#clear ip bgp 100
R5#clear ip bgp 400
R5#
00:49:41: %BGP-5-ADJCHANGE: neighbor 172.16.0.2 Down User reset
R5#
00:49:43: %BGP-5-ADJCHANGE: neighbor 172.16.0.17 Down User reset
R5#

Now, if you look at the routes on R1, it will only transit R1 to reach networks that are directly connected to R1:
R1#sho ip route
<---snip--->
172.16.0.0/30 is subnetted, 5 subnets
B 172.16.0.16 [20/0] via 172.16.0.1, 00:01:28
B 172.16.0.12 [20/0] via 172.16.0.6, 00:09:24
B 172.16.0.8 [20/0] via 172.16.0.6, 00:09:24
C 172.16.0.4 is directly connected, FastEthernet0/0
C 172.16.0.0 is directly connected, FastEthernet3/0
10.0.0.0/32 is subnetted, 5 subnets
B 10.254.254.2 [20/0] via 172.16.0.6, 00:09:24
B 10.254.254.3 [20/0] via 172.16.0.6, 00:09:24
C 10.254.254.1 is directly connected, Loopback0
B 10.254.254.4 [20/0] via 172.16.0.6, 00:02:21
B 10.254.254.5 [20/0] via 172.16.0.1, 00:01:28
R1#

As you can see, to reach R4's loopback address now, R1 now will route traffic through R2 rather than through R5.

Now, what would happen if there was a break somewhere in between R2 and R4?
R2#conf t
R2(config)#int fa0/0
R2(config-if)#shut

R1#ping 10.254.254.4

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.254.254.4, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R1#

R5 will not propagate any routes except those that you have explicitly allowed, and as a result, R1 no longer has a route to R4.

Cisco Intro to QoS and CoS, Part 2 -- A Deeper Dive into Marking Traffic

In our last lab, we set up a simple network, and showed how applying a QoS policy to prioritize certain traffic over other traffic could help high-priority, time-sensitive applications work properly, even during times of network congestion. But what did all of the access lists, class maps and policy maps really do? In this lab, we'll take a closer look at what the class maps and policy maps are really doing at the packet layer.

First, we'll start with a new network diagram:


As we stated last time, the first step in designing an effective QoS policy is identifying the traffic on your network, and deciding how you want to prioritize it. On this network, we will sort network traffic into the following classes:
  1. Core Control: OSPF, in this case (which is automatically identified and marked by the router itself);
  2. VoIP: We will be simulating voice traffic by streaming an MP4 video file from the host CentOS6 to hosts on the "voice" VLAN (VLAN 100);
  3. Call Signaling: We will also be simulating call signaling traffic with SSH from the "voice" VLAN (VLAN 100);
  4. Routine: Bulk data traffic from the "LAN" VLAN (VLAN 10);
  5. default: Anything not otherwise classified.


Here are the access lists that we will use to identify the traffic:
ip access-list extended CALLSIGNALING
 permit tcp 192.168.100.0 0.0.0.255 any eq 22
 permit tcp any eq 22 192.168.100.0 0.0.0.255
 deny ip any any
ip access-list extended ROUTINE
 permit ip 192.168.1.0 0.0.0.255 any
 permit ip any 192.168.1.0 0.0.0.255
 deny ip any any
ip access-list extended VOIP
 permit tcp 192.168.100.0 0.0.0.255 any eq www
 permit tcp any eq www 192.168.100.0 0.0.0.255
 deny ip any any
!

Like I said earlier, the router automatically identifies and marks routing protocol traffic, so we don't need to create a separate ACL for that.

After creating the ACL's to match the network traffic, we will create the class maps:
class-map match-any NETWORK_CONTROL
 match ip dscp cs6
class-map match-any VOIP
 match ip dscp ef
 match access-group name VOIP
class-map match-any CALLSIGNALING
 match ip dscp cs3
 match ip dscp af31
 match access-group name CALLSIGNALING
class-map match-any ROUTINE
 match ip dscp cs2
 match access-group name ROUTINE
!

Let's discuss the class map in a little more detail. First, notice that the first line of each class map contains the phrase "match-any." Take, for example, the class map for CALLSIGNALING. We have a line that states, "match ip dscp cs3," and then we have two other "match..." statements following that line. Since we created the class map using the "match-any" statement, then we are essentially using a logical OR to match either the DSCP marking CS3, the DSCP marking AF31 or the "CALLSIGNALING" access list. If we had used the phrase, "match-all," then we would be performing a logical AND against all of the statements (which would never match, as the DSCP CS3 and DSCP AF31 markings are mutually exclusive...but it can be useful if you are matching on other criteria than DSCP markings).

We will explain why we are matching against either DSCP markings or an access list shortly, but for now, let's proceed to the policy-map:
policy-map EGRESS
 class VOIP
  priority percent 20
 class CALLSIGNALING
  bandwidth percent 5
 class ROUTINE
  bandwidth percent 50
  random-detect dscp-based
 class NETWORK_CONTROL
  bandwidth percent 5
 class class-default
  bandwidth percent 1
  random-detect dscp-based
!
policy-map EGRESS-BW
 class class-default
  shape average 10000000
  service-policy EGRESS
!
policy-map INGRESS
 class VOIP
  set dscp ef
 class CALLSIGNALING
  set dscp af31
 class ROUTINE
  set dscp cs2
 class class-default
  set dscp cs1

We have created three policy-maps in this example: EGRESS, EGRESS-BW, and INGRESS (creative names, no?).

EGRESS-BW is pretty simple: essentially, we are shaping all of the traffic in this policy to 10Mbps, then calling the "EGRESS" policy. This is a pretty common strategy, as it allows you to create a parent policy (EGRESS-BW, in this case) to define the shaper/policer, and then a child policy to break out the bandwidth to the individual traffic classes. This allows for a very flexible approach, since you can create multiple parent policies that call the same child policy, allowing you to quickly make changes to your traffic shaper.

The EGRESS policy matches traffic against the class-maps that we previously defined, then divvies that bandwidth up into the individual traffic classes by percentage. There are two details worth a little extra discussion in the EGRESS policy. First, notice that class VOIP uses "priority percent..." whereas all of the other classes use "bandwidth percent..." This places VOIP traffic on the priority (low-latency) queue. The other classes buffer incoming traffic, placing it on a queue until the router gets a chance to transmit it on the interface; class VOIP, on the other hand is transmitted IMMEDIATELY. This is because VoIP traffic is very sensitive to jitter, and queueing up traffic before transmitting it causes latency. However, be aware that if traffic in class VOIP cannot be transmitted immediately, it will be DISCARDED! Therefore, you don't want to use the "priority" statement unless it is for a traffic class that can tolerate drops, but cannot tolerate jitter. Second, notice how class "ROUTINE" and class "class-default" (a built-in traffic class on Cisco routers) use the statement, "random-detect dscp-based?" This statement tells the QoS policy to enable the "random early detection" scheduler, which will randomly discard packets as the traffic queues become congested. The thresholds at which RED begins to discard traffic are configurable, but in this example, we are using the default settings, as this is beyond the scope of this tutorial. At first, randomly discarding traffic might sound like a bad idea, but the idea behind this strategy is that by randomly discarding the occasional packet before the network becomes fully congested, the service sending the traffic that is being dropped will begin to slow down the rate at which it is transmitted, thus delaying the onset of congestion. However, we do not want to enable this feature in class VOIP or class CALLSIGNALING, as this is priority traffic that you really don't want to drop.

Finally, the INGRESS policy is where the traffic inbound to the router from the network clients (Knoppix_Clone_1 and Knoppix_Clone_2) is identified and marked. The only action taken by the INGRESS policy is applying the DSCP markings to the traffic, based upon the access control lists. In truth, if an incoming packet already had a DSCP marking, the INGRESS policy would happily classify traffic based upon that, unless other configuration items were present in the router config, but for now, let's just assume that all incoming traffic has no DSCP markings, and therefore the only lines in the class-maps that will match are the "match access-group name..." statements.

So...this is great, in theory, but does it actually work? To find out, I ran tcpdump on the CentOS6 host to capture all incoming network traffic. If the router configuration is good, then...:
  1. Any traffic coming from 192.168.1.x should be marked with a DSCP value of CS2;
  2. Any traffic coming from 192.168.100.x outbound to any host on port 22 should be marked with a DSCP value of AF31;
  3. Any traffic coming from 192.168.100.x outbound to any host on port 80 should be marked with a DSCP value of EF;
  4. and Any traffic originating on the router itself that is identified as OSPF should be marked with a DSCP value of CS61.


After running several tests (streaming the MP4 file on CentOS6 to the Knoppix Clone on the VoIP VLAN, SSH'ing to CentOS6 from the Knoppix host on the VoIP VLAN, and connecting to the HTTP process on CentOS 6 from the Knoppix host on the LAN VLAN), I copied the capture file from CentOS6 to my local desktop, and opened up the file with Wireshark. Let's look at traffic originating on 192.168.1.x:


Yep, "Class Selector 2" is the value of the DSCP marking for traffic originating on 192.168.1.2, so it looks like our QoS policies are correctly identifying traffic on the LAN VLAN, and correctly marking it with a CS2 DSCP marking. Next let's look for traffic originating on the Voice VLAN, outbound for CentOS6 on port 22 (SSH):


Perfect! This screen capture shows that traffic originating on 192.168.100.2, sent to 100.64.1.2 on port 22 was correctly identified and marked with a DSCP value of AF31. Next, let's see if traffic from 192.168.100.2 to 100.64.1.2 on port 80 is properly marked as "EF:"


Yep, this traffic has a DSCP marking of "EF," just as expected. Finally, let's verify that the OSPF multicast traffic from the router is properly marked as CS6:


That looks good, too.

Now that we have verified that traffic is being classified and marked appropriately, let's look at a very useful command for troubleshooting QoS and congestion on a Cisco router. Suppose that one of your users calls to report problems with their voice-over-IP telephones. They've already had the telephone tech look at the phone and the Call Manager, but because the phone tech found nothing wrong, they suspect it is a network problem. How can you tell if your QoS policy is dropping packets, due to excessive traffic in one of your queues?

If I had received a call such as this, I would start by looking at the QoS statistics for the outbound interface on the router -- in this case, FastEthernet1/0:
R1#sho policy-map int fa1/0  
FastEthernet1/0

  Service-policy output: EGRESS-BW

    Class-map: class-default (match-any)
      4298 packets, 316655 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: any
      Traffic Shaping
           Target/Average   Byte   Sustain   Excess    Interval  Increment
             Rate           Limit  bits/int  bits/int  (ms)      (bytes)  
         10000000/10000000  62500  250000    250000    25        31250    

        Adapt  Queue     Packets   Bytes     Packets   Bytes     Shaping
        Active Depth                         Delayed   Delayed   Active
        -      0         4298      316655    0         0         no

      Service-policy : EGRESS

        Class-map: VOIP (match-any)
          1556 packets, 103168 bytes
          5 minute offered rate 0 bps, drop rate 0 bps
          Match: ip dscp ef
            1556 packets, 103168 bytes
            5 minute rate 0 bps
          Match: access-group name VOIP
            0 packets, 0 bytes
            5 minute rate 0 bps
          Queueing
            Strict Priority
            Output Queue: Conversation 264
            Bandwidth 20 (%)
            Bandwidth 2000 (kbps) Burst 50000 (Bytes)
            (pkts matched/bytes matched) 0/0
            (total drops/bytes drops) 0/0

        Class-map: CALLSIGNALING (match-any)
          1035 packets, 79610 bytes
          5 minute offered rate 0 bps, drop rate 0 bps
          Match: ip dscp cs3
            0 packets, 0 bytes
            5 minute rate 0 bps
          Match: ip dscp af31
            1035 packets, 79610 bytes
            5 minute rate 0 bps
          Match: access-group name CALLSIGNALING
            0 packets, 0 bytes
            5 minute rate 0 bps
          Queueing
            Output Queue: Conversation 265
            Bandwidth 5 (%)
            Bandwidth 500 (kbps)
            (pkts matched/bytes matched) 0/0
        (depth/total drops/no-buffer drops) 0/0/0
             exponential weight: 9
             mean queue depth: 0

   dscp    Transmitted      Random drop      Tail drop    Minimum Maximum  Mark
           pkts/bytes       pkts/bytes       pkts/bytes    thresh  thresh  prob
   af11       0/0               0/0              0/0           32      40  1/10
   af12       0/0               0/0              0/0           28      40  1/10
   af13       0/0               0/0              0/0           24      40  1/10
   af21       0/0               0/0              0/0           32      40  1/10
   af22       0/0               0/0              0/0           28      40  1/10
   af23       0/0               0/0              0/0           24      40  1/10
   af31    1035/79610           0/0              0/0           32      40  1/10
   af32       0/0               0/0              0/0           28      40  1/10
   af33       0/0               0/0              0/0           24      40  1/10
   af41       0/0               0/0              0/0           32      40  1/10
   af42       0/0               0/0              0/0           28      40  1/10
   af43       0/0               0/0              0/0           24      40  1/10
    cs1       0/0               0/0              0/0           22      40  1/10
    cs2       0/0               0/0              0/0           24      40  1/10
    cs3       0/0               0/0              0/0           26      40  1/10
    cs4       0/0               0/0              0/0           28      40  1/10
    cs5       0/0               0/0              0/0           30      40  1/10
    cs6       0/0               0/0              0/0           32      40  1/10
    cs7       0/0               0/0              0/0           34      40  1/10
     ef       0/0               0/0              0/0           36      40  1/10
   rsvp       0/0               0/0              0/0           36      40  1/10
default       0/0               0/0              0/0           20      40  1/10


        Class-map: ROUTINE (match-any)
          284 packets, 23609 bytes
          5 minute offered rate 0 bps, drop rate 0 bps
          Match: ip dscp cs2
            284 packets, 23609 bytes
            5 minute rate 0 bps
          Match: access-group name ROUTINE
            0 packets, 0 bytes
            5 minute rate 0 bps
          Queueing
            Output Queue: Conversation 266
            Bandwidth 50 (%)
            Bandwidth 5000 (kbps)
            (pkts matched/bytes matched) 0/0
        (depth/total drops/no-buffer drops) 0/0/0
             exponential weight: 9
             mean queue depth: 0

   dscp    Transmitted      Random drop      Tail drop    Minimum Maximum  Mark
           pkts/bytes       pkts/bytes       pkts/bytes    thresh  thresh  prob
   af11       0/0               0/0              0/0           32      40  1/10
   af12       0/0               0/0              0/0           28      40  1/10
   af13       0/0               0/0              0/0           24      40  1/10
   af21       0/0               0/0              0/0           32      40  1/10
   af22       0/0               0/0              0/0           28      40  1/10
   af23       0/0               0/0              0/0           24      40  1/10
   af31       0/0               0/0              0/0           32      40  1/10
   af32       0/0               0/0              0/0           28      40  1/10
   af33       0/0               0/0              0/0           24      40  1/10
   af41       0/0               0/0              0/0           32      40  1/10
   af42       0/0               0/0              0/0           28      40  1/10
   af43       0/0               0/0              0/0           24      40  1/10
    cs1       0/0               0/0              0/0           22      40  1/10
    cs2     284/23609           0/0              0/0           24      40  1/10
    cs3       0/0               0/0              0/0           26      40  1/10
    cs4       0/0               0/0              0/0           28      40  1/10
    cs5       0/0               0/0              0/0           30      40  1/10
    cs6       0/0               0/0              0/0           32      40  1/10
    cs7       0/0               0/0              0/0           34      40  1/10
     ef       0/0               0/0              0/0           36      40  1/10
   rsvp       0/0               0/0              0/0           36      40  1/10
default       0/0               0/0              0/0           20      40  1/10


        Class-map: class-default (match-any)
          1423 packets, 110268 bytes
          5 minute offered rate 0 bps, drop rate 0 bps
          Match: any
          Queueing
            Output Queue: Conversation 267
            Bandwidth 1 (%)
            Bandwidth 100 (kbps) Max Threshold 64 (packets)
            (pkts matched/bytes matched) 0/0
        (depth/total drops/no-buffer drops) 0/0/0
R1#

cough...cough...That's a lot of output...what does it mean?

First, we need a little more information from the user. Were they having problems answering the phone, transferring the call to another number, or hanging up once the call was complete? If so, you would need to look into the call signaling queue. Was the call garbled, or was the audio cutting out during the call? In that case, you would need to look at the VOIP queue. Let's assume that the caller complained of call signaling symptoms. We would narrow our focus to the statistics relating to class CALLSIGNALING. I won't re-copy all of the output from class CALLSIGNALING, since it's so long, but scroll back up and look at the output starting with "Class-map CALLSIGNALING (match-any)." The first thing I would check is how many drops you've had in this queue. See the line that says, "(depth/total drops/no-buffer drops) 0/0/0?" That tells you that you have allocated adequate bandwidth for class CALLSIGNALING. You do not have any packets in the buffer waiting to be transmitted, and you have not dropped any packets since the last time the interface counters were cleared or since the service policy was instated (whichever was later). Also, look at this part of the output:
   dscp    Transmitted      Random drop      Tail drop    Minimum Maximum  Mark
           pkts/bytes       pkts/bytes       pkts/bytes    thresh  thresh  prob
<...snip...>
   af31    1035/79610           0/0              0/0           32      40  1/10
<...snip...>

"Random Drops" are where your random early detection scheduler begins dropping packets before the network becomes fully congested. "Tail Drops" are where your queue has already buffered all the packets it can hold, and new incoming packets are being discarded, since there is no room left on the queue to store them. In this case, both tail drops and random drops are zero, meaning that the QoS policy has not dropped any traffic with the AF31 marking (since that was all that we were marking with our INGRESS policy; typically, you would want to look at both CS3 and AF31 in a call signaling queue).

Since there are no dropped packets in the call signaling queue, you would need to look elsewhere for the problem (for example, look for dropped packets on the router on the other side of the circuit, or make sure that the router is recognizing traffic from the phone as belonging to class VOIP or class CALLSIGNALING, as appropriate). However, suppose you saw output like this:
        Class-map: CALLSIGNALING (match-any)
          2901880 packets, 192238996 bytes
          30 second offered rate 4000 bps, drop rate 0 bps
          Match: ip dscp cs3 (24) af31 (26)
            2901880 packets, 192238996 bytes
            30 second rate 4000 bps
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops) 0/12155/0
          (pkts output/bytes output) 2889729/190911691
          bandwidth 320 kbps

In this case, you have dropped 12,155 packets(!) since the last time the counters were cleared (which was about four days ago, although you can't tell that from the output above). This means that you have not allocated adequate bandwidth to class CALLSIGNALING, and either 1) you need to allocate more bandwidth to handle the call volume, or 2) you have traffic on this circuit that is not being marked appropriately. On this router, I strongly suspect that there is traffic on the circuit that is being marked incorrectly, as we have already allocated 320Kbps to class CALLSIGNALING, which is a LOT of bandwidth for call signaling traffic.

Footnotes:
1:Yes, it is silly to run OSPF on this router, since there are no other routers with which it can share routes. The only reason I configured OSPF in this lab is to show that the router does, indeed, automatically mark OSPF traffic with the CS6 DSCP marking.

Cisco Intro to QoS and CoS, Part 1 -- Identifying, Classifying and Marking Traffic

One problem found in any network of reasonable size is how to handle congestion. When there is more bandwidth available than clients require, there is (of course!) no problem. However, some types of network traffic -- such as streaming media -- are particularly bandwidth intensive, and often, very sensitive to variations in delay time. Consider, for example, HTTP traffic: an end user will most likely not notice the variation in transit times ("jitter") of the various packets that make up a requested download. On the other hand, a Voice over IP call is very sensitive to jitter. If the packets making up a Voice over IP call or videoconference don't arrive within a reasonably consistent timeframe, the call quality will suffer, and your end users will not be happy. Even worse, what would happen if your dynamic routing protocols cannot send updates and keepalive traffic in a timely fashion? Clearly, there needs to be a way to prioritize certain types of traffic to ensure that your clients receive sufficient bandwidth for their needs.

Consider the following network:


On the left-hand side of the network drawing, we have three Linux hosts, clients of the server on the right-hand side of the drawing. These three hosts will be in competition with each other for network resources, and because we are running OSPF (or routing protocol of your choice -- and yes, a dynamic routing protocol is overkill on a network this simple, but stay with me...) on R1 and R2, they will be competing with the network clients for bandwidth as well. Making this problem even more apparent, I have connected all of the PC's to the network via FastEthernet interfaces, but only connected R1 and R2 with a single T1 line. Due to the extreme mismatch between LAN and WAN capabilities, any one of the network clients can easily saturate the "WAN" link between R1 and R2. Don't believe me? Let's do some tests...

On the host "CentOS6," I have configured the "echo" service, and on the host "CentOS6_Clone," I have installed a network diagnostic tool called "tcpspray." With no other traffic on the network, let's use tcpspray to see how long it takes to send a flurry of data to the "CentOS6" server:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 25.670507 seconds (30.955 kbytes/s)
Transmitted 1024000 bytes in 25.146152 seconds (39.768 kbytes/s)
[root@centos6_clone ~]#

Also on the host "CentOS6," I am running a web server, and one of the web pages hosted on the server is an image gallery that loads a 5x5 array of JPEG images (get your mind out of the gutter! They're pictures of motorcycles that inspired me while I was building my Cafe Racer project!). How would the tcpspray bandwidth be affected if one of the Knoppix clients was downloading the images to populate the web page at the same time? Let's find out...:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 118.753065 seconds (8.421 kbytes/s)
Transmitted 1024000 bytes in 104.348504 seconds (9.583 kbytes/s)
[root@centos6_clone ~]#

Wow...it took almost five times as long to receive the replies, due to the congestion on the network! Fortunately, quality of service and/or class of service policies can allow us to prioritize certain kinds of traffic, as our network needs dictate.

Keep in mind that nothing is free, however. If we prioritize some kinds of traffic, that necessarily comes at the expense of other traffic. Therefore, you have to have a good idea what kinds of traffic cross your network, and how they should be balanced against each other...which is a good place for us to begin with our policy. Let's define the traffic that we want prioritized.

As I mentioned earlier, the parameters I used for tcpspray specified that we would use the "echo" service for our traffic. Let's include SSH as priority traffic so we can manage our routers (you are using SSH for network management, right?). As I also mentioned earlier, our network control traffic is also high priority. Too keep the discussion simple for now, we'll assume everything else is "best effort."

Now that we have a good idea how we want to prioritize our traffic, we need a way to have the router filter our priority traffic. If you think that sounds like a job for an Access Control List, you're exactly right:

ip access-list extended CORE_GOLD
 permit tcp any eq echo any
 permit tcp any any eq echo
!
ip access-list extended CUST_EF
 permit tcp any eq 22 any
 permit tcp any any eq 22
!

After identifying the priority traffic with an ACL, we then create "class maps" to map that traffic into different classes (kind of sounds like a tautology, sorry!):

class-map match-any CORE_CONTROL
 description Network control OSPF/BGP/etc
 match ip precedence 6
 match ip precedence 7
class-map match-any CUST-EF
 match access-group name CUST_EF
class-map match-any CORE_GOLD
 description Low-latency/priority queue
 match access-group name CORE_GOLD
!

We have now created three classes of priority traffic:
  1. CORE_CONTROL, for our network control traffic,
  2. CUST-EF, for high priority traffic ("expedited forwarding"), and
  3. CORE_GOLD, for low-latency traffic.

Our next step is to use these maps to define how the different classes of traffic will be handled:

policy-map CUST-IN
 class CUST-EF
  set ip dscp ef
 class class-default
  set dscp cs3
policy-map CUST-Generic
 class CORE_GOLD
  priority percent 50
 class CORE_CONTROL
  bandwidth percent 10
 class class-default
  bandwidth percent 1
!

For our "CUST-EF" traffic, we have said that we want to place a special marking, "dscp ef," on the packets that match this traffic. This gives our "CUST-EF" traffic high priority on the network. For "CORE_GOLD" traffic, we have said that we want to give that traffic 50 per cent of the available bandwidth. For "CORE_CONTROL" traffic, we have said that we want to give such traffic 10 per cent of the available bandwidth (network control traffic is typically small-volume, bursty traffic, so it is not necessary to reserve a large amount of bandwidth for this traffic -- we just need to ensure that the traffic isn't starved for bandwidth during times of congestion). Notice that we also have another de facto class of traffic in the policy maps: class-default. This is for traffic that didn't match any of our access control lists. In the "CUST-IN" policy map, we mark it with "dscp cs3" -- a low priority traffic queue, and in the "CUST-Generic" policy map, we don't reserve any bandwidth for this class.

At this point, we have established the framework for our QoS/CoS configuration, but we haven't actually implemented it yet. Just as an access-control list used to filter certain kinds of traffic has to be applied to an interface before it is useful, so does a policy map:

interface Serial1/0
service-policy output CUST-Generic
!

We also will apply the "CUST-IN" policy map to the ingress interface, VLAN 10 on R1 and Fa0/0 on R2:

R1(config)#int vlan 10
R1(config-if)#service-policy input CUST-IN
R1(config-if)#

R2(config)#int fa0/0
R2(config-if)#service-policy input CUST-IN
R2(config-if)#

Now, let's run our tcpspray test again, while the Knoppix client reloads the web page with the 25 JPEG images:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 78.483684 seconds (12.742 kbytes/s)
Transmitted 1024000 bytes in 69.900585 seconds (14.306 kbytes/s)
[root@centos6_clone ~]#

These results are still slower than our initial baseline test, with no congestion on the network. However, it is significantly better than the test with no QoS/CoS and a congested network.

Friday, August 21, 2015

Cisco BGP Local Preference

As I've mentioned before, I work at a service provider. I recently had a customer come to my employer to ask if we could build a network for them that would allow them to replicate a subnet at two separate locations. When everything was working as expected, they wanted traffic to this subnet to be routed to their main location. However, in the event of a network outage, they wanted traffic to this subnet to be routed to their off-site backup location.

No problem. BGP will allow us to set up weighting on one of the routes so that we can prefer a route from one of the customer's routers, but will revert to the less preferred route if the primary site goes off-line for any reason. To explain how it's done, we'll use this example network:


R1 and R2 are my routers. R3 and R4 are the customer's routers, with R3 being the primary location, and R4 being the secondary site. R5 is $Random_Internet_Host.

We'll start by configuring the IP addresses on each of the routers.

R1:
R1#sho ip int brie
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0/0            10.0.0.1        YES manual up                    up
FastEthernet1/0            172.16.3.1      YES manual up                    up
FastEthernet2/0            100.64.17.1   YES manual up                    up
FastEthernet3/0            unassigned      YES unset  administratively down down
Loopback0                  10.254.254.1    YES manual up                    up
R1#

R2:
R2#sho ip int brie
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0/0            10.0.0.2        YES manual up                    up
FastEthernet1/0            172.16.4.1      YES manual up                    up
FastEthernet2/0            unassigned      YES unset  administratively down down
FastEthernet3/0            unassigned      YES unset  administratively down down
Loopback0                  10.254.254.2    YES manual up                    up
R2#

R3:
R3#sho ip int brie
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0/0            192.168.1.1     YES manual up                    up
FastEthernet1/0            172.16.3.2      YES manual up                    up
FastEthernet2/0            unassigned      YES unset  administratively down down
FastEthernet3/0            unassigned      YES unset  administratively down down
Loopback0                  10.10.10.3      YES manual up                    up
R3#

R4:
R4#sho ip int brie
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0/0            192.168.1.1     YES manual up                    up
FastEthernet1/0            172.16.4.2      YES manual up                    up
FastEthernet2/0            unassigned      YES unset  administratively down down
FastEthernet3/0            unassigned      YES unset  administratively down down
Loopback0                  10.10.10.4      YES manual up                    up
R4#

We'll skip R5's IP configuration, since it doesn't really matter for this lab -- any subnet(s) that isn't being used elsewhere is fine on R5.

Next, we'll start with an ordinary BGP configuration on each of the routers:

R1:
router bgp 42
no synchronization
bgp router-id 10.254.254.1
bgp log-neighbor-changes
network 10.0.0.0 mask 255.255.255.0
network 172.16.3.0 mask 255.255.255.252
network 100.64.17.0 mask 255.255.255.0
neighbor 10.0.0.2 remote-as 42
neighbor 172.16.3.2 remote-as 2358
neighbor 100.64.17.5 remote-as 2112
no auto-summary
!

R2:
router bgp 42
no synchronization
bgp router-id 10.254.254.2
bgp log-neighbor-changes
network 10.0.0.0 mask 255.255.255.0
network 172.16.4.0 mask 255.255.255.252
neighbor 10.0.0.1 remote-as 42
neighbor 172.16.4.2 remote-as 2358
no auto-summary
!

R3:
router bgp 2358
no synchronization
bgp router-id 10.10.10.3
bgp log-neighbor-changes
network 10.10.10.3 mask 255.255.255.255
network 172.16.3.0 mask 255.255.255.252
network 192.168.1.0
neighbor 172.16.3.1 remote-as 42
no auto-summary
!

R4:
router bgp 2358
no synchronization
bgp router-id 10.10.10.4
bgp log-neighbor-changes
network 10.10.10.4 mask 255.255.255.255
network 172.16.4.0 mask 255.255.255.252
network 192.168.1.0
neighbor 172.16.4.1 remote-as 42
no auto-summary
!

Again, we'll skip R5, as nothing special is going on there. It's just a straight peering configuration with R1.

Give the routers time to establish BGP adjacencies and to discover all of the routes on this network, then on R1 and R2, run the "show ip route" command:

R1#sho ip route
<...snip...>
     169.254.0.0/24 is subnetted, 1 subnets
B       169.254.1.0 [20/0] via 100.64.17.5, 01:59:05
     172.16.0.0/30 is subnetted, 2 subnets
B       172.16.4.0 [200/0] via 10.0.0.2, 00:37:33
C       172.16.3.0 is directly connected, FastEthernet1/0
     8.0.0.0/32 is subnetted, 1 subnets
B       8.8.8.8 [20/0] via 100.64.17.5, 01:59:05
     10.0.0.0/8 is variably subnetted, 5 subnets, 2 masks
B       10.254.254.2/32 [200/0] via 10.0.0.2, 00:37:33
B       10.10.10.3/32 [20/0] via 172.16.3.2, 00:37:25
C       10.0.0.0/24 is directly connected, FastEthernet0/0
C       10.254.254.1/32 is directly connected, Loopback0
B       10.10.10.4/32 [200/0] via 172.16.4.2, 00:37:14
     12.0.0.0/16 is subnetted, 1 subnets
B       12.12.0.0 [20/0] via 100.64.17.5, 01:59:06
C    100.64.17.0/24 is directly connected, FastEthernet2/0
B    192.168.1.0/24 [20/0] via 172.16.3.2, 00:37:25
R1#

R2#sho ip route
<...snip...>
     169.254.0.0/24 is subnetted, 1 subnets
B       169.254.1.0 [200/0] via 100.64.17.5, 00:37:13
     172.16.0.0/30 is subnetted, 2 subnets
C       172.16.4.0 is directly connected, FastEthernet1/0
B       172.16.3.0 [200/0] via 10.0.0.1, 00:38:13
     8.0.0.0/32 is subnetted, 1 subnets
B       8.8.8.8 [200/0] via 100.64.17.5, 00:37:13
     10.0.0.0/8 is variably subnetted, 4 subnets, 2 masks
C       10.254.254.2/32 is directly connected, Loopback0
B       10.10.10.3/32 [200/0] via 172.16.3.2, 00:38:01
C       10.0.0.0/24 is directly connected, FastEthernet0/0
B       10.10.10.4/32 [20/0] via 172.16.4.2, 00:37:59
     12.0.0.0/16 is subnetted, 1 subnets
B       12.12.0.0 [200/0] via 100.64.17.5, 00:37:14
B    100.64.17.0/24 [200/0] via 10.0.0.1, 00:38:14
B    192.168.1.0/24 [20/0] via 172.16.4.2, 00:37:59
R2#

If you'll notice, R1 has discovered the route to 192.168.1.0/24 via 172.16.3.2 (R3), but R2 has discovered the route to 192.168.1.0/24 via 172.16.4.2 (R4). This is not what we want. We want all traffic routed to R3 until and unless R3 goes off-line; right now, traffic will be routed to either R3 OR R4 based upon whether it passes through R1 or R2. To fix this, we'll create an access list to match the 192.168.1.0/24 subnet, then apply it to a route map, and finally, apply the route map to the BGP configuration on R2:

R2(config)#access-list 5 permit 192.168.1.0 0.0.0.255
R2(config)#route-map LPREF permit 10
R2(config-route-map)#match ip address 5
R2(config-route-map)#set weight 100
R2(config-route-map)#route-map LPREF permit 20
R2(config-route-map)#exit
R2(config)#router bgp 42
R2(config-router)#neighbor 10.0.0.1 route-map LPREF in
R2(config-router)#

At this point, if you run the "sho ip route" command again, you'll notice that nothing has changed. Clear the BGP session with R1 to apply the change to your BGP configuration:

R2#clear ip bgp 42
R2#
02:43:14: %BGP-5-ADJCHANGE: neighbor 10.0.0.1 Down User reset
R2#
02:43:19: %BGP-5-ADJCHANGE: neighbor 10.0.0.1 Up
R2#sho ip route | inc 192.168.1.
B 192.168.1.0/24 [200/0] via 172.16.3.2, 00:00:12
R2#

BGP uses hop count to determine the preferred path when it has multiple routes to a subnet. Since there is one hop to 192.168.1.0/24 via 172.16.4.2, and two hops to 192.168.1.0/24 via 172.16.3.2 (from R2's perspective), R2 will route to R4 by default. What we've done is adjust the weighting for routes from R1 matching "192.168.1.xxx" to 100, so that R2 will prefer the route through R1 and R3 over the route from R4:

R2#sho ip bgp
<...snip...>
   Network          Next Hop            Metric LocPrf Weight Path
<...snip...>
*>i192.168.1.0      172.16.3.2               0    100    100 2358 i
*                   172.16.4.2               0             0 2358 i
<...snip...>
R2#

If you shut fa-1/0 on R3, then wait about four minutes, you'll see both R1 and R2 change their route to 192.168.1.0/24 to traverse R4 rather than R3. This is kind of a long time for an outage, but that delay can be tuned by adjusting the BGP timers. However, adjusting the timers too aggressively can lead to other problems. That means that a proper discussion on tuning BGP timers is probably beyond the scope of this lesson, so I'll save that for another day.

Wednesday, September 24, 2014

Advanced Cisco Routing -- MPLS VRFs

Several years ago, I worked for a telecommunications provider that was rolling out what I believe was one of the very first -- if the THE very first -- MPLS network in the state of Alaska. The network admins often joked that their network was not just cutting edge; it was bleeding edge. At that time, the network admins worked very closely with Cisco to build and troubleshoot the network, since Cisco was still making sure the tech worked as advertised. Since then, technology has changed, and MPLS has become much more common, even though one of the major drivers that led to the development of MPLS has become more or less a moot point: hardware has improved to the point, where it is no longer significantly faster to switch or route traffic based upon labels than IP or MAC addresses.

For example, the company I currently work for has implemented a ring (!) network with an MPLS core, simply because using MPLS makes it easier for us to configure customer networks across our service provider network. Rather than visiting each node in the ring to create a VLAN for that customer, then adding the VLAN to a topology group (again, on every node in the network), we simply create a VLL or VPLS on the endpoints, and we're done. It's a configuration change on two nodes in the case of a VLL, rather than every node in the ring (with VPLS, it depends upon how many endpoints the client requires, so in some cases, there isn't much to be gained by using MPLS rather than a plain VLAN, and a VPLS introduces some overhead into the network that a VLAN doesn't).

In this lab, we'll create a "service-provider" network in GNS3 consisting of three routers, R1, R2 and R3, and two customer networks, R4 and R5 belonging to one customer, and R6 and R7 belonging to a second customer. The network topology looks like this:


Disclaimer: This lab was created using Cisco 2600-series routers with IOS version 12.3. This is a really, really old version of code, on really, really old (virtual) hardware, since that's all I have access to, currently. Consequently, Cisco's implementation of MPLS may be somewhat different on modern hardware and firmware versions. Hopefully, this will be close enough to get you started, however.

Since both customer networks are Layer-3 (routed) networks, we will use VRF's to logically separate the two customer networks from each other. In this sense, a VRF can more or less be considered to be the Layer-3 equivalent of a VLAN: the VRF's will partition the service provider network into to separate logical networks, including routing tables, that keep addressing and routing from the customer networks from interfering with each other. This has two major implications: first, the two customers in our lab don't have to coordinate IP addressing with each other (in fact, in our example, R4 and R7 have the exact same IP address and subnet!); second, the service provider will participate in the customer's routing!

Let's get started. First, we'll set up the service provider network, including the routing process for the network core. On R1:
interface Loopback0
ip address 10.10.10.1 255.255.255.255
!
interface Serial0/0
ip address 192.168.12.1 255.255.255.0
!
interface Serial0/1
ip address 192.168.13.1 255.255.255.0
!
router ospf 1138
router-id 10.10.10.1
log-adjacency-changes
network 10.10.10.1 0.0.0.0 area 0.0.0.0
network 192.168.12.0 0.0.0.255 area 0.0.0.0
network 192.168.13.0 0.0.0.255 area 0.0.0.0
!

R2 and R3 have similar configurations, and none of this is new, so I won't repeat their configs here.

Next, on a Cisco router, we have to enable CEF (Cisco Express Forwarding). It's trivial to do:
R1(config)#ip cef

Next, we create the two VRFs. Technically, you don't need MPLS to create a VRF (Cisco calls this "VRF Lite"), nor do you need VRFs to run MPLS (for instance, if you are using VLLs or VPLS'). However, VRFs and MPLS work very well together, and for this lab, they solve the problem we have quite handily:
ip vrf left
rd 100:1
route-target export 100:1
route-target import 100:1
!
ip vrf right
rd 200:1
route-target export 200:1
route-target import 200:1
!

As you can see in the configuration above, I was highly original with the name of the VRFs, using "left" for routers R4 and R5, and "right" for R6 and R7. When implementing your own MPLS networks, I'd recommend keeping the VRF names short, easy to type, and easy to remember, as you'll be using them quite a bit in the router configurations. After creating the two VRFs, we use the "rd <AS:nn" command to create the "route distinguisher," using AS 100 for VRF left and AS 200 for VRF right. This is the label that you will be using in various places to differentiate between the two customer routing tables that you will be using in this network. According to Cisco's context-sensitive help function, you can also use an IP address rather than an AS number for the route distinguisher, but why type more than you have to? AS numbers are shorter! ;)

After creating your two VRFs, you next enable MPLS on the network interfaces that will be part of your MPLS core. In this case, that is S0/0 and S0/1 on R1, R2 and R3: interface Serial0/0
mpls ip

Just for giggles, exit out of configuration mode (or stay in config mode and prepend the following command with the word "do") and run "sho run":
interface Serial0/0
ip address 192.168.12.1 255.255.255.0
tag-switching ip
!
interface Serial0/1
ip address 192.168.13.1 255.255.255.0
tag-switching ip
!

Wait a minute...we didn't type "tag-switching ip" in the Serial interface configuration! We used "mpls ip!" What gives? Well, way back when MPLS was still being developed, Cisco was doing essentially the same thing, but called it "tag switching." When the standard was formalized, the technology was given the name MPLS, and Cisco implemented both the tag-switching and mpls commands on the command line. As I understand, there are some slight differences between the tag-switching and mpls command -- and I'm not sure what, exactly those difference might be, so I won't elaborate on that rather vague statement, except to say that you should probably stick with the mpls command, if that's what you are actually trying to build -- but the "show run" output rewrites either command as "tag-switching."

At this point, you know have a working MPLS core, as can be proven by running the "show mpls ldp neighbor" command:
R1#sho mpls ldp neighbor
    Peer TDP Ident: 10.10.10.3:0; Local TDP Ident 10.10.10.1:0
        TCP connection: 10.10.10.3.11008 - 10.10.10.1.711
        State: Oper; PIEs sent/rcvd: 165/165; Downstream
        Up time: 02:19:39
        TDP discovery sources:
          Serial0/1, Src IP addr: 192.168.13.3
        Addresses bound to peer TDP Ident:
          192.168.13.3    10.10.10.3      192.168.23.3    
    Peer TDP Ident: 10.10.10.2:0; Local TDP Ident 10.10.10.1:0
        TCP connection: 10.10.10.2.11004 - 10.10.10.1.711
        State: Oper; PIEs sent/rcvd: 167/166; Downstream
        Up time: 02:19:38
        TDP discovery sources:
          Serial0/0, Src IP addr: 192.168.12.2
        Addresses bound to peer TDP Ident:
          192.168.12.2    10.10.10.2      192.168.23.2    
R1#

Now, we need to tell MPLS which interfaces will be accepting traffic that should be encapsulated inside the MPLS label. This is done with the "ip vrf forwarding..." command. Let's also configure the IP addresses on the customer-facing Ethernet interfaces as we do this:
R1(config)#int eth0/0
R1(config-if)#ip vrf forwaring left
R1(config-if)#ip address 172.16.0.1 255.255.255.252
R1(config-if)#no shut

The only difference between a typical interface configuration and what we did here is that we used the "ip vrf forwarding left" command to tell the MPLS process that this interface is an endpoint of the VRF "left" tunnel(s). Do the same thing for Eth0/1 on R1, and for both E0/0 and E0/1 on R2 and R3, as well.

Be careful here! If you apply the IP address before you run the "ip vrf forwarding..." command, IOS will display an error message telling you that the IP address has been removed from the interface configuration! If you don't see the warning, you can spend quite a while trying to figure out why you aren't seeing the routes you expect across your MPLS core (DAMHIKT!)

This is where things start to get fun. You've basically got a complete MPLS configuration now -- you've enabled MPLS on the various network interfaces, you've got MPLS distributing labels for the various routes, and you can see the MPLS peering is working as it should. However, there is one last piece of the puzzle left: now, you need a way to distribute routes across your MPLS core. First, apply the IP addresses and routing processes to your "customer" routers just as you normally would if R4/R5 and R6/R7 were directly connected to each other. For example, on R4:
interface Loopback0
ip address 10.10.10.4 255.255.255.255
!
interface Ethernet0/0
ip address 172.16.0.2 255.255.255.252
half-duplex
!
router ospf 42
router-id 10.10.10.4
log-adjacency-changes
redistribute connected subnets
network 172.16.0.0 0.0.0.3 area 0.0.0.0
!

If you're paying attention, this is the point where you might scratch your head and object, "Wait a minute...with the 'network 172.16.0.0 0.0.0.3 area 0.0.0.0' command, I'm advertising my OSPF routing process to the service provider!" And you'd be exactly right. With MPLS, the service provider participates in your routing process, but because your traffic is being encapsulated in MPLS, that routing process is only participating with your other endpoints. In this example, R4 and R5 will share routes through the service provider's network, but will not see routes from R6 and R7 (and vice versa).

To accomplish this task on the service provider's network, the service provider will need to turn up separate OSPF process for each customer. In the lab, I also created separate loopback interfaces for each OSPF process because OSPF will complain if you try to enable OSPF without including any local interfaces in the process -- for example, on R2, if I try to turn up an OSPF process for VRF "right" (Edit: I'm not sure it's necessary to turn up OSPF for VRF "right" on R2 or VRF "left" on R3, as we will be setting up BGP to share routes between the OSPF processes; I'll investigate this in a later post). However, when turning up a separate OSPF process, you must tell OSPF which VRF this routing process belongs to:
interface Loopback1
description loopback for OSPF 100 -- VRF LEFT
ip vrf forwarding left
ip address 10.0.100.1 255.255.255.255
!
interface Loopback2
description loopback for OSPF 200 -- VRF RIGHT
ip vrf forwarding right
ip address 10.0.200.1 255.255.255.255
!
router ospf 100 vrf left
router-id 10.0.100.1
network 172.16.0.0 0.0.0.3 area 0.0.0.0
!
router ospf 200 vrf right
router-id 10.0.200.1
network 172.16.0.0 0.0.0.3 area 0.0.0.0
!

Notice that I included the "ip vrf forwaring..." command on the loopback interfaces!

If you execute the "sho route" command on any of the customer routers now, however, you will see that they are only aware of the locally connected routes and the loopback interface of the locally connected service provider router (i.e., R4 will only see the IP address of loopback1 on R1, R7 will only see the IP address of loopback2 on R1, etc.). This is because the OSPF processes that we just turned up are only sharing routes with the customer router connected to their Ethernet interface (OSPF 100 on R1 is only sharing routes through eth0/0 to R4, etc.). To enable routing across the MPLS core, we need to enable another routing process (!) to redistribute routes between these routing processes. If that sounds like a perfect job for BGP, you are exactly right:
router bgp 65510
no synchronization
bgp log-neighbor-changes
neighbor 10.10.10.2 remote-as 65510
neighbor 10.10.10.2 update-source Loopback0
neighbor 10.10.10.3 remote-as 65510
neighbor 10.10.10.3 update-source Loopback0
no auto-summary
!
address-family vpnv4
neighbor 10.10.10.2 activate
neighbor 10.10.10.2 send-community extended
neighbor 10.10.10.3 activate
neighbor 10.10.10.3 send-community extended
exit-address-family
!
address-family ipv4 vrf right
redistribute ospf 200
no auto-summary
no synchronization
exit-address-family
! address-family ipv4 vrf left
redistribute ospf 100
no auto-summary
no synchronization
exit-address-family
!

Notice the "address-family ipv4 vrf left" and "address-family ipv4 vrf right" statements. Under these statements, we have "redistribute ospf <process ID>" commands. These commands tell BGP to share routes learned via these OSPF process to the appropriate VRFs. That is, BGP should share routes learned via OSPF process 100 with VRF "left" and OSPF process 200 with VRF "right." This is good, but not sufficient. BGP will now know about any routes that OSPF knows, but OSPF doesn't yet know about all of the routes that BGP has in its routing table (you can verify this by running the "show ip route vrf left" and "show ip route vrf right" commands on any of the service provider routers). Let's take care of that now:
R1(config)#router ospf 100
R1(config-router)#redistribute bgp 65510 subnets
R1(config-router)#router ospf 200
R1(config-router)#redistribute bgp 65510 subnets

With this final configuration command, you should now have routes across the MPLS core between the customer routers. R4 should be able to ping/telnet/etc. to R5 (and vice versa) and R6 should be able to ping/telnet/etc. to R7 (and vice versa). However, neither R4 nor R5 should be able to communicate with R6 or R7.

Troubleshooting:
Enabling MPLS introduces a couple of complications to troubleshooting. As I mentioned earlier, I was fortunate enough to be an admin at a telco that was building out an MPLS core back in my early days of system and network administration. On one occasion, I saw an alarm for a customer's network, and logged into one of the MPLS edge routers to try to troubleshoot. However, when I tried to ping the customer's router, I was dismayed to find that my router apparently didn't have a route to that host. You can see this for yourself by attempting to ping a customer router from R1, R2 or R3:
R1#ping 172.16.2.2

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 172.16.2.2, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R1#
If you try "show route," you'll see a similar issue:
R1#sho ip route
<...snip...>
C    192.168.12.0/24 is directly connected, Serial0/0
C    192.168.13.0/24 is directly connected, Serial0/1
     10.0.0.0/32 is subnetted, 3 subnets
O       10.10.10.2 [110/65] via 192.168.12.2, 01:09:36, Serial0/0
O       10.10.10.3 [110/65] via 192.168.13.3, 01:09:36, Serial0/1
C       10.10.10.1 is directly connected, Loopback0
O    192.168.23.0/24 [110/128] via 192.168.13.3, 01:09:36, Serial0/1
                     [110/128] via 192.168.12.2, 01:09:36, Serial0/0
R1#

If you'll notice, all of the customer routers were configured with 172.16.x.y IP addresses, but there are no 172.16.x.y IP addresses in the routing table on R1. Yet, if you try a ping from R4 to R5 (or R6 to R7), you will see that it works:
R4#ping 172.16.2.2

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 172.16.2.2, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 8/19/52 ms
R4#

How is this possible?

If you'll recall, I commented earlier that MPLS is rather like the Layer-3 equivalent of a VLAN, in that MPLS encapsulates the routing tables of the various VRFs into their own logical partitions, and that these routing tables are not shared amongst other VRFs. Nor are they shared with the global routing table of the service provider routers. If you want to view the routing tables inside a VRF or ping across the VRF, you have to explicitly tell the router so:
R1#ping vrf left 172.16.2.2

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 172.16.2.2, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 8/36/140 ms
R1#sho ip route vrf left

Routing Table: left
<...snip...&rt;
Gateway of last resort is not set

     172.16.0.0/30 is subnetted, 2 subnets
C       172.16.0.0 is directly connected, Ethernet0/0
B       172.16.2.0 [200/0] via 10.10.10.2, 03:01:06
     10.0.0.0/32 is subnetted, 2 subnets
O E2    10.10.10.4 [110/20] via 172.16.0.2, 01:17:46, Ethernet0/0
C       10.0.100.1 is directly connected, Loopback1
R1#

As a fellow network admin and blogger so eloquently put it, "Pretty much any command that you would normally use, add 'vrf <vrf name>' to it." This tells the router that you are specifically asking about the routing tables inside the specified VRF, which makes sense, if you think about it. In this lab, both R4 and R7 have the same IP address. From the service provider router's standpoint, how would it know whether it should ping R4 or R7 if all you did was "ping 172.16.0.2?" Consequently, you have to explicitly tell the router that it is to ping 172.16.0.2 inside VRF "left" (for example).

For more on the subject, check out the following web pages that I found to be helpful while trying to learn how to configure MPLS on Cisco routers:
Packet Life: Creating MPLS VPNs
Cisco Dreamer: MPLS Basics
Router Jockey: Cisco MPLS VRF Configuration and Demo