bgpd: import/export rt for BGP vrf

Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
This commit is contained in:
Mitesh Kanjariya 2017-10-08 18:34:29 -07:00 committed by Mitesh Kanjariya
parent fe1dc5a374
commit c581d8b0f4
4 changed files with 399 additions and 34 deletions

View File

@ -301,12 +301,12 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn,
* VNIs but the same across routers (in the same AS) for a particular
* VNI.
*/
static void form_auto_rt(struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl)
static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
{
struct ecommunity_val eval;
struct ecommunity *ecomadd;
encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval);
@ -2107,11 +2107,152 @@ static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp)
bgp_evpn_free(bgp, vpn);
}
/*
* Derive AUTO import RT for BGP VRF - L3VNI
*/
static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
{
UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
}
/*
* Delete AUTO import RT from BGP VRF - L3VNI
*/
static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
{
evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
}
/*
* Derive AUTO export RT for BGP VRF - L3VNI
*/
static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
{
UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
}
/*
* Delete AUTO export RT from BGP VRF - L3VNI
*/
static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)
{
evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
}
/*
* Public functions.
*/
void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni,
struct list *rtl)
{
struct listnode *node, *nnode, *node_to_del;
struct ecommunity *ecom, *ecom_auto;
struct ecommunity_val eval;
encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
ecom_auto = ecommunity_new();
ecommunity_add_val(ecom_auto, &eval);
node_to_del = NULL;
for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
if (ecommunity_match(ecom, ecom_auto)) {
ecommunity_free(&ecom);
node_to_del = node;
}
}
if (node_to_del)
list_delete_node(rtl, node_to_del);
ecommunity_free(&ecom_auto);
}
void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomadd)
{
/* Remove auto generated RT */
evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
/* Add the newly configured RT to RT list */
listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
//TODO_MITESH: handle RT change (uninstall old routes and install new
//routes matching this RT)
}
void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel)
{
struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
struct ecommunity *ecom = NULL;
/* remove the RT from the RT list */
for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
if (ecommunity_match(ecom, ecomdel)) {
ecommunity_free(&ecom);
node_to_del = node;
break;
}
}
if (node_to_del)
list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
/* fallback to auto import rt, if this was the last RT */
if (list_isempty(bgp_vrf->vrf_import_rtl)) {
UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
}
//TODO_MITESH: handle import RT change
//uninstall old routes, install new routes matching this RT
}
void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomadd)
{
/* remove auto-generated RT */
evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
/* Add the new RT to the RT list */
listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd);
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
/* TODO_MITESH: update all routes */
}
void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel)
{
struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
struct ecommunity *ecom = NULL;
/* Remove the RT from the RT list */
for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
if (ecommunity_match(ecom, ecomdel)) {
ecommunity_free(&ecom);
node_to_del = node;
break;
}
}
if (node_to_del)
list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
/* fall back to auto-generated RT if this was the last RT */
if (list_isempty(bgp_vrf->vrf_export_rtl)) {
UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
evpn_auto_rt_export_add_for_vrf(bgp_vrf);
}
//TODO_MITESH: update all mac-ip routes
}
/*
* Handle change to BGP router id. This is invoked twice by the change
* handler, first before the router id has been changed and then after
@ -2515,7 +2656,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
{
form_auto_rt(bgp, vpn, vpn->import_rtl);
form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
/* Map RT to VNI */
@ -2527,7 +2668,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
{
form_auto_rt(bgp, vpn, vpn->export_rtl);
form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
}
@ -2820,7 +2961,11 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
/* set the router mac - to be used in mac-ip routes for this vrf */
memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr));
//TODO_MITESH: auto derive RD/RT
/* auto derive RD/RT */
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
evpn_auto_rt_export_add_for_vrf(bgp_vrf);
//TODO_MITESH: update all the local mac-ip routes with l3vni/rmac info
@ -2847,7 +2992,11 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
/* remove the Rmac from the BGP vrf */
memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr));
/* TODO_MITESH: delete auto RD/RT */
/* delete RD/RT */
if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl))
list_delete(bgp_vrf->vrf_import_rtl);
if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl))
list_delete(bgp_vrf->vrf_export_rtl);
/* TODO_MITESH: update all local mac-ip routes */
@ -3004,6 +3153,12 @@ void bgp_evpn_cleanup(struct bgp *bgp)
if (bgp->vnihash)
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
if (bgp->vrf_import_rtl)
list_delete(bgp->vrf_import_rtl);
bgp->vrf_import_rtl = NULL;
if (bgp->vrf_export_rtl)
list_delete(bgp->vrf_export_rtl);
bgp->vrf_export_rtl = NULL;
bf_free(bgp->rd_idspace);
}
@ -3021,6 +3176,13 @@ void bgp_evpn_init(struct bgp *bgp)
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
bgp->vrf_import_rtl = list_new();
bgp->vrf_import_rtl->cmp =
(int (*)(void *, void *))evpn_route_target_cmp;
bgp->vrf_export_rtl = list_new();
bgp->vrf_export_rtl->cmp =
(int (*)(void *, void *))evpn_route_target_cmp;
bf_init(bgp->rd_idspace, UINT16_MAX);
/*assign 0th index in the bitfield, so that we start with id 1*/
bf_assign_zero_index(bgp->rd_idspace);

