From b72c9e14756feba0af0ebff2159069c5b55224fc Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sat, 19 Dec 2020 11:04:40 +0900 Subject: [PATCH] bgpd: cli for SRv6 SID alloc to redirect to vrf (step4) 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 --- bgpd/bgp_main.c | 2 + bgpd/bgp_mplsvpn.c | 197 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_mplsvpn.h | 12 +++ bgpd/bgp_vty.c | 73 +++++++++++++++++ 4 files changed, 284 insertions(+) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index d545becded..6736671f37 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -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, diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index eb68d84c06..c16edc20f5 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -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; diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 91a073d5d7..172372bdf0 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -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); } } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a236d4e30c..fb181eab3d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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" -- 2.39.5