Add BGP configuration for BGPSentinel peer (#15714)

Why I did it
For route registry service, in order to block hijacked routes, IBGP session needs to be set up from BGP sentinel service to SONiC, and BGP sentinel service advertise the same route with higher local-preference and no export community. So that SONiC takes the route from BGP sentinel as the best path and does not advertise the route to EBGP peers.
In order to do that, new route-maps are needed. So this change adds a new set of templates, keeping BGPSentinel peers out of the other templates.

Work item tracking
Microsoft ADO (number only): 24451346
How I did it
Add sentinel_community in constants.yml, route from BGPSentinel do not match this community will be denied.
Add support to convert BGPSentinel related configuration in the BGPPeerPassive element of the minigraph to a new BGP_SENTINELS table in CONFIG_DB
Add a new set of "sentinels" templates to docker-fpm-frr
Add a new BGP peer manager to bgpcfgd, to add neighbors from the BGP_SENTINELS table using the "sentinels" templates
Add a test case for minigraph.py, making sure the BGPSentinel and BGPSentinelV6 elements create BGP_SENTINELS DB entry.
Add a set of test cases for the new sentinels templates in sonic-bgpcfgd tests.
Add sonic-bgp-sentinel.yang and a set of testcases for the yang file.

How to verify it
Testcases and UT newly added would pass.
Setup IPv4 and IPv6 BGPSentinel services in minigraph, and load minigraph, show CONFIG_DB and "show runningconfig bgp", configuration would be loaded successfully.
Using t1-lag topo and setup IBGP session from BGPSentinel to SONiC loopback address, IBGP session would up.
Advertise route from BGPSentinel to T1 with sentinel_community, higher local-preference and no-export communiyt. In T1, show bgp route, the result is "Not advertise to any EBGP peer".
Withdraw the route in BGPSentinel, in T1, route would advertise to EBGP peers.
Advertise route from T1 that does not match sentinel_community, in T1, would not see the route in show bgp route.
This commit is contained in:
guangyao6 2023-07-21 09:32:29 +08:00 committed by GitHub
parent bb99552f03
commit 9567c06570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 401 additions and 14 deletions

View File

@ -0,0 +1,31 @@
!
! template: bgpd/templates/sentinels/instance.conf.j2
!
neighbor {{ bgp_session['name'] }} peer-group
neighbor {{ bgp_session['name'] }} remote-as {{ bgp_asn }}
{% if bgp_session['src_address'] is defined %}
neighbor {{ bgp_session['name'] }} update-source {{ bgp_session['src_address'] | ip }}
{% endif %}
{% for ip_range in bgp_session['ip_range'].split(',') %}
bgp listen range {{ ip_range }} peer-group {{ bgp_session['name'] }}
{% endfor %}
!
address-family ipv4
neighbor {{ bgp_session['name'] }} activate
neighbor {{ bgp_session['name'] }} addpath-tx-all-paths
neighbor {{ bgp_session['name'] }} soft-reconfiguration inbound
neighbor {{ bgp_session['name'] }} route-map FROM_BGP_SENTINEL in
neighbor {{ bgp_session['name'] }} route-map TO_BGP_SENTINEL out
neighbor {{ bgp_session['name'] }} maximum-prefix 200
exit-address-family
address-family ipv6
neighbor {{ bgp_session['name'] }} activate
neighbor {{ bgp_session['name'] }} addpath-tx-all-paths
neighbor {{ bgp_session['name'] }} soft-reconfiguration inbound
neighbor {{ bgp_session['name'] }} route-map FROM_BGP_SENTINEL in
neighbor {{ bgp_session['name'] }} route-map TO_BGP_SENTINEL out
neighbor {{ bgp_session['name'] }} maximum-prefix 200
exit-address-family
!
! end of template: bgpd/templates/sentinels/instance.conf.j2
!

View File

@ -0,0 +1,7 @@
!
! template: bgpd/templates/sentinels/peer-group.conf.j2
!
! nothing is here
!
! end of template: bgpd/templates/sentinels/peer-group.conf.j2
!

View File

@ -0,0 +1,16 @@
!
! template: bgpd/templates/sentinels/policies.conf.j2
!
{% if constants.bgp.sentinel_community is defined %}
bgp community-list standard sentinel_community permit {{ constants.bgp.sentinel_community }}
!
route-map FROM_BGP_SENTINEL permit 100
match community sentinel_community
{% endif %}
!
route-map FROM_BGP_SENTINEL deny 200
!
route-map TO_BGP_SENTINEL permit 100
!
! end of template: bgpd/templates/sentinels/policies.conf.j2
!

View File

@ -4,6 +4,7 @@ constants:
"2" : 65433
bgp:
traffic_shift_community: 12345:12345
sentinel_community: 12345:12346
families:
- ipv4
- ipv6
@ -58,3 +59,7 @@ constants:
enabled: true
db_table: "BGP_VOQ_CHASSIS_NEIGHBOR"
template_dir: "voq_chassis"
sentinels: # peer_type
enabled: true
db_table: "BGP_SENTINELS"
template_dir: "sentinels"

View File

@ -60,6 +60,7 @@ def do_work():
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_MONITORS", "monitors", False),
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_PEER_RANGE", "dynamic", False),
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_VOQ_CHASSIS_NEIGHBOR", "voq_chassis", False),
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_SENTINELS", "sentinels", False),
# AllowList Managers
BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES"),
# BBR Manager

View File

@ -0,0 +1,8 @@
{
"bgp_asn": "555",
"bgp_session": {
"ip_range": "10.10.20.0/24,20.20.20.0/24",
"name": "BGPSentinel",
"src_address": "1.1.1.1"
}
}

View File

@ -0,0 +1,8 @@
{
"bgp_asn": "555",
"bgp_session": {
"ip_range": "2603:10a0:321:82f9::/64,2603:10a1:30a:8000::/59",
"name": "BGPSentinelV6",
"src_address": "fc00:1::32"
}
}

View File

@ -0,0 +1,28 @@
!
! template: bgpd/templates/sentinels/instance.conf.j2
!
neighbor BGPSentinel peer-group
neighbor BGPSentinel remote-as 555
neighbor BGPSentinel update-source 1.1.1.1
bgp listen range 10.10.20.0/24 peer-group BGPSentinel
bgp listen range 20.20.20.0/24 peer-group BGPSentinel
!
address-family ipv4
neighbor BGPSentinel activate
neighbor BGPSentinel addpath-tx-all-paths
neighbor BGPSentinel soft-reconfiguration inbound
neighbor BGPSentinel route-map FROM_BGP_SENTINEL in
neighbor BGPSentinel route-map TO_BGP_SENTINEL out
neighbor BGPSentinel maximum-prefix 200
exit-address-family
address-family ipv6
neighbor BGPSentinel activate
neighbor BGPSentinel addpath-tx-all-paths
neighbor BGPSentinel soft-reconfiguration inbound
neighbor BGPSentinel route-map FROM_BGP_SENTINEL in
neighbor BGPSentinel route-map TO_BGP_SENTINEL out
neighbor BGPSentinel maximum-prefix 200
exit-address-family
!
! end of template: bgpd/templates/sentinels/instance.conf.j2
!

View File

@ -0,0 +1,28 @@
!
! template: bgpd/templates/sentinels/instance.conf.j2
!
neighbor BGPSentinelV6 peer-group
neighbor BGPSentinelV6 remote-as 555
neighbor BGPSentinelV6 update-source fc00:1::32
bgp listen range 2603:10a0:321:82f9::/64 peer-group BGPSentinelV6
bgp listen range 2603:10a1:30a:8000::/59 peer-group BGPSentinelV6
!
address-family ipv4
neighbor BGPSentinelV6 activate
neighbor BGPSentinelV6 addpath-tx-all-paths
neighbor BGPSentinelV6 soft-reconfiguration inbound
neighbor BGPSentinelV6 route-map FROM_BGP_SENTINEL in
neighbor BGPSentinelV6 route-map TO_BGP_SENTINEL out
neighbor BGPSentinelV6 maximum-prefix 200
exit-address-family
address-family ipv6
neighbor BGPSentinelV6 activate
neighbor BGPSentinelV6 addpath-tx-all-paths
neighbor BGPSentinelV6 soft-reconfiguration inbound
neighbor BGPSentinelV6 route-map FROM_BGP_SENTINEL in
neighbor BGPSentinelV6 route-map TO_BGP_SENTINEL out
neighbor BGPSentinelV6 maximum-prefix 200
exit-address-family
!
! end of template: bgpd/templates/sentinels/instance.conf.j2
!

View File

@ -0,0 +1,7 @@
!
! template: bgpd/templates/BGP_SPEAKER/peer-group.conf.j2
!
! nothing is here
!
! end of template: bgpd/templates/BGP_SPEAKER/peer-group.conf.j2
!

View File

@ -0,0 +1,7 @@
{
"constants": {
"bgp": {
"sentinel_community": "12345:12346"
}
}
}

View File

@ -0,0 +1,6 @@
{
"constants": {
"bgp": {
}
}
}

View File

@ -0,0 +1,14 @@
!
! template: bgpd/templates/sentinels/policies.conf.j2
!
bgp community-list standard sentinel_community permit 12345:12346
!
route-map FROM_BGP_SENTINEL permit 100
match community sentinel_community
!
route-map FROM_BGP_SENTINEL deny 200
!
route-map TO_BGP_SENTINEL permit 100
!
! end of template: bgpd/templates/sentinels/policies.conf.j2
!

View File

@ -0,0 +1,10 @@
!
! template: bgpd/templates/sentinels/policies.conf.j2
!
!
route-map FROM_BGP_SENTINEL deny 200
!
route-map TO_BGP_SENTINEL permit 100
!
! end of template: bgpd/templates/sentinels/policies.conf.j2
!

View File

@ -152,3 +152,15 @@ def test_voq_chassis_pg():
def test_voq_chassis_instance():
test_data = load_tests("voq_chassis", "instance.conf")
run_tests("voq_chassis_instance", *test_data)
def test_sentinel_policies():
test_data = load_tests("sentinels", "policies.conf")
run_tests("sentinel_policies", *test_data)
def test_sentinel_pg():
test_data = load_tests("sentinels", "peer-group.conf")
run_tests("sentinel_pg", *test_data)
def test_sentinel_instance():
test_data = load_tests("sentinels", "instance.conf")
run_tests("sentinel_instance", *test_data)

View File

@ -887,6 +887,7 @@ def parse_cpg(cpg, hname, local_devices=[]):
bgp_voq_chassis_sessions = {}
myasn = None
bgp_peers_with_range = {}
bgp_sentinel_sessions = {}
for child in cpg:
tag = child.tag
if tag == str(QName(ns, "PeeringSessions")):
@ -956,6 +957,14 @@ def parse_cpg(cpg, hname, local_devices=[]):
name = bgpPeer.find(str(QName(ns1, "Name"))).text
ip_range = bgpPeer.find(str(QName(ns1, "PeersRange"))).text
ip_range_group = ip_range.split(';') if ip_range and ip_range != "" else []
if name == "BGPSentinel" or name == "BGPSentinelV6":
bgp_sentinel_sessions[name] = {
'name': name,
'ip_range': ip_range_group
}
if bgpPeer.find(str(QName(ns, "Address"))) is not None:
bgp_sentinel_sessions[name]['src_address'] = bgpPeer.find(str(QName(ns, "Address"))).text
else:
bgp_peers_with_range[name] = {
'name': name,
'ip_range': ip_range_group
@ -985,7 +994,7 @@ def parse_cpg(cpg, hname, local_devices=[]):
bgp_internal_sessions = filter_bad_asn(bgp_internal_sessions)
bgp_voq_chassis_sessions = filter_bad_asn(bgp_voq_chassis_sessions)
return bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, myasn, bgp_peers_with_range, bgp_monitors
return bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, myasn, bgp_peers_with_range, bgp_monitors, bgp_sentinel_sessions
def parse_meta(meta, hname):
@ -1537,7 +1546,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
if child.tag == str(QName(ns, "DpgDec")):
(intfs, lo_intfs, mvrf, mgmt_intf, voq_inband_intfs, vlans, vlan_members, dhcp_relay_table, pcs, pc_members, acls, acl_table_types, vni, tunnel_intfs, dpg_ecmp_content, static_routes, tunnel_intfs_qos_remap_config) = parse_dpg(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname)
(bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors, bgp_sentinel_sessions) = parse_cpg(child, hostname)
elif child.tag == str(QName(ns, "PngDec")):
(neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speed_png, console_ports, mux_cable_ports, png_ecmp_content) = parse_png(child, hostname, dpg_ecmp_content)
elif child.tag == str(QName(ns, "UngDec")):
@ -1553,7 +1562,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
(intfs, lo_intfs, mvrf, mgmt_intf, voq_inband_intfs, vlans, vlan_members, dhcp_relay_table, pcs, pc_members, acls, acl_table_types, vni, tunnel_intfs, dpg_ecmp_content, static_routes, tunnel_intfs_qos_remap_config) = parse_dpg(child, asic_name)
host_lo_intfs = parse_host_loopback(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices)
(bgp_sessions, bgp_internal_sessions, bgp_voq_chassis_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors, bgp_sentinel_sessions) = parse_cpg(child, asic_name, local_devices)
elif child.tag == str(QName(ns, "PngDec")):
(neighbors, devices, port_speed_png) = parse_asic_png(child, asic_name, hostname)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
@ -1670,6 +1679,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
results['BGP_PEER_RANGE'] = bgp_peers_with_range
results['BGP_INTERNAL_NEIGHBOR'] = bgp_internal_sessions
results['BGP_VOQ_CHASSIS_NEIGHBOR'] = bgp_voq_chassis_sessions
results['BGP_SENTINELS'] = bgp_sentinel_sessions
if mgmt_routes:
# TODO: differentiate v4 and v6
next(iter(mgmt_intf.values()))['forced_mgmt_routes'] = mgmt_routes
@ -1962,6 +1972,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
if is_storage_device and 'BackEnd' in current_device['type']:
results['BGP_MONITORS'] = {}
results['BGP_PEER_RANGE'] = {}
results['BGP_SENTINELS'] = {}
results['VLAN'] = vlans
results['VLAN_MEMBER'] = vlan_members

View File

@ -108,6 +108,24 @@
<RouteMapOut i:nil="true"/>
<Vrf i:nil="true"/>
</BGPPeer>
<BGPPeer i:type="a:BGPPeerPassive">
<ElementType>BGPPeer</ElementType>
<Address>10.2.0.20</Address>
<RouteMapIn i:nil="true"/>
<RouteMapOut i:nil="true"/>
<Vrf i:nil="true"/>
<a:Name>BGPSentinel</a:Name>
<a:PeersRange>10.154.232.0/21;10.42.168.0/21</a:PeersRange>
</BGPPeer>
<BGPPeer i:type="a:BGPPeerPassive">
<ElementType>BGPPeer</ElementType>
<Address>fc00:1::32</Address>
<RouteMapIn i:nil="true"/>
<RouteMapOut i:nil="true"/>
<Vrf i:nil="true"/>
<a:Name>BGPSentinelV6</a:Name>
<a:PeersRange>2603:10a0:321:82f9::/64;2603:10a1:30a:8000::/59</a:PeersRange>
</BGPPeer>
</a:Peers>
<a:RouteMaps/>
</a:BGPRouterDeclaration>

View File

@ -729,6 +729,21 @@ class TestCfgGen(TestCase):
output = self.run_script(argument)
self.assertEqual(output.strip(), "")
def test_minigraph_bgp_sentinel(self):
argument = ['-m', self.sample_graph_simple, '-p', self.port_config, '-v', "BGP_SENTINELS[\'BGPSentinel\']"]
output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
utils.to_dict("{'name': 'BGPSentinel', 'ip_range': ['10.154.232.0/21','10.42.168.0/21'], 'src_address': '10.2.0.20'}")
)
argument = ['-m', self.sample_graph_simple, '-p', self.port_config, '-v', "BGP_SENTINELS[\'BGPSentinelV6\']"]
output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
utils.to_dict("{'name': 'BGPSentinelV6', 'ip_range': ['2603:10a0:321:82f9::/64','2603:10a1:30a:8000::/59'], 'src_address': 'fc00:1::32'}")
)
def test_minigraph_sub_port_intf_resource_type_non_backend_tor(self, check_stderr=True):
self.verify_sub_intf_non_backend_tor(graph_file=self.sample_resource_graph, check_stderr=check_stderr)