View File

@ -197,7 +197,15 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
p->prefix.ip.ipaddr_v4 = originator_ip;
}
extern void evpn_rt_delete_auto(struct bgp*, vni_t, struct list*);
extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp*,
struct ecommunity*);
extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp*,
struct ecommunity*);
extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp*,
struct ecommunity*);
extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp*,
struct ecommunity*);
extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
struct bgpevpn *vpn);
extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,

View File

@ -1169,40 +1169,15 @@ DEFUN(no_evpnrt5_network,
}
#if defined(HAVE_CUMULUS)
static void evpn_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn,
struct list *rtl)
{
struct listnode *node, *nnode, *node_to_del;
struct ecommunity *ecom, *ecom_auto;
struct ecommunity_val eval;
encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
ecom_auto = ecommunity_new();
ecommunity_add_val(ecom_auto, &eval);
node_to_del = NULL;
for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
if (ecommunity_match(ecom, ecom_auto)) {
ecommunity_free(&ecom);
node_to_del = node;
}
}
if (node_to_del)
list_delete_node(rtl, node_to_del);
ecommunity_free(&ecom_auto);
}
static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
evpn_rt_delete_auto(bgp, vpn, vpn->import_rtl);
evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
}
static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
evpn_rt_delete_auto(bgp, vpn, vpn->export_rtl);
evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl);
}
/*
@ -3090,6 +3065,215 @@ static int bgp_evpn_rt_matches_existing(struct list *rtl,
return 0;
}
/* display L3VNI related info for a VRF instance */
DEFUN (show_bgp_vrf_l3vni_info,
show_bgp_vrf_l3vni_info_cmd,
"show bgp vrf VRFNAME l3vni info",
SHOW_STR
BGP_STR
"show bgp vrf\n"
"VRF Name\n"
"L3-VNI\n"
"L3-VNI info\n")
{
char buf[ETHER_ADDR_STRLEN];
int idx_vrf = 3;
const char *name = NULL;
struct bgp *bgp = NULL;
struct listnode *node = NULL;
//struct bgpevpn *vpn = NULL;
struct ecommunity *ecom = NULL;
name = argv[idx_vrf]->arg;
bgp = bgp_lookup_by_name(name);
if (!bgp) {
vty_out(vty, "BGP instance for VRF %s not found",
name);
return CMD_WARNING;
}
vty_out(vty, "BGP VRF: %s\n", name);
vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
vty_out(vty, " Rmac: %s\n",
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
/*vty_out(vty, " L2-VNI List:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
vty_out(vty, "%u ", vpn->vni);
vty_out(vty, "\n");*/
vty_out(vty, " Export-RTs:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
vty_out(vty, "%s ", ecommunity_str(ecom));
vty_out(vty, "\n");
vty_out(vty, " Import-RTs:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
vty_out(vty, "%s ", ecommunity_str(ecom));
vty_out(vty, "\n");
return CMD_SUCCESS;
}
/* import/export rt for l3vni-vrf */
DEFUN (bgp_evpn_vrf_rt,
bgp_evpn_vrf_rt_cmd,
"route-target <both|import|export> RT",
"Route Target\n"
"import and export\n"
"import\n"
"export\n"
"Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
{
int rt_type;
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
struct ecommunity *ecomadd = NULL;
if (!bgp)
return CMD_WARNING;
if (!strcmp(argv[1]->arg, "import"))
rt_type = RT_TYPE_IMPORT;
else if (!strcmp(argv[1]->arg, "export"))
rt_type = RT_TYPE_EXPORT;
else if (!strcmp(argv[1]->arg, "both"))
rt_type = RT_TYPE_BOTH;
else {
vty_out(vty, "%% Invalid Route Target type\n");
return CMD_WARNING;
}
/* Add/update the import route-target */
if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
ecomadd = ecommunity_str2com(argv[2]->arg,
ECOMMUNITY_ROUTE_TARGET, 0);
if (!ecomadd) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
ecommunity_str(ecomadd);
/* Do nothing if we already have this import route-target */
if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
ecomadd))
bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd);
}
/* Add/update the export route-target */
if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
ecomadd = ecommunity_str2com(argv[2]->arg,
ECOMMUNITY_ROUTE_TARGET, 0);
if (!ecomadd) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
ecommunity_str(ecomadd);
/* Do nothing if we already have this export route-target */
if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
ecomadd))
bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd);
}
return CMD_SUCCESS;
}
DEFUN (no_bgp_evpn_vrf_rt,
no_bgp_evpn_vrf_rt_cmd,
"no route-target <both|import|export> RT",
NO_STR
"Route Target\n"
"import and export\n"
"import\n"
"export\n"
"ASN:XX or A.B.C.D:XX\n")
{
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
int rt_type, found_ecomdel;
struct ecommunity *ecomdel = NULL;
if (!bgp)
return CMD_WARNING;
if (!strcmp(argv[2]->arg, "import"))
rt_type = RT_TYPE_IMPORT;
else if (!strcmp(argv[2]->arg, "export"))
rt_type = RT_TYPE_EXPORT;
else if (!strcmp(argv[2]->arg, "both"))
rt_type = RT_TYPE_BOTH;
else {
vty_out(vty, "%% Invalid Route Target type\n");
return CMD_WARNING;
}
if (rt_type == RT_TYPE_IMPORT) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
vty_out(vty,
"%% Import RT is not configured for this VRF\n");
return CMD_WARNING;
}
} else if (rt_type == RT_TYPE_EXPORT) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
vty_out(vty,
"%% Export RT is not configured for this VRF\n");
return CMD_WARNING;
}
} else if (rt_type == RT_TYPE_BOTH) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
&& !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
vty_out(vty,
"%% Import/Export RT is not configured for this VRF\n");
return CMD_WARNING;
}
}
ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
if (!ecomdel) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
ecommunity_str(ecomdel);
if (rt_type == RT_TYPE_IMPORT) {
if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
ecomdel)) {
vty_out(vty,
"%% RT specified does not match configuration for this VRF\n");
return CMD_WARNING;
}
bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
} else if (rt_type == RT_TYPE_EXPORT) {
if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
ecomdel)) {
vty_out(vty,
"%% RT specified does not match configuration for this VRF\n");
return CMD_WARNING;
}
bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
} else if (rt_type == RT_TYPE_BOTH) {
found_ecomdel = 0;
if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
ecomdel)) {
bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
found_ecomdel = 1;
}
if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
ecomdel)) {
bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
found_ecomdel = 1;
}
if (!found_ecomdel) {
vty_out(vty,
"%% RT specified does not match configuration for this VRF\n");
return CMD_WARNING;
}
}
return CMD_SUCCESS;
}
DEFUN (bgp_evpn_vni_rt,
bgp_evpn_vni_rt_cmd,
@ -3364,6 +3548,7 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
@ -3374,6 +3559,8 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
install_element(BGP_NODE, &bgp_evpn_vrf_rt_cmd);
install_element(BGP_NODE, &no_bgp_evpn_vrf_rt_cmd);
install_element(BGP_EVPN_VNI_NODE,
&bgp_evpn_advertise_default_gw_vni_cmd);
install_element(BGP_EVPN_VNI_NODE,

View File

@ -418,6 +418,14 @@ struct bgp {
/* vrf flags */
uint32_t vrf_flags;
#define BGP_VRF_AUTO (1 << 0)
#define BGP_VRF_IMPORT_RT_CFGD (1 << 1)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 2)
/* import rt list for the vrf instance */
struct list *vrf_import_rtl;
/* export rt list for the vrf instance */
struct list *vrf_export_rtl;
QOBJ_FIELDS
};