bgpd: Implement ACCEPT_OWN extended community

TL;DR: rfc7611.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
Donatas Abraitis 2022-09-18 22:18:13 +03:00
parent 984eb32b58
commit 46dbf9d0c0
25 changed files with 667 additions and 32 deletions

View File

@ -23,6 +23,7 @@
#include "lib/json.h" #include "lib/json.h"
#include "bgpd/bgp_route.h" #include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
/* Communities attribute. */ /* Communities attribute. */
struct community { struct community {
@ -109,4 +110,30 @@ extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
struct community *community); struct community *community);
extern void bgp_aggr_community_remove(void *arg); extern void bgp_aggr_community_remove(void *arg);
/* This implies that when propagating routes into a VRF, the ACCEPT_OWN
* community SHOULD NOT be propagated.
*/
static inline void community_strip_accept_own(struct attr *attr)
{
struct community *old_com = bgp_attr_get_community(attr);
struct community *new_com = NULL;
uint32_t val = COMMUNITY_ACCEPT_OWN;
if (old_com && community_include(old_com, val)) {
new_com = community_dup(old_com);
val = htonl(val);
community_del_val(new_com, &val);
if (!old_com->refcnt)
community_free(&old_com);
if (!new_com->size) {
community_free(&new_com);
bgp_attr_set_community(attr, NULL);
} else {
bgp_attr_set_community(attr, new_com);
}
}
}
#endif /* _QUAGGA_BGP_COMMUNITY_H */ #endif /* _QUAGGA_BGP_COMMUNITY_H */

View File

