]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: cli for SRv6 SID alloc to redirect to vrf (step4)
authorHiroki Shirokura <slank.dev@gmail.com>
Sat, 19 Dec 2020 02:04:40 +0000 (11:04 +0900)
committerMark Stapp <mjs@voltanet.io>
Wed, 2 Jun 2021 14:24:48 +0000 (10:24 -0400)
This commit add cil to configure BGP SRv6-VPN sid allocation.
Almost mechanism are based on BGP MPLS-VPN.

User can allocate and export sid with using following config.
Then bgpd try to allocate new SID to redirect vpn to vrf using
SRv6 localsid End.DT4/DT6. Currently linux kernel will regect
End.DT4 route install due to no-implementation.
(at-least today's FRR's ci kernel.)

So now we only supports BGP SRv6-VPNv6.

router bgp 1
 segment-routing srv6
  locator loc1
 !
 address-family ipv6 vpn
 exit-address-family
!
router bgp 1 vrf vrf10
 address-family ipv6 unicast
  sid vpn export 1    !!(option1)!!
  sid vpn export auto !!(option2)!!
 exit-address-family
!

Signed-off-by: Hiroki Shirokura <slank.dev@gmail.com>
bgpd/bgp_main.c
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_vty.c

index d545becded1af12479f24d69363fa8010f12b474..6736671f37d1e5ed96a78b2f3f30c1fe75025343 100644 (file)
@@ -317,6 +317,8 @@ static int bgp_vrf_enable(struct vrf *vrf)
                bgp_instance_up(bgp);
                vpn_leak_zebra_vrf_label_update(bgp, AFI_IP);
                vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6);
+               vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP);
+               vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP6);
                vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP,
                                    bgp_get_default(), bgp);
                vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, AFI_IP,
index eb68d84c069780532cb011406a4fa10bea692560..c16edc20f520b5a1cb8dbe1970073499bbb8909e 100644 (file)
@@ -47,6 +47,7 @@
 #include "bgpd/bgp_nexthop.h"
 #include "bgpd/bgp_nht.h"
 #include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_memory.h"
 
 #ifdef ENABLE_BGP_VNC
 #include "bgpd/rfapi/rfapi_backend.h"
@@ -356,6 +357,85 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi)
        bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
 }
 
