Segment Routing on IOS-XR

Cisco has released some support for segment-routing on IOS-XR 5.2.0 so what better time to lab it up. I’ve got four IOS-XRv boxes running 5.2.0:

RP/0/0/CPU0:XR1#sh ver | include XR
Cisco IOS XR Software, Version 5.2.0[Default]

Currently IS-IS is the only protocol with support in XR. There are drafts to get this working in both OSPFv2 and OSPFv3

Segment Routing?

Segment routing is a huge topic. In the long run it’ll make it very easy for an SDN controller to force packets through the network in any way it wants. The draft says that it can use the existing MPLS data plane (aka labels) or the IPv6 data plane (header extensions). Right now support is for the MPLS data plane only. The nice thing here is that all devices that can currently switch based on labels should really only need a software upgrade to run segment routing in it’s current form.

Currently, in order to populate the MPLS data plane with labels you need a MPLS control plane protocol to distribute those labels. With segment routing, those labels are distributed with the IGP. Your core is now simplified as it’s only running the IGP with no LDP or RSVP. Your core no longer needs to keep LDP or RSVP state at all.

Traffic Engineering

Take the following simple diagram into consideration:
SR-1
I’d like to use both paths to get from PE1 to PE2 for different taffic flows. This is possible with RSVP by creating multiple RSVP-TE tunnels:
SR-2
The above works perfectly fine, but those P routers need to keep state for each and every RSVP tunnel going through them. In segment routing, there is a concept of a node segment and adjaceny segment. There are also other segment types but I won’t go into that yet. With the MPLS dataplane, each segment has a label. I can therefore force traffic to go over a certain segment by adding the segment label to the stack.
SR-3
In the above diagram, if I want PE1 to send to PE2 via the shortest path, it simply imposes the node segment of PE2 onto the packet and sends it on. Every router in the core knows what PE2’s node segment is and as such the packet is pushed through using only that single label. Note that standard MPLS PHP behaviour is still used:
SR-4

If I wanted to force traffic to PE2 to go over the P1-P2 link and then the P2-P3 link, I would stack the labels to ensure it went that way. It’s the ingress PE that decides this:
SR-5
PE1 has stacked the labels in such a way that it forces the packet to go to particular segments. The core does not need to contain any of the LSP state. It simply installs the labels from the IGPs previously sent.

Configuration

Segment Routing in 5.2.0 has been enabled, but at a preliminary level only. IS-IS is the only IGP supported. MPLS dataplane is only supported. I can’t seem to find a way to advertise adjaceny segments yet, only node segments. All of the above is fine for an MPLS L3VPN lab. I’ll be using the following topology:
SR-6
The CEs are running OSPFv2 and advertising their loopbacks into OSPF:

interface Loopback0
 ip address 100.100.100.100 255.255.255.255
 ip ospf 1 area 0
!
interface GigabitEthernet0/0.11
 encapsulation dot1Q 11
 ip address 10.0.11.1 255.255.255.0
 ip ospf 1 area 0

The PE config is pretty standard:

vrf CUS1
 address-family ipv4 unicast
  import route-target
   100:1
  !
  export route-target
   100:1
  !
 !
!
router ospf CUS1
 vrf CUS1
  redistribute bgp 100
  area 0
   interface GigabitEthernet0/0/0/0.11
   !
  !
 !
!
router bgp 100
 address-family vpnv4 unicast
 !
 neighbor 4.4.4.4
  remote-as 100
  update-source Loopback0
  address-family vpnv4 unicast
  !
 !
 vrf CUS1
  rd 100:4
  address-family ipv4 unicast
   redistribute ospf CUS1
  !
 !
!

XR1 has a VPNv4 session with XR4 and advertising the prefixes over. Segment routing is now enabled under the core IGP, IS-IS:

router isis 1
 is-type level-2-only
 net 49.0001.0000.0000.0001.00
 address-family ipv4 unicast
  metric-style wide
  segment-routing mpls
 !
 interface Loopback0
  address-family ipv4 unicast
   prefix-sid index 1000
  !
 !
 interface GigabitEthernet0/0/0/1
  address-family ipv4 unicast
  !
 !
 interface GigabitEthernet0/0/0/2
  address-family ipv4 unicast
  !
 !
!

For now you can only configure the node ID under the loopback interface. Once this is all done, I should have a labbeled router to R4’s loopback, without LDP or RSVP:

RP/0/0/CPU0:XR1#show cef  4.4.4.4 | include labels
Sun Aug 10 19:48:51.587 UTC
     local label 904000      labels imposed {904000}
     local label 904000      labels imposed {904000}