@ -42,6 +42,7 @@
#include "bgpd/bgp_packet.h" #include "bgpd/bgp_packet.h"
#include "bgpd/bgp_vty.h" #include "bgpd/bgp_vty.h"
#include "bgpd/bgp_vpn.h" #include "bgpd/bgp_vpn.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_zebra.h" #include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nexthop.h"
@ -996,6 +997,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (nexthop_self_flag) if (nexthop_self_flag)
bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF);
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN))
bgp_path_info_set_flag(bn, bpi, BGP_PATH_ACCEPT_OWN);
if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi,
source_bpi, bpi, bgp_orig, p, source_bpi, bpi, bgp_orig, p,
debug)) debug))
@ -1036,6 +1040,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (nexthop_self_flag) if (nexthop_self_flag)
bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF); bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF);
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN))
bgp_path_info_set_flag(bn, new, BGP_PATH_ACCEPT_OWN);
bgp_path_info_extra_get(new); bgp_path_info_extra_get(new);
/* /*
@ -1217,6 +1224,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
XFREE(MTYPE_ECOMMUNITY_STR, s); XFREE(MTYPE_ECOMMUNITY_STR, s);
} }
community_strip_accept_own(&static_attr);
/* Nexthop */ /* Nexthop */
/* if policy nexthop not set, use 0 */ /* if policy nexthop not set, use 0 */
if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags, if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags,
@ -1362,7 +1371,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
* because of loop checking. * because of loop checking.
*/ */
if (new_info) if (new_info)
vpn_leak_to_vrf_update(from_bgp, new_info); vpn_leak_to_vrf_update(from_bgp, new_info, NULL);
} }
void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
@ -1518,10 +1527,40 @@ void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
} }
} }
static bool static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi,
vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ struct prefix_rd *rd, afi_t afi)
struct bgp *from_bgp, /* from */ {
struct bgp_path_info *path_vpn) /* route */ struct listnode *node, *nnode;
struct bgp *bgp;
if (!rd)
return NULL;
/* If ACCEPT_OWN is not enabled for this path - return. */
if (!CHECK_FLAG(bpi->flags, BGP_PATH_ACCEPT_OWN))
return NULL;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue;
if (!CHECK_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_RD_SET))
continue;
/* Check if we have source VRF by RD value */
if (memcmp(&bgp->vpn_policy[afi].tovpn_rd.val, rd->val,
ECOMMUNITY_SIZE) == 0)
return bgp;
}
return NULL;
}
static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
struct bgp *from_bgp, /* from */
struct bgp_path_info *path_vpn,
struct prefix_rd *prd)
{ {
const struct prefix *p = bgp_dest_get_prefix(path_vpn->net); const struct prefix *p = bgp_dest_get_prefix(path_vpn->net);
afi_t afi = family2afi(p->family); afi_t afi = family2afi(p->family);
@ -1558,9 +1597,22 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
return false; return false;
} }
/* A route MUST NOT ever be accepted back into its source VRF, even if
* it carries one or more RTs that match that VRF.
*/
if (prd && memcmp(&prd->val, &to_bgp->vpn_policy[afi].tovpn_rd.val,
ECOMMUNITY_SIZE) == 0) {
if (debug)
zlog_debug(
"%s: skipping import, match RD (%pRD) of src VRF (%s) and the prefix (%pFX)",
__func__, prd, to_bgp->name_pretty, p);
return false;
}
if (debug) if (debug)
zlog_debug("%s: updating %pFX to vrf %s", __func__, p, zlog_debug("%s: updating RD %pRD, %pFX to vrf %s", __func__,
to_bgp->name_pretty); prd, p, to_bgp->name_pretty);
/* shallow copy */ /* shallow copy */
static_attr = *path_vpn->attr; static_attr = *path_vpn->attr;
@ -1585,6 +1637,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
ecommunity_free(&old_ecom); ecommunity_free(&old_ecom);
} }
community_strip_accept_own(&static_attr);
/* /*
* Nexthop: stash and clear * Nexthop: stash and clear
* *
@ -1711,9 +1765,16 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
/* /*
* For VRF-2-VRF route-leaking, * For VRF-2-VRF route-leaking,
* the source will be the originating VRF. * the source will be the originating VRF.
*
* If ACCEPT_OWN mechanism is enabled, then we SHOULD(?)
* get the source VRF (BGP) by looking at the RD.
*/ */
struct bgp *src_bgp = bgp_lookup_by_rd(path_vpn, prd, afi);
if (path_vpn->extra && path_vpn->extra->bgp_orig) if (path_vpn->extra && path_vpn->extra->bgp_orig)
src_vrf = path_vpn->extra->bgp_orig; src_vrf = path_vpn->extra->bgp_orig;
else if (src_bgp)
src_vrf = src_bgp;
else else
src_vrf = from_bgp; src_vrf = from_bgp;
@ -1723,8 +1784,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
return true; return true;
} }
bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */ bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
struct bgp_path_info *path_vpn) /* route */ struct bgp_path_info *path_vpn,
struct prefix_rd *prd)
{ {
struct listnode *mnode, *mnnode; struct listnode *mnode, *mnnode;
struct bgp *bgp; struct bgp *bgp;
@ -1741,7 +1803,7 @@ bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
if (!path_vpn->extra if (!path_vpn->extra
|| path_vpn->extra->bgp_orig != bgp) { /* no loop */ || path_vpn->extra->bgp_orig != bgp) { /* no loop */
leak_success |= vpn_leak_to_vrf_update_onevrf( leak_success |= vpn_leak_to_vrf_update_onevrf(
bgp, from_bgp, path_vpn); bgp, from_bgp, path_vpn, prd);
} }
} }
return leak_success; return leak_success;
@ -1897,7 +1959,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from,
continue; continue;
vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from, vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from,
bpi); bpi, NULL);
} }
} }
} }

View File

@ -72,7 +72,8 @@ extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
afi_t afi); afi_t afi);
extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp, extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
struct bgp_path_info *path_vpn); struct bgp_path_info *path_vpn,
struct prefix_rd *prd);
extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp,
struct bgp_path_info *path_vpn); struct bgp_path_info *path_vpn);

View File

@ -139,7 +139,8 @@ static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc,
*/ */
return (bgp_zebra_num_connects() == 0 || return (bgp_zebra_num_connects() == 0 ||
(bnc && (bnc->nexthop_num > 0 && (bnc && (bnc->nexthop_num > 0 &&
(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) ||
CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
bnc->bgp->srv6_enabled || bnc->bgp->srv6_enabled ||
bgp_isvalid_nexthop_for_ebgp(bnc, path) || bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
bgp_isvalid_nexthop_for_mplsovergre(bnc, path))))); bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))));

View File

