Skip to content

Commit

Permalink
bgpd: cli for SRv6 SID alloc to redirect to vrf (step4)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
slankdev authored and Mark Stapp committed Jun 2, 2021
1 parent a0281b2 commit b72c9e1
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 0 deletions.
2 changes: 2 additions & 0 deletions bgpd/bgp_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
197 changes: 197 additions & 0 deletions bgpd/bgp_mplsvpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions bgpd/bgp_mplsvpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
Expand Down
73 changes: 73 additions & 0 deletions bgpd/bgp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit b72c9e1

Please sign in to comment.