View File

@ -424,6 +424,13 @@ group name and IP ranges in **BGP_PEER_RANGE** table.
"ip_range": [
"10.2.0.0/16"
]
},
"BGPSentinel": {
"name": "BGPSentinel",
"ip_range": [
"10.1.0.0/24"
],
"src_address": "10.1.0.32"
}
}
}

View File

@ -188,7 +188,8 @@ setup(
'./yang-models/sonic-port-qos-map.yang',
'./yang-models/sonic-static-route.yang',
'./yang-models/sonic-system-port.yang',
'./yang-models/sonic-macsec.yang']),
'./yang-models/sonic-macsec.yang',
'./yang-models/sonic-bgp-sentinel.yang']),
('cvlyang-models', ['./cvlyang-models/sonic-acl.yang',
'./cvlyang-models/sonic-bgp-common.yang',
'./cvlyang-models/sonic-bgp-global.yang',
@ -261,7 +262,8 @@ setup(
'./cvlyang-models/sonic-port-qos-map.yang',
'./cvlyang-models/sonic-static-route.yang',
'./cvlyang-models/sonic-system-port.yang',
'./cvlyang-models/sonic-macsec.yang']),
'./cvlyang-models/sonic-macsec.yang',
'./cvlyang-models/sonic-bgp-sentinel.yang']),
],
zip_safe=False,
)

View File

@ -1618,6 +1618,23 @@
"src_address": "10.1.0.32"
}
},
"BGP_SENTINELS": {
"BGPSentinel": {
"ip_range": [
"10.1.0.0/24"
],
"name": "BGPSentinel",
"src_address": "10.1.0.32"
},
"BGPSentinelV6": {
"ip_range": [
"2603:10a0:321:82f9::/64",
"2603:10a1:30a:8000::/59"
],
"name": "BGPSentinelV6",
"src_address": "fc00:1::32"
}
},
"BGP_MONITORS": {
"5.6.7.8": {
"admin_status": "up",

View File

@ -197,6 +197,20 @@
"BGP_ALLOWED_PREFIXES_LIST_INVALID_PREFIXES_IPV6": {
"desc": "Invalid IPv6 prefix.",
"eStrKey" : "Pattern"
},
"BGP_SENTINEL_ALL_VALID_V4": {
"desc": "Configure BGP Sentinel table."
},
"BGP_SENTINEL_ALL_VALID_V6": {
"desc": "Configure BGP SentinelV6 table."
},
"BGP_SENTINEL_INVALID_RANGE_IPV4": {
"desc": "does not satisfy.",
"eStr": "Invalid value \"10.0.0.0/48\" in \"ip_range\" element."
},
"BGP_SENTINEL_INVALID_RANGE_IPV6": {
"desc": "does not satisfy.",
"eStr": "Invalid value \"fc00:f0::/129\" in \"ip_range\" element."
}
}

View File

@ -1591,6 +1591,62 @@
]
}
}
},
"BGP_SENTINEL_ALL_VALID_V4": {
"sonic-bgp-sentinel:sonic-bgp-sentinel": {
"sonic-bgp-sentinel:BGP_SENTINELS": {
"BGP_SENTINELS_LIST": [
{
"sentinel_name": "BGPSentinel",
"name": "BGPSentinel",
"src_address": "10.1.0.32",
"ip_range": ["10.1.0.0/24"]
}
]
}
}
},
"BGP_SENTINEL_ALL_VALID_V6": {
"sonic-bgp-sentinel:sonic-bgp-sentinel": {
"sonic-bgp-sentinel:BGP_SENTINELS": {
"BGP_SENTINELS_LIST": [
{
"sentinel_name": "BGPSentinelV6",
"name": "BGPSentinelV6",
"src_address": "fc00:1::32",
"ip_range": ["2603:10a0:321:82f9::/64", "2603:10a1:30a:8000::/59"]
}
]
}
}
},
"BGP_SENTINEL_INVALID_RANGE_IPV4": {
"sonic-bgp-sentinel:sonic-bgp-sentinel": {
"sonic-bgp-sentinel:BGP_SENTINELS": {
"BGP_SENTINELS_LIST": [
{
"sentinel_name": "BGPSentinel",
"name": "BGPSentinel",
"src_address": "10.1.0.32",
"ip_range": ["10.0.0.0/48", "10.1.0.0/24"]
}
]
}
}
},
"BGP_SENTINEL_INVALID_RANGE_IPV6": {
"sonic-bgp-sentinel:sonic-bgp-sentinel": {
"sonic-bgp-sentinel:BGP_SENTINELS": {
"BGP_SENTINELS_LIST": [
{
"sentinel_name": "BGPSentinelV6",
"name": "BGPSentinelV6",
"src_address": "fc00:1::32",
"ip_range": ["fc00:f0::/129", "fc00:a0::/64"]
}
]
}
}
}
}

View File

@ -0,0 +1,58 @@
module sonic-bgp-sentinel {
namespace "http://github.com/Azure/sonic-bgp-sentinel";
prefix pr;
yang-version 1.1;
import ietf-inet-types {
prefix inet;
}
import sonic-types {
prefix stypes;
}
organization
"SONiC";
contact
"SONiC";
description
"SONIC BGP Sentinel YANG";
revision 2023-06-06 {
description
"Initial revision.";
}
container sonic-bgp-sentinel {
container BGP_SENTINELS {
list BGP_SENTINELS_LIST {
key "sentinel_name";
leaf sentinel_name {
type string;
description "BGP Sentinel name";
}
leaf name {
type string;
must "(current() = current()/../sentinel_name)" {
error-message "Invalid BGP Sentinel name";
}
description "BGP Sentinel name";
}
leaf src_address {
type inet:ip-address;
description "Source address to use for connection";
}
leaf-list ip_range {
type stypes:sonic-ip-prefix;
description "A range of addresses";
}
}
}
}
}