RP/0/0/CPU0:XR1#show mpls int gigabitEthernet 0/0/0/1 detail
Sun Aug 10 19:49:25.145 UTC
Interface GigabitEthernet0/0/0/1:
        LDP labelling not enabled
        LSP labelling not enabled
        MPLS ISIS enabled
        MPLS enabled

There are two labels are XR1 has two equal cost paths to XR4. A quick traceroute will show the same label:

RP/0/0/CPU0:XR1#traceroute 4.4.4.4
Sun Aug 10 19:50:16.191 UTC

Type escape sequence to abort.
Tracing the route to 4.4.4.4

 1  10.0.12.2 [MPLS: Label 904000 Exp 0] 9 msec  0 msec  0 msec
 2  10.0.24.4 0 msec  *  0 msec

Note that L3VPN still uses an inner label, the service/VPN label. The outer transport label has been replaced with the segment routing label. A traceroute from CE1 to CE2 will confirm this:

CE1#traceroute 200.200.200.200 so lo0 numeric
Type escape sequence to abort.
Tracing the route to 200.200.200.200
VRF info: (vrf in name/id, vrf out name/id)
  1 10.0.11.10 1 msec 1 msec 1 msec
  2 10.0.12.2 [MPLS: Labels 904000/16001 Exp 0] 4 msec 3 msec 3 msec
  3 10.0.24.4 [MPLS: Label 16001 Exp 0] 3 msec 7 msec 3 msec
  4 10.0.42.2 4 msec *  4 msec

Conclusions

  • Basic segment routing is increadibly easy to enable
  • I don’t see ISPs changing from RSVP-TE to SR anytime soon, but I think it will happen eventually
  • SDN is a great use case for SR, as the controller can inform PEs which segment labels to stack onto a packet as it ingresses the router
  • Perhaps even the host itself could send a packet with an SR stack imposed. Maybe that host has learnt this stack from the SDN controller? Time will tell

OSPF Enhancements in recent IOS versions

OSPFv3 Authentication Trailer

In 2011 I wrote an article showing that in order to provide authenticated OSPFv3 neighbour sessions, you needed the security license on IOS.

Manav Bhatia commented on that post stating they were working on an IETF standard to fix this. That draft became RFC6506 and then RFC7166

Cisco has added support for RFC7166 as of IOS 15.4(2)T and IOS-XE 3.11S

Configuration is very quick and easy. Note that OSPFv3 authentication headers do not support md5 according to the RFC. If you configure your key chain with md5, it will not work.
OSPFv3 AUTH

R1#sh run int gi0/0.12
Building configuration...

Current configuration : 125 bytes
!
interface GigabitEthernet0/0.12
 encapsulation dot1Q 12
 ipv6 address 2001:DB8:12:0:10:1:2:1/64
 ospfv3 1 ipv6 area 0
end

Standard interface config. I’ll now configure the key chain and authenticate ensure all area 0 adjacencies:

R1#sh run | sec key chain
key chain AUTH
 key 1
  key-string RFC
  cryptographic-algorithm hmac-sha-512

R1#sh run | sec router ospfv3
router ospfv3 1
 router-id 1.1.1.1
 !
 address-family ipv6 unicast
  authentication mode strict
  area 0 authentication key-chain AUTH
 exit-address-family

Verify:

R1#show ospfv3 interface
GigabitEthernet0/0.12 is up, line protocol is up
  Link Local Address FE80::A8AA:11FF:FE11:1111, Interface ID 15
  Area 0, Process ID 1, Instance ID 0, Router ID 1.1.1.1
  Network Type BROADCAST, Cost: 1
  Cryptographic authentication enabled with strict key lifetime
    Sending SA: Key 1, Algorithm HMAC-SHA-512 - key chain AUTH
  Transmit Delay is 1 sec, State BDR, Priority 1
  Designated Router (ID) 2.2.2.2, local address FE80::A8AA:22FF:FE22:2222
  Backup Designated router (ID) 1.1.1.1, local address FE80::A8AA:11FF:FE11:1111
  Timer intervals configured, Hello 10, Dead 40, Wait 40, Retransmit 5
    Hello due in 00:00:06
  Graceful restart helper support enabled
  Index 1/1/1, flood queue length 0
  Next 0x0(0)/0x0(0)/0x0(0)
  Last flood scan length is 2, maximum is 2
  Last flood scan time is 0 msec, maximum is 0 msec
  Neighbor Count is 1, Adjacent neighbor count is 1
    Adjacent with neighbor 2.2.2.2  (Designated Router)
  Suppress hello for 0 neighbor(s)


R1#show ospfv3 neighbor

          OSPFv3 1 address-family ipv6 (router-id 1.1.1.1)