+/*
+ * This function informs zebra of the srv6-function this vrf sets on routes
+ * leaked to VPN. Zebra should install this srv6-function in the kernel with
+ * an action of "End.DT4/6's IP FIB to route the PDU."
+ */
+void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+       enum seg6local_action_t act;
+       struct seg6local_context ctx = {{0}};
+       struct in6_addr *tovpn_sid = NULL;
+       struct in6_addr *tovpn_sid_ls = NULL;
+       struct vrf *vrf;
+       char buf[256] = {0};
+
+       if (bgp->vrf_id == VRF_UNKNOWN) {
+               if (debug)
+                       zlog_debug("%s: vrf %s: afi %s: vrf_id not set, "
+                                  "can't set zebra vrf label",
+                                  __func__, bgp->name_pretty, afi2str(afi));
+               return;
+       }
+
+       tovpn_sid = bgp->vpn_policy[afi].tovpn_sid;
+       if (!tovpn_sid) {
+               if (debug)
+                       zlog_debug("%s: vrf %s: afi %s: sid not set", __func__,
+                                  bgp->name_pretty, afi2str(afi));
+               return;
+       }
+
+       if (debug) {
+               inet_ntop(AF_INET6, tovpn_sid, buf, sizeof(buf));
+               zlog_debug("%s: vrf %s: afi %s: setting sid %s for vrf id %d",
+                          __func__, bgp->name_pretty, afi2str(afi), buf,
+                          bgp->vrf_id);
+       }
+
+       vrf = vrf_lookup_by_id(bgp->vrf_id);
+       if (!vrf)
+               return;
+
+       ctx.table = vrf->data.l.table_id;
+       act = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4
+               : ZEBRA_SEG6_LOCAL_ACTION_END_DT6;
+       zclient_send_localsid(zclient, tovpn_sid, bgp->vrf_id, act, &ctx);
+
+       tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
+       *tovpn_sid_ls = *tovpn_sid;
+       bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls;
+}
+
+/*
+ * If zebra tells us vrf has become unconfigured, tell zebra not to
+ * use this srv6-function to forward to the vrf anymore
+ */
+void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+       if (bgp->vrf_id == VRF_UNKNOWN) {
+               if (debug)
+                       zlog_debug("%s: vrf %s: afi %s: vrf_id not set, "
+                                  "can't set zebra vrf label",
+                                  __func__, bgp->name_pretty, afi2str(afi));
+               return;
+       }
+
+       if (debug)
+               zlog_debug("%s: deleting sid for vrf %s afi (id=%d)", __func__,
+                          bgp->name_pretty, bgp->vrf_id);
+
+       zclient_send_localsid(zclient,
+               bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent,
+               bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL);
+       XFREE(MTYPE_BGP_SRV6_SID,
+             bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent);
+}
+
 int vpn_leak_label_callback(
        mpls_label_t label,
        void *labelid,
@@ -417,6 +497,123 @@ int vpn_leak_label_callback(
        return 0;
 }
 
+static void sid_register(struct bgp *bgp, const struct in6_addr *sid,
+                        const char *locator_name)
+{
+       struct bgp_srv6_function *func;
+       func = XCALLOC(MTYPE_BGP_SRV6_FUNCTION,
+                      sizeof(struct bgp_srv6_function));
+       func->sid = *sid;
+       snprintf(func->locator_name, sizeof(func->locator_name),
+                "%s", locator_name);
+       listnode_add(bgp->srv6_functions, func);
+}
+
+static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid)
+{
+       struct listnode *node;
+       struct bgp_srv6_function *func;
+       for (ALL_LIST_ELEMENTS_RO(bgp->srv6_functions, node, func))
+               if (sid_same(&func->sid, sid))
+                       return true;
+       return false;
+}
+
+/* if index != 0: try to allocate as index-mode
+ * else: try to allocate as auto-mode */
+static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
+                         struct in6_addr *sid)
+{
+       struct listnode *node;
+       struct prefix_ipv6 *chunk;
+       struct in6_addr sid_buf;
+       bool alloced = false;
+
+       if (!bgp || !sid)
+               return false;
+
+       for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) {
+               sid_buf = chunk->prefix;
+               if (index != 0) {
+                       sid_buf.s6_addr[15] = index;
+                       if (sid_exist(bgp, &sid_buf))
+                               return false;
+                       alloced = true;
+                       break;
+               } else {
+                       for (size_t i=1; i<255; i++) {
+                               sid_buf.s6_addr16[7] = i;
+                               if (sid_exist(bgp, &sid_buf))
+                                       continue;
+                               alloced = true;
+                               break;
+                       }
+               }
+       }
+
+       if (!alloced)
+               return false;
+
+       sid_register(bgp, &sid_buf, bgp->srv6_locator_name);
+       *sid = sid_buf;
+       return true;
+}
+
+void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+       bool alloced = false;
+       char buf[256];
+       struct in6_addr *sid;
+       uint32_t tovpn_sid_index = 0;
+       bool tovpn_sid_auto = false;
+
+       if (debug)
+               zlog_debug("%s: try to allocate new SID for vrf %s: afi %s",
+                          __func__, bgp_vrf->name_pretty, afi2str(afi));
+
+       /* skip when tovpn sid is already allocated on vrf instance */
+       if (bgp_vrf->vpn_policy[afi].tovpn_sid)
+               return;
+
+       /* skip when bgp vpn instance ins't allocated
+        * or srv6 locator chunk isn't allocated */
+       if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks || !bgp_vrf)
+               return;
+
+       tovpn_sid_index = bgp_vrf->vpn_policy[afi].tovpn_sid_index;
+       tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+                                   BGP_VPN_POLICY_TOVPN_SID_AUTO);
+
+       /* skip when VPN isn't configured on vrf-instance */
+       if (tovpn_sid_index == 0 && !tovpn_sid_auto)
+               return;
+
+       /* check invalid case both configured index and auto */
+       if (tovpn_sid_index != 0 && tovpn_sid_index) {
+               zlog_err("%s: index-mode and auto-mode both selected. ignored.",
+                        __func__);
+               return;
+       }
+
+       sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
+       alloced = alloc_new_sid(bgp_vpn, tovpn_sid_index, sid);
+       if (!alloced) {
+               zlog_debug("%s: not allocated new sid for vrf %s: afi %s",
+                          __func__, bgp_vrf->name_pretty, afi2str(afi));
+               return;
+       }
+
+       if (debug) {
+               inet_ntop(AF_INET6, sid, buf, sizeof(buf));
+               zlog_debug("%s: new sid %s allocated for vrf %s: afi %s",
+                          __func__, buf, bgp_vrf->name_pretty,
+                          afi2str(afi));
+       }
+       bgp_vrf->vpn_policy[afi].tovpn_sid = sid;
+       return;
+}
+
 static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
 {
        uint32_t i, j;
index 91a073d5d73c6f0cf0d894c32546a922a94ac468..172372bdf0bfa59b3a1982403ac901b1286620d7 100644 (file)
@@ -77,7 +77,10 @@ extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn,
 
 extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi);
 extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi);
 extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
+extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi);
 extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
                                afi_t afi, safi_t safi);
 void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
@@ -237,6 +240,15 @@ static inline void vpn_leak_postchange(vpn_policy_direction_t direction,
                        vpn_leak_zebra_vrf_label_update(bgp_vrf, afi);
                }
 
+               if (!bgp_vrf->vpn_policy[afi].tovpn_sid)
+                       ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi);
+
+               if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid,
+                            bgp_vrf->vpn_policy[afi].
+                            tovpn_zebra_vrf_sid_last_sent)) {
+                       vpn_leak_zebra_vrf_sid_update(bgp_vrf, afi);
+               }
+
                vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi);
        }
 }
index a236d4e30c6e9fcb79684825ca480185cc5bf82d..fb181eab3d8cf0f75a14fc0f7f48e74248defadb 100644 (file)
@@ -9232,6 +9232,77 @@ DEFPY (af_label_vpn_export,
        return CMD_SUCCESS;
 }
 
+DEFPY (af_sid_vpn_export,
+       af_sid_vpn_export_cmd,
+       "[no] sid vpn export <(1-255)$sid_idx|auto$sid_auto>",
+       NO_STR
+       "sid value for VRF\n"
+       "Between current address-family and vpn\n"
+       "For routes leaked from current address-family to vpn\n"
+       "Sid allocation index\n"
+       "Automatically assign a label\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       afi_t afi;
+       int debug = 0;
+       int idx = 0;
+       bool yes = true;
+
+       if (argv_find(argv, argc, "no", &idx))
+               yes = false;
+       debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+                BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+       afi = vpn_policy_getafi(vty, bgp, false);
+       if (afi == AFI_MAX)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       if (!yes) {
+               /* implement me */
+               vty_out(vty, "It's not implemented");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* skip when it's already configured */
+       if ((sid_idx != 0 && bgp->vpn_policy[afi].tovpn_sid_index != 0)
+           || (sid_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+                                      BGP_VPN_POLICY_TOVPN_SID_AUTO)))
+               return CMD_SUCCESS;
+
+       /* mode change between sid_idx and sid_auto isn't supported.
+        * user must negate sid vpn export when they want to change
+        * the mode */
+       if ((sid_auto && bgp->vpn_policy[afi].tovpn_sid_index != 0)
+           || (sid_idx != 0 && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+                                          BGP_VPN_POLICY_TOVPN_SID_AUTO))) {
+               vty_out(vty, "it's already configured as %s.\n",
+                       sid_auto ? "auto-mode" : "idx-mode");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* pre-change */
+       vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                          bgp_get_default(), bgp);
+
+       if (sid_auto) {
+               /* SID allocation auto-mode */
+               if (debug)
+                       zlog_debug("%s: auto sid alloc.", __func__);
+               SET_FLAG(bgp->vpn_policy[afi].flags,
+                        BGP_VPN_POLICY_TOVPN_SID_AUTO);
+       } else {
+               /* SID allocation index-mode */
+               if (debug)
+                       zlog_debug("%s: idx %ld sid alloc.", __func__, sid_idx);
+               bgp->vpn_policy[afi].tovpn_sid_index = sid_idx;
+       }
+
+       /* post-change */
+       vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                           bgp_get_default(), bgp);
+       return CMD_SUCCESS;
+}
+
 ALIAS (af_label_vpn_export,
        af_no_label_vpn_export_cmd,
        "no label vpn export",
@@ -19493,6 +19564,8 @@ void bgp_vty_init(void)
        /* srv6 commands */
        install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd);
        install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd);
+       install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd);
+       install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd);
 }
 
 #include "memory.h"