@ -103,10 +103,6 @@ DEFINE_HOOK(bgp_rpki_prefix_status,
const struct prefix *prefix), const struct prefix *prefix),
(peer, attr, prefix)); (peer, attr, prefix));
/* Render dest to prefix_rd based on safi */
static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
safi_t safi);
/* Extern from bgp_dump.c */ /* Extern from bgp_dump.c */
extern const char *bgp_origin_str[]; extern const char *bgp_origin_str[];
extern const char *bgp_origin_long_str[]; extern const char *bgp_origin_long_str[];
@ -881,6 +877,49 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
return 0; return 0;
} }
/* If a BGP speaker supports ACCEPT_OWN and is configured for the
* extensions defined in this document, the following step is inserted
* after the LOCAL_PREF comparison step in the BGP decision process:
* When comparing a pair of routes for a BGP destination, the
* route with the ACCEPT_OWN community attached is preferred over
* the route that does not have the community.
* This extra step MUST only be invoked during the best path selection
* process of VPN-IP routes.
*/
if (safi == SAFI_MPLS_VPN &&
(CHECK_FLAG(new->peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN) ||
CHECK_FLAG(exist->peer->af_flags[afi][safi],
PEER_FLAG_ACCEPT_OWN))) {
bool new_accept_own = false;
bool exist_accept_own = false;
uint32_t accept_own = COMMUNITY_ACCEPT_OWN;
if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
new_accept_own = community_include(
bgp_attr_get_community(newattr), accept_own);
if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
exist_accept_own = community_include(
bgp_attr_get_community(existattr), accept_own);
if (new_accept_own && !exist_accept_own) {
*reason = bgp_path_selection_accept_own;
if (debug)
zlog_debug(
"%s: %s wins over %s due to accept-own",
pfx_buf, new_buf, exist_buf);
return 1;
}
if (!new_accept_own && exist_accept_own) {
*reason = bgp_path_selection_accept_own;
if (debug)
zlog_debug(
"%s: %s loses to %s due to accept-own",
pfx_buf, new_buf, exist_buf);
return 0;
}
}
/* 3. Local route check. We prefer: /* 3. Local route check. We prefer:
* - BGP_ROUTE_STATIC * - BGP_ROUTE_STATIC
* - BGP_ROUTE_AGGREGATE * - BGP_ROUTE_AGGREGATE
@ -3883,6 +3922,60 @@ static void bgp_attr_add_no_export_community(struct attr *attr)
bgp_attr_set_community(attr, new); bgp_attr_set_community(attr, new);
} }
static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi,
struct attr *attr, const struct prefix *prefix,
int *sub_type)
{
struct listnode *node, *nnode;
struct bgp *bgp;
bool accept_own_found = false;
if (safi != SAFI_MPLS_VPN)
return false;
/* Processing of the ACCEPT_OWN community is enabled by configuration */
if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN))
return false;
/* The route in question carries the ACCEPT_OWN community */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
struct community *comm = bgp_attr_get_community(attr);
if (community_include(comm, COMMUNITY_ACCEPT_OWN))
accept_own_found = true;
}
/* The route in question is targeted to one or more destination VRFs
* on the router (as determined by inspecting the Route Target(s)).
*/
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue;
if (accept_own_found &&
ecommunity_include(
bgp->vpn_policy[afi]
.rtlist[BGP_VPN_POLICY_DIR_TOVPN],
bgp_attr_get_ecommunity(attr))) {
if (bgp_debug_update(peer, prefix, NULL, 1))
zlog_debug(
"%pBP prefix %pFX has ORIGINATOR_ID, but it's accepted due to ACCEPT_OWN",
peer, prefix);
/* Treat this route as imported, because it's leaked
* already from another VRF, and we got an updated
* version from route-reflector with ACCEPT_OWN
* community.
*/
*sub_type = BGP_ROUTE_IMPORTED;
return true;
}
}
return false;
}
int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
struct attr *attr, afi_t afi, safi_t safi, int type, struct attr *attr, afi_t afi, safi_t safi, int type,
int sub_type, struct prefix_rd *prd, mpls_label_t *label, int sub_type, struct prefix_rd *prd, mpls_label_t *label,
@ -3996,12 +4089,20 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
} }
} }
/* Route reflector originator ID check. */ /* Route reflector originator ID check. If ACCEPT_OWN mechanism is
* enabled, then take care of that too.
*/
bool accept_own = false;
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID) if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)
&& IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) { && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) {
peer->stat_pfx_originator_loop++; accept_own =
reason = "originator is us;"; bgp_accept_own(peer, afi, safi, attr, p, &sub_type);
goto filtered; if (!accept_own) {
peer->stat_pfx_originator_loop++;
reason = "originator is us;";
goto filtered;
}
} }
/* Route reflector cluster ID check. */ /* Route reflector cluster ID check. */
@ -4499,8 +4600,13 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
bgp_path_info_unset_flag(dest, pi, bgp_path_info_unset_flag(dest, pi,
BGP_PATH_VALID); BGP_PATH_VALID);
} }
} else } else {
if (accept_own)
bgp_path_info_set_flag(dest, pi,
BGP_PATH_ACCEPT_OWN);
bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
}
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
if (safi == SAFI_MPLS_VPN) { if (safi == SAFI_MPLS_VPN) {
@ -4550,8 +4656,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
} }
if ((SAFI_MPLS_VPN == safi) if ((SAFI_MPLS_VPN == safi)
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
leak_success = vpn_leak_to_vrf_update(bgp, pi, prd);
leak_success = vpn_leak_to_vrf_update(bgp, pi);
} }
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
@ -4659,8 +4764,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
} }
bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID);
} }
} else } else {
if (accept_own)
bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN);
bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
}
/* Addpath ID */ /* Addpath ID */
new->addpath_rx_id = addpath_id; new->addpath_rx_id = addpath_id;
@ -4706,7 +4815,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
} }
if ((SAFI_MPLS_VPN == safi) if ((SAFI_MPLS_VPN == safi)
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
leak_success = vpn_leak_to_vrf_update(bgp, new); leak_success = vpn_leak_to_vrf_update(bgp, new, prd);
} }
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
if (SAFI_MPLS_VPN == safi) { if (SAFI_MPLS_VPN == safi) {
@ -6427,7 +6536,8 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
if (SAFI_MPLS_VPN == safi if (SAFI_MPLS_VPN == safi
&& bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
vpn_leak_to_vrf_update(bgp, pi); vpn_leak_to_vrf_update(bgp, pi,
&bgp_static->prd);
} }
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd, rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd,
@ -6467,7 +6577,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
if (SAFI_MPLS_VPN == safi if (SAFI_MPLS_VPN == safi
&& bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
vpn_leak_to_vrf_update(bgp, new); vpn_leak_to_vrf_update(bgp, new, &bgp_static->prd);
} }
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi, rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi,
@ -8826,6 +8936,8 @@ const char *bgp_path_selection_reason2str(enum bgp_path_selection_reason reason)
return "Weight"; return "Weight";
case bgp_path_selection_local_pref: case bgp_path_selection_local_pref:
return "Local Pref"; return "Local Pref";
case bgp_path_selection_accept_own:
return "Accept Own";
case bgp_path_selection_local_route: case bgp_path_selection_local_route:
return "Local Route"; return "Local Route";
case bgp_path_selection_confed_as_path: case bgp_path_selection_confed_as_path:
@ -11902,8 +12014,8 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd,
/* /*
* Return rd based on safi * Return rd based on safi
*/ */
static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
safi_t safi) safi_t safi)
{ {
switch (safi) { switch (safi) {
case SAFI_MPLS_VPN: case SAFI_MPLS_VPN:
@ -11912,7 +12024,6 @@ static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
return (struct prefix_rd *)(bgp_dest_get_prefix(dest)); return (struct prefix_rd *)(bgp_dest_get_prefix(dest));
default: default:
return NULL; return NULL;
} }
} }