Neighbor ID     Pri   State           Dead Time   Interface ID    Interface
2.2.2.2           1   FULL/DR         00:00:33    15              GigabitEthernet0/0.12

Oddly, IOS-XR 5.2.0 still does not support this RFC. Only the previous IPSec authentication:

RP/0/0/CPU0:XR6(config-ospfv3-ar)#authentication ?
  disable  Do not authenticate OSPFv3 packets
  ipsec    Use IPSec AH authentication

OSPFv2 Multiarea Adjacency

In 2012 I wrote another post explaining the problem of suboptimal routing in OSPFv2. RFC5185 was created to allow a single interface to be in multiple areas. At the time of writing that original post, this feature was only in Junos and IOS-XE. This has now been added to IOS 15.4(1)T I recommend you read the above post first to understand the issue.

I’ll use a similar topology to that original post. I’ve substituted R5 and R6 with IOS-XR boxes:
OSPF - MA

XR5 goes over the primary link, but XR6 goes over the backup:

RP/0/0/CPU0:XR5#traceroute 4.4.4.4
Thu Aug  7 21:39:21.188 UTC

Type escape sequence to abort.
Tracing the route to 4.4.4.4

 1  10.0.15.1 9 msec  0 msec  0 msec
 2  10.0.14.4 0 msec  *  0 msec


RP/0/0/CPU0:XR6#traceroute 4.4.4.4
Thu Aug  7 21:39:31.999 UTC

Type escape sequence to abort.
Tracing the route to 4.4.4.4

 1  10.0.26.2 0 msec  0 msec  0 msec
 2  10.0.24.4 0 msec  *  0 msec

Configuration is pretty simple. Add the original area plus the second area to the interface needed:

interface GigabitEthernet0/0.13
 encapsulation dot1Q 13
 ip address 10.0.13.1 255.255.255.0
 ip ospf multi-area 4
 ip ospf 1 area 0

To verify:

R1#show ip ospf 1 multi-area
OSPF_MA0 is down, line protocol is down
  Primary Interface GigabitEthernet0/0.13, Area 4
  Interface ID 17
  MTU is 1500 bytes
  Interface DOWN as link is not P2P

An interesting caveat, the interface needs to be in point-to-point mode for this to work:

R1(config)#int gi0/0.13
R1(config-subif)#ip ospf net point-to-point

Once I’ve made the above changes on R1, R2, and R3:

R1#show ip ospf 1 multi-area
OSPF_MA0 is up, line protocol is up
  Primary Interface GigabitEthernet0/0.13, Area 4
  Interface ID 17
  MTU is 1500 bytes
  Neighbor Count is 1

A traceroute from XR6 should now follow the path over the primary link:

RP/0/0/CPU0:XR6#traceroute 4.4.4.4
Thu Aug 7 21:55:58.302 UTC

Type escape sequence to abort.
Tracing the route to 4.4.4.4

 1  10.0.26.2 0 msec  0 msec  0 msec
 2  10.0.23.3 0 msec  0 msec  0 msec
 3  10.0.13.1 0 msec  0 msec  0 msec
 4  10.0.14.4 0 msec  *  0 msec

OSPF Multi-Area Adjacency is one of those things that can fix some odd corner case topologies. I would not recommend it. The issue is that now R3 has a full area 4 and area 0 database. It’s also messy. Rather redesign your network!

IOS-XR has had this feature since v3.4.1 – A quick config on XR6:

RP/0/0/CPU0:XR6#sh run router ospf
Thu Aug 7 22:05:25.833 UTC
router ospf 1
 area 0
  interface GigabitEthernet0/0/0/0.26
   network point-to-point
  !
 !
 area 10
  multi-area-interface GigabitEthernet0/0/0/0.26
  !
 !
!

To verify on XR you need to look at the last few lines on a show ospf interface:

RP/0/0/CPU0:XR6#show ospf interface gi0/0/0/0.26
Thu Aug 7 22:05:53.841 UTC

GigabitEthernet0/0/0/0.26 is up, line protocol is up
  Internet Address 10.0.26.6/24, Area 0
  Process ID 1, Router ID 10.0.26.6, Network Type POINT_TO_POINT, Cost: 1
  Transmit Delay is 1 sec, State POINT_TO_POINT, MTU 1500, MaxPktSz 1500
  Timer intervals configured, Hello 10, Dead 40, Wait 40, Retransmit 5
    Hello due in 00:00:07:158
  Index 1/1, flood queue length 0
  Next 0(0)/0(0)
  Last flood scan length is 1, maximum is 1
  Last flood scan time is 0 msec, maximum is 0 msec
  LS Ack List: current length 0, high water mark 16
  Neighbor Count is 1, Adjacent neighbor count is 1
    Adjacent with neighbor 2.2.2.2
  Suppress hello for 0 neighbor(s)
  Multi-area interface Count is 1
    Multi-Area interface exist in area 10 Neighbor Count is 1

