From bdd2bf57bf7851fed1306a2216ba568fa843b5b6 Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Thu, 25 Jun 2026 07:46:07 +0200 Subject: [PATCH 1/2] Add static default routes when originating BGP default routes Some devices (Junos, BIRD) need a default route in the routing table to originate a BGP default route. To simplify the configuration templates, the bgp.session plugin adds the necessary static routes and activates the 'routing' module on the affected node(s). The functionality is used to implement BGP default routes for BIRD Also added: * Coverage test to check all potential scenarios (IPv4, IPv6, DS, single neighbor, multiple neighbors, routing module active or not) * Non-default-route BGP neighbors in the integration test to check whether the default route is propagated only to the correct neighbors --- netsim/daemons/bird.yml | 2 +- netsim/daemons/bird/bgp.j2 | 1 - netsim/daemons/bird/bgp.macros.j2 | 27 +- netsim/extra/bgp.session/plugin.py | 53 +- tests/coverage/expected/bgp-default.yml | 1079 +++++++++++++++++ tests/coverage/input/bgp-default.yml | 73 ++ .../bgp.session/04-default-originate.yml | 79 +- 7 files changed, 1281 insertions(+), 33 deletions(-) create mode 100644 tests/coverage/expected/bgp-default.yml create mode 100644 tests/coverage/input/bgp-default.yml diff --git a/netsim/daemons/bird.yml b/netsim/daemons/bird.yml index 224e89956e..12888a5048 100644 --- a/netsim/daemons/bird.yml +++ b/netsim/daemons/bird.yml @@ -54,7 +54,7 @@ features: 2octet: [ standard ] import: [ ospf, connected, static, vrf ] bfd: true - default_originate: true + default_originate: static gtsm: true passive: true password: true diff --git a/netsim/daemons/bird/bgp.j2 b/netsim/daemons/bird/bgp.j2 index b31ea75c2d..b502aa0127 100644 --- a/netsim/daemons/bird/bgp.j2 +++ b/netsim/daemons/bird/bgp.j2 @@ -3,7 +3,6 @@ {% if 'router_id' in bgp %} router id {{ bgp.router_id }}; {% endif %} -{{ bgp_config.bgp_default_originate_routes(bgp) }} {{ bgp_config.bgp_advertise_list('bgp_advertise',bgp.advertise) }} {{ bgp_config.bgp_prefixes_function('bgp_prefixes',bgp,'bgp_advertise',bgp.import|default([]),True) }} {{ bgp_config.bgp_export_filters('bgp_export_','bgp_prefixes') }} diff --git a/netsim/daemons/bird/bgp.macros.j2 b/netsim/daemons/bird/bgp.macros.j2 index 598446f079..233df74bd9 100644 --- a/netsim/daemons/bird/bgp.macros.j2 +++ b/netsim/daemons/bird/bgp.macros.j2 @@ -16,22 +16,6 @@ accept; {% endmacro %} -{# - # bgp_default_originate_routes: Render reject static routes used to originate default routes toward selected BGP neighbors. - #} -{% macro bgp_default_originate_routes(bgp_data) %} -{% for ngb in bgp_data.neighbors|default([]) if ngb.default_originate|default(False) %} -{% if loop.first %} -{% for _af in ['ipv4','ipv6'] if _af in af %} -protocol static static_bgp_default_{{ _af }} { - {{ _af }}; - route {{ '0.0.0.0/0' if _af=='ipv4' else '::/0' }} reject; -} -{% endfor %} -{% endif %} -{% endfor %} -{% endmacro %} - {# # bgp_advertise_list: Render BIRD prefix-set definitions for the configured advertise list. #} @@ -55,17 +39,14 @@ define {{ list_name }}_{{ pfx_af }} = [ #} {% macro bgp_prefixes_function(func_name,bgp_data,list_name,import_list,include_default_static) %} function {{ func_name }}( bool originate_default ) { - if net.len = 0 && !originate_default - then reject "Don't originate default route"; + if net.len = 0 + then if !originate_default + then reject "Don't originate default route"; + else accept "BGP default route origination: ",net; if source ~ [ RTS_BGP ] then accept "BGP route:", net; -{% if include_default_static %} - if proto ~ "static_bgp_default*" - then accept "BGP prefix origination:", net; - -{% endif %} {% for proto in import_list|default([]) %} if source ~ [ {{ netlab_import_map[proto] }} ] then accept "{{ proto }} route:", net; diff --git a/netsim/extra/bgp.session/plugin.py b/netsim/extra/bgp.session/plugin.py index 7096aa0db5..62edf2f3f2 100644 --- a/netsim/extra/bgp.session/plugin.py +++ b/netsim/extra/bgp.session/plugin.py @@ -3,7 +3,7 @@ from box import Box, BoxList from netsim import api, modules -from netsim.data import append_to_list +from netsim.data import append_to_list, get_box from netsim.utils import log from netsim.utils import routing as _bgp @@ -156,6 +156,56 @@ def process_bfd_requests(ndata: Box, topology: Box) -> None: log.IncorrectValue, _config_name) +''' +Check whether a node originating a default route needs static default route(s) +''' +BGP_DEFAULT = { + 'ipv4': { 'ipv4': '0.0.0.0/0', 'floating': True, 'nexthop.discard': True }, + 'ipv6': { 'ipv6': '::/0', 'floating': True, 'nexthop.discard': True }} + +def process_default_requests(ndata: Box, topology: Box) -> None: + if _bgp.get_device_bgp_feature('default_originate',ndata,topology) != 'static': + return # The node does not need static routes to originate default + + for ngb in _bgp.neighbors(ndata): + if not 'default_originate' in ngb: # Does the neighbor need a BGP default route? + continue # Nope? Cool + + route_list = [ # Find relevant static routes based on neighbor AFs + BGP_DEFAULT[af] for af in log.AF_LIST if af in ngb + ] + if not route_list: + continue + + append_to_list(ndata,'module','routing') # Activate the routing module in the node + append_to_list(topology,'module','routing') # ... assuming the developer setting BGP features was sane ;) + + # Get VRF (or global) data and its static routes + # + ngb_vrf = ngb.get('_src_vrf',None) + routes = ndata.get('routing.static',[]) + + for sr_data in route_list: # Iterate over routes that have to be added + for af in log.AF_LIST: # We have to check all AFs + if af not in sr_data: # Is this AF relevant for the current entry? + continue + + # Get existing route(s) where the AF prefix matches what we need + have_routes = [ r for r in routes if af in r and r[af] == sr_data[af] ] + if ngb_vrf is None: # Filter the routes we found based on the VRF + have_routes = [ r for r in have_routes if 'vrf' not in r ] + else: + have_routes = [ r for r in have_routes if r.get('vrf',None) == ngb_vrf ] + if have_routes: # Did the user specify their own default route(s)? + continue # Cool, nothing to do ;) + + # No per-AF default route in the target VRF yet, add it + # + sr_final = sr_data # Assume we can use the static route data as-is + if ngb_vrf: # But if we have a VRF neighbor, we have to add VRF info + sr_final = get_box({'vrf': ngb_vrf}) + sr_data + append_to_list(ndata,'routing.static',sr_final) + ''' Zap a BGP neighbor: remove all usable IP addresses, local_if and ifindex ''' @@ -288,6 +338,7 @@ def post_transform(topology: Box) -> None: copy_local_attributes(ndata,topology) process_tcpao_secrets(ndata,topology) process_bfd_requests(ndata,topology) + process_default_requests(ndata,topology) have_rs = process_rs_requests(ndata,topology) or have_rs # We need to do the RS-related EBGP session cleanup in a second pass diff --git a/tests/coverage/expected/bgp-default.yml b/tests/coverage/expected/bgp-default.yml new file mode 100644 index 0000000000..d1a5379746 --- /dev/null +++ b/tests/coverage/expected/bgp-default.yml @@ -0,0 +1,1079 @@ +bgp: + advertise_loopback: true + community: + ebgp: + - standard + ibgp: + - standard + - extended + next_hop_self: true +groups: + as65000: + members: + - dut + as65001: + members: + - d2 + as65002: + members: + - d3 + as65011: + members: + - x1 + as65012: + members: + - x2 + as65100: + members: + - v1 + as65101: + members: + - v2 + as65200: + members: + - v3 + as65300: + members: + - v4 +input: +- coverage/input/bgp-default.yml +- package:topology-defaults.yml +links: +- _linkname: links[1] + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.1/30 + node: dut + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.2/30 + node: x1 + linkindex: 1 + node_count: 2 + prefix: + ipv4: 10.1.0.0/30 + role: external + type: p2p +- _linkname: links[2] + interfaces: + - bgp: + default_originate: true + ifindex: 2 + ifname: eth2 + ipv4: 10.1.0.5/30 + node: dut + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.6/30 + node: x2 + linkindex: 2 + node_count: 2 + prefix: + ipv4: 10.1.0.4/30 + role: external + type: p2p +- _linkname: links[3] + interfaces: + - bgp: + default_originate: true + ifindex: 3 + ifname: eth3 + ipv4: 10.1.0.9/30 + node: dut + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.10/30 + node: v1 + linkindex: 3 + node_count: 2 + prefix: + ipv4: 10.1.0.8/30 + role: external + type: p2p + vrf: t1 +- _linkname: links[4] + interfaces: + - bgp: + default_originate: true + ifindex: 4 + ifname: eth4 + ipv4: 10.1.0.13/30 + node: dut + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.14/30 + node: v2 + linkindex: 4 + node_count: 2 + prefix: + ipv4: 10.1.0.12/30 + role: external + type: p2p + vrf: t1 +- _linkname: links[5] + interfaces: + - bgp: + default_originate: true + ifindex: 5 + ifname: eth5 + ipv4: 172.17.0.0/31 + ipv6: 2001:db8:1::1/64 + node: dut + - ifindex: 1 + ifname: eth1 + ipv4: 172.17.0.1/31 + ipv6: 2001:db8:1::2/64 + node: v3 + linkindex: 5 + node_count: 2 + pool: ds + prefix: + ipv4: 172.17.0.0/31 + ipv6: 2001:db8:1::/64 + role: external + type: p2p + vrf: t2 +- _linkname: links[6] + interfaces: + - bgp: + default_originate: true + ifindex: 6 + ifname: eth6 + ipv6: 2001:db8:2::1/64 + node: dut + - ifindex: 2 + ifname: eth2 + ipv6: 2001:db8:2::2/64 + node: v3 + linkindex: 6 + node_count: 2 + pool: v6 + prefix: + ipv6: 2001:db8:2::/64 + role: external + type: p2p + vrf: t3 +- _linkname: links[7] + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.17/30 + node: d2 + - ifindex: 2 + ifname: eth2 + ipv4: 10.1.0.18/30 + node: x1 + linkindex: 7 + node_count: 2 + prefix: + ipv4: 10.1.0.16/30 + role: external + type: p2p +- _linkname: links[8] + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.21/30 + node: d3 + - ifindex: 3 + ifname: eth3 + ipv4: 10.1.0.22/30 + node: x1 + linkindex: 8 + node_count: 2 + prefix: + ipv4: 10.1.0.20/30 + role: external + type: p2p +message: 'Checks the default static route origination needed to originate the + + BGP default route(s) + + ' +module: +- routing +- bgp +- vrf +name: input +nodes: + d2: + _features: + bgp: + default_originate: static + af: + ipv4: true + bgp: + _session_clear: + - 10.1.0.18 + advertise: + - ipv4: 10.0.0.2/32 + advertise_loopback: true + as: 65001 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - activate: + ipv4: true + as: 65011 + default_originate: true + ifindex: 1 + ipv4: 10.1.0.18 + name: x1 + type: ebgp + next_hop_self: true + router_id: 10.0.0.2 + box: none + config: + - bgp.session + device: none + id: 2 + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.17/30 + linkindex: 7 + name: d2 -> x1 + neighbors: + - ifname: eth2 + ipv4: 10.1.0.18/30 + node: x1 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.2/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.102 + mac: ca:fe:00:02:00:00 + module: + - routing + - bgp + name: d2 + routing: + static: + - floating: true + ipv4: 0.0.0.0/0 + nexthop: + discard: true + d3: + af: + ipv4: true + bgp: + _session_clear: + - 10.1.0.22 + advertise: + - ipv4: 10.0.0.3/32 + advertise_loopback: true + as: 65002 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - activate: + ipv4: true + as: 65011 + default_originate: true + ifindex: 1 + ipv4: 10.1.0.22 + name: x1 + type: ebgp + next_hop_self: true + router_id: 10.0.0.3 + box: none + config: + - bgp.session + device: none + id: 3 + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.21/30 + linkindex: 8 + name: d3 -> x1 + neighbors: + - ifname: eth3 + ipv4: 10.1.0.22/30 + node: x1 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.3/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.103 + mac: ca:fe:00:03:00:00 + module: + - bgp + name: d3 + dut: + _features: + bgp: + default_originate: static + af: + ipv4: true + ipv6: true + vpnv4: true + vpnv6: true + bgp: + _session_clear: + - 10.1.0.2 + - 10.1.0.6 + advertise: + - ipv4: 10.0.0.1/32 + advertise_loopback: true + as: 65000 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - activate: + ipv4: true + as: 65011 + default_originate: true + ifindex: 1 + ipv4: 10.1.0.2 + name: x1 + type: ebgp + - activate: + ipv4: true + as: 65012 + default_originate: true + ifindex: 2 + ipv4: 10.1.0.6 + name: x2 + type: ebgp + next_hop_self: true + router_id: 10.0.0.1 + box: none + config: + - bgp.session + device: none + id: 1 + interfaces: + - bgp: + default_originate: true + ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.1/30 + linkindex: 1 + name: dut -> x1 + neighbors: + - ifname: eth1 + ipv4: 10.1.0.2/30 + node: x1 + role: external + type: p2p + - bgp: + default_originate: true + ifindex: 2 + ifname: eth2 + ipv4: 10.1.0.5/30 + linkindex: 2 + name: dut -> x2 + neighbors: + - ifname: eth1 + ipv4: 10.1.0.6/30 + node: x2 + role: external + type: p2p + - bgp: + default_originate: true + ifindex: 3 + ifname: eth3 + ipv4: 10.1.0.9/30 + linkindex: 3 + name: dut -> v1 + neighbors: + - ifname: eth1 + ipv4: 10.1.0.10/30 + node: v1 + role: external + type: p2p + vrf: t1 + - bgp: + default_originate: true + ifindex: 4 + ifname: eth4 + ipv4: 10.1.0.13/30 + linkindex: 4 + name: dut -> v2 + neighbors: + - ifname: eth1 + ipv4: 10.1.0.14/30 + node: v2 + role: external + type: p2p + vrf: t1 + - bgp: + default_originate: true + ifindex: 5 + ifname: eth5 + ipv4: 172.17.0.0/31 + ipv6: 2001:db8:1::1/64 + linkindex: 5 + name: dut -> v3 + neighbors: + - ifname: eth1 + ipv4: 172.17.0.1/31 + ipv6: 2001:db8:1::2/64 + node: v3 + pool: ds + role: external + type: p2p + vrf: t2 + - bgp: + default_originate: true + ifindex: 6 + ifname: eth6 + ipv6: 2001:db8:2::1/64 + linkindex: 6 + name: dut -> v3 + neighbors: + - ifname: eth2 + ipv6: 2001:db8:2::2/64 + node: v3 + pool: v6 + role: external + type: p2p + vrf: t3 + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.1/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.101 + mac: ca:fe:00:01:00:00 + module: + - routing + - bgp + - vrf + name: dut + routing: + static: + - ipv4: 0.0.0.0/0 + nexthop: + idx: 0 + intf: eth5 + ipv4: 172.17.0.1 + ipv6: 2001:db8:1::2 + vrf: t2 + - floating: true + ipv4: 0.0.0.0/0 + nexthop: + discard: true + - floating: true + ipv4: 0.0.0.0/0 + nexthop: + discard: true + vrf: t1 + - floating: true + ipv6: ::/0 + nexthop: + discard: true + vrf: t2 + - floating: true + ipv6: ::/0 + nexthop: + discard: true + vrf: t3 + vrf: + as: 65000 + vrfs: + t1: + af: + ipv4: true + bgp: + _session_clear: + - 10.1.0.10 + - 10.1.0.14 + import: + connected: + auto: true + neighbors: + - _src_vrf: t1 + activate: + ipv4: true + as: 65100 + default_originate: true + ifindex: 3 + ipv4: 10.1.0.10 + name: v1 + type: ebgp + - _src_vrf: t1 + activate: + ipv4: true + as: 65101 + default_originate: true + ifindex: 4 + ipv4: 10.1.0.14 + name: v2 + type: ebgp + export: + - '65000:1' + id: 1 + import: + - '65000:1' + rd: '65000:1' + vrfidx: 100 + t2: + af: + ipv4: true + ipv6: true + bgp: + _session_clear: + - 172.17.0.1 + - 2001:db8:1::2 + import: + connected: + auto: true + neighbors: + - _src_vrf: t2 + activate: + ipv4: true + ipv6: true + as: 65200 + default_originate: true + ifindex: 5 + ipv4: 172.17.0.1 + ipv6: 2001:db8:1::2 + name: v3 + type: ebgp + export: + - '65000:2' + id: 2 + import: + - '65000:2' + rd: '65000:2' + vrfidx: 101 + t3: + af: + ipv6: true + bgp: + _session_clear: + - 2001:db8:2::2 + import: + connected: + auto: true + neighbors: + - _src_vrf: t3 + activate: + ipv6: true + as: 65200 + default_originate: true + ifindex: 6 + ipv6: 2001:db8:2::2 + name: v3 + type: ebgp + export: + - '65000:3' + id: 3 + import: + - '65000:3' + rd: '65000:3' + vrfidx: 102 + v1: + af: + ipv4: true + bgp: + advertise: + - ipv4: 10.0.0.6/32 + advertise_loopback: true + as: 65100 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - _vrf: t1 + activate: + ipv4: true + as: 65000 + ifindex: 1 + ipv4: 10.1.0.9 + name: dut + type: ebgp + next_hop_self: true + router_id: 10.0.0.6 + box: none + device: none + id: 6 + interfaces: + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.10/30 + linkindex: 3 + name: v1 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth3 + ipv4: 10.1.0.9/30 + node: dut + vrf: t1 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.6/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.106 + mac: ca:fe:00:06:00:00 + module: + - bgp + name: v1 + v2: + af: + ipv4: true + bgp: + advertise: + - ipv4: 10.0.0.7/32 + advertise_loopback: true + as: 65101 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - _vrf: t1 + activate: + ipv4: true + as: 65000 + ifindex: 1 + ipv4: 10.1.0.13 + name: dut + type: ebgp + next_hop_self: true + router_id: 10.0.0.7 + box: none + device: none + id: 7 + interfaces: + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.14/30 + linkindex: 4 + name: v2 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth4 + ipv4: 10.1.0.13/30 + node: dut + vrf: t1 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.7/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.107 + mac: ca:fe:00:07:00:00 + module: + - bgp + name: v2 + v3: + af: + ipv4: true + ipv6: true + bgp: + advertise: + - ipv4: 10.0.0.8/32 + advertise_loopback: true + as: 65200 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + ipv6: true + neighbors: + - _vrf: t2 + activate: + ipv4: true + ipv6: true + as: 65000 + ifindex: 1 + ipv4: 172.17.0.0 + ipv6: 2001:db8:1::1 + name: dut + type: ebgp + - _vrf: t3 + activate: + ipv6: true + as: 65000 + ifindex: 2 + ipv6: 2001:db8:2::1 + name: dut + type: ebgp + next_hop_self: true + router_id: 10.0.0.8 + box: none + device: none + id: 8 + interfaces: + - ifindex: 1 + ifname: eth1 + ipv4: 172.17.0.1/31 + ipv6: 2001:db8:1::2/64 + linkindex: 5 + name: v3 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth5 + ipv4: 172.17.0.0/31 + ipv6: 2001:db8:1::1/64 + node: dut + vrf: t2 + pool: ds + role: external + type: p2p + - ifindex: 2 + ifname: eth2 + ipv6: 2001:db8:2::2/64 + linkindex: 6 + name: v3 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth6 + ipv6: 2001:db8:2::1/64 + node: dut + vrf: t3 + pool: v6 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.8/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.108 + mac: ca:fe:00:08:00:00 + module: + - bgp + name: v3 + v4: + af: + ipv4: true + bgp: + advertise: + - ipv4: 10.0.0.9/32 + advertise_loopback: true + as: 65300 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: [] + next_hop_self: true + router_id: 10.0.0.9 + box: none + device: none + id: 9 + interfaces: [] + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.9/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.109 + mac: ca:fe:00:09:00:00 + module: + - bgp + name: v4 + x1: + af: + ipv4: true + bgp: + advertise: + - ipv4: 10.0.0.4/32 + advertise_loopback: true + as: 65011 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - activate: + ipv4: true + as: 65000 + ifindex: 1 + ipv4: 10.1.0.1 + name: dut + type: ebgp + - activate: + ipv4: true + as: 65001 + ifindex: 2 + ipv4: 10.1.0.17 + name: d2 + type: ebgp + - activate: + ipv4: true + as: 65002 + ifindex: 3 + ipv4: 10.1.0.21 + name: d3 + type: ebgp + next_hop_self: true + router_id: 10.0.0.4 + box: none + device: none + id: 4 + interfaces: + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.2/30 + linkindex: 1 + name: x1 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth1 + ipv4: 10.1.0.1/30 + node: dut + role: external + type: p2p + - ifindex: 2 + ifname: eth2 + ipv4: 10.1.0.18/30 + linkindex: 7 + name: x1 -> d2 + neighbors: + - bgp: + default_originate: true + ifname: eth1 + ipv4: 10.1.0.17/30 + node: d2 + role: external + type: p2p + - ifindex: 3 + ifname: eth3 + ipv4: 10.1.0.22/30 + linkindex: 8 + name: x1 -> d3 + neighbors: + - bgp: + default_originate: true + ifname: eth1 + ipv4: 10.1.0.21/30 + node: d3 + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.4/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.104 + mac: ca:fe:00:04:00:00 + module: + - bgp + name: x1 + x2: + af: + ipv4: true + bgp: + advertise: + - ipv4: 10.0.0.5/32 + advertise_loopback: true + as: 65012 + community: + ebgp: + - standard + ibgp: + - standard + - extended + localas_ibgp: + - standard + - extended + ipv4: true + neighbors: + - activate: + ipv4: true + as: 65000 + ifindex: 1 + ipv4: 10.1.0.5 + name: dut + type: ebgp + next_hop_self: true + router_id: 10.0.0.5 + box: none + device: none + id: 5 + interfaces: + - ifindex: 1 + ifname: eth1 + ipv4: 10.1.0.6/30 + linkindex: 2 + name: x2 -> dut + neighbors: + - bgp: + default_originate: true + ifname: eth2 + ipv4: 10.1.0.5/30 + node: dut + role: external + type: p2p + loopback: + bgp: + advertise: true + ifindex: 0 + ifname: Loopback0 + ipv4: 10.0.0.5/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.105 + mac: ca:fe:00:05:00:00 + module: + - bgp + name: x2 +plugin: +- bgp.session +prefix: + any: + ipv4: 0.0.0.0/0 + ipv6: ::/0 +provider: libvirt +vrf: + as: 65000 +vrfs: + t1: + export: + - '65000:1' + id: 1 + import: + - '65000:1' + rd: '65000:1' + t2: + export: + - '65000:2' + id: 2 + import: + - '65000:2' + rd: '65000:2' + t3: + export: + - '65000:3' + id: 3 + import: + - '65000:3' + rd: '65000:3' diff --git a/tests/coverage/input/bgp-default.yml b/tests/coverage/input/bgp-default.yml new file mode 100644 index 0000000000..69ffcd3fe2 --- /dev/null +++ b/tests/coverage/input/bgp-default.yml @@ -0,0 +1,73 @@ +message: | + Checks the default static route origination needed to originate the + BGP default route(s) + +defaults.device: none +module: [ bgp ] +plugin: [ bgp.session ] + +addressing: + ds: + ipv4: 172.17.0.0/24 + prefix: 31 + ipv6: 2001:db8:1::/48 + v6: + ipv6: 2001:db8:2::/48 + +vrfs: + t1: + t2: + t3: + +nodes: + dut: # Most extensive tests, check all global/VRF/AF combos + module: [ bgp, vrf, routing ] + bgp.as: 65000 + _features.bgp.default_originate: static + routing.static: + - ipv4: 0.0.0.0/0 + vrf: t2 + nexthop.node: v3 + d2: # Ensure the 'routing' module is added to the node + module: [ bgp ] + _features.bgp.default_originate: static + bgp.as: 65001 + d3: # Ensure there are no static routes created for 'regular' device + module: [ bgp ] + bgp.as: 65002 + x1: + bgp.as: 65011 + x2: + bgp.as: 65012 + v1: + bgp.as: 65100 + v2: + bgp.as: 65101 + v3: + bgp.as: 65200 + v4: + bgp.as: 65300 + +links: +- dut.bgp.default_originate: True + x1: +- dut.bgp.default_originate: True + x2: +- dut.bgp.default_originate: True + v1: + vrf: t1 +- dut.bgp.default_originate: True + v2: + vrf: t1 +- dut.bgp.default_originate: True + v3: + vrf: t2 + pool: ds +- dut.bgp.default_originate: True + v3: + vrf: t3 + pool: v6 +- d2.bgp.default_originate: True + x1: +- d3.bgp.default_originate: True + x1: diff --git a/tests/integration/bgp.session/04-default-originate.yml b/tests/integration/bgp.session/04-default-originate.yml index 8aa982497a..2fdec10711 100644 --- a/tests/integration/bgp.session/04-default-originate.yml +++ b/tests/integration/bgp.session/04-default-originate.yml @@ -11,7 +11,7 @@ groups: probes: device: frr provider: clab - members: [ x1, x2 ] + members: [ x1, x2, r1, r2 ] defaults.bgp.as: 65000 bgp.advertise_loopback: false @@ -29,10 +29,22 @@ nodes: dut: module: [ bgp, vrf ] role: router - x1: + x1: # Global default route bgp.as: 65100 - x2: + bgp.advertise_loopback: True + loopback: + ipv4: 172.17.0.1/32 + ipv6: 2001:db8:cafe::1/128 + x2: # VRF default route bgp.as: 65200 + bgp.advertise_loopback: True + loopback: + ipv4: 172.17.0.2/32 + ipv6: 2001:db8:cafe::2/128 + r1: # No default route, global + bgp.as: 65101 + r2: # No default route, VRF + bgp.as: 65102 links: - dut: @@ -42,19 +54,23 @@ links: bgp.default_originate: true vrf: tenant x2: +- dut-r1 +- dut: + vrf: tenant + r2: validate: ebgp_v4: description: Check IPv4 EBGP sessions with DUT wait_msg: Waiting for EBGP session establishment wait: ebgp_session - nodes: [ x1 ] + nodes: [ x1, r1 ] plugin: bgp_neighbor(node.bgp.neighbors,'dut') ebgp_v6: description: Check IPv6 EBGP sessions with DUT wait_msg: Waiting for EBGP session establishment wait: ebgp_session - nodes: [ x1 ] + nodes: [ x1, r1 ] plugin: bgp_neighbor(node.bgp.neighbors,'dut',af='ipv6') def_ipv4: description: Check whether DUT advertises IPv4 default route to X1 @@ -68,17 +84,42 @@ validate: wait_msg: Waiting for BGP convergence nodes: [ x1 ] plugin: bgp_prefix('::/0',af='ipv6') + prop_ipv4: + description: Check the propagation of IPv4 routes X1 -> R1 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r1 ] + plugin: bgp_prefix(nodes.x1.loopback.ipv4) + prop_ipv6: + description: Check the propagation of IPv6 routes X1 -> R1 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r1 ] + plugin: bgp_prefix(nodes.x1.loopback.ipv6,af='ipv6') + nd_ipv4: + description: Check whether DUT advertises IPv4 default route to R1 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r1 ] + plugin: bgp_prefix('0.0.0.0/0',state='missing') + nd_ipv6: + description: Check whether DUT advertises IPv6 default route to R1 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r1 ] + plugin: bgp_prefix('::/0',af='ipv6',state='missing') + vrf_ebgp_v4: description: Check IPv4 EBGP sessions with DUT (vrf) wait_msg: Waiting for EBGP session establishment wait: ebgp_session - nodes: [ x2 ] + nodes: [ x2, r2 ] plugin: bgp_neighbor(node.bgp.neighbors,'dut') vrf_ebgp_v6: description: Check IPv6 EBGP sessions with DUT (vrf) wait_msg: Waiting for EBGP session establishment wait: ebgp_session - nodes: [ x2 ] + nodes: [ x2, r2 ] plugin: bgp_neighbor(node.bgp.neighbors,'dut',af='ipv6') vrf_def_ipv4: description: Check whether DUT (vrf) advertises IPv4 default route to X2 @@ -92,3 +133,27 @@ validate: wait_msg: Waiting for BGP convergence nodes: [ x2 ] plugin: bgp_prefix('::/0',af='ipv6') + vrf_prop_ipv4: + description: Check the propagation of IPv4 routes X2 -> R2 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r2 ] + plugin: bgp_prefix(nodes.x2.loopback.ipv4) + vrf_prop_ipv6: + description: Check the propagation of IPv6 routes X2 -> R2 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r2 ] + plugin: bgp_prefix(nodes.x2.loopback.ipv6,af='ipv6') + vrf_nd_ipv4: + description: Check whether DUT advertises VRF IPv4 default route to R2 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r2 ] + plugin: bgp_prefix('0.0.0.0/0',state='missing') + vrf_nd_ipv6: + description: Check whether DUT advertises VRF IPv6 default route to R2 + wait: bgp_scan_time + wait_msg: Waiting for BGP convergence + nodes: [ r2 ] + plugin: bgp_prefix('::/0',af='ipv6',state='missing') From 04bd332111b234333d4288203c8a060fa94facbe Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Mon, 29 Jun 2026 10:16:04 +0200 Subject: [PATCH 2/2] Fix: Rewrite the creation of static route data --- netsim/extra/bgp.session/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim/extra/bgp.session/plugin.py b/netsim/extra/bgp.session/plugin.py index 62edf2f3f2..c05492bab5 100644 --- a/netsim/extra/bgp.session/plugin.py +++ b/netsim/extra/bgp.session/plugin.py @@ -201,9 +201,9 @@ def process_default_requests(ndata: Box, topology: Box) -> None: # No per-AF default route in the target VRF yet, add it # - sr_final = sr_data # Assume we can use the static route data as-is + sr_final = get_box(sr_data) # Assume we can use the static route data as-is if ngb_vrf: # But if we have a VRF neighbor, we have to add VRF info - sr_final = get_box({'vrf': ngb_vrf}) + sr_data + sr_final.vrf = ngb_vrf append_to_list(ndata,'routing.static',sr_final) '''