Route Leaking with Junos

I’ve been working on a few projects recently that have in one way or another required the leaking of routes between different routing tables / routing instances. When I first started working with Junos I did find RIB groups a bit confusing, so here goes with a post about the feature.

In this post I’m going to show you three ways to leak routes between tables – using RIB groups, Instance Import and Logical Tunnels.

Lab topology

In this post I will re-use the topology I created in my last vMX post.

routeleak

The topology consists of 2 x vMX,  PE1 and PE2 will be the main routers on each vMX, and INTGW and CE2 will be Logical Systems.

The link between PE2 and CE2 will be via a VRF routing-instance “red”. All other routes are in table inet.0.

The object of the lab is to leak routes between inet.0 and red.inet.0 on PE2. We will leak a default route between inet.0 and red.inet.0 and leak CE2’s loopback in to inet.0

First let’s setup the topology…

INTGW

This device is simulating an Internet gateway. I will originate a default route in IS-IS to the rest of the topology.

Note: the Loopback address is not advertised in to IS-IS.

[email protected]> show configuration logical-systems INTGW
interfaces {
    ge-0/0/2 {
        unit 0 {
            family inet {
                address 192.168.12.1/24;
            }
            family iso;
        }
    }
    lo0 {
        unit 1 {
            family inet {
                address 1.1.1.1/32;
            }
            family iso {
                address 49.0001.0001.0001.0001.00;
            }
        }
    }
}
protocols {
    isis {
        export DEFAULT;
        interface ge-0/0/2.0 {
            point-to-point;
            level 1 disable;
        }
    }
}
policy-options {
    policy-statement DEFAULT {
        from {
            protocol aggregate;
            route-filter 0.0.0.0/0 exact;
        }
        then accept;
    }
}
routing-options {
    aggregate {
        route 0.0.0.0/0;
    }
}

PE1

This router really isn’t doing much of interest. It is simply running IS-IS on the interfaces between INTGW and PE2

PE2

This router is learning routes from INTGW and PE2 via IS-IS. The interface connection to CE2 is placed in a routing-instance “red”. PE2 and CE2 are exchanging routes using OSPF.

interfaces {
    ge-0/0/1 {
        unit 0 {
            family inet {
                address 192.168.34.3/24;
            }
        }
    }
}
routing-instances {
    red {
        instance-type virtual-router;
        interface ge-0/0/1.0;
        protocols {
            ospf {
                area 0.0.0.0 {
                    interface ge-0/0/1.0;
                }
            }
        }
    }
}

CE2

Nothing special about CE2.

interfaces {
    ge-0/0/2 {
        unit 0 {
            family inet {
                address 192.168.34.4/24;
            }
        }
    }
    lo0 {
        unit 4 {
            family inet {
                address 4.4.4.4/32;
            }
        }
    }
}
protocols {
    ospf {
        area 0.0.0.0 {
            interface ge-0/0/2.0;
            interface lo0.4 {
                passive;
            }
        }
    }
}

Objective

Our objective to to be able to to ping the Loopback address on INTGW from CE2. I am not advertising the address in to IS-IS so reachability is achieved via the default route.

[email protected]> show route 0.0.0.0

inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[IS-IS/165] 01:31:47, metric 30
                    > to 192.168.23.2 via ge-0/0/3.0

[email protected]> show route 1.1.1.1/32

[email protected]> ping rapid count 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
!
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.370/3.370/3.370/nan ms

[email protected]>

If we take a look at INTGW and CE2, neither will currently have reachability to one another.

INTGW:
[email protected]:INTGW> ping rapid count 1 4.4.4.4
PING 4.4.4.4 (4.4.4.4): 56 data bytes
ping: sendto: No route to host
.
--- 4.4.4.4 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss

CE2:
[email protected]:CE2> ping rapid count 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
ping: sendto: No route to host
.
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss

RIB Groups

First of all I will go though how to accomplish the objective with RIB Groups alone.

Essentially a RIB group will allow you to take a route that would be normally be destined for one table, e.g. inet.0, and place that route in another table also, e.g. red.inet.0. This can be done for static, connected, or dynamic routing.

A rib-group is created as below

routing-options {
    rib-groups {
        INET0_to_RED {
            import-rib [ inet.0 red.inet.0 ];
        }
    }
}