OSPFv3 Multiarea Adjacency

IOS 15.4(2)T and IOS-XE 3.11S now has support for multi-area adjacency for OSPFv3.

The config is identical to OSPFv2 so I’m not going to go over it here.

That’s a wrap for today.

Splitting a module from a python app

My OSPF checker is getting a bit big. The majority of the code is the function that parses the OSPF output and returns the required values.

I’d like to continue to refine what it can pull out. I’d also like to check non-IOS devices like Junos and IOS-XR output.

A function can very easily be moved into a new file and then called as a module. The great thing about this is that others can use the same module in different applications of their own. I can also create a separate module per OS that I’m interested in. Each can be edited separately.

The IOS OSPF checker has now been split into it’s own module like so:

import re
import sys

def ospf_information(i):
    int_list = {}
    ospf = re.split(r'[\n](?=GigabitEthernet|FastEthernet|Serial|Tunnel|Loopback|Dialer|BVI|Vlan|Virtual-Access)',i)
    print(ospf)
    for o in ospf:
        properties = {}
        interface =  re.search(r'(GigabitEthernet|FastEthernet|Serial|Tunnel|Loopback|Dialer|BVI|Vlan|Virtual-Access)[0-9]{1,4}/?[0-9]{0,4}.
?[0-9]{0,4}/?[0-9]{0,3}/?[0-9]{0,3}/?[0-9]{0,3}:?[0-9]{0,3}',o)
        if not interface:
            continue
        interface = interface.group()
        ip = re.search(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2})',o)
        if not ip:
            ip = re.search(r'Interface is unnumbered. Using address of [a-zA-Z]{1,10}[0-9]{1,5}/?[0-9]{0,5}.?[0-9]{0,5}',o)
            properties['IP'] = ip.group()
        else:
            properties['IP'] = ip.group()
        a = re.search(r'Area ([\s]{0,3}[0-9]{1,5})',o)
        properties['Area'] = a.group(1)
        n = re.search(r'Network Type ([\s]{0,3}[a-zA-Z_]{0,20})',o)
        properties['Net'] = n.group(1)
        c = re.search(r'Cost: ([0-9]{1,5})',o)
        properties['Cost'] = c.group(1)
        s = re.search(r'line protocol is[\s]([a-zA-Z]{1,4})',o)
        properties['Status'] = s.group(1)
        p = re.search(r'Passive',o)
        if p:
            properties['Neigh'] = "Passive Interface"
            properties['Adj'] = None
        else:
            ne = re.search(r'(?:Neighbor Count is )([0-9]{1,3})',o)
            if not ne:
                properties['Neigh'] = None
            else:
                properties['Neigh'] = ne.group(1)
            ad = re.search(r'(?:Adjacent neighbor count is )([0-9]{1,3})',o)
            if not ad:
                properties['Adj'] = None
            else:
                properties['Adj'] = ad.group(1)
        h = re.search(r'Hello ([0-9]{1,3})',o)
        if not h:
            properties['Hello'] = None
        else:
            properties['Hello'] = h.group(1)
        d = re.search(r'Dead ([0-9]{1,3})',o)
        if not d:
            properties['Dead'] = None
        else:
            properties['Dead'] = d.group(1)
        int_list[interface]=properties
    return int_list

if __name__ == "__main__":
    f = open(sys.argv[1])
    info = f.read()
    f.close()
    ospf = ospf_information(info)
    print("This device contains "+str(len(ospf))+" ospf enabled interfaces")
    print(ospf)

A couple of things to note here. The module now returns a dictionary. This allows any app using this module to easily extract whatever values it chooses instead of iterating through a list. The last section of code allows me to run the module directly against some raw router output directly to pull information out. This part is not run if calling as a module.

In my main application I now simply import the module and change how I call it slightly:

import ospfios
 ospf_int = ospfios.ospf_information(output)

I’ve started a preliminary Junos OSPF module which will return similar values:

import re
import sys