View File

@ -289,7 +289,7 @@ struct bgp_path_info {
int lock; int lock;
/* BGP information status. */ /* BGP information status. */
uint16_t flags; uint32_t flags;
#define BGP_PATH_IGP_CHANGED (1 << 0) #define BGP_PATH_IGP_CHANGED (1 << 0)
#define BGP_PATH_DAMPED (1 << 1) #define BGP_PATH_DAMPED (1 << 1)
#define BGP_PATH_HISTORY (1 << 2) #define BGP_PATH_HISTORY (1 << 2)
@ -306,6 +306,7 @@ struct bgp_path_info {
#define BGP_PATH_RIB_ATTR_CHG (1 << 13) #define BGP_PATH_RIB_ATTR_CHG (1 << 13)
#define BGP_PATH_ANNC_NH_SELF (1 << 14) #define BGP_PATH_ANNC_NH_SELF (1 << 14)
#define BGP_PATH_LINK_BW_CHG (1 << 15) #define BGP_PATH_LINK_BW_CHG (1 << 15)
#define BGP_PATH_ACCEPT_OWN (1 << 16)
/* BGP route type. This can be static, RIP, OSPF, BGP etc. */ /* BGP route type. This can be static, RIP, OSPF, BGP etc. */
uint8_t type; uint8_t type;
@ -852,4 +853,6 @@ extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr);
const char * const char *
bgp_path_selection_reason2str(enum bgp_path_selection_reason reason); bgp_path_selection_reason2str(enum bgp_path_selection_reason reason);
extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi); extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi);
extern const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
safi_t safi);
#endif /* _QUAGGA_BGP_ROUTE_H */ #endif /* _QUAGGA_BGP_ROUTE_H */