Note: the first entry after import-rib is not where we are pulling the routes from, it is where the route would normally be placed.

This config is simply stating any routes that would normally be placed in inet.0 should also be placed in red.inet.0.

However, creating the rib-group alone will not achieve anything – the rib-group must be applied elsewhere in the configuration. You have several options depending on what you want to do:

  • Interface routes – set routing-options interface-routes rib-group <name>
  • Static routes – set routing-options rib-group <name>
  • Dynamic routes, these are applied per protocol, e.g. set protocols ospf rib-group <name>

For this lab we’ll be leaking the IS-IS routes, so I apply the rib-group to IS-IS. Remember as I am leaking routes from inet.0 to red.inet.0 I must apply the rib-group in the master config, not under the routing instance.

The rib-group is applied in to the table where the routes would normally be placed.

set protocols isis rib-group inet INET0_to_RED

Now let’s take a look in the red.inet.0 table, do we see the routes?

[email protected]> show route table red.inet.0

red.inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[IS-IS/165] 05:05:37, metric 30
                    > to 192.168.23.2 via ge-0/0/3.0
2.2.2.2/32         *[IS-IS/18] 05:05:37, metric 10
                    > to 192.168.23.2 via ge-0/0/3.0
4.4.4.4/32         *[OSPF/10] 06:20:33, metric 1
                    > to 192.168.34.4 via ge-0/0/1.0
192.168.12.0/24    *[IS-IS/18] 05:05:37, metric 20
                    > to 192.168.23.2 via ge-0/0/3.0
192.168.34.0/24    *[Direct/0] 06:20:48
                    > via ge-0/0/1.0
192.168.34.3/32    *[Local/0] 06:20:48
                      Local via ge-0/0/1.0
224.0.0.5/32       *[OSPF/10] 06:20:48, metric 1
                      MultiRecv

Awesome! The IS-IS routes are there – we can see the default route and also the loopback on PE1. But what if we wanted to leak the default only? Junos has that covered with an import policy.

I’ll create a policy to accept the default only and apply that to the rib-group.

Just a quick note on the import policy – as IS-IS has a default import policy of accept, I need to add a final term to reject otherwise I will match everything! See this Juniper doc for a reminder of the default import/export policies for the various routing protocols.

policy-options {
    policy-statement DEFAULT {
        term t1 {
            from {
                route-filter 0.0.0.0/0 exact;
            }
            then accept;
        }
        term t2 {
            then reject;
        }
    }
}
rib-groups {
    INET0_to_RED {
        import-rib [ inet.0 red.inet.0 ];
        import-policy DEFAULT;
    }
}

Now the routing table only has the default route leaked!

[email protected]> show route table red.inet.0

red.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[IS-IS/165] 00:04:57, metric 30
                    > to 192.168.23.2 via ge-0/0/3.0
4.4.4.4/32         *[OSPF/10] 06:46:46, metric 1
                    > to 192.168.34.4 via ge-0/0/1.0
192.168.34.0/24    *[Direct/0] 06:47:01
                    > via ge-0/0/1.0
192.168.34.3/32    *[Local/0] 06:47:01
                      Local via ge-0/0/1.0
224.0.0.5/32       *[OSPF/10] 06:47:01, metric 1
                      MultiRecv

Cool, so at this point the red routing-instance now has a default, but what about CE2, can that see the default?

[email protected]> show route logical-system CE2

inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

4.4.4.4/32         *[Direct/0] 06:50:16
                    > via lo0.4
192.168.34.0/24    *[Direct/0] 06:53:12
                    > via ge-0/0/2.0
192.168.34.4/32    *[Local/0] 06:53:12
                      Local via ge-0/0/2.0
224.0.0.5/32       *[OSPF/10] 06:51:52, metric 1
                      MultiRecv

No default route there! Well I purposely made my life difficult by running a different protocol between PE2 and CE2. Remember PE1 and PE2 are talking IS-IS, but PE2 and CE2 are talking OSPF. So whilst the IS-IS route is now in the red.inet.0 table, we need to create an export policy to redistribute the IS-IS route over to CE2 via OSPF. The export policy is applied to the routing-instance.

Note this time my policy does not need an explicit reject to be configured as the default export policy for OSPF is reject.

policy-options {
    policy-statement FROM_ISIS {
        from protocol isis;
        then accept;
    }
}