def ospf_information(i):
    int_list = {}
    ospf = re.split(r'[\n](?=ge|fe|lo|ae|et|fxp)',i)
    for o in ospf:
        properties = {}
        interface =  re.search(r'(ge|fe|lo|ae|et|fxp)([0-9]?)([-]?){0,1}[0-9]{1,5}/?[0-9]{0,5}/?[0-9]{0,5}/?[0-9]?[.][0-9]{1,5}',o)
        if not interface:
            continue
        interface = interface.group()
        ip = re.search(r'Address: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})',o)
        properties['IP'] = ip.group(1)
        c = re.search(r'Cost: ([0-9]{1,5})',o)
        properties['Cost'] = c.group(1)
        ad = re.search(r'(?:Adj count: )([0-9]{1,3})',o)
        properties['Adj'] = ad.group(1)
        h = re.search(r'Hello: ([0-9]{1,3})',o)
        properties['Hello'] = h.group(1)
        d = re.search(r'Dead: ([0-9]{1,3})',o)
        properties['Dead'] = d.group(1)
        int_list[interface]=properties
    return int_list

if __name__ == "__main__":
    f = open(sys.argv[1])
    info = f.read()
    f.close()
    ospf = ospf_information(info)
    print("This device contains "+str(len(ospf))+" ospf enabled interfaces")
    print(ospf)

A quick run directly on a small Junos box:

[email protected]:~/git/ospf_checker$ python3 ospfjunos.py junos.txt
This device contains 4 ospf enabled interfaces
{'ge-1/3/0.641': {'IP': '10.11.31.227', 'Cost': '10', 'Adj': '1', 'Hello': '10', 'Dead': '40'}, 'lo0.0': {'IP': '10.11.225.224', 'Cost': '0', 'Adj': '0', 'Hello': '10', 'Dead': '40'}, 'ge-0/0/0.643': {'IP': '10.11.31.90', 'Cost': '10', 'Adj': '1', 'Hello': '10', 'Dead': '40'}, 'ge-0/2/0.644': {'IP': '10.11.31.94', 'Cost': '10', 'Adj': '1', 'Hello': '10', 'Dead': '40'}}

Practising with regular expressions

My last post stated I needed a bit of practise with regular expressions. Python has a live interpreter which is extremely handy for testing out regular expression matches. I’ve created a text file and dumped a whole load of code version, serial, device type, IP, etc outputs in. I can then use regular expression matches to see if I can catch everything I want.

This is my random text with some values I want to capture:

Version 15.3(1)S 5B:9C:CD:5E:D9:65 So if he into shot half many long. SN: FOC1716X2LW
PID: ME-3600X-24TS-MSN: FCZ4836X2LW
PID: PWR-ME3KX-AC Attention he extremity unwilling on otherwise. Conviction up partiality as
China fully 5B:12:EA:4F:E0:57him every fat was world grave. Version 12.2(25)SEB
Version 15.2(4)S2-Version 12.2(50)SE3,abcd.1234.AB14
Version 12.4(4)XD5, SN: LIT171218BU
SN: FOC1716X2LW 192.168.10.5, 10.1.1.4/32; 8.8.8.8Version 15.2(4)S2
Version 12.4(15)T10 Yet e9:f4:43:3a:53:91jennings resolved disposed off.Version 12.2(55)SE1
Version 12.2(55)SE5 1.1.50.1, SN: ABC5256X2LW,,PID: ME-3600X-24TS-M

Regex Basics

Whole books have been written on regular expressions, so I’m not going to re-invent the wheel. However let’s go over some basics that I have found handy in the re module in Python.
If we wanted to match any occurrences of a – z at a position we can use:

[a-z]

This only matches lower-case a – z. If you wanted to match uppercase as well there are two ways to do so:

[a-zA-Z]

or

[a-z], text, re.IGNORECASE

If I wanted to match 2 numbers, I could do it two ways as well:

[0-9][0-9]

or

[0-9]{2}

What if I wanted to match anything from 1 – 3 numbers in succession? :

[0-9]{1,3}

Special characters can also be matched. Whitespace and Tab is as follows:

[\s\t]

Certain special characters need an escape character. Brackets is an example:

[\(\)]

Match IOS Version

First lets read our jumbled file and get it into a string:

>>> f = open('string.txt')
>>> text = f.read()
>>> type(text)
<class 'str'>

IOS seems to always use uppercase for Version, but lets not assume anything. I need to match Version, space, two numbers, dot, one or more numbers, open brackets, one or more numbers, close brackets, one or more letters or numbers:

v = re.findall(r'version[\s\t]{0,3}[0-9]{2}.[0-9]{0,3}\([0-9]{0,3}\)[a-z0-9]{0,3}', text, re.IGNORECASE)

When I echo back just v, I get a list of items it’s matched:

>>> v
['Version 15.3(1)S', 'Version 12.2(25)SEB', 'Version 15.2(4)S2', 'Version 12.2(50)SE3', 'Version 12.4(4)XD5', 'Version 15.2(4)S2',
'Version 12.4(15)T10', 'Version 12.2(55)SE1', 'Version 12.2(55)SE5']

