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 <slank.dev@gmail.com>
This commit is contained in:
Hiroki Shirokura 2020-12-19 11:04:40 +09:00 committed by Mark Stapp
parent a0281b2eab
commit b72c9e1475
4 changed files with 284 additions and 0 deletions

View 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,

View 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;

View 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);
}
}

View 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"