Using apply-groups to insert policy in a predictable manner….

Recently I’ve been solving some interesting problems around how routing policy can be implemented. What I’ve been trying to do is have a way that enables me to quickly insert a policy at the start of a set of routing-policies on a BGP neighbor to stop sharing routes.

For those who are not aware, you can implement multiple policies in a chain on a BGP session. You do this like so;

[edit protocols bgp group R2]
[email protected]# show 
export [ policy-1 policy-2 policy-3 ];
neighbor 192.168.3.1 {
    peer-as 2;
}

In this configuration, all policies will be evaluated sequentially. Policy with matching terms will take action as defined in the policy on the routes matched. Routes will continue to be processed by subsequent terms in each policy until a terminating action is hit. A “terminating action” is something like an “accept” or “reject” action (once you hit one of these you use the actions gathered so far including the terminating action, then stop processing)

Let us say for the sake of this test that the policies we have applied to our peer do 2x as-path prepends, add a MED value, then accept all routes;

[edit policy-options]
[email protected]# show 
policy-statement policy-1 {
    then as-path-prepend "4 4";
}
policy-statement policy-2 {
    then {
        metric 400;
    }
}
policy-statement policy-3 {
    then accept;
}

This would result in the following routes being sent from this router (note the MED and as-path prepends);

[email protected]# run show route advertising-protocol bgp 192.168.3.1    

inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 172.16.172.0/24         Self                 400                4 4 [4] I
* 192.168.3.0/24          Self                 400                4 4 [4] I
* 192.168.6.0/24          Self                 400                4 4 [4] I

In the scenario I described at the start, the aim was to be able to easily (with one line of configuration) insert a policy at the start of the policy-set that rejected all routes. However, as described above, this peer might have existing policies that allow routes to be sent.

Assume we say that I have already created the following policy;

[edit policy-options]
+   policy-statement policy-reject {
+       then reject;
+   }

If I quickly activated this by adding it as an export policy, it would appear as the last policy on the policy-chain;

[email protected]# set protocols bgp group R2 export policy-reject 

[edit]
[email protected]# show | compare 
[edit protocols bgp group R2]
-    export [ policy-1 policy-2 policy-3 ];
+    export [ policy-1 policy-2 policy-3 policy-reject ];

As we can see below, this does not reject any of the routes, because the reject action is processed after a terminating accept action in policy-3;

[email protected]# run show route advertising-protocol bgp 192.168.3.1              

inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 172.16.172.0/24         Self                 400                4 4 [4] I
* 192.168.3.0/24          Self                 400                4 4 [4] I
* 192.168.6.0/24          Self                 400                4 4 [4] I

In order to implement this in the manner I want with this way of doing it, I would have to do the following to achieve it;

[edit protocols bgp group R2]
[email protected]# delete export 
[email protected]# set export policy-reject
[email protected]# set export policy-1
[email protected]# set export policy-2
[email protected]# set export policy-3

You could shorten this by using the “insert” functionality, however you are still requiring a re-ordering of policies as opposed to a one liner to activate this.

The trick I have come up to solve this is quite cool. I am adding a term to an existing policy with an apply-group;

[email protected]# show | compare 
[edit]
+ groups {
+     policy-reject {
+         policy-options {
+             policy-statement policy-1 {
+                 term reject-term {
+                     then reject;
+                 }
+             }
+         }
+     }
+ }

When we activate this apply-group, the term gets inserted into policy-1, which is already ordered to be applied prior to the terminating accept action in policy-3;

[email protected]# show | compare 
[edit]
+ apply-groups policy-reject;

We can see here that it is implemented in policy-1;

[edit policy-options policy-statement policy-1]
[email protected]# show | display inheritance 
##
## 'reject-term' was inherited from group 'policy-reject'
##
term reject-term {
    ##
    ## 'then' was inherited from group 'policy-reject'
    ## 'reject' was inherited from group 'policy-reject'
    ##
    then reject;
}
then as-path-prepend "4 4";

As policy-1 is already the first policy to be processed, this ensures that without having to re-order our existing policies we can insert a reject policy at the start. We can now check the result;

[email protected]# run show route advertising-protocol bgp 192.168.3.1    

[edit]
[email protected]#

And it is working!

You might ask why I did not just apply a new policy in an apply-group instead of a new term? The reason for this is that new policies are added at the end of the policy chain, while a new term in a pre-existing policy uses the existing place that policy has in the policy-chain.

For my purposes, this has enabled me to give out a one-line command which a large group of people can use to disable the advertisement of certain routes at certain times. As with everything in Junos though, there are many possible uses for this set of functionality. Hope this helps!

Subpolicies

