]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: Implement ACCEPT_OWN extended community
authorDonatas Abraitis <donatas@opensourcerouting.org>
Sun, 18 Sep 2022 19:18:13 +0000 (22:18 +0300)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Wed, 12 Oct 2022 14:48:43 +0000 (17:48 +0300)
TL;DR: rfc7611.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
25 files changed:
bgpd/bgp_community.h
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_nht.c
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_table.h
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
doc/user/bgp.rst
tests/topotests/bgp_accept_own/__init__.py [new file with mode: 0644]
tests/topotests/bgp_accept_own/ce1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/ce1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/ce2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/ce2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/pe1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/pe1/ldpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/pe1/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/pe1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/rr1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/rr1/ldpd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/rr1/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/rr1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_accept_own/test_bgp_accept_own.py [new file with mode: 0644]

index 616ddb4405b371228e4797558313e1c7b4a505c4..05a5d4486a0a95c74fce9cbde0466006270e5dc2 100644 (file)
@@ -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 */
index 08748d2ebac027b2c47d76f48af279f7b8b44f9a..66eef1aa522ea095b128b0c4d6295232a88e9303 100644 (file)
@@ -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);
                        }
                }
        }
index 46607a38c48d7ba4cd7e6a514ef6aa3cbc16a6b4..9af4cdf3a258a58c2636a937f96d51278c0dd59d 100644 (file)
@@ -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);
index 7274bcdb21db07c183730e0df397188d7c3f9c7d..7eeab373a01773cd6a43fe2ecef719fc3ed54e99 100644 (file)
@@ -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)))));
index 0b411d0ec1c452d5d8b7658cf92b059f8774bff9..f6b6cb93dbc9df91dfc252d796c361e69bc2c107 100644 (file)
@@ -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;
-
        }
 }
 
index ddef4ca1bbfabbcb3e0d4460c4726185720219f7..22d28ecd0080a546690bb5b630fda5ac1d5a8722 100644 (file)
@@ -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 */
index 86cd4f3da105add288fe36d4fe8dd0feeb8ead52..91d92ebd6d4acfffad7cb51e0f0f89208883debd 100644 (file)
@@ -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,
index 10396b8b4fe31dce22758c242ab8c84daceb3354..b85f3707b64d8839812fea481177587f6a0694fe 100644 (file)
@@ -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);
index 188402d0b4f9f7fde3519df17d0673aeb998ab6f..40e6c90dfcec23e379445582d211869a181d50c7 100644 (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_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. */
index b9d24969e3471d31cb03d75daf3540c3c4ef497b..44e225b043e18b88c399328482269e482a4ff82a 100644 (file)
@@ -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];
index cd7a0adff93bd83c26aec5e7691f7efe18f5b72a..8a16c57e6cdfc70801bfcafa5ba216d83256b632 100644 (file)
@@ -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 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf
new file mode 100644 (file)
index 0000000..fa53a42
--- /dev/null
@@ -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 (file)
index 0000000..7863ae1
--- /dev/null
@@ -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 (file)
index 0000000..cdf8898
--- /dev/null
@@ -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 (file)
index 0000000..829967e
--- /dev/null
@@ -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 (file)
index 0000000..8631293
--- /dev/null
@@ -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 (file)
index 0000000..7c1ea33
--- /dev/null
@@ -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 (file)
index 0000000..1a5e1a0
--- /dev/null
@@ -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 (file)
index 0000000..71476d2
--- /dev/null
@@ -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 (file)
index 0000000..4f0a6ab
--- /dev/null
@@ -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 (file)
index 0000000..0369901
--- /dev/null
@@ -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 (file)
index 0000000..b598246
--- /dev/null
@@ -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 (file)
index 0000000..aa3f633
--- /dev/null
@@ -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 (file)
index 0000000..161530b
--- /dev/null
@@ -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))