Routing Policies

Module Introduction

Before you start the hands-on part of this module, you should load the appropriate configuration and verify that the testbed is up and running by executing the corresponding robot file:

student@tour:~/trainings_resources/robot$ robot policies/policies_setup.robot

In order to get a better understanding, the lab setup is shown in the picture below.

policy lab
Figure 1. Policy Lab Setup

Policy Framework Overview

In general, a router uses various routing protocols (e.g., static routing, OSPF, BGP, …​) to learn about possible destinations. Depending on the routing protocols in use, it chooses the best (shortest) path based on protocol specific algorithms and stores it in its routing table. If multiple routing protocols are used in parallel, the router prefers routes from protocols with numerical lower route preference. Finally, the router than installs the active path into the forwarding table which is used to forward packets out the proper interface. In addition, those active paths will be sent to the corresponding protocol neighbors to help other routers building an end-to-end path.

Routing policies are control plane mechanisms that can modify the routers default route selection behavior. The goal of using routing policies is to optimize the traffic flow based on parameters other than shortest path, e.g., network utilization or business advantages like financial costs. Key aspects of routing policies are

  • accepting or rejecting routes based on BGP path attributes

  • modification of IGP metrics or BGP path attributes prior to accepting from peer or advertising to peer in order to influence routing decisions

  • redistribution of routes into routing protocols, e.g., static routes

In RBFS, a routing policy is defined by a policy statement which is composed of one or more blocks called ordinals that are daisy-chained and evaluated step-by-step. Each ordinal consists of a set of match rules and a set of actions rules.

policy statement
Figure 2. Policy Statement Components

A route is evaluated in each ordinal until a match is found. In case of a match, the corresponding actions will be executed. If there is no match, the action block will not be executed, and the next ordinal will be processed.

If the match block is empty, it is assumed that all routes match and the action block will be executed.

The action block can contain one or more action rules. An action rule can be either a modifying action, i.e., changing certain parameters associated with the route, or a terminating action, i.e., allowing or rejecting the route.

The action block is optional. The implicit default action of an ordinal is permit. However, it is best practice to configure a permit action explicitly to avoid confusion.

Action rules are evaluated step-by-step. Modifying action rules must be in front of the terminating action in order to get executed. If no ordinal exists and if the policy is used, then all routes or objects will be denied.

If the policy does not have an ordinal, that matches a specific prefix, then the prefix is finally denied.

First Policy

The policy framework is generic and can serve multiple purposes and applications. It can control route import into a routing instance, route redistribution into protocols such as BGP, OSPF, or IS-IS, as well as the route updates sent to and received from BGP neighbors. Since the most common use case is the control of routing updates in BGP, we will focus on BGP in this module.

BGP routing policies have two main attachment points: as import policies and as export policies. The following figure provides a short summary of the BGP route evaluation process for instance default and IPv4 unicast address-family:

bgp policy processing
Figure 3. BGP Policy Processing in RBFS

As BGP policies can become very complex, we will start with a simple policy example. In our pre-configured lab environment, R1 sends three prefixes to its neighbor that originate within AS 64501:

cfg> show bgp rib-out ipv4 unicast peer R2 | grep "64501$"
           172.16.0.0/30             0       -          Incomplete      172.16.0.1         64501
           192.168.0.1/32            0       -          Incomplete      172.16.0.1         64501
           172.16.0.4/30             0       -          Incomplete      172.16.0.1         64501

As we can see, the advertised prefixes are the links to R2 and R3, as well as the loopback address. These prefixes are direct routes and are redistributed into BGP. We’ll configure a policy named LOOP_ONLY that only allows the redistribution of the loopback address into BGP. A policy is created using the command set policy statement <name>.

cfg> set policy statement LOOP_ONLY ordinal 1 match rule 1 type ipv4-prefix
cfg> set policy statement LOOP_ONLY ordinal 1 match rule 1 match-type exact
cfg> set policy statement LOOP_ONLY ordinal 1 match rule 1 value 192.168.0.1/32
cfg> set policy statement LOOP_ONLY ordinal 1 match rule 1 value-type discrete
cfg> set policy statement LOOP_ONLY ordinal 1 action rule 1 operation return-permit

This policy will have a single ordinal that matches the IPv4 prefix corresponding to the loopback address. The match-type exact ensures that the prefix must exactly match the defined prefix, while the value-type discrete indicates that a single value is used.

After defining the policy, we need to attach it. In this example, we want to control the routes that are redistributed into BGP. To do so, we use the command set instance <name> protocol bgp address-family <afi> <safi> redistribute <protocol> policy <policy-name>. Then, commit the configuration changes.

cfg> set instance default protocol bgp address-family ipv4 unicast redistribute direct policy LOOP_ONLY
cfg> commit

We can confirm that only the loopback prefix is advertised to peers.

cfg> show bgp rib-out ipv4 unicast peer R2 | grep "64501$"
           192.168.0.1/32            0       -          Incomplete      172.16.0.1         64501

After we had a first look on the updates that R1 sends to it’s peers, we can also inspect what R1 receives from it’s BGP neighbors, for example R2:

cfg> show bgp rib-in ipv4 unicast peer R2
Flags: & - Imported, ! - Error, N - RPKI Unknown, I - RPKI Invalid, V - RPKI Valid
Instance: default, AFI: ipv4, SAFI: unicast
  Hostname: R2, Peer IP: 172.16.0.2
  Source IP: 172.16.0.1, Total routes: 21
    Flags  Prefix                    Next Hop               MED         Lpref       AS Path
           10.0.2.0/24               172.16.0.2             0           -           64502          (1)
           10.0.3.0/24               172.16.0.2             0           -           64502, 64503   (1)
           10.0.4.0/24               172.16.0.2             0           -           64502, 64504   (1)
           172.16.40.0/24            172.16.0.2             0           -           64502, 64504
           172.16.41.0/24            172.16.0.2             0           -           64502, 64504
           172.16.42.0/24            172.16.0.2             0           -           64502, 64504
           172.16.43.0/25            172.16.0.2             0           -           64502, 64504
  <...>

RFC 1918 defines ranges of IPv4 addresses that should not be used on the public Internet. These ranges include 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16. In our lab environment, the last two blocks are used for demonstration purposes, but the 10.0.0.0/8 range is not. However, both R2 and R3 advertise prefixes from this block.

Exercise 1: Configuring Simple Import Policy

Configure a policy named NET10 containing a single ordinal that matches the 10.0.0.0/8 network (tip: use the or-longer match-type). The policy should reject the matching prefix. Afterwards, apply this policy as an export policy to the peer groups ISP2 and ISP3 at the correct address-family.

Click to reveal the answer
cfg> set policy statement NET10 ordinal 1 match rule 1 type ipv4-prefix
cfg> set policy statement NET10 ordinal 1 match rule 1 match-type or-longer
cfg> set policy statement NET10 ordinal 1 match rule 1 value 10.0.0.0/8
cfg> set policy statement NET10 ordinal 1 match rule 1 value-type discrete
cfg> set policy statement NET10 ordinal 1 action rule 1 operation return-deny
cfg> set instance default protocol bgp peer-group ISP2 address-family ipv4 unicast policy import NET10
cfg> set instance default protocol bgp peer-group ISP3 address-family ipv4 unicast policy import NET10
cfg> commit

After evaluating the policy, we find the result is not as expected:

cfg> show bgp fib
cfg>

The good news is that the 10.0.0.0/8 prefix is no longer accepted. The bad news is that no other prefixes are accepted either.

This happens because the default behavior of a policy is to reject all prefixes that are not explicitly accepted. In our first example, LOOP_ONLY, this behavior was exactly what we wanted, i.e., only one prefix was meant to be accepted. However, in the NET10 policy, there are no terms that explicitly accept other prefixes. As a result, everything is rejected. To fix this, you must add an ordinal that explicitly accepts all other prefixes, which is placed after the reject rule for 10.0.0.0/8.

Policy Chaining

Especially when used at a BGP peering router, the complexity of policy configurations for these routes can become significant. Individual policies can easily span hundreds of lines of code, making both operation and troubleshooting more challenging. Additionally, some policy rules (e.g., filtering RFC 1918 private addresses) are frequently used, and keeping them up to date can require additional effort.

To address these challenges, RBFS offers the option to implement policy chaining. Policy chaining allows you to break down a large, complex policy into smaller, modular policies that are executed in a predefined order, making them easier to maintain. This modular approach not only simplifies operation, as the policies become more readable and understandable, but also makes implementing changes easier by providing a single touchpoint, thus reducing the risk of inconsistencies.

In RBFS, configuring a policy chain is straightforward: you simply add multiple import or export policies to the corresponding attachment point, creating a list of policies rather than an atomic entry. The policy chain is then evaluated in the same manner as a single policy — ordinal by ordinal — just as if it were one large policy.

policy chaining
Figure 4. Splitting single policy into policy chain

It’s important to remember that policy execution stops if a return-permit or return-deny action is encountered. To ensure that all policies are properly evaluated, make sure there are no actions for unmatched prefixes at the end of a policy. Otherwise, the next policy in the chain may not be evaluated.

The action operation goto-next-policy bypasses the remaining rules of the current policy and jumps to the beginning of the next policy in the chain.
Exercise 2: Policy Chaining

Create three policies named ASN_FILTER, IMPORT_ISP2, and IMPORT_ISP3. The ASN_FILTER policy should contain no ordinals for the time being, while both the IMPORT_ISP2 and IMPORT_ISP3 policy should contain a single ordinal accepting all prefixes. Choose an ordinal number 99 allowing to insert additional ordinals later.

Apply the ASN_FILTER and IMPORT_ISP2 policy to peer-group ISP2, and the ASN_FILTER and IMPORT_ISP3 policy to peer-group ISP3. The existing NET10 policy should be in place.

Click to reveal the answer
cfg> set policy statement ASN_FILTER
cfg> set policy statement IMPORT_ISP2 ordinal 99 action rule 1 operation return-permit
cfg> set policy statement IMPORT_ISP3 ordinal 99 action rule 1 operation return-permit
cfg> set instance default protocol bgp peer-group ISP2 address-family ipv4 unicast policy import ASN_FILTER
cfg> set instance default protocol bgp peer-group ISP2 address-family ipv4 unicast policy import IMPORT_ISP2
cfg> set instance default protocol bgp peer-group ISP3 address-family ipv4 unicast policy import ASN_FILTER
cfg> set instance default protocol bgp peer-group ISP3 address-family ipv4 unicast policy import IMPORT_ISP3
cfg> commit

We can now verify that all the subnets from the 10.0.0.0/8 are discarded by the NET10 policy, while all remaining prefixes are accepted:

cfg> show bgp fib
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 14
  Prefix                    Preference      Label                Next Hop
  172.16.40.0/24            20              -                    172.16.0.2
                                            -                    172.16.0.6
  172.16.41.0/24            20              -                    172.16.0.2
                                            -                    172.16.0.6
  172.16.42.0/24            20              -                    172.16.0.2
                                            -                    172.16.0.6
  172.16.42.128/27          20              -                    172.16.0.6
                                            -                    172.16.0.6
  172.16.43.0/25            20              -                    172.16.0.2
                                            -                    172.16.0.6
<...>
cfg> show bgp fib | grep "10\."
cfg>

Policy Match Rules

The general syntax of a match rule is given by the syntax

set policy statement <policy-name> ordinal <ordinal> match rule <rule> <attribute> <value>

There are four attribute/value pairs that are all mandatory:

  • type specifies the kind of metric which should be matched, e.g., ipv4-prefix, as-path, community, etc.

  • match-type defines how the value should match the attribute type. The most frequent one is exact, but there is also support for various prefix-length matches as well as regular expressions (regex).

  • value-type indicates whether the given match value is either a single value (discrete) or a list of values.

  • value defines the attribute value.

For a full list of attribute types and supported match types please refer to the Policy User Guide.

The AS_PATH attribute is a crucial component of BGP. It enables decision-making based on autonomous systems rather than individual prefixes, offering a more scalable approach than maintaining lists of specific prefixes.

The AS_PATH is a sequence of AS numbers that a prefix has traversed.

RBFS supports various match types when working with AS_PATH:

  • exact: the value matches the AS_PATH exactly

  • exists: the value matches at least one entry in the AS_PATH

  • regex: the value matches a standard Linux egrep regular expression

  • origin: the value matches the originating AS number

  • transit: the value matches any transit AS number, i.e., an AS number that is neither the peer AS nor the originating AS

  • neighbor: the value matches the AS number of the neighboring (peer) AS

Now that we’ve covered the theory, let’s look at an example. Consider the prefix 172.16.50.0/24, received from both peers R2 and R3:

cfg> show bgp rib-in ipv4 unicast peer R2 172.16.50.0/24
Flags: & - Imported, ! - Error, N - RPKI Unknown, I - RPKI Invalid, V - RPKI Valid
Instance: default, AFI: ipv4, SAFI: unicast
  Hostname: R2, Peer IP: 172.16.0.2
  Source IP: 172.16.0.1, Total routes: 0
    Flags  Prefix                    Next Hop               MED         Lpref       AS Path
           172.16.50.0/24            172.16.0.2             0           -           64502, 64504, 64505

The output reveals that this prefix is originated in AS 64505.

Exercise 3: Matching Originating ASN in AS_PATH

Our policy is to reject any prefixes that originate from AS 64505. Add a new ordinal to the existing ASN_FILTER policy that matches all such prefixes and rejects them.

Click to reveal the answer
cfg> set policy statement ASN_FILTER ordinal 1 match rule 1 type as-path
cfg> set policy statement ASN_FILTER ordinal 1 match rule 1 match-type origin
cfg> set policy statement ASN_FILTER ordinal 1 match rule 1 value 64505
cfg> set policy statement ASN_FILTER ordinal 1 match rule 1 value-type discrete
cfg> set policy statement ASN_FILTER ordinal 1 action rule 1 operation return-deny
cfg> commit
cfg> show bgp fib 172.16.50.0/24
cfg>

We continue examining the list of prefixes received by router R2 and turn our attention to the prefixes 172.16.51.0/24 and 172.16.52.0/24.

cfg> show bgp rib-in ipv4 unicast peer R2 | grep 172.16.5[12].0/24
           172.16.51.0/24            172.16.0.2             0           -           64502, 64504, 65100, 64499
           172.16.52.0/24            172.16.0.2             0           -           64502, 64504, 65200, 64499

In this training, we are using AS numbers from the range 64496–64511, which are reserved by IANA for documentation purposes. IANA has also defined two blocks of private AS numbers that should not be used on the public Internet: 64512–65534 and 4200000000–4294967294.

Looking at the AS_PATH in the output above, we can see that private AS numbers appear within the path. If we want to filter these prefixes, using atomic values in match rules does not scale well. Using regular expressions might improve the situation slightly.

RBFS provides a method for matching ranges of AS numbers in the AS_PATH. This is done using a construct called a policy list, which is a named list of values that can be referenced by match rules in a policy statement. Policy lists are defined using the set policy list <list-name> command, followed by the type of values (e.g., as-path). A policy list can include multiple ordinals that specify the list’s values. For AS_PATH matching, policy lists also support defining ranges using the sub-type range.

Exercise 4: Matching AS Number Ranges in AS_PATH

Configure a policy list named PRIVATE_ASN of type as-path, and define the two ranges: 64512–65534 and 4200000000–4294967294. Then, add a new ordinal to the existing policy ASN_FILTER that matches these values across all transit ASes, and configure it to reject them.

Click to reveal the answer
cfg> set policy list PRIVATE_ASN as-path sub-type range
cfg> set policy list PRIVATE_ASN as-path ordinal 1 value 64512
cfg> set policy list PRIVATE_ASN as-path ordinal 1 value2 65534
cfg> set policy list PRIVATE_ASN as-path ordinal 2 value 4200000000
cfg> set policy list PRIVATE_ASN as-path ordinal 2 value2 4294967294
cfg> set policy statement ASN_FILTER ordinal 2 match rule 1 type as-path
cfg> set policy statement ASN_FILTER ordinal 2 match rule 1 value-type list
cfg> set policy statement ASN_FILTER ordinal 2 match rule 1 match-type transit
cfg> set policy statement ASN_FILTER ordinal 2 match rule 1 value PRIVATE_ASN
cfg> set policy statement ASN_FILTER ordinal 2 action rule 1 operation return-deny
cfg> commit
cfg> show bgp fib | grep 172.16.5[12].0/24
cfg>

