summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2022-09-18 21:18:13 +0200
committerDonatas Abraitis <donatas@opensourcerouting.org>2022-10-12 16:48:43 +0200
commit46dbf9d0c0b99f60767793ef3b688f95175edc6e (patch)
treecaa2b86397dc6c9dec7969e9ea3a91119e211293
parentMerge pull request #11159 from maduri111/bgpd-orr (diff)
downloadfrr-46dbf9d0c0b99f60767793ef3b688f95175edc6e.tar.xz
frr-46dbf9d0c0b99f60767793ef3b688f95175edc6e.zip
bgpd: Implement ACCEPT_OWN extended community
TL;DR: rfc7611. Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
-rw-r--r--bgpd/bgp_community.h27
-rw-r--r--bgpd/bgp_mplsvpn.c84
-rw-r--r--bgpd/bgp_mplsvpn.h3
-rw-r--r--bgpd/bgp_nht.c3
-rw-r--r--bgpd/bgp_route.c147
-rw-r--r--bgpd/bgp_route.h5
-rw-r--r--bgpd/bgp_table.h1
-rw-r--r--bgpd/bgp_vty.c34
-rw-r--r--bgpd/bgpd.c1
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--doc/user/bgp.rst17
-rw-r--r--tests/topotests/bgp_accept_own/__init__.py0
-rw-r--r--tests/topotests/bgp_accept_own/ce1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/ce1/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/ce2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/ce2/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/pe1/bgpd.conf49
-rw-r--r--tests/topotests/bgp_accept_own/pe1/ldpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/pe1/ospfd.conf7
-rw-r--r--tests/topotests/bgp_accept_own/pe1/zebra.conf15
-rw-r--r--tests/topotests/bgp_accept_own/rr1/bgpd.conf25
-rw-r--r--tests/topotests/bgp_accept_own/rr1/ldpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/rr1/ospfd.conf7
-rw-r--r--tests/topotests/bgp_accept_own/rr1/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/test_bgp_accept_own.py198
25 files changed, 667 insertions, 32 deletions
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index 616ddb440..05a5d4486 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -23,6 +23,7 @@
#include "lib/json.h"
#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
/* Communities attribute. */
struct community {
@@ -109,4 +110,30 @@ extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
struct community *community);
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 */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 08748d2eb..66eef1aa5 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -42,6 +42,7 @@
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_vpn.h"
+#include "bgpd/bgp_community.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_nexthop.h"
@@ -996,6 +997,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (nexthop_self_flag)
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,
source_bpi, bpi, bgp_orig, p,
debug))
@@ -1036,6 +1040,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (nexthop_self_flag)
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);
/*
@@ -1217,6 +1224,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
+ community_strip_accept_own(&static_attr);
+
/* Nexthop */
/* if policy nexthop not set, use 0 */
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.
*/
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 */
@@ -1518,10 +1527,40 @@ void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
}
}
-static bool
-vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
- struct bgp *from_bgp, /* from */
- struct bgp_path_info *path_vpn) /* route */
+static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi,
+ struct prefix_rd *rd, afi_t afi)
+{
+ 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);
afi_t afi = family2afi(p->family);
@@ -1558,9 +1597,22 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
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)
- zlog_debug("%s: updating %pFX to vrf %s", __func__, p,
- to_bgp->name_pretty);
+ zlog_debug("%s: updating RD %pRD, %pFX to vrf %s", __func__,
+ prd, p, to_bgp->name_pretty);
/* shallow copy */
static_attr = *path_vpn->attr;
@@ -1585,6 +1637,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
ecommunity_free(&old_ecom);
}
+ community_strip_accept_own(&static_attr);
+
/*
* 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,
* 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)
src_vrf = path_vpn->extra->bgp_orig;
+ else if (src_bgp)
+ src_vrf = src_bgp;
else
src_vrf = from_bgp;
@@ -1723,8 +1784,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
return true;
}
-bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
- struct bgp_path_info *path_vpn) /* route */
+bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
+ struct bgp_path_info *path_vpn,
+ struct prefix_rd *prd)
{
struct listnode *mnode, *mnnode;
struct bgp *bgp;
@@ -1741,7 +1803,7 @@ bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
if (!path_vpn->extra
|| path_vpn->extra->bgp_orig != bgp) { /* no loop */
leak_success |= vpn_leak_to_vrf_update_onevrf(
- bgp, from_bgp, path_vpn);
+ bgp, from_bgp, path_vpn, prd);
}
}
return leak_success;
@@ -1897,7 +1959,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from,
continue;
vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from,
- bpi);
+ bpi, NULL);
}
}
}
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 46607a38c..9af4cdf3a 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -72,7 +72,8 @@ extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
afi_t afi);
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,
struct bgp_path_info *path_vpn);
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 7274bcdb2..7eeab373a 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -139,7 +139,8 @@ static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc,
*/
return (bgp_zebra_num_connects() == 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 ||
bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))));
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 0b411d0ec..f6b6cb93d 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -103,10 +103,6 @@ DEFINE_HOOK(bgp_rpki_prefix_status,
const struct prefix *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 const char *bgp_origin_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;
}
+ /* 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:
* - BGP_ROUTE_STATIC
* - BGP_ROUTE_AGGREGATE
@@ -3883,6 +3922,60 @@ static void bgp_attr_add_no_export_community(struct attr *attr)
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,
struct attr *attr, afi_t afi, safi_t safi, int type,
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)
&& IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) {
- peer->stat_pfx_originator_loop++;
- reason = "originator is us;";
- goto filtered;
+ accept_own =
+ bgp_accept_own(peer, afi, safi, attr, p, &sub_type);
+ if (!accept_own) {
+ peer->stat_pfx_originator_loop++;
+ reason = "originator is us;";
+ goto filtered;
+ }
}
/* 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_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);
+ }
#ifdef ENABLE_BGP_VNC
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)
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
-
- leak_success = vpn_leak_to_vrf_update(bgp, pi);
+ leak_success = vpn_leak_to_vrf_update(bgp, pi, prd);
}
#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);
}
- } 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);
+ }
/* 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)
&& (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
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
&& 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
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
&& 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
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";
case bgp_path_selection_local_pref:
return "Local Pref";
+ case bgp_path_selection_accept_own:
+ return "Accept Own";
case bgp_path_selection_local_route:
return "Local Route";
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
*/
-static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
- safi_t safi)
+const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
+ safi_t safi)
{
switch (safi) {
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));
default:
return NULL;
-
}
}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index ddef4ca1b..22d28ecd0 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -289,7 +289,7 @@ struct bgp_path_info {
int lock;
/* BGP information status. */
- uint16_t flags;
+ uint32_t flags;
#define BGP_PATH_IGP_CHANGED (1 << 0)
#define BGP_PATH_DAMPED (1 << 1)
#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_ANNC_NH_SELF (1 << 14)
#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. */
uint8_t type;
@@ -852,4 +853,6 @@ extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr);
const char *
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 const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
+ safi_t safi);
#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 86cd4f3da..91d92ebd6 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -63,6 +63,7 @@ enum bgp_path_selection_reason {
bgp_path_selection_evpn_lower_ip,
bgp_path_selection_weight,
bgp_path_selection_local_pref,
+ bgp_path_selection_accept_own,
bgp_path_selection_local_route,
bgp_path_selection_confed_as_path,
bgp_path_selection_as_path,
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 10396b8b4..b85f3707b 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -8327,6 +8327,32 @@ ALIAS_HIDDEN(
"Only give warning message when limit is exceeded\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" */
DEFPY (neighbor_soo,
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 */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) {
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, &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" */
install_element(BGP_IPV4_NODE, &neighbor_soo_cmd);
install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 188402d0b..40e6c90df 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -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_DISABLE_ADDPATH_RX, 0, peer_change_reset},
{PEER_FLAG_SOO, 0, peer_change_reset},
+ {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset},
{0, 0, 0}};
/* Proper action set. */
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index b9d24969e..44e225b04 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1462,6 +1462,7 @@ struct peer {
#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29)
#define PEER_FLAG_SOO (1ULL << 30)
#define PEER_FLAG_ORR_GROUP (1ULL << 31) /* Optimal-Route-Reflection */
+#define PEER_FLAG_ACCEPT_OWN (1ULL << 32)
/* BGP Optimal Route Reflection Group name */
char *orr_group_name[AFI_MAX][SAFI_MAX];
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index cd7a0adff..8a16c57e6 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1670,6 +1670,23 @@ Configuring Peers
turning on this command will allow BGP to install v4 routes with
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
This command causes bgp to take down ebgp peers immediately
diff --git a/tests/topotests/bgp_accept_own/__init__.py b/tests/topotests/bgp_accept_own/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/__init__.py
diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf
new file mode 100644
index 000000000..fa53a4291
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/ce1/zebra.conf b/tests/topotests/bgp_accept_own/ce1/zebra.conf
new file mode 100644
index 000000000..7863ae165
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce1/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf
new file mode 100644
index 000000000..cdf8898c9
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/ce2/zebra.conf b/tests/topotests/bgp_accept_own/ce2/zebra.conf
new file mode 100644
index 000000000..829967e36
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce2/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf
new file mode 100644
index 000000000..863129373
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/pe1/ldpd.conf b/tests/topotests/bgp_accept_own/pe1/ldpd.conf
new file mode 100644
index 000000000..7c1ea33a3
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/ldpd.conf
@@ -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
diff --git a/tests/topotests/bgp_accept_own/pe1/ospfd.conf b/tests/topotests/bgp_accept_own/pe1/ospfd.conf
new file mode 100644
index 000000000..1a5e1a06b
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/ospfd.conf
@@ -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
diff --git a/tests/topotests/bgp_accept_own/pe1/zebra.conf b/tests/topotests/bgp_accept_own/pe1/zebra.conf
new file mode 100644
index 000000000..71476d2ae
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf
new file mode 100644
index 000000000..4f0a6ab0f
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/rr1/ldpd.conf b/tests/topotests/bgp_accept_own/rr1/ldpd.conf
new file mode 100644
index 000000000..036990186
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/ldpd.conf
@@ -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
diff --git a/tests/topotests/bgp_accept_own/rr1/ospfd.conf b/tests/topotests/bgp_accept_own/rr1/ospfd.conf
new file mode 100644
index 000000000..b598246ef
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/ospfd.conf
@@ -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
diff --git a/tests/topotests/bgp_accept_own/rr1/zebra.conf b/tests/topotests/bgp_accept_own/rr1/zebra.conf
new file mode 100644
index 000000000..aa3f633bf
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_accept_own/test_bgp_accept_own.py b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py
new file mode 100644
index 000000000..161530b48
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py
@@ -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))