To get a slightly cleaner output I could use an iterator and print one item in the string at a time:

>>> for i in v:
	print(i)

	
Version 15.3(1)S
Version 12.2(25)SEB
Version 15.2(4)S2
Version 12.2(50)SE3
Version 12.4(4)XD5
Version 15.2(4)S2
Version 12.4(15)T10
Version 12.2(55)SE1
Version 12.2(55)SE5

Match Hardware

A ‘show inventory’ on IOS lists all hardware prefaced with PID: – So let’s start with that:

>>> h = re.findall(r'pid:[\s\t]{0,3}[a-z0-9\-]{0,12}', text, re.IGNORECASE)
>>> for i in h:
	print(i)

	
PID: ME-3600X-24T
PID: PWR-ME3KX-AC
PID: ME-3600X-24T

The regex matches pid: (ignoring case), zero to three tabs or whitespace, followed by zero to twelve letters or numbers, including hyphens.

Match Serial Number

This is slightly easier. Show inventory on IOS prefaces the serial number with SN:

>>> s = re.findall(r'sn:[\s\t]{0,3}[a-z0-9\-]{0,20}', text, re.IGNORECASE)
>>> for i in s:
	print(i)

	
SN: FOC1716X2LW
SN: FCZ4836X2LW
SN: LIT171218BU
SN: FOC1716X2LW
SN: ABC5256X2LW

Match IP addresses

Note that this particular expression will also match invalid IPs. i.e. it will also match 999.999.999.999:

>>> ip = re.findall(r'((?:[0-9]{1,3}\.){3}[0-9]{1,3})', text)
>>> for i in ip:
	print(i)

	
192.168.10.5
10.1.1.4
8.8.8.8
1.1.50.1

Match MAC addresses

MAC address format can sometimes be quite different. Sometimes it looks like ff:ff:ff:ff:ff:ff, sometimes FF:FF:FF:FF:FF:FF, sometimes ffff.ffff.ffff. Let’s first concentrate on the first two:

>>> m = re.findall(r'((?:[0-9a-f]{2}:){5}[0-9a-f]{2})', text, re.IGNORECASE)
>>> for i in m:
	print(i)

	
5B:9C:CD:5E:D9:65
5B:12:EA:4F:E0:57
e9:f4:43:3a:53:91

To find the other type of MAC:

>>> m = re.findall(r'((?:[0-9a-f]{4}\.){2}[0-9a-f]{4})', text, re.IGNORECASE)
>>> for i in m:
	print(i)

	
abcd.1234.AB14

Conclusions

Regular expressions are awfully handy. I’m slowly getting more used to them. Once you figure out patterns it becomes a bit easier. The next step for me is to aggregate both MAC address patterns as well as search for all manner of IPv6 addresses!

OSPF as the PE-CE routing protocols deep dive – Part 3 of 3 – Loop Prevention

Read part 1
Read part 2
Read part 3

 
When customer sites are single-homed, there is no possibility of a loop forming, unless of course your customer decides to set up a bunch of GRE tunnels and run OSPF over that, but I digress. If a site is multi-homed, or two sites have a back-door between them, it’s essential that route from BGP going into OSPF, do not go back into BGP.

Let’s create a slightly different diagram for this one. R3 is now also a PE router:

The loop prevention used ultimately depends on whether a prefix comes in as internal or external. If a sham-link is configured and all OSPF routes are intra-area, no loop prevention is needed. Standard SPF is run everything is fine. This is because everything is seen in area 0, and SPF can run with full knowledge of the entire area.

As soon as type3s and type5s are used, OSPF becomes a little more distance vector like. ABRs/ASBRs originate new LSAs and other OSPF router believe what is told to them. This makes is possible for loops to appear when multual redistribution is occuring.

The down bit

Let’s go back to RFC 4577, specifically section 4.2.5.1

When a type 3 LSA is sent from a PE router to a CE router, the DN bit [OSPF-DN] in the LSA Options field MUST be set. This is used to ensure that if any CE router sends this type 3 LSA to a PE router, the PE router will not redistribute it further.

When a PE router needs to distribute to a CE router a route that comes from a site outside the latter’s OSPF domain, the PE router presents itself as an ASBR (Autonomous System Border Router), and distributes the route in a type 5 LSA. The DN bit [OSPF-DN] MUST be set in these LSAs to ensure that they will be ignored by any other PE routers that receive them.