When using a policy list, the value-type is no longer discrete (representing an atomic value), but list.

Before we conclude this section on AS_PATH match criteria, let’s examine the prefix 172.16.53.0/24:

cfg> show bgp rib-in ipv4 unicast peer R2 | grep 172.16.53.0/24
           172.16.53.0/24            172.16.0.2             0           -           64502, 64504, 64504, 64504, 64504, 64504

The output shows that this prefix originates from AS 64504, and the originating AS has inserted its own AS number multiple times. This technique is called AS_PATH prepending, and it is used to artificially lengthen a path, making it less preferable in BGP route selection, where AS_PATH length is one of the selection criteria.

While AS_PATH prepending is generally acceptable, an autonomous system may want to enforce an upper limit on the AS_PATH length. This can be done using the value-type length (instead of discrete) in the match rule.

Exercise 5: Matching AS_PATH Length

Configure an additional ordinal in the existing policy ASN_FILTER to match all AS_PATHs with a length of five or more, and configure it to reject those paths.

Click to reveal the answer
cfg> set policy statement ASN_FILTER ordinal 3 match rule 1 type as-path
cfg> set policy statement ASN_FILTER ordinal 3 match rule 1 value-type length
cfg> set policy statement ASN_FILTER ordinal 3 match rule 1 match-type greater-or-exact
cfg> set policy statement ASN_FILTER ordinal 3 match rule 1 value 5
cfg> set policy statement ASN_FILTER ordinal 3 action rule 1 operation return-deny
cfg> commit
cfg> show bgp fib 172.16.53.0/24
cfg>

Matching on IPv4 and IPv6 prefixes is one of the most common use cases for network policies. For the sake of simplicity, we will focus on IPv4 prefixes in this module; however, all statements are also applicable to IPv6 prefixes.

The ipv4-prefix and ipv6-prefix attributes support several match-type options:

  • exact: The matching prefix must be identical to the specified value.

  • longer: The matching prefix must be a subnet of the specified prefix.

  • or-longer: The matching prefix must be either the specified prefix or any of its subnets.

  • regex: The value is matched using a standard Linux egrep-compatible regular expression.

  • prefix-length-exact: The prefix length must exactly match the configured value.

  • prefix-length-greater: The prefix length must be greater than the configured value.

  • prefix-length-greater-or-exact: The prefix length must be equal to or greater than the configured value.

We already encountered the exact match type in the first section. Often, when dealing with large routing tables, you need to filter a range of networks and their subnets—even if you don’t know the specific subnets being advertised. In such cases, the longer (only subnets) and or-longer (network and all subnets) match types are especially useful. The relationship between exact, longer, and or-longer is illustrated in the figure below.

policy longer
Figure 5. Prefix Match Type

Now let’s move on to our next example. We will inspect the routes advertised by R2 within the range 172.16.40.0/21.

cfg> show bgp rib-in ipv4 unicast peer R2 | grep 172.16.4
           172.16.40.0/24            172.16.0.2             0           -           64502, 64504
           172.16.41.0/24            172.16.0.2             0           -           64502, 64504
           172.16.42.0/24            172.16.0.2             0           -           64502, 64504
           172.16.43.0/25            172.16.0.2             0           -           64502, 64504
           172.16.44.0/24            172.16.0.2             0           -           64502, 64504
           172.16.45.0/24            172.16.0.2             0           -           64502, 64504
           172.16.46.0/24            172.16.0.2             0           -           64502, 64504    (1)
           172.16.47.0/24            172.16.0.2             0           -           64502, 64504    (1)
           172.16.43.64/28           172.16.0.2             0           -           64502, 64504
           172.16.43.80/28           172.16.0.2             0           -           64502, 64504
           172.16.42.128/27          172.16.0.2             0           -           64502, 64504

A prefix list is a policy list of type ipv4-prefix or ipv6-prefix. It can be used with the match types exact, longer, and or-longer. So, let’s do a short exercise.

Exercise 6: Configuring IPv4 Prefix List

Configure a prefix list named PREFIX_LIST of type ipv4-prefix, and include the prefixes 172.16.46.0/24 and 172.16.47.0/24. Then, add ordinal number 1 to the existing policy IMPORT_ISP2, matching this prefix list exactly and rejecting the prefixes.

Click to reveal the answer
cfg> set policy list PREFIX_LIST ipv4-prefix ordinal 1 value 172.16.46.0/24
cfg> set policy list PREFIX_LIST ipv4-prefix ordinal 2 value 172.16.47.0/24
cfg> set policy statement IMPORT_ISP2 ordinal 1 match rule 1 type ipv4-prefix
cfg> set policy statement IMPORT_ISP2 ordinal 1 match rule 1 value-type list
cfg> set policy statement IMPORT_ISP2 ordinal 1 match rule 1 match-type exact
cfg> set policy statement IMPORT_ISP2 ordinal 1 match rule 1 value PREFIX_LIST
cfg> set policy statement IMPORT_ISP2 ordinal 1 action rule 1 operation return-deny
cfg> commit

verifying the show bgp fib output reveals, that both prefixes are only accepted from R3, but no longer from R2:

cfg> show bgp fib 172.16.46.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  172.16.46.0/24            20              -                    172.16.0.6
show bgp fib 172.16.47.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  172.16.47.0/24            20              -                    172.16.0.6

Match-Type Prefix-Length

In large-scale environments it is often difficult to keep prefix lists or match criteria based on certain prefixes up-to-date. A common scenario is that you want to accept reachability information from your neighboring peers, but do not want to accept small subnets but larger networks or aggregates. RBFS supports the match-types prefix-length-exact, prefix-length-greater and prefix-length-greater-or-exact to accomplish this task. These options allow you to filter on the prefix-length rather than the network. Therefore, only the prefix length in the value option is used!

Example: ipv4-prefix with value=0.0.0.0/24

  • prefix-length-exact matches all routes with prefix /24

  • prefix-length-greater matches all routes with prefix /25, /26, ../32

  • prefix-length-greater-or-exact matches alle routes with prefix /24, /25, ../32

0.0.0.0/24 and 10.10.10.0/24 result in the same matches as only the prefix length is considered.

Let’s review the RIB-IN for the routes advertised by R2:

cfg> show bgp rib-in ipv4 unicast peer R2
           172.16.40.0/24            172.16.0.2             0           -           64502, 64504
           172.16.41.0/24            172.16.0.2             0           -           64502, 64504
           172.16.42.0/24            172.16.0.2             0           -           64502, 64504
           172.16.43.0/25            172.16.0.2             0           -           64502, 64504   (1)
           172.16.44.0/24            172.16.0.2             0           -           64502, 64504
           172.16.45.0/24            172.16.0.2             0           -           64502, 64504
           172.16.46.0/24            172.16.0.2             0           -           64502, 64504
           172.16.47.0/24            172.16.0.2             0           -           64502, 64504
           192.168.0.2/32            172.16.0.2             0           -           64502          (2)
           192.168.0.3/32            172.16.0.2             0           -           64502, 64503   (2)
           192.168.0.4/32            172.16.0.2             0           -           64502, 64504   (2)
           172.16.43.64/28           172.16.0.2             0           -           64502, 64504   (3)
           172.16.43.80/28           172.16.0.2             0           -           64502, 64504   (3)
           172.16.42.128/27          172.16.0.2             0           -           64502, 64504   (3)
<...>
1 Loopback addresses of R2, R3, and R4.
2 Prefixes that should be filtered.

We can see that several prefixes have been learned—most with a prefix length of up to /25. However, there are also prefixes with lengths of /27 and /28. Suppose we want to accept only prefixes with a length of up to /25 and reject any longer prefixes. That said, we may still want to retain loopback prefixes from the 192.168.0.0/24 subnet.

Exercise 7: Configuring Prefix Length Matching

Add an ordinal number 2 to the existing policy IMPORT_ISP2, which matches on all subnets of the 172.16.0.0/16 network with a prefix-length of 26 or larger. These prefixes should be rejected.

Click to reveal the answer

We define a ordinal with match-type prefix-length-greater-or-exact to meet the requirement. The value must contain a value including /26.

cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 1 type ipv4-prefix
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 1 value-type discrete
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 1 match-type prefix-length-greater-or-exact
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 1 value 0.0.0.0/26
cfg> set policy statement IMPORT_ISP2 ordinal 2 action rule 1 operation return-deny
cfg> commit

After committing, we can see that the prefixes with prefix-length 27 and 28 received from R2 are rejected, while only those from R3 remain. However, the loopback prefixes are also rejected, because they have prefix-length of 32.

cfg> cfg> show bgp fib 172.16.43.64/28
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  172.16.43.64/28           20              -                    172.16.0.6
cfg> show bgp fib 192.168.0.2/32
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  192.168.0.2/32            20              -                    172.16.0.2

In order to meet the second requirement, we need to add a second match rule, restricting the filter to the 172.16.0.0/16. As both match rules need to be combined, the match-logic must be set to and for this ordinal.

cfg> set policy statement IMPORT_ISP2 ordinal 2 match-logic and
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 2 type ipv4-prefix
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 2 value-type discrete
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 2 match-type or-longer
cfg> set policy statement IMPORT_ISP2 ordinal 2 match rule 2 value 172.16.0.0/16
cfg> commit

Match-Type Exact vs Exists

In our next example, we’ll explore how to match BGP communities. Communities are BGP attributes that do not directly influence the route selection process. Instead, they serve as markers that allow additional information to be attached to routes, making policy filtering and management easier.

In addition to standard communities, BGP also supports large communities, which extend the value range, and extended communities, which have application-specific semantics. In this section, we’ll focus only on standard communities, but the matching process is similar for both large and extended communities.

We know that R4 uses the community 64504:123 to tag its prefixes internally, but sometimes forgets to remove it. So, let’s check whether any prefixes still carry this community.

show bgp rib-in ipv4 unicast community 64504:123
Flags: & - Imported, ! - Error, N - RPKI Unknown, I - RPKI Invalid, V - RPKI Valid
Instance: default, AFI: ipv4, SAFI: unicast
  Hostname: R2, Peer IP: 172.16.0.2
  Source IP: 172.16.0.1, Total routes: 1
    Flags  Prefix                    Next Hop               MED         Lpref       AS Path
           172.16.41.0/24            172.16.0.2             0           -           64502, 64504  (1)
  Hostname: R3, Peer IP: 172.16.0.6
  Source IP: 172.16.0.5, Total routes: 1
    Flags  Prefix                    Next Hop               MED         Lpref       AS Path
           172.16.41.0/24            172.16.0.6             0           -           64503, 64504  (1)