The export policy is applied to the routing-instance.

set routing-instances red protocols ospf export FROM_ISIS

Now we have an OSPF external default route present on CE2.

[email protected]> show route logical-system CE2

inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[OSPF/150] 00:00:02, metric 30, tag 0
                    > to 192.168.34.3 via ge-0/0/2.0
4.4.4.4/32         *[Direct/0] 06:55:43
                    > via lo0.4
192.168.34.0/24    *[Direct/0] 06:58:39
                    > via ge-0/0/2.0
192.168.34.4/32    *[Local/0] 06:58:39
                      Local via ge-0/0/2.0
224.0.0.5/32       *[OSPF/10] 06:57:19, metric 1
                      MultiRecv

Great, so CE2 knows how to route to INTGW via the default, but INTGW will not know how to route back at this point. I could do NAT on PE2 to hide the address of CE2, or repeat the rib-group process but this time to leak routes from red.inet.0 to inet.0 on PE2. We’ll do it with a rib-group.

[email protected]# show | compare
[edit routing-options rib-groups]
+   RED_to_INET0 {
+       import-rib [ red.inet.0 inet.0 ];
+   }
[edit routing-instances red protocols ospf]
+     rib-group RED_to_INET0;
[edit policy-options]
+   policy-statement FROM_OSPF {
+       from {
+           protocol ospf;
+           route-filter 4.4.4.4/32 exact;
+       }
+ 
[edit protocols isis]
+   export [ FROM_OSPF ];

Notice this time that the order of the import-rib has changed. We are copying routes from red.inet.0, as this is where the routes would normally be placed so red.inet.0 is the first entry in the import-rib statement. This also reminds us where to apply the rib-group.

The rib-group is applied to the routing-instance OSPF process, and again we must export the OSPF routes to IS-IS. Note this export is applied to the master IS-IS process, not the routing instance.

[email protected]> show route table inet.0 0.0.0.0/0 exact

inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[IS-IS/165] 09:12:27, metric 20
                    > to 192.168.12.1 via ge-0/0/1.0

At this point we should have reachability between CE2 and INTGW.

[email protected]> ping 1.1.1.1 source 4.4.4.4 rapid logical-system CE2
PING 1.1.1.1 (1.1.1.1): 56 data bytes
!!!!!
--- 1.1.1.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.890/4.173/4.364/0.181 ms

Success !

Instance Import

Now to repeat this again, but this time using instance import! I’ll start by clearing out the rib-groups.

delete routing-options rib-groups INET0_to_RED
delete routing-options rib-groups RED_to_INET0
delete protocols isis rib-group inet INET0_to_RED
delete routing-instances red protocols ospf rib-group RED_to_INET0

First of all I’ll create a policy to import routes from inet.0 to red.inet.0

policy-options {
    policy-statement FROM_GLOBAL {
        term t1 {
            from {
                instance master;
                route-filter 0.0.0.0/0 exact;
            }
            then accept;
        }
        term t2 {
            then reject;
        }
    }
}

This policy is simply saying for a default route in the master inet.0 table then accept for import and deny everything else. We then apply this policy to the red routing-instance.

set routing-instances red routing-options instance-import FROM_GLOBAL

Now the policy has been configured, table red.inet.0 has a default route imported from the master instance. Since I left the IS-IS to OSPF export in place from the previous rib-group exercise, CE2 will also have the default route.

[email protected]> show route table red.inet.0 0.0.0.0

red.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[IS-IS/165] 00:06:22, metric 30
                    > to 192.168.23.2 via ge-0/0/3.0

[email protected]> show route 0.0.0.0 logical-system CE2

inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[OSPF/150] 00:06:29, metric 30, tag 0
                    > to 192.168.34.3 via ge-0/0/2.0

I now need to leak the CE2 loopback IP between the red.inet.0 table and the master inet.0 table. Again this is a simple routing policy that is applied to the master routing-options.

routing-options {
    instance-import FROM_RED;
}
policy-options {
    policy-statement FROM_RED {
        term t1 {
            from {
                instance red;
                route-filter 4.4.4.4/32 exact;
            }
            then accept;
        }
        term t2 {
            then reject;
        }
    }
}

As the OSPF to IS-IS exports were left in place from the rib-group exercise, INTGW should now have the 4.4.4.4/32 route, and it does.

[email protected]> show route 4.4.4.4/32 logical-system INTGW

inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

4.4.4.4/32         *[IS-IS/165] 00:02:54, metric 21
                    > to 192.168.12.2 via ge-0/0/2.0

Verification – can I ping from CE2 to INTGW, yes!

[email protected]> ping 1.1.1.1 rapid source 4.4.4.4 logical-system CE2
PING 1.1.1.1 (1.1.1.1): 56 data bytes
!!!!!
--- 1.1.1.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.879/8.912/24.706/8.031 ms

Using the instance-import feature is perhaps a little more intuitive than rib-groups, although both can achieve the same end result.

Logical Tunnel

A final way of doing the leaking is to use Logical Tunnel interfaces. The configuration is very simple – an LT is created with one end of the tunnel in the master inet.0 table, and the other end added to red routing instance. We then simply run a routing protocol or static routing via the tunnel interface.

First of all we create the tunnel interfaces and assign one side to the correct routing instance.  This is a vMX so I also need to enable the tunnel services.

chassis {
    fpc 0 {
        pic 0 {
            tunnel-services;
        }
    }
}
interfaces {
    lt-0/0/0 {
        unit 0 {
            encapsulation ethernet;
            peer-unit 1;
            family inet {
                address 10.0.0.1/24;
            }
        }
        unit 1 {
            encapsulation ethernet;
            peer-unit 0;
            family inet {
                address 10.0.0.2/24;
            }
        }
    }
}
routing-instances {
    red {
        interface lt-0/0/0.1;      
        }
    }
}