There are deployed implementations that do not set the DN bit, but instead use OSPF route tagging to ensure that a type 5 LSA generated by a PE router will be ignored by any other PE router that may receive it. A special OSPF route tag, which we will call the VPN Route Tag (see Section 4.2.5.2), is used for this purpose. To ensure backward compatibility, all implementations adhering to this specification MUST by default support the VPN Route Tag procedures specified in Sections 4.2.5.2, 4.2.8.1, and 4.2.8.2. When it is no longer necessary to use the VPN Route Tag in a particular deployment, its use (both sending and receiving) may be disabled by configuration.

Essentially, if an LSA arrives at a PE with the down bit set, that will never be redistributed into BGP. This prevents the route from leaking in from one PE back into another PE.

Down Bit – IOS

R7 is advertising it’s loopback address. No sham-links are used and so R4 will originate a type3 LSA to R6:

R6#show ip ospf database summary 7.7.7.7  adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Summary Net Link States (Area 0)

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 441
  Options: (No TOS-capability, DC, Downward)
  LS Type: Summary Links(Network)
  Link State ID: 7.7.7.7 (summary Network Number)
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000003
  Checksum: 0x5636
  Length: 28
  Network Mask: /32
        MTID: 0         Metric: 2

Options state ‘Downward’ – This LSA is flooded to R6 -> R5 -> R3. R3, another PE, will have the LSA (all databases need to match remember) but it will not use the LSA. The routing bit will not be set, and it will not redistribute that into BGP either:

R3#  show ip ospf database summary 7.7.7.7  adv-router 4.4.4.4

            OSPF Router with ID (10.0.35.3) (Process ID 1)

                Summary Net Link States (Area 0)

  LS age: 597
  Options: (No TOS-capability, DC, Downward)
  LS Type: Summary Links(Network)
  Link State ID: 7.7.7.7 (summary Network Number)
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000003
  Checksum: 0x5636
  Length: 28
  Network Mask: /32
        MTID: 0         Metric: 2

The same happens vice-versa. Any LSA originated by R3 to R5, will be received but not used by R4.

Down Bit – IOS-XR

No change in IOS-XR behaviour. You need to be sure your domain-ids match to get a type3 between IOS and IOS-XE:

R6#sh ip ospf database summary 7.7.7.7 adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Summary Net Link States (Area 0)

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 20
  Options: (No TOS-capability, DC, Downward)
  LS Type: Summary Links(Network)
  Link State ID: 7.7.7.7 (summary Network Number)
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0x5A34
  Length: 28
  Network Mask: /32
        MTID: 0         Metric: 2

Down bit set on the type3.

Route tags – IOS

Let’s go back to the RFC to see what this is all about. Section 4.2.5.2

If a particular VRF in a PE is associated with an instance of OSPF, then by default it MUST be configured with a special OSPF route tag value, which we call the VPN Route Tag. By default, this route tag MUST be included in the Type 5 LSAs that the PE originates (as the result of receiving a BGP-distributed VPN-IPv4 route, see Section 4.2.8) and sends to any of the attached CEs.

The configuration and inclusion of the VPN Route Tag is required for backward compatibility with deployed implementations that do not set the DN bit in type 5 LSAs. The inclusion of the VPN Route Tag may be disabled by configuration if it has been determined that it is no longer needed for backward compatibility.

The value of the VPN Route Tag is arbitrary but must be distinct from any OSPF Route Tag being used within the OSPF domain. Its value MUST therefore be configurable. If the Autonomous System number of the VPN backbone is two bytes long, the default value SHOULD be an automatically computed tag based on that Autonomous System number

If the Autonomous System number is four bytes long, then a Route Tag value MUST be configured, and it MUST be distinct from any Route Tag used within the VPN itself.

If a PE router needs to use OSPF to distribute to a CE router a route that comes from a site outside the CE router’s OSPF domain, the PE router SHOULD present itself to the CE router as an Autonomous System Border Router (ASBR) and SHOULD report such routes as AS-external routes. That is, these PE routers originate Type 5 LSAs reporting the extra-domain routes as AS-external routes. Each such Type 5 LSA MUST contain an OSPF route tag whose value is that of the VPN Route Tag. This tag identifies the route as having come from a PE router. The VPN Route Tag MUST be used to ensure that a Type 5 LSA originated by a PE router is not redistributed through the OSPF area to another PE router.

Note that it says the OSPF should set a route-tag when the implementation doesn’t support setting the down bit in type5 LSAs. Also note in the previous RFC quote that it did note an implementation could set the down bit in type5s if desired. At this point I’ve stopped advertising R7’s loopback directly into OSPF and simply redistributed the loopback. This ensures that the LSA is external.