A useful feature I’ve been playing with is the ability to use “sub” policies within JUNOS policies for the purposes of matching. This is quite useful for me in some of the work I’ve been doing, as I’ve managed to shrink ~300 lines of policy to ~120 lines by implementing this as much as possible.

Essentially the way it works is that you can use a policy as a from criterion in another policy. i.e.

[edit policy-options policy-statement peerXXX-bgp-export]
[email protected]# show 
term default {
    from policy subpolicy_match_default;
    then reject;
}

[edit policy-options policy-statement subpolicy_match_default]
[email protected]# show 
term v4-default {
    from {
        family inet;
        route-filter 0.0.0.0/0 exact;
    }
    then accept;
}
term v6-default {
    from {
        family inet6;
        route-filter 0::/0 exact;
    }
    then accept;
}
then reject;

What will happen is that everything that is accepted by subpolicy_match_default is a MATCH for the purposes of term default in policy peerXXX-bgp-export. Everything that is rejected by subpolicy_match_default is NO MATCH for the purposes of this. So in this case both the v4 and v6 default routes will be accepted by the sub-policy which will cause the main policy to reject them for export to peerXXX (as they match the term in the main policy which then rejects them), while all other routes will be rejected (as they do not match the term in the main policy which is doing the reject (given that routes rejected by the sub policy are not a match for the purposes of the main policy).

A fairly trivial feature probably, but hellishly useful when configuring lots of similar policies to be able to refer to what is effectively a set of repetitively used subroutines within your policy….

VRF import on JUNOS – gotchas

I’ve been playing quite a bit with VRF import policies on Juniper MXs. To briefly recap for those who haven’t played with Junipers before, you can either specify in the VRF configuration to import VRF target X, or you can create an import policy to do more specific / custom things.

While what I’m trying to achieve isn’t basic, it’s definitely not beyond the scope of what you should be able to do, however I’ve been finding that it’s not implemented as well as I had expected which is disappointing. While I’ve always found JUNOS policy far easier to work with than IOS route-maps, in the case of the two issues I struck today described below, I’m really hating on the JUNOS approach!

No regexp on VRF import policies.

In both these scenarios, I was trying to create a policy that imports blackhole routes. One of the great things in JUNOS is how well integrated the regexp functionality is, letting you do tasks that would traditionally take multiple terms in a policy (largely replicating the same bits of config barring one slight variable) and compress them down to a single term. Unfortunately this works for everything… except for this! In this specific use case, I was trying to import blackhole routes from ANY internet VRF to my VRF.

An example (note that in this example all Internet VRFs were in the range of 50[0-9];

set policy-options community InternetAny members target:12345:50.
set routing-instances InternetInternational vrf-import InternetInternationalImport

[email protected]# show policy-options policy-statement InternetInternationalImport
term Blackhole {
from community InternetAny;
then accept;
}

[edit]

[email protected]# commit check
error: InternetInternational: vrf-import policy cannot have wildcard target communities error: configuration check-out failed [edit]

Doing some digging I’ve found an article on the Juniper website stating this limitation, but with no sensible reason as to why they have decided not to support the normal JUNOS goodness for this feature.

Matching of multiple communities pushed to creating lots of community group combinations

A feature that is normally common to JUNOS and IOS is the ability to match on a criterion of multiple communities. Again – except for VRF Import in JUNOS. The advantage of this (going back to my blackholing example) is that I might want to say match on any Internet VRF target + my blackhole community to accept blackhole routes into the VRF. This is actually doable, just not in the way you would think. Here’s what I tried to do (probably more easily displayed without set format, note that InternetDomestic is a target community and Blackhole is a standard community);

[email protected]# show policy-options policy-statement InternetInternationalImport 
term Blackhole {
    from community [ Blackhole InternetDomestic ];
    then accept;
}
[edit]

[email protected]# commit check
error: InternetInternational: vrf-import policy permits accept action only if matching conditions contain a target community
error: configuration check-out failed

[edit]

As it turns out, any communities you match in a policy must include at least one target communities in their members. The solution ended up being to do something like this;

[email protected]# show policy-options policy-statement InternetInternationalImport
term Blackhole {
from community InternetDomestic_Plus_Blackhole;
then accept;
}
[edit]
[email protected]# show policy-options community InternetDomestic_Plus_Blackhole
members [ target:12345:500 12345:666 ];

[edit]

However, my issue with this is that you then have an explosion of communitiy definitions for every possible combination of communities that you might want to use in policy – and at the end of the day matching on combination X but not combination Y of communities is a job for policy really, not for community definitions (which should just carry named definitions of groups of community members).

Hope this helps others tinkering with this stuff on JUNOS…