View File

@ -63,6 +63,7 @@ enum bgp_path_selection_reason {
bgp_path_selection_evpn_lower_ip, bgp_path_selection_evpn_lower_ip,
bgp_path_selection_weight, bgp_path_selection_weight,
bgp_path_selection_local_pref, bgp_path_selection_local_pref,
bgp_path_selection_accept_own,
bgp_path_selection_local_route, bgp_path_selection_local_route,
bgp_path_selection_confed_as_path, bgp_path_selection_confed_as_path,
bgp_path_selection_as_path, bgp_path_selection_as_path,

View File

@ -8327,6 +8327,32 @@ ALIAS_HIDDEN(
"Only give warning message when limit is exceeded\n" "Only give warning message when limit is exceeded\n"
"Force checking all received routes not only accepted\n") "Force checking all received routes not only accepted\n")
/* "neighbor accept-own" */
DEFPY (neighbor_accept_own,
neighbor_accept_own_cmd,
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor accept-own",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Enable handling of self-originated VPN routes containing ACCEPT_OWN community\n")
{
struct peer *peer;
afi_t afi = bgp_node_afi(vty);
safi_t safi = bgp_node_safi(vty);
int ret;
peer = peer_and_group_lookup_vty(vty, neighbor);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (no)
ret = peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ACCEPT_OWN);
else
ret = peer_af_flag_set(peer, afi, safi, PEER_FLAG_ACCEPT_OWN);
return bgp_vty_return(vty, ret);
}
/* "neighbor soo" */ /* "neighbor soo" */
DEFPY (neighbor_soo, DEFPY (neighbor_soo,
neighbor_soo_cmd, neighbor_soo_cmd,
@ -17362,6 +17388,10 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
} }
} }
/* accept-own */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ACCEPT_OWN))
vty_out(vty, " neighbor %s accept-own\n", addr);
/* soo */ /* soo */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) { if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) {
char *soo_str = ecommunity_ecom2str( char *soo_str = ecommunity_ecom2str(
@ -19571,6 +19601,10 @@ void bgp_vty_init(void)
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd); install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
/* neighbor accept-own */
install_element(BGP_VPNV4_NODE, &neighbor_accept_own_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_accept_own_cmd);
/* "neighbor soo" */ /* "neighbor soo" */
install_element(BGP_IPV4_NODE, &neighbor_soo_cmd); install_element(BGP_IPV4_NODE, &neighbor_soo_cmd);
install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd); install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd);

View File

@ -4313,6 +4313,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_WEIGHT, 0, peer_change_reset_in}, {PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset}, {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
{PEER_FLAG_SOO, 0, peer_change_reset}, {PEER_FLAG_SOO, 0, peer_change_reset},
{PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset},
{0, 0, 0}}; {0, 0, 0}};
/* Proper action set. */ /* Proper action set. */

View File

