The hidden gem in IOS-XE: bgp safe-ebgp-policy

:: Table of content

  • Intro
  • Testdrive
  • Checks
  • Turn it on
  • Conclusion

:: Intro

Back in 2019 when I studied for my CCNP service Provider SPROUTE, I learned about IOS-XR and when we enable BGP and set up the sessions, nothing happens. We always need a route policy to enable advertisement and reception of prefix information.

The official statement is:

“IOS XR requires a routing policy to be associated with an EBGP peer as a security measure to ensure that routes are not accidentally accepted or advertised. If a route policy is not configured in the appropriate address-family, then NLRIs are discarded upon receipt and no NLRIs are advertised to EBGP peers.”

So, we have BGP route security to prevent nasty things with wrong regex implementations.

A typical network engineer in an equipment room after changing a BGP policy the wrong way.

Very and very recently, like 10 days ago, I saw this tweet from my friend David at CiscoL!ve 2024 in Las Vegas:

Cisco has “secretly” added a gem in IOS-XE from version 17.2.1 and we didn’t knew it.

Source: https://www.cisco.com/c/en/us/td/docs/routers/ios/config/17-x/ip-routing/b-ip-routing/m_irg-external-sp-0.html

:: Testdrive

My beast machine was asleep for a few days but was hungry to test this shorty.

Let’s setup a the simplest BGP topology ever:

C8000v v17.05.01a in autonomous mode

We have set up a point-to-point public transit between the routers and added a Loopback0 interface on both sides with each a unique network subnet and advertise both networks with a network statement.

Let’s show the basic configurations of both routers:

============= C8Kv-1 Configurations ============= 
Current configuration : 5938 bytes
!
! Last configuration change at 20:38:00 UTC Fri Jun 7 2024
!
version 17.5
service timestamps debug datetime msec
service timestamps log datetime msec
! Call-home is enabled by Smart-Licensing.
service call-home
!
hostname C8Kv-1
!
interface Loopback0
 ip address 192.168.10.1 255.255.255.0
!
interface GigabitEthernet1
 ip address 203.25.163.25 255.255.255.252
 negotiation auto
!
router bgp 100
 bgp log-neighbor-changes
 network 192.168.10.0
 neighbor 203.25.163.26 remote-as 200
!
end
============= C8Kv-2 Configurations ============= 
Current configuration : 5938 bytes
!
! Last configuration change at 20:37:57 UTC Fri Jun 7 2024
!
version 17.5
service timestamps debug datetime msec
service timestamps log datetime msec
! Call-home is enabled by Smart-Licensing.
service call-home
!
hostname C8Kv-2
!
interface Loopback0
 ip address 192.168.20.1 255.255.255.0
!
interface GigabitEthernet1
 ip address 203.25.163.26 255.255.255.252
 negotiation auto
!         
router bgp 200
 bgp log-neighbor-changes
 network 192.168.20.0
 neighbor 203.25.163.25 remote-as 100
!
end

:: Checks:

============= C8Kv-1 BGP Table ============= 
C8Kv-1#sh ip bgp
BGP table version is 3, local router ID is 192.168.10.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.10.0     0.0.0.0                  0         32768 i
 *>   192.168.20.0     203.25.163.26            0             0 200 i

============= C8Kv-2 BGP Table ============= 
C8Kv-2#  sh ip bgp
BGP table version is 3, local router ID is 192.168.20.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.10.0     203.25.163.25            0             0 100 i
 *>   192.168.20.0     0.0.0.0                  0         32768 i

:: Turn it on

C8Kv-1(config-router)#do sh run | sec bgp
router bgp 100
 bgp log-neighbor-changes
 bgp safe-ebgp-policy >> added command
 network 192.168.10.0
 neighbor 203.25.163.26 remote-as 200

Immediately, an unreachable is sent for the advertised prefix 192.168.10.0/24, and a route refresh is pushed to C8Kv-2 to withdraw the prefix 192.168.10.0/24.

From C8Kv-1:

C8Kv-1(config-router)#
*Jun 16 09:35:21.951: BGP(0): (base) 203.25.163.26 send unreachable (format) 192.168.10.0/24
*Jun 16 09:35:41.266: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) rcvd Refresh Start-of-RIB
*Jun 16 09:35:41.266: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) refresh_epoch is 3
*Jun 16 09:35:41.266: BGP(0): 203.25.163.26 rcvd UPDATE w/ attr: nexthop 203.25.163.26, origin i, metric 0, merged path 200, AS_PATH 
*Jun 16 09:35:41.266: BGP(0): 203.25.163.26 rcvd 192.168.20.0/24 -- DENIED due to: ebgp-safe-policy
*Jun 16 09:35:41.266: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) rcvd Refresh End-of-RIB

From C8Kv-2:

*Jun 16 09:35:21.964: BGP: nbr_topo global 203.25.163.25 IPv4 Unicast:base (0x7FC913AAF450:1) rcvd Refresh Start-of-RIB
*Jun 16 09:35:21.964: BGP: nbr_topo global 203.25.163.25 IPv4 Unicast:base (0x7FC913AAF450:1) refresh_epoch is 4
*Jun 16 09:35:21.965: BGP(0): 203.25.163.25 rcv UPDATE about 192.168.10.0/24 -- withdrawn
*Jun 16 09:35:21.965: BGP(0): no valid path for 192.168.10.0/24
*Jun 16 09:35:21.965: BGP: nbr_topo global 203.25.163.25 IPv4 Unicast:base (0x7FC913AAF450:1) rcvd Refresh End-of-RIB
*Jun 16 09:35:21.965: BGP: topo global:IPv4 Unicast:base Remove_fwdroute for 192.168.10.0/24
*Jun 16 09:35:41.277: BGP(0): (base) 203.25.163.25 send UPDATE (format) 192.168.20.0/24, next 203.25.163.26, metric 0, path Local

Interestingly enough from the perspective of C8Kv-1, the prefix 192.168.20.0/24 is also not received. This implies both inbound and outbound are controlled by the ebgp-safe-policy feature as perfectly commercially advertised.

C8Kv-1#sh ip bgp
BGP table version is 4, local router ID is 192.168.10.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.10.0     0.0.0.0                  0         32768 i


C8Kv-2#sh ip bgp 
BGP table version is 6, local router ID is 192.168.20.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.20.0     0.0.0.0                  0         32768 i

Implement Route-policy with ebgp-safe-policy for AS 100 on router C8Kv-1:

!
ip prefix-list advertised-routes seq 5 permit 192.168.10.0/24 le 28
ip prefix-list received-routes seq 5 permit 192.168.20.0/24 le 28
!
route-map bgp-inbound-accept permit 10 
 match ip address prefix-list received-routes
route-map bgp-outbound-accept permit 10 
 match ip address prefix-list advertised-routes
!
router bgp 100
 neighbor 203.25.163.26 route-map bgp-inbound-accept in
 neighbor 203.25.163.26 route-map bgp-outbound-accept out
!

Debug BGP neighbor on C8Kv-1:

*Jun 16 09:47:22.643: BGP(0): (base) 203.25.163.26 send UPDATE (format) 192.168.10.0/24, next 203.25.163.25, metric 0, path Local
*Jun 16 09:47:41.691: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) rcvd Refresh Start-of-RIB
*Jun 16 09:47:41.691: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) refresh_epoch is 4
*Jun 16 09:47:41.692: BGP(0): 203.25.163.26 rcvd UPDATE w/ attr: nexthop 203.25.163.26, origin i, metric 0, merged path 200, AS_PATH 
*Jun 16 09:47:41.692: BGP(0): 203.25.163.26 rcvd 192.168.20.0/24
*Jun 16 09:47:41.692: BGP: nbr_topo global 203.25.163.26 IPv4 Unicast:base (0x7F943AB93C78:1) rcvd Refresh End-of-RIB
*Jun 16 09:47:41.692: BGP(0): Revise route installing 1 of 1 routes for 192.168.20.0/24 -> 203.25.163.26(global) to main IP table

Debug BGP neighbor on C8Kv-2:

*Jun 16 09:47:22.657: BGP(0): Revise route installing 1 of 1 routes for 192.168.10.0/24 -> 203.25.163.25(global) to main IP table
*Jun 16 09:47:41.704: BGP(0): (base) 203.25.163.25 send UPDATE (format) 192.168.20.0/24, next 203.25.163.26, metric 0, path Local
============= C8Kv-1 BGP Table ============= 
C8Kv-1#sh ip bgp
BGP table version is 5, local router ID is 192.168.10.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.10.0     0.0.0.0                  0         32768 i
 *>   192.168.20.0     203.25.163.26            0             0 200 i

============= C8Kv-2 BGP Table ============= 
C8Kv-2#sh ip bgp
BGP table version is 7, local router ID is 192.168.20.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>   192.168.10.0     203.25.163.25            0             0 100 i
 *>   192.168.20.0     0.0.0.0                  0         32768 i

:: Conclusion

This feature introduces an additional security perimeter on advertised and received routes with BGP like IOS-XR has from the start.

Misconfigurations, especially made on the fly, could lead to receiving too many prefixes or advertising the wrong prefixes to your partner. In practice, it’s better to block all-in – and outbound prefixes until a proper policy is set.

Would it solve the misconfiguration failures? I don’t believe it will, but it will force the user to think about what they do and might prevent first step failures. If it fails the second step, I suggest to tighten your policies on change management and educate your personnel.

:: Resources

Lab on Github

IOS-XE configuration guide