It appears that the prefix 172.16.41.0/24 has the 64504:123 community attached. We’ll handle removing the community later—first, let’s see how to match the prefix.

Exercise 8: Configuring Exact Community Matching

Add an ordinal number 3 to the existing policy IMPORT_ISP2, which matches on the standard community 64504:123. As a result, the matched prefixes should be rejected.

Click to reveal the answer
cfg> set policy statement IMPORT_ISP2 ordinal 3 match rule 1 type community
cfg> set policy statement IMPORT_ISP2 ordinal 3 match rule 1 value-type discrete
cfg> set policy statement IMPORT_ISP2 ordinal 3 match rule 1 match-type exact
cfg> set policy statement IMPORT_ISP2 ordinal 3 match rule 1 value 64504:123
cfg> set policy statement IMPORT_ISP2 ordinal 3 action rule 1 operation return-deny
cfg> commit

After applying the configuration changes, we check the result.

cfg> show bgp fib 172.16.41.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  172.16.41.0/24            20              -                    172.16.0.2
                                            -                    172.16.0.6
cfg>

That’s not exactly what we expected. The prefix received from R2 should have been rejected — but it’s still present. Is there something wrong with our configuration? Not quite. The issue lies in a faulty assumption we made. Let’s take a closer look at the detailed output.

cfg> show bgp rib-in ipv4 unicast community 64504:123 detail
Instance: default, AFI: ipv4, SAFI: unicast
  Hostname: R2, Peer IP: 172.16.0.2
  Source IP: 172.16.0.1, Total routes: 1
    Prefix: 172.16.41.0/24, Received path ID: 0, Next hop: 172.16.0.2
      Status: Valid
      Protocol source: bgp
      Origin: Incomplete
      Send path ID: 130982003
      AS path: [64502, 64504]
      MED: 0
      Community: ['64504:123', '64504:321']        (1)
      Last update: 0d:00h:01m:58s
<...>

The key point is that the prefix carries not just the 64504:123 community, but also the 64504:321 community. When using the exact match type, the match fails because the community list doesn’t exactly match the single community we specified. There are two ways to resolve this issue:

  • Use a policy list that includes all expected communities, or

  • Switch to the exists match type, which does not require an exact match - only that at least one of the specified communities is present.

Exercise 9: Configuring Exists Community Matching

Change the match-type of ordinal 3 of policy IMPORT_ISP2 to exists and verify proper operation.

Click to reveal the answer
cfg> set policy statement IMPORT_ISP2 ordinal 3 match rule 1 match-type exists
cfg> commit

We can now confirm, that we only receive the prefix 172.16.41.0/24 from R3, where no policy is in place so far.

cfg> show bgp fib 172.16.41.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                    Preference      Label                Next Hop
  172.16.41.0/24            20              -                    172.16.0.6
cfg>

Match-Type Regular Expression

Regular expressions (short regex) are a way to define patterns which can be very useful in searching and replacing a sequence of characters within a string. With RBFS policies, regex can be used as a match-type for most attribute type, e.g., IP prefixes, communities, large-communities, extended communities, and as-path.

A regex pattern uses a few special purpose characters:

  • {m,n} between m and n characters

  • ? match 0 or 1 time (equal to {0,1})

  • * match 0 or more times (equal to {0,})

  • + match 1 or more times (equal to {1,})

  • [] defines a range of acceptable values

  • () is used for grouping

In our last example, we used a community match to filter a specific route received from R2. The same prefix is still received from R3, so let’s take an alternative approach to filter it.

Exercise 10: Regular Expression Matching

Add a new ordinal 3 to existing policy IMPORT_ISP3, that matches on all communities from AS 64504. Use the regular expression "64504.*" to reject these prefixes.

Click to reveal the answer
cfg> set policy statement IMPORT_ISP3 ordinal 3 match rule 1 type community
cfg> set policy statement IMPORT_ISP3 ordinal 3 match rule 1 value-type discrete
cfg> set policy statement IMPORT_ISP3 ordinal 3 match rule 1 match-type regex
cfg> set policy statement IMPORT_ISP3 ordinal 3 match rule 1 value 64504:.*
cfg> set policy statement IMPORT_ISP3 ordinal 3 action rule 1 operation return-deny
cfg> commit
cfg> show bgp fib 172.16.41.0/24
cfg>

Policy Action Rules

Now that we’ve covered the most important match rules supported in RBFS, let’s take a closer look at the action rules. We’ve already used return-permit and return-deny, but when working with BGP, it’s often useful to modify route attributes. The general syntax of an action rule is given by the syntax

set policy statement <policy-name> ordinal <ordinal> action rule <rule> <attribute> <value>

There are three well-known attributes possible:

  • operation <operation-type>

  • type <attribute-type>

  • value <value>

The type and value attribute depend on the routing protocol in use. For IGP protocols (like OSPF and IS-IS) the igp-metric type is the most common one. For BGP, the types local-preference, med, community, large-community and extended-community are regularly used. For a full list of attribute types and supported operation types, please have look at the Policy Configuration.

The operation can be either a terminating or flow control operation or an operation that modifies a route attribute or metric. There are two terminating operations and two flow control operation:

  • return-deny stops policy evaluation and rejects the corresponding routes

  • return-permit stops policy evaluation and accepts the corresponding routes

  • goto-next-ordinal does not take final decision and policy evaluation continues with the next ordinal

  • goto-next-policy does not take final decision and policy evaluation continues with the next policy in the policy chain