From here, it’s a simple matter of running a routing protocol via the tunnel. As my red routing-instance is using OSPF routing with CE2, I configure the LT interfaces in OSPF within the master config and the routing-instance.

Note, because I’m running IS-IS between PE1 and PE2, on PE2 I’m also redistributing IS-IS routes to OSPF and OSPF routes to IS-IS to provide reachability.

[email protected]# show | compare
[edit protocols]
+   ospf {
+       export FROM_ISIS;
+       area 0.0.0.0 {
+           interface lt-0/0/0.0;
+       }
+   }
+   isis {
+       export FROM_OSPF;
+   }
[edit routing-instances red protocols ospf area 0.0.0.0]
+       interface lt-0/0/0.1;

[email protected]> show route logical-system CE2 0.0.0.0

inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[OSPF/150] 00:06:42, metric 30, tag 0
                    > to 192.168.34.3 via ge-0/0/2.0

[email protected]> show route table inet.0 4.4.4.4

inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

4.4.4.4/32         *[IS-IS/165] 00:07:08, metric 12
                    > to 192.168.23.3 via ge-0/0/3.0

Can I ping from CE2 to INTGW – yes!

[email protected]# run ping 1.1.1.1 source 4.4.4.4 rapid logical-system CE2
PING 1.1.1.1 (1.1.1.1): 56 data bytes
!!!!!
--- 1.1.1.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.330/3.609/3.884/0.213 ms

Conclusion

I’ve shown three way to leak routes between routing tables on Junos. There are of course many other ways of doing this – static route with next-table, or if I was running MPLS VPNs in this lab I’d also have route-targets to play with, or the auto-export feature for prefix leaking between local VRFs.

If you would like to see a post about these other methods, please say so in the comments. Thanks for reading this post 🙂

 

 

 

 

5 thoughts on “Route Leaking with Junos”

  1. This is really a great post! I had no idea you could do this using the instance commands within the policy. Like you mentioned, I’ve had to tweak and tune the rib import statements because they are simply not intuitive to me – I’ll be using the instance method going forward.

    Blogging can be quite thankless – so just know that I appreciate it! I’d also be interested to see future posts about MPLS route-targets and the next-hop to a table. The more tips and tricks on this the better!

    Thanks!

    -Mike

    1. Hi Mike, thanks for reading the post and the comments 🙂 No worries, I’ll put together at an MPLS RT and next-table version of this post when I get a spare moment! I’ll check out your blog too 😉

  2. Awesome, thx!

    Had to implement the same but i’m asking myself, what is the best / most used option? I figured it out with RIB but others are telling me to do it with the logical tunnel.

    Thx!

Leave a Reply

Your email address will not be published. Required fields are marked *