University network borders tend to be more complicated than those in similarly sized corporate organizations. I recently helped a peer from another university transition from IOS to Junos for border routing. While most of the configuration was straightforward, he required a unique conditional routing policy. Since I’ve been working with Junos for many years (starting around version 4.5 on M20s), surely I could help with some policy. Famous last words!

Some informal disclaimer verbiage about our colleagueship – we both work for researching universities that encourage sharing ideas and knowledge within the educational community. The aspects of this relationship may not be commonplace in the commercial world.

A description of the border routing environment:

The university campus network (one Autonomous System (AS)) peers with a regional research network (different AS)

The university manages the regional’s border router.

The regional network border router and campus border router have separate connections to different ISPs.

Under normal circumstances, both the campus and regional use the campus-connected ISP for commodity Internet access to save money. Stated another way – under normal circumstances, the campus provides transit to the regional, but if that path isn’t available, the regional provides transit to the campus.

Because of legacy hardware limitations, there is no load sharing or traffic steering, it’s all or none. Changing ISP peering arrangements was outside the scope of the project.

The campus utilizes the regional border router to provide Internet access in the event that the campus ISP is unavailable. (see Fig 1).

How IOS solves the problem

In the previous IOS based border routers, this was accomplished through a conditional routing configuration, which contained an “advertise-map” action based upon “non-exist-map” criteria. This information is documented on Cisco’s website: http://www.cisco.com/c/en/us/support/docs/ip/border-gateway-protocol-bgp/16137-cond-adv.html

Here is the IOS configuration this document will reference.

router bgp 65444 address-family ipv4 neighbor 10.28.92.177 advertise-map to_sendondPrefISP_advertise_map non-exist-map to_secondPrefISP_exist_map route-map to_sendondPrefISP_advertise_map permit 10 match ip address prefix-list to_SecondPrefISP_conditional route-map to_secondPrefISP_exist_map permit 10 match ip address prefix-list default_route_from_commodity match community community_list_from_commodity ip prefix-list default_route_from_commodity seq 5 permit 0.0.0.0/0 ip community-list standard community_list_from_commodity permit 65444:2001 ip prefix-list to_SecondPrefISP_conditional seq 5 permit ip prefix-list to_SecondPrefISP_conditional seq 10 permit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 router bgp 65444 address - family ipv4 neighbor 10.28.92.177 advertise - map to_sendondPrefISP_advertise_map non - exist - map to_secondPrefISP_exist_map route - map to_sendondPrefISP_advertise_map permit 10 match ip address prefix - list to_SecondPrefISP_conditional route - map to_secondPrefISP_exist_map permit 10 match ip address prefix - list default_route_from_commodity match community community_list_from_commodity ip prefix - list default_route_from_commodity seq 5 permit 0.0.0.0 / 0 ip community - list standard community_list_from_commodity permit 65444 : 2001 ip prefix - list to_SecondPrefISP_conditional seq 5 permit ip prefix - list to_SecondPrefISP_conditional seq 10 permit

The above configuration performs the following:

Sets the criteria for the non-exist map to_secondPrefISP_exist_map Check the RIB for the existance of prefix 0.0.0.0/0 with a community tag of 65444:2001

If-and-only-if both criteria are NOT present, then the policy outlined in the advertise-map to_sendondPrefISP_advertise_map is then used for BGP announcements to neighbor 10.28.92.177 the prefixes in the prefix-list to_SecondPrefISP_conditional will be advertised. While not present, other BGP attributes could also be advertised.

is then used for BGP announcements to neighbor 10.28.92.177

As Junos documentation suggested, our first attempt utilized the Junos conditional feature within a routing import policy. Unfortunately, the Junos conditional feature alone only tested for the existence of a route in a specific table. No other attributes, such as community (which was done in the IOS config) could be specified.

While we were attempting to figure out how to make this work, the following alternatives were considered “just-in-case.”

Option 1: The regional network could test for the status of the campus’ ISP link, by checking for the existence of the /31 link address in its own RIB. Of course the campus router would need to advertise this network to an accepting regional router. It is unusual to advertise such infrastructure prefixes, but in a controlled situation (again, both autonomous systems are under single administrative control) it seemed appropriate and possible. We analyzed the risk – the campus ISP link could remain up while routing with that same ISP could be down. However, this seemed very unlikely.

Option 2: The university requested that the primary ISP advertise a /32 (say, the loopback of the peering router). The loopback address should not appear through another path, and testing for this prefix would be a good indicator of proper routing behavior. This was something a bit out of the ordinary, and the ISP (understandably) refused.

The Junos Solution: Policy Condition + RIB Groups

As it turns out, we were much closer to a solution than we realized. The conditional policy was key, but there were additional steps that needed to be taken. As mentioned above, Junos conditionals can test for the existence of a route in a specific table, but not for other attributes. We needed to create a separate routing table (rib-group) for our conditional criteria. Specifically, the default route advertised by the campus network would need to be the ONLY prefix copied into said separate table. This rib-group provided a means around the conditional limitations we originally encountered.