With terminating or flow control operations, there is no additional type or value attribute associated.

In addition, there are a couple of modifying operations, e.g.

  • add|substract|multiply|divide allows to change the numeric value of a route metric with fundamental arithmetic operations, e.g., BGP Local Preference value

  • prepend|append which allows to add another entry to the beginning or the end of a list of metrics, e.g., BGP AS_PATH or community lists

  • override replaces the value of a given metric with another value.

  • delete-attribute removes the corresponding metric attribute from the route object

  • delete-attribute-match deletes the specific value of an attribute from the route

When modifying BGP attributes of a prefix, it’s generally best to postpone the final accept or reject decision, allowing the updated attributes to influence later policy stages. To enable this, the actions goto-next-ordinal or goto-next-policy are typically used in combination with attribute modifications.

Removing BGP Attributes

Let’s revisit the community attribute example from the previous section. We demonstrated how communities are matched by rejecting them. In some cases, it is necessary to exclude communities from route updates, for instance, when they are only relevant to internal peers.

Exercise 11: Removing BGP Communities from Prefix

Modify the ordinal 3 of both policies IMPORT_ISP2 and IMPORT_ISP3 to delete the matched attribute instead of rejecting the prefix. Also configure the policies to not immediately accept the prefix, but to move on to the next ordinal for processing. Is there any difference in the result for R2 and R3?

Click to reveal the answer
cfg> set policy statement IMPORT_ISP2 ordinal 3 action rule 1 type community
cfg> set policy statement IMPORT_ISP2 ordinal 3 action rule 1 operation delete-attribute-match
cfg> set policy statement IMPORT_ISP3 ordinal 3 action rule 2 operation goto-next-ordinal
cfg> set policy statement IMPORT_ISP3 ordinal 3 action rule 1 type community
cfg> set policy statement IMPORT_ISP3 ordinal 3 action rule 1 operation delete-attribute-match
cfg> set policy statement IMPORT_ISP3 ordinal 3 action rule 2 operation goto-next-ordinal
cfg> commit

Again, we take a look at the output of the show bgp fib command:

cfg> show bgp fib 172.16.41.0/24 detail
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix: 172.16.41.0/24
    Next hop key: 110100e05f7b0753a7690b4ae9e45c40bd6f26812a5582cd
    Route source: bgp, Send path ID: 130982003, Received path ID: None
    AS path: [64502, 64504], Originator ID: None, Origin: Incomplete
    Community: ['64504:321']          (1)
    Extended community: None
    Large community: None
    Cluster list: None
    IGP metric: 4294967295, Local preference: None, Multi exit discriminator: 0
    Preference: 20, External route: None, Readvertised route: None
    Route up: None
    Next hop: 172.16.0.2, Label: -
    Next hop: 172.16.0.6, Label:

The update received from R2 still has a community attached, because we only matched on the 64504:123 community and removed it, but the 64504:321 community is still present. If we want to remove all communities, we can use the delete-attribute action instead.

Overwriting Attribute Values

If we want to replace the value of an attribute with another - regardless of its original value - we can use the overwrite operation. This works for both numerical attributes (such as igp-metric, local-preference, or med) and non-numerical attributes (such as community, ipv4-nexthop, or as-path).

For example, let’s configure a policy that prefers the path via R2 over the path via R3, using the prefix 172.16.44.0/24:

cfg> show bgp fib 172.16.44.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                                        Preference      Label                Next Hop
  172.16.44.0/24                                20              -                    172.16.0.2
                                                                -                    172.16.0.6

With multipath enabled, we will have two equally valid entries in the FIB. From the BGP route selection process, we know that local-preference is one of the highest priority attributes considered. So, to prefer the R2 path, we’ll increase the local-preference value for the update received via R2.

Exercise 12: Setting BGP Local Preference

Configure a new ordinal (number 4) for policy IMPORT_ISP2 that matches prefix 172.16.44.0/24. Set the local preference value to 200 and continue processing with the next ordinal in the policy.

Click to reveal the answer
cfg> set policy statement IMPORT_ISP2 ordinal 4 match rule 1 type ipv4-prefix
cfg> set policy statement IMPORT_ISP2 ordinal 4 match rule 1 value-type discrete
cfg> set policy statement IMPORT_ISP2 ordinal 4 match rule 1 match-type exact
cfg> set policy statement IMPORT_ISP2 ordinal 4 match rule 1 value 172.16.44.0/24
cfg> set policy statement IMPORT_ISP2 ordinal 4 action rule 1 operation overwrite
cfg> set policy statement IMPORT_ISP2 ordinal 4 action rule 1 type local-preference
cfg> set policy statement IMPORT_ISP2 ordinal 4 action rule 1 value 200
cfg> set policy statement IMPORT_ISP2 ordinal 4 action rule 2 operation goto-next-ordinal
cfg> commit
cfg> show bgp fib 172.16.44.0/24
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix                                        Preference      Label                Next Hop
  172.16.44.0/24                                20              -                    172.16.0.2
