Merge pull request #12861 from opensourcerouting/fix/bgp_confederation_with_astype

bgpd: Confederation fixes with remote-as external/internal
This commit is contained in:
Donald Sharp 2023-02-22 12:17:25 -05:00 committed by GitHub
commit 71286b6dcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 217 additions and 5 deletions

View File

@ -1645,6 +1645,14 @@ static enum bgp_attr_parse_ret bgp_attr_aspath_check(struct peer *const peer,
*/
struct aspath *aspath;
/* Refresh peer's type. If we set e.g.: AS_EXTERNAL/AS_INTERNAL,
* then peer->sort remains BGP_PEER_EBGP/IBGP, hence we need to
* have an actual type before checking.
* This is especially a case for BGP confederation peers, to avoid
* receiving and treating AS_PATH as malformed.
*/
(void)peer_sort(peer);
/* Confederation sanity check. */
if ((peer->sort == BGP_PEER_CONFED
&& !aspath_left_confed_check(attr->aspath))
@ -4295,8 +4303,22 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
aspath = aspath_delete_confed_seq(aspath);
if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
/* A confed member, so we need to do the
* AS_CONFED_SEQUENCE thing if it's outside a common
* administration.
* Configured confederation peers MUST be validated
* under BGP_PEER_CONFED, but if we have configured
* remote-as as AS_EXTERNAL, we need to check again
* if the peer belongs to us.
*/
if (bgp_confederation_peers_check(bgp, peer->as)) {
aspath = aspath_dup(attr->aspath);
aspath = aspath_add_confed_seq(aspath,
peer->local_as);
} else {
/* Stuff our path CONFED_ID on the front */
aspath = aspath_add_seq(aspath, bgp->confed_id);
}
} else {
if (peer->change_local_as) {
/* If replace-as is specified, we only use the

View File

@ -2041,9 +2041,9 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if,
/* If the peer is not part of our confederation, and its not an
iBGP peer then spoof the source AS */
if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)
&& !bgp_confederation_peers_check(bgp, *as)
&& bgp->as != *as)
if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION) &&
!bgp_confederation_peers_check(bgp, *as) && *as &&
bgp->as != *as)
local_as = bgp->confed_id;
else
local_as = bgp->as;

View File

@ -0,0 +1,12 @@
router bgp 65001
no bgp ebgp-requires-policy
bgp confederation identifier 65300
bgp confederation peers 65002 65003
neighbor fabric peer-group
neighbor fabric remote-as external
neighbor 192.168.1.2 peer-group fabric
neighbor 192.168.2.2 remote-as external
address-family ipv4 unicast
neighbor fabric activate
exit-address-family
!

View File

@ -0,0 +1,7 @@
!
int r1-eth0
ip address 192.168.1.1/24
!
int r1-eth1
ip address 192.168.2.1/24
!

View File

@ -0,0 +1,13 @@
router bgp 65002
no bgp ebgp-requires-policy
no bgp network import-check
bgp confederation identifier 65300
bgp confederation peers 65001
neighbor fabric peer-group
neighbor fabric remote-as external
neighbor 192.168.1.1 peer-group fabric
address-family ipv4 unicast
network 172.16.255.254/32
neighbor fabric activate
exit-address-family
!

View File

@ -0,0 +1,4 @@
!
int r2-eth0
ip address 192.168.1.2/24
!

View File

@ -0,0 +1,10 @@
router bgp 65003
no bgp ebgp-requires-policy
no bgp network import-check
bgp confederation identifier 65300
bgp confederation peers 65001
neighbor 192.168.2.1 remote-as external
address-family ipv4 unicast
network 172.16.255.254/32
exit-address-family
!

View File

@ -0,0 +1,4 @@
!
int r3-eth0
ip address 192.168.2.2/24
!

View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
# Copyright (c) 2022 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
"""
Test if BGP confederation works properly when using
remote-as internal/external.
Also, check if the same works with peer-groups as well.
"""
import os
import sys
import json
import pytest
import functools
pytestmark = pytest.mark.bgpd
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
pytestmark = [pytest.mark.bgpd]
def setup_module(mod):
topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r3")}
tgen = Topogen(topodef, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 1):
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
router.load_config(
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_confederation_astype():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
def _bgp_converge():
output = json.loads(r1.vtysh_cmd("show bgp summary json"))
expected = {
"ipv4Unicast": {
"peerCount": 2,
"peers": {
"192.168.1.2": {
"hostname": "r2",
"remoteAs": 65002,
"localAs": 65001,
"pfxRcd": 1,
"state": "Established",
},
"192.168.2.2": {
"hostname": "r3",
"remoteAs": 65003,
"localAs": 65001,
"pfxRcd": 1,
"state": "Established",
},
},
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Can't converge"
def _bgp_check_neighbors():
output = json.loads(r1.vtysh_cmd("show bgp neighbors json"))
expected = {
"192.168.1.2": {
"nbrCommonAdmin": True,
"nbrConfedExternalLink": True,
"hostname": "r2",
},
"192.168.2.2": {
"nbrCommonAdmin": True,
"nbrConfedExternalLink": True,
"hostname": "r3",
},
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_neighbors)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Can't see neighbors to be in BGP confederation"
def _bgp_check_routes():
output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json"))
expected = {
"routes": {
"172.16.255.254/32": [
{
"valid": True,
"pathFrom": "external",
"path": "(65003)",
},
{
"valid": True,
"pathFrom": "external",
"path": "(65002)",
},
]
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_routes)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Can't see routes to be in BGP confederation"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))