Usually when an ASBR originates a type5, that type5 remains unchanged in the domain. i.e. the originating router is the same. However according to the quote above, the PE need to originate a new type5 to the attached CE. This we see on R6:

R6#show ip ospf database external 7.7.7.7  adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Type-5 AS External Link States

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 38
  Options: (No TOS-capability, DC)
  LS Type: AS External Link
  Link State ID: 7.7.7.7 (External Network Number )
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0x77C7
  Length: 36
  Network Mask: /32
        Metric Type: 2 (Larger than any link state path)
        MTID: 0
        Metric: 20
        Forward Address: 0.0.0.0
        External Route Tag: 3489661028

Notice no down bit. Also note the originator of this type5 is R4 itself. Finally the route has an external route tag of 3489661028

Much like the down bit, if a PE router receives an external LSA with a domain tag that matches it’s own, that LSA will not be used or redistributed

R3#show ip ospf 1 database external 7.7.7.7 adv-router 4.4.4.4

            OSPF Router with ID (10.0.35.3) (Process ID 1)

                Type-5 AS External Link States

  LS age: 744
  Options: (No TOS-capability, DC)
  LS Type: AS External Link
  Link State ID: 7.7.7.7 (External Network Number )
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0x77C7
  Length: 36
  Network Mask: /32
        Metric Type: 2 (Larger than any link state path)
        MTID: 0
        Metric: 20
        Forward Address: 0.0.0.0
        External Route Tag: 3489661028

No routing bit set, no redistribution happening.

Route tags – IOS-XR

R6#sh ip ospf database external 7.7.7.7 adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Type-5 AS External Link States

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 11
  Options: (No TOS-capability, DC)
  LS Type: AS External Link
  Link State ID: 7.7.7.7 (External Network Number )
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0xEFCE
  Length: 36
  Network Mask: /32
        Metric Type: 2 (Larger than any link state path)
        MTID: 0
        Metric: 20
        Forward Address: 0.0.0.0
        External Route Tag: 3489661028

IOS-XR and IOS have the same behaviour.

IOS – 32bit AS number – Route-tag

The RFC states that when using 16bit AS numbers, the domain tag is automatically derived. When using a 32bit AS number, it should be manually configured. You are able to manually set this even when using a 16bit number with the domain-tag command. You can see above that when using a 16bit number it was automatic. Let’s move to a 32bit number and see what we see.
A quick change of the BGP sessions:

R4#sh run | sec router bgp
router bgp 4294967295
 no bgp default ipv4-unicast
 bgp log-neighbor-changes
 neighbor 2.2.2.2 remote-as 4294967295
 neighbor 2.2.2.2 update-source Loopback0
 neighbor 3.3.3.3 remote-as 4294967295
 neighbor 3.3.3.3 update-source Loopback0

Take a look at the type5 on R6. The domain-tag matches the 32bit AS number directly. This is not 100% confirming to the RFC which states it should be manually set:

R6#sh ip ospf database external 7.7.7.7 adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Type-5 AS External Link States

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 76
  Options: (No TOS-capability, DC)
  LS Type: AS External Link
  Link State ID: 7.7.7.7 (External Network Number )
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0x2C48
  Length: 36
  Network Mask: /32
        Metric Type: 2 (Larger than any link state path)
        MTID: 0
        Metric: 20
        Forward Address: 0.0.0.0
        External Route Tag: 4294967295

Of course, R3 will not use that LSA as it’s domain-tag matches.

Considering the domain-tag matches, it stands to reason that any inter-AS VPN using OSPF would be susceptible to routing loops as each SP will have a different domain-tag. One of them could manually set it to match the other.

32bit AS number – Route-tag – IOS-XR

IOS-XR’s 32bit external behaviour is identical to IOS:

R6#sh ip ospf database external 7.7.7.7 adv-router 4.4.4.4

            OSPF Router with ID (6.6.6.6) (Process ID 1)

                Type-5 AS External Link States

  Routing Bit Set on this LSA in topology Base with MTID 0
  LS age: 76
  Options: (No TOS-capability, DC)
  LS Type: AS External Link
  Link State ID: 7.7.7.7 (External Network Number )
  Advertising Router: 4.4.4.4
  LS Seq Number: 80000001
  Checksum: 0xA44F
  Length: 36
  Network Mask: /32
        Metric Type: 2 (Larger than any link state path)
        MTID: 0
        Metric: 20
        Forward Address: 0.0.0.0
        External Route Tag: 4294967295

Once again, IOS and IOS-XR have the same behaviour.

Notes

  • Unlike parts 1 and 2 of this blog, IOS and IOS-XR finally show identical behaviour when it comes to loop prevention.