cfg> show bgp fib 172.16.44.0/24 detail
Instance: default, AFI: ipv4, SAFI: unicast, Total routes: 1
  Prefix: 172.16.44.0/24
    Next hop key: 04c06a1cc357518ee460f9b05aed6daf1dbac5f2881a2f41
    Route source: bgp, Send path ID: 130982003, Received path ID: None
    AS path: [64502, 64504], Originator ID: None, Origin: Incomplete
    Community: None
    Extended community: None
    Large community: None
    Cluster list: None
    IGP metric: 4294967295, Local preference: 200, Multi exit discriminator: 0
    Preference: 20, External route: None, Readvertised route: None
    Route up: None
    Next hop: 172.16.0.2, Label: -

Prepending and Appending Attributes

In BGP, several attributes are lists, meaning they can hold multiple values. The most important of these are the AS_PATH, community, large community, and extended community attributes. The AS_PATH differs from the community attributes in two key ways: First, the order of entries in the AS_PATH matters because they represent the route’s direction, from the origin to the local router. Second, the same value can appear multiple times in the AS_PATH, whereas it doesn’t make sense to repeat a community value.

Earlier, we mentioned that artificially extending the AS_PATH is a common method for influencing the route selection process. While local-preference is primarily used to determine the exit point out of an AS, the length of the AS_PATH is often used to affect the entry point into the autonomous system.

Using the overwrite method in this scenario is not ideal, as it would discard the existing AS_PATH information, especially if the prefix was received from another AS and is not locally originated. Therefore, two other operations, append and prepend, are available. Append adds an entry at the end of the list, while prepend adds an entry at the beginning.

When dealing with community, the order doesn’t matter, so both append and prepend can be used interchangeably. However, for AS_PATH, additional AS numbers are typically prepended to preserve the originating AS.

Exercise 13: Prepending the BGP AS_PATH Attribute

Configure a new policy EXPORT_ISP3, that prepends the AS number 64501 to the existing AS_PATH. Apply this policy as an export policy to peer-group ISP3.

Click to reveal the answer
cfg> set policy statement EXPORT_ISP3 ordinal 1 action rule 1 type as-path
cfg> set policy statement EXPORT_ISP3 ordinal 1 action rule 1 operation prepend
cfg> set policy statement EXPORT_ISP3 ordinal 1 action rule 1 value 64501
cfg> set policy statement EXPORT_ISP3 ordinal 1 action rule 2 operation return-permit
cfg> set instance default protocol bgp peer-group ISP3 address-family ipv4 unicast policy export EXPORT_ISP3
cfg> commit
cfg> show bgp rib-out ipv4 unicast peer R3
Flags: N - RPKI Unknown, I - RPKI Invalid, V - RPKI Valid
Instance: default, AFI: ipv4, SAFI: unicast
  Peer: R3, Peer IP: 172.16.0.6, Sent routes: 15
    Flags  Prefix                    MED     Lpref      Origin          Next Hop           AS Path
           172.16.40.0/24            0       -          Incomplete      172.16.0.5         64501, 64501, 64502, 64504
           172.16.41.0/24            0       -          Incomplete      172.16.0.5         64501, 64501, 64502, 64504
           172.16.42.0/24            0       -          Incomplete      172.16.0.5         64501, 64501, 64502, 64504
           172.16.43.0/25            0       -          Incomplete      172.16.0.5         64501, 64501, 64502, 64504
           <...>

Policy Test

Policies can get fairly complex and therefore it is desirable to test a policy without applying it to operational traffic. RBFS provides a way to test a policy before attaching it to a protocol with the test policy run command. For policy tests we need three information:

  • the name of the policy to be tested

  • the daemon which is responsible to execute the policy

  • the routing information against which the policy is evaluated

Consider we want to test the operation of the IMPORT_ISP2 policy prior to attaching it to peer R2. As this is a BGP peering, the bgp.appd.1 is the daemon responsible for evaluating the policy. All routes which are learned from R2 are stored in the RIB-IN for this peer. Thus, we can execute the test.

The test policy command requires the corresponding BDS table as an input.
op> test policy run bgp.appd.1 policy-name IMPORT_ISP2 table default.bgp.rib-in.ipv4.unicast.172.16.0.2.172.16.0.1

The policy test feature will create two result tables. The result table with the name <bds_table>.policy.permit will show all objects permitted by the policy, the one named <bds_table>.policy.deny will show all objects denied by the policy.

op> show datastore bgp.appd.1 table default.bgp.rib-in.ipv4.unicast.172.16.0.2.172.16.0.1.policy.deny
Object: 0, Sequence: 1, Last update: Tue Aug 12 12:29:38 GMT +0000 2025
  Attribute                                               Type                           Length     Value
  status (1)                                              uint8 (2)                           1     Valid
  recv_path_id (2)                                        uint32 (4)                          4     0
  prefix4 (3)                                             ipv4prefix (13)                     5     172.16.46.0/24
  send_path_id (11)                                       uint32 (4)                          4     130982003
  bgp_nh4 (15)                                            ipv4addr (12)                       4     172.16.0.2
  source (20)                                             uint8 (2)                           1     bgp
  sub_src (21)                                            uint8 (2)                           1     Local-Peer
  as_path (24)                                            array (7), uint32 (4)               8     [64502, 64504]
  origin (25)                                             uint8 (2)                           1     Incomplete
  med (26)                                                uint32 (4)                          4     0
  peer_type (27)                                          uint8 (2)                           1     2
  igp_metric (59)                                         uint32 (4)                          4     4294967295
<...>

Summary

This module outlined the building blocks of the RBFS routing policy framework. You should be able to understand and configure policies using different match and action rules.

If you have completed the exercise, you can check the results by executing

student@tour:~/trainings_resources/robot$ robot policies/policies_verify.robot