These configuration snippets are from the regional’s router:

[edit routing-options] static { /* Aggregate Null Routes*/ route 10.0.0.0/8 discard; route 192.168.1.0/24 discard; } [edit protocols bgp] group campus { type external; family inet { unicast { rib-group rg_Test-for-other-SP; } } peer-as 65222; neighbor 172.18.10.2; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [ edit routing - options ] static { /* Aggregate Null Routes*/ route 10.0.0.0 / 8 discard ; route 192.168.1.0 / 24 discard ; } [ edit protocols bgp ] group campus { type external ; family inet { unicast { rib - group rg_Test - for - other - SP ; } } peer - as 65222 ; neighbor 172.18.10.2 ; }

Just for reference, we are showing that the Null routes needed for BGP announcement are in the configuration. But, the key part here is the family inet unicast rib-group statement which tells the router to refer to the rib-group for copying specific routes between inet.0 to other tables.

[edit routing-options rib-groups] rg_Test-for-other-SP { import-rib [ inet.0 defaultConditional.inet.0 ]; import-policy po_Default-only; } 1 2 3 4 5 [ edit routing - options rib - groups ] rg_Test - for - other - SP { import - rib [ inet . 0 defaultConditional . inet . 0 ] ; import - policy po_Default - only ; }

This will copy routes from inet.0 (the primary table) to defaultConditional.inet.0 (the newly created table) based on the specified import policy po_Default-only . This policy matches only the default route to be copied into the defaultConditional.inet.0 table.

[edit policy-options policy-statement po_Default-only] term default-only { from { route-filter 0.0.0.0/0 exact; } then accept; } term reject-all-else { then reject; } 1 2 3 4 5 6 7 8 9 10 [ edit policy - options policy - statement po_Default - only ] term default - only { from { route - filter 0.0.0.0 / 0 exact ; } then accept ; } term reject - all - else { then reject ; }

In order to create this table, we needed to first create a routing instance:

routing-instances { defaultConditional { instance-type virtual-router; } } 1 2 3 4 5 routing - instances { defaultConditional { instance - type virtual - router ; } }

We now have sufficient criteria to use in our export policy towards the more expensive ISP connected to the regional network. That configuration looks like:

[edit protocols bgp] group ExpensiveISP { type external; import po_LP-low; export po_Conditional-Advert-to-Expensive-ISP; peer-as 65333; neighbor 172.16.20.20; } [edit policy-options policy-statement po_Conditional-Advert-to-Expensive-ISP] term default { from { route-filter 10.0.0.0/8 exact; route-filter 192.168.1.0/24 exact; condition cnd_pref-default; } then reject; } term allow { from { route-filter 10.0.0.0/8 exact; route-filter 192.168.1.0/24 exact; } then accept; } term reject-all { then reject; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ edit protocols bgp ] group ExpensiveISP { type external ; import po_LP - low ; export po_Conditional - Advert - to - Expensive - ISP ; peer - as 65333 ; neighbor 172.16.20.20 ; } [ edit policy - options policy - statement po_Conditional - Advert - to - Expensive - ISP ] term default { from { route - filter 10.0.0.0 / 8 exact ; route - filter 192.168.1.0 / 24 exact ; condition cnd_pref - default ; } then reject ; } term allow { from { route - filter 10.0.0.0 / 8 exact ; route - filter 192.168.1.0 / 24 exact ; } then accept ; } term reject - all { then reject ; }

This is where the magic happens. The first term deals with the normal situation where the cheaper ISP is operational. We match the prefixes we want to advertise AND check a condition cnd_pref-default .

[edit policy-options condition cnd_pref-default if-route-exists { 0.0.0.0/0; table defaultConditional.inet.0; } 1 2 3 4 5 [ edit policy - options condition cnd_pref - default if - route - exists { 0.0.0.0 / 0 ; table defaultConditional . inet . 0 ; }

The conditional statement cnd_pref-default tests for two criteria:

the existence of the default route in the routing table we created above defaultConditional.inet.0 .

The campus AS should be receiving routes from the less expensive ISP, therefore, the regional then action for exporting prefixes to the expensive ISP is to reject . If the tests in that term aren’t met, which should only be the conditional line assuming that the static routes aren’t deleted, the next term allow permits the prefixes we need to advertise. The last term, reject-all prevents us from leaking an entire table to the ISP.

For outbound routing, we lowered the BGP LOCALPREF metric on the incoming advertisements from the expensive ISP. By BGP design, this is enough to keep that expensive path from becoming active while the less expensive (prefered) route is available.

This policy was a bit more challenging that I initially anticipated because Junos does not have the ability to test for route attributes using the ‘condition’ feature. Easy alternatives were available that met all or most of the objectives, but figuring out how to replicate the IOS policy in Junos was an instructive exercise. This was a collaborative learned exercise with Joe Marentette, Noaman Khan, and myself.