Merge pull request #14537 from opensourcerouting/feature/bgpd_aod

bgpd: Implement EBGP-OAD peering type
This commit is contained in:
Russ White 2023-10-11 10:22:26 -04:00 committed by GitHub
commit 97d8e5cecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 202 additions and 8 deletions

View File

@ -1839,7 +1839,9 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args)
* UPDATE message SHALL be handled using the approach of "treat-as-
* withdraw".
*/
if (peer->sort == BGP_PEER_IBGP && length != 4) {
if ((peer->sort == BGP_PEER_IBGP ||
peer->sub_sort == BGP_PEER_EBGP_OAD) &&
length != 4) {
flog_err(EC_BGP_ATTR_LEN,
"LOCAL_PREF attribute length isn't 4 [%u]", length);
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@ -1849,7 +1851,7 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args)
/* If it is contained in an UPDATE message that is received from an
external peer, then this attribute MUST be ignored by the
receiving speaker. */
if (peer->sort == BGP_PEER_EBGP) {
if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD) {
STREAM_FORWARD_GETP(peer->curr, length);
return BGP_ATTR_PARSE_PROCEED;
}
@ -3267,7 +3269,8 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args)
* the default value of AIGP_SESSION SHOULD be "enabled".
*/
if (peer->sort == BGP_PEER_EBGP &&
!CHECK_FLAG(peer->flags, PEER_FLAG_AIGP)) {
(!CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) ||
peer->sub_sort != BGP_PEER_EBGP_OAD)) {
zlog_warn(
"%pBP received AIGP attribute, but eBGP peer do not support it",
peer);
@ -4484,7 +4487,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
}
/* Local preference. */
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) {
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED ||
peer->sub_sort == BGP_PEER_EBGP_OAD) {
stream_putc(s, BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_LOCAL_PREF);
stream_putc(s, 4);
@ -4850,6 +4854,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
/* AIGP */
if (bpi && attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP) &&
(CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) ||
peer->sub_sort == BGP_PEER_EBGP_OAD ||
peer->sort != BGP_PEER_EBGP)) {
/* At the moment only AIGP Metric TLV exists for AIGP
* attribute. If more comes in, do not forget to update

View File

@ -2299,8 +2299,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
/* Remove MED if its an EBGP peer - will get overwritten by route-maps
*/
if (peer->sort == BGP_PEER_EBGP
&& attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD &&
attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
if (from != bgp->peer_self && !transparent
&& !CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_MED_UNCHANGED))
@ -2514,8 +2514,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
return false;
if (bgp_in_graceful_shutdown(bgp)) {
if (peer->sort == BGP_PEER_IBGP
|| peer->sort == BGP_PEER_CONFED) {
if (peer->sort == BGP_PEER_IBGP ||
peer->sort == BGP_PEER_CONFED ||
peer->sub_sort == BGP_PEER_EBGP_OAD) {
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
attr->local_pref = BGP_GSHUT_LOCAL_PREF;
} else {

View File

@ -128,6 +128,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->bgp = src->bgp;
dst->sort = src->sort;
dst->sub_sort = src->sub_sort;
dst->as = src->as;
dst->v_routeadv = src->v_routeadv;
dst->flags = src->flags;

View File

@ -6900,6 +6900,28 @@ DEFPY(no_neighbor_role,
return ret;
}
DEFPY (neighbor_oad,
neighbor_oad_cmd,
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor oad",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Set peering session type to EBGP-OAD\n")
{
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, neighbor);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (no)
peer->sub_sort = 0;
else if (peer->sort == BGP_PEER_EBGP)
peer->sub_sort = BGP_PEER_EBGP_OAD;
return CMD_SUCCESS;
}
/* disable-connected-check */
DEFUN (neighbor_disable_connected_check,
neighbor_disable_connected_check_cmd,
@ -17894,6 +17916,9 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
? " strict-mode"
: "");
if (peer->sub_sort == BGP_PEER_EBGP_OAD)
vty_out(vty, " neighbor %s oad\n", addr);
/* ttl-security hops */
if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) {
if (!peer_group_active(peer)
@ -19461,6 +19486,9 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &neighbor_role_strict_cmd);
install_element(BGP_NODE, &no_neighbor_role_cmd);
/* "neighbor oad" commands. */
install_element(BGP_NODE, &neighbor_oad_cmd);
/* "neighbor aigp" commands. */
install_element(BGP_NODE, &neighbor_aigp_cmd);

View File

@ -3147,6 +3147,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer,
peer->as_type = group->conf->as_type;
peer->as = group->conf->as;
peer->sort = group->conf->sort;
peer->sub_sort = group->conf->sub_sort;
}
ptype = peer_sort(peer);

View File

@ -971,6 +971,14 @@ enum bgp_peer_sort {
BGP_PEER_CONFED,
};
/* BGP peering sub-types
* E.g.:
* EBGP-OAD - https://datatracker.ietf.org/doc/html/draft-uttaro-idr-bgp-oad
*/
enum bgp_peer_sub_sort {
BGP_PEER_EBGP_OAD = 1,
};
/* BGP message header and packet size. */
#define BGP_MARKER_SIZE 16
#define BGP_HEADER_SIZE 19
@ -1197,6 +1205,7 @@ struct peer {
as_t local_as;
enum bgp_peer_sort sort;
enum bgp_peer_sub_sort sub_sort;
/* Peer's Change local AS number. */
as_t change_local_as;

View File

@ -1432,6 +1432,23 @@ Defining Peers
peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN`
command the connection will be denied.
.. clicmd:: neighbor PEER oad
Mark a peer belonging to the One Administrative Domain.
Some networks span more than one autonomous system and require more
flexibility in the propagation of path attributes.It is worth noting that
these multi-AS networks have a common or single administrative entity.
These networks are said to belong to One Administrative Domain (OAD).
It is desirable to carry IBGP-only attributes across EBGP peerings when
the peers belong to an OAD.
Enabling this peering sub-type will allow the propagation of non-transitive
attributes across EBGP peerings (e.g. local-preference). Make sure to
turn this peering type on for all peers in the OAD.
Disabled by default.
.. clicmd:: bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group PGNAME
Accept connections from any peers in the specified prefix. Configuration

View File

View File

@ -0,0 +1,11 @@
!
int r1-eth0
ip address 192.168.1.1/24
!
router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
neighbor 192.168.1.2 timers 1 3
neighbor 192.168.1.2 timers connect 1
neighbor 192.168.1.2 oad
!

View File

@ -0,0 +1,18 @@
!
int r2-eth0
ip address 192.168.1.2/24
!
int r2-eth1
ip address 192.168.2.2/24
!
router bgp 65002
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external
neighbor 192.168.1.1 timers 1 3
neighbor 192.168.1.1 timers connect 1
neighbor 192.168.1.1 oad
neighbor 192.168.2.1 remote-as external
neighbor 192.168.2.1 timers 1 3
neighbor 192.168.2.1 timers connect 1
neighbor 192.168.2.1 oad
!

View File

@ -0,0 +1,22 @@
!
int lo
ip address 10.10.10.10/32
!
int r3-eth0
ip address 192.168.2.1/24
!
router bgp 65003
no bgp ebgp-requires-policy
neighbor 192.168.2.2 remote-as external
neighbor 192.168.2.2 timers 1 3
neighbor 192.168.2.2 timers connect 1
neighbor 192.168.2.2 oad
!
address-family ipv4 unicast
redistribute connected route-map connected
exit-address-family
!
route-map connected permit 10
set local-preference 123
set metric 123
!

View File

@ -0,0 +1,81 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
# Copyright (c) 2023 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
"""
Test if local-preference is passed between different EBGP peers when
EBGP-OAD is configured.
"""
import os
import re
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
from lib.common_config import step
pytestmark = [pytest.mark.bgpd]
def setup_module(mod):
topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3")}
tgen = Topogen(topodef, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for _, (rname, router) in enumerate(router_list.items(), 1):
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_dynamic_capability_role():
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 ipv4 unicast 10.10.10.10/32 json"))
expected = {
"paths": [
{
"aspath": {"string": "65002 65003"},
"metric": 123,
"locPrf": 123,
}
]
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(
_bgp_converge,
)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
assert result is None, "Can't converge"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))