@ -1462,6 +1462,7 @@ struct peer {
#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29) #define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29)
#define PEER_FLAG_SOO (1ULL << 30) #define PEER_FLAG_SOO (1ULL << 30)
#define PEER_FLAG_ORR_GROUP (1ULL << 31) /* Optimal-Route-Reflection */ #define PEER_FLAG_ORR_GROUP (1ULL << 31) /* Optimal-Route-Reflection */
#define PEER_FLAG_ACCEPT_OWN (1ULL << 32)
/* BGP Optimal Route Reflection Group name */ /* BGP Optimal Route Reflection Group name */
char *orr_group_name[AFI_MAX][SAFI_MAX]; char *orr_group_name[AFI_MAX][SAFI_MAX];

View File

@ -1670,6 +1670,23 @@ Configuring Peers
turning on this command will allow BGP to install v4 routes with turning on this command will allow BGP to install v4 routes with
v6 nexthops if you do not have v4 configured on interfaces. v6 nexthops if you do not have v4 configured on interfaces.
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
Enable handling of self-originated VPN routes containing ``accept-own`` community.
This feature allows you to handle self-originated VPN routes, which a BGP speaker
receives from a route-reflector. A 'self-originated' route is one that was
originally advertised by the speaker itself. As per :rfc:`4271`, a BGP speaker rejects
advertisements that originated the speaker itself. However, the BGP ACCEPT_OWN
mechanism enables a router to accept the prefixes it has advertised, when reflected
from a route-reflector that modifies certain attributes of the prefix.
A special community called ``accept-own`` is attached to the prefix by the
route-reflector, which is a signal to the receiving router to bypass the ORIGINATOR_ID
and NEXTHOP/MP_REACH_NLRI check.
Default: disabled.
.. clicmd:: bgp fast-external-failover .. clicmd:: bgp fast-external-failover
This command causes bgp to take down ebgp peers immediately This command causes bgp to take down ebgp peers immediately

View File

@ -0,0 +1,12 @@
!
debug bgp updates
!
router bgp 65010
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
address-family ipv4 unicast
redistribute connected
exit-address-family
!

View File

@ -0,0 +1,9 @@
!
interface lo
ip address 172.16.255.1/32
!
interface ce1-eth0
ip address 192.168.1.1/24
!
ip forwarding
!

View File

@ -0,0 +1,12 @@
!
debug bgp updates
!
router bgp 65020
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
address-family ipv4 unicast
redistribute connected
exit-address-family
!

View File

@ -0,0 +1,9 @@
!
interface lo
ip address 172.16.255.2/32
!
interface ce2-eth0
ip address 192.168.2.1/24
!
ip forwarding
!

View File

@ -0,0 +1,49 @@
!
debug bgp updates
debug bgp vpn leak-from-vrf
debug bgp vpn leak-to-vrf
debug bgp nht
!
router bgp 65001
bgp router-id 10.10.10.10
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
neighbor 10.10.10.101 remote-as internal
neighbor 10.10.10.101 update-source 10.10.10.10
neighbor 10.10.10.101 timers 1 3
neighbor 10.10.10.101 timers connect 1
address-family ipv4 vpn
neighbor 10.10.10.101 activate
neighbor 10.10.10.101 attribute-unchanged
exit-address-family
!
router bgp 65001 vrf Customer
bgp router-id 192.168.1.2
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
address-family ipv4 unicast
label vpn export 10
rd vpn export 192.168.1.2:2
rt vpn import 192.168.1.2:2
rt vpn export 192.168.1.2:2
export vpn
import vpn
exit-address-family
!
router bgp 65001 vrf Service
bgp router-id 192.168.2.2
no bgp ebgp-requires-policy
neighbor 192.168.2.1 remote-as external
neighbor 192.168.2.1 timers 1 3
neighbor 192.168.2.1 timers connect 1
address-family ipv4 unicast
label vpn export 20
rd vpn export 192.168.2.2:2
rt vpn import 192.168.2.2:2
rt vpn export 192.168.2.2:2
export vpn
import vpn
exit-address-family
!

View File

@ -0,0 +1,12 @@
mpls ldp
router-id 10.10.10.10
!
address-family ipv4
discovery transport-address 10.10.10.10
!
interface pe1-eth2
exit
!
exit-address-family
!
exit

View File

@ -0,0 +1,7 @@
interface pe1-eth2
ip ospf dead-interval 4
ip ospf hello-interval 1
!
router ospf
router-id 10.10.10.10
network 0.0.0.0/0 area 0

View File

@ -0,0 +1,15 @@
!
interface lo
ip address 10.10.10.10/32
!
interface pe1-eth0 vrf Customer
ip address 192.168.1.2/24
!
interface pe1-eth1 vrf Service
ip address 192.168.2.2/24
!
interface pe1-eth2
ip address 10.0.1.1/24
!
ip forwarding
!

View File

@ -0,0 +1,25 @@
!
debug bgp updates
!
router bgp 65001
bgp router-id 10.10.10.101
bgp route-reflector allow-outbound-policy
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
neighbor 10.10.10.10 remote-as internal
neighbor 10.10.10.10 update-source 10.10.10.101
neighbor 10.10.10.10 timers 1 3
neighbor 10.10.10.10 timers connect 1
address-family ipv4 vpn
neighbor 10.10.10.10 activate
neighbor 10.10.10.10 route-reflector-client
neighbor 10.10.10.10 route-map pe1 out
exit-address-family
!
route-map pe1 permit 10
set extcommunity rt 192.168.1.2:2 192.168.2.2:2
set community 65001:111 accept-own additive
set ip next-hop unchanged
route-map pe1 permit 20
exit
!

View File

@ -0,0 +1,12 @@
mpls ldp
router-id 10.10.10.101
!
address-family ipv4
discovery transport-address 10.10.10.101
!
interface rr1-eth0
exit
!
exit-address-family
!
exit

View File

@ -0,0 +1,7 @@
interface rr1-eth0
ip ospf dead-interval 4
ip ospf hello-interval 1
!
router ospf
router-id 10.10.10.101
network 0.0.0.0/0 area 0

View File

@ -0,0 +1,9 @@
!
interface lo
ip address 10.10.10.101/32
!
interface rr1-eth0
ip address 10.0.1.2/24
!
ip forwarding
!

View File

@ -0,0 +1,198 @@
#!/usr/bin/env python
#
# Copyright (c) 2022 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
#
"""
"""
import os
import sys
import json
import pytest
import functools
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 build_topo(tgen):
tgen.add_router("ce1")
tgen.add_router("ce2")
tgen.add_router("pe1")
tgen.add_router("rr1")
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["ce1"])
switch.add_link(tgen.gears["pe1"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["ce2"])
switch.add_link(tgen.gears["pe1"])
switch = tgen.add_switch("s3")
switch.add_link(tgen.gears["pe1"])
switch.add_link(tgen.gears["rr1"])
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
pe1 = tgen.gears["pe1"]
rr1 = tgen.gears["rr1"]
pe1.run("ip link add Customer type vrf table 1001")
pe1.run("ip link set up dev Customer")
pe1.run("ip link set pe1-eth0 master Customer")
pe1.run("ip link add Service type vrf table 1002")
pe1.run("ip link set up dev Service")
pe1.run("ip link set pe1-eth1 master Service")
pe1.run("sysctl -w net.mpls.conf.pe1-eth2.input=1")
rr1.run("sysctl -w net.mpls.conf.rr1-eth0.input=1")
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))
)
router.load_config(
TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
)
router.load_config(
TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_accept_own():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
pe1 = tgen.gears["pe1"]
ce2 = tgen.gears["ce2"]
step("Check if routes are not installed in PE1 from RR1 (due to ORIGINATOR_ID)")
def _bgp_check_received_routes_due_originator_id():
output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
expected = {"peers": {"10.10.10.101": {"pfxRcd": 0, "pfxSnt": 4}}}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_received_routes_due_originator_id)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "Failed, received routes from RR1 regardless ORIGINATOR_ID"
step("Enable ACCEPT_OWN for RR1")
pe1.vtysh_cmd(
"""
configure terminal
router bgp 65001
address-family ipv4 vpn
neighbor 10.10.10.101 accept-own
"""
)
step("Check if we received routes due to ACCEPT_OWN from RR1")
def _bgp_check_received_routes_with_modified_rts():
output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
expected = {"peers": {"10.10.10.101": {"pfxRcd": 4, "pfxSnt": 4}}}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_received_routes_with_modified_rts)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert (
result is None
), "Failed, didn't receive routes from RR1 with ACCEPT_OWN enabled"
step(
"Check if 172.16.255.1/32 is imported into vrf Service due to modified RT list at RR1"
)
def _bgp_check_received_routes_with_changed_rts():
output = json.loads(
pe1.vtysh_cmd("show bgp vrf Service ipv4 unicast 172.16.255.1/32 json")
)
expected = {
"paths": [
{
"community": {
"string": "65001:111"
},
"extendedCommunity": {
"string": "RT:192.168.1.2:2 RT:192.168.2.2:2"
},
}
]
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_received_routes_with_changed_rts)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert (
result is None
), "Failed, routes are not imported from RR1 with modified RT list"
step("Check if 172.16.255.1/32 is announced to CE2")
def _bgp_check_received_routes_from_pe():
output = json.loads(ce2.vtysh_cmd("show ip route 172.16.255.1/32 json"))
expected = {
"172.16.255.1/32": [
{
"protocol": "bgp",
"installed": True,
"nexthops": [{"ip": "192.168.2.2"}],
}
]
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_received_routes_from_pe)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert (
result is None
), "Failed, didn't receive 172.16.255.1/32 from PE1"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))