From 7022da35c2a9c047296ab8189efdfff95dd3ddb2 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 25 Feb 2021 16:06:38 -0500 Subject: bgpd: evpn L3 RTs list config and auto boilerplate Add functionality to allow EVPN L3 RTs to be configured via a list rather than one at a time. Also add boilerplate config for forcing auto derivation of RTs via config. Signed-off-by: Stephen Worley --- bgpd/bgp_evpn_vty.c | 215 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 74 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 6ba516c39..a0d39c30c 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -5833,22 +5833,102 @@ DEFUN (show_bgp_vrf_l3vni_info, return CMD_SUCCESS; } +static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) +{ + /* Do nothing if we already have this route-target */ + if (is_import) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecom)) + bgp_evpn_configure_import_rt_for_vrf(bgp, ecom); + else + return -1; + } else { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecom)) + bgp_evpn_configure_export_rt_for_vrf(bgp, ecom); + else + return -1; + } + + return 0; +} + +static int del_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) +{ + /* Verify we already have this route-target */ + if (is_import) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecom)) + return -1; + + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecom); + } else { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecom)) + return -1; + + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecom); + } + + return 0; +} + +static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, + struct cmd_token **argv, int rt_idx, bool is_add, + bool is_import) +{ + int ret = CMD_SUCCESS; + struct ecommunity *ecom = NULL; + + for (int i = rt_idx; i < argc; i++) { + ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET, + 0); + + if (!ecom) { + vty_out(vty, "%% Malformed Route Target list\n"); + ret = CMD_WARNING; + continue; + } + + ecommunity_str(ecom); + + if (is_add) { + if (add_rt(bgp, ecom, is_import) != 0) { + vty_out(vty, + "%% RT specified already configured for this VRF: %s\n", + argv[i]->arg); + ecommunity_free(&ecom); + ret = CMD_WARNING; + } + + } else { + if (del_rt(bgp, ecom, is_import) != 0) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF: %s\n", + argv[i]->arg); + ret = CMD_WARNING; + } + + ecommunity_free(&ecom); + } + } + + return ret; +} + /* import/export rt for l3vni-vrf */ DEFUN (bgp_evpn_vrf_rt, bgp_evpn_vrf_rt_cmd, - "route-target RT", + "route-target RTLIST...", "Route Target\n" "import and export\n" "import\n" "export\n" - "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { + int ret = CMD_SUCCESS; + int tmp_ret = CMD_SUCCESS; int rt_type; struct bgp *bgp = VTY_GET_CONTEXT(bgp); - struct ecommunity *ecomadd = NULL; if (!bgp) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; if (!strcmp(argv[1]->arg, "import")) rt_type = RT_TYPE_IMPORT; @@ -5858,46 +5938,53 @@ DEFUN (bgp_evpn_vrf_rt, rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); - return CMD_WARNING; - } - - ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - if (!ecomadd) { - vty_out(vty, "%% Malformed Route Target list\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } - ecommunity_str(ecomadd); /* Add/update the import route-target */ - if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) { - /* 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); - } + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true); - /* Add/update the export route-target */ - if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) { - /* 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); - } + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; - return CMD_SUCCESS; + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, false); + + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; + + return ret; +} + +DEFUN (bgp_evpn_vrf_rt_auto, + bgp_evpn_vrf_rt_auto_cmd, + "route-target auto", + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Automatically derive route target\n") +{ + // TODO: auto + vty_out(vty, "AUTO TODO\n"); + return CMD_WARNING_CONFIG_FAILED; } DEFUN (no_bgp_evpn_vrf_rt, no_bgp_evpn_vrf_rt_cmd, - "no route-target RT", + "no route-target RTLIST...", NO_STR "Route Target\n" "import and export\n" "import\n" "export\n" - EVPN_ASN_IP_HELP_STR) + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { struct bgp *bgp = VTY_GET_CONTEXT(bgp); - int rt_type, found_ecomdel; - struct ecommunity *ecomdel = NULL; + int ret = CMD_SUCCESS; + int tmp_ret = CMD_SUCCESS; + int rt_type; if (!bgp) return CMD_WARNING; @@ -5910,7 +5997,7 @@ DEFUN (no_bgp_evpn_vrf_rt, rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } if (rt_type == RT_TYPE_IMPORT) { @@ -5934,56 +6021,34 @@ DEFUN (no_bgp_evpn_vrf_rt, } } - 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_BOTH || rt_type == RT_TYPE_IMPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, true); - if (rt_type == RT_TYPE_IMPORT) { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, - ecomdel)) { - ecommunity_free(&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)) { - ecommunity_free(&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 (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; - if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, - ecomdel)) { - bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); - found_ecomdel = 1; - } + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, false); - if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, - ecomdel)) { - bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); - found_ecomdel = 1; - } + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; - if (!found_ecomdel) { - ecommunity_free(&ecomdel); - vty_out(vty, - "%% RT specified does not match configuration for this VRF\n"); - return CMD_WARNING; - } - } + return ret; +} - ecommunity_free(&ecomdel); - return CMD_SUCCESS; +DEFUN (no_bgp_evpn_vrf_rt_auto, + no_bgp_evpn_vrf_rt_auto_cmd, + "no route-target auto", + NO_STR + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Automatically derive route target\n") +{ + // TODO: auto + vty_out(vty, "AUTO TODO\n"); + return CMD_WARNING_CONFIG_FAILED; } DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd, @@ -6593,6 +6658,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_auto_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_auto_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd); -- cgit v1.2.3 From ca337b4641da5064a865d1a902ede158f50773f5 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Wed, 17 Feb 2021 16:11:49 -0500 Subject: bgpd: abstract ecom into struct for l3 route targets Abstract the ecommunity into a container struct for L3 route targets so that we can add some additional info via flags to go along with RT configs without modifying the used elsewhere ecommunity struct. This functions as a wrapper everywhere its used including the import/export lists. The flags will be used in later commits to change behavior when importing/exporting routes. Signed-off-by: Stephen Worley --- bgpd/bgp_evpn.c | 189 ++++++++++++++++++++++++++++++------------------ bgpd/bgp_evpn_private.h | 14 +++- bgpd/bgp_evpn_vty.c | 97 +++++++++++++++---------- 3 files changed, 192 insertions(+), 108 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 0642c966e..d2cb39656 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -62,6 +62,7 @@ DEFINE_QOBJ_TYPE(bgpevpn); DEFINE_QOBJ_TYPE(bgp_evpn_es); +DEFINE_MTYPE_STATIC(BGPD, VRF_ROUTE_TARGET, "L3 Route Target"); /* * Static function declarations @@ -340,12 +341,47 @@ int bgp_evpn_route_target_cmp(struct ecommunity *ecom1, return strcmp(ecom1->str, ecom2->str); } +/* + * Compare L3 Route Targets. + */ +static int evpn_vrf_route_target_cmp(struct vrf_route_target *rt1, + struct vrf_route_target *rt2) +{ + return bgp_evpn_route_target_cmp(rt1->ecom, rt2->ecom); +} + void bgp_evpn_xxport_delete_ecomm(void *val) { struct ecommunity *ecomm = val; ecommunity_free(&ecomm); } +/* + * Delete l3 Route Target. + */ +static void evpn_vrf_rt_del(void *val) +{ + struct vrf_route_target *l3rt = val; + + ecommunity_free(&l3rt->ecom); + + XFREE(MTYPE_VRF_ROUTE_TARGET, l3rt); +} + +/* + * Allocate a new l3 Route Target. + */ +static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom) +{ + struct vrf_route_target *l3rt; + + l3rt = XCALLOC(MTYPE_VRF_ROUTE_TARGET, sizeof(struct vrf_route_target)); + + l3rt->ecom = ecom; + + return l3rt; +} + /* * Mask off global-admin field of specified extended community (RT), * just retain the local-admin field. @@ -498,7 +534,9 @@ static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn, static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) { struct ecommunity_val eval; - struct ecommunity *ecomadd, *ecom; + struct ecommunity *ecomadd; + struct vrf_route_target *l3rt; + struct vrf_route_target *newrt; bool ecom_found = false; struct listnode *node; @@ -508,15 +546,16 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) ecomadd = ecommunity_new(); ecommunity_add_val(ecomadd, &eval, false, false); - for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) - if (ecommunity_cmp(ecomadd, ecom)) { + for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) + if (ecommunity_cmp(ecomadd, l3rt->ecom)) { ecom_found = true; break; } - if (!ecom_found) - listnode_add_sort(rtl, ecomadd); - else + if (!ecom_found) { + newrt = evpn_vrf_rt_new(ecomadd); + listnode_add_sort(rtl, newrt); + } else ecommunity_free(&ecomadd); } @@ -714,8 +753,9 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, struct ecommunity_val eval_rmac; bgp_encap_types tnl_type; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; struct ecommunity *old_ecom; + struct ecommunity *ecom; struct list *vrf_export_rtl = NULL; /* Encap */ @@ -739,10 +779,10 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, /* Add the export RTs for L3VNI/VRF */ vrf_export_rtl = bgp_vrf->vrf_export_rtl; - for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom)) + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, l3rt)) bgp_attr_set_ecommunity( - attr, - ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom)); + attr, ecommunity_merge(bgp_attr_get_ecommunity(attr), + l3rt->ecom)); /* add the router mac extended community */ if (!is_zero_mac(&attr->rmac)) { @@ -779,6 +819,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, bgp_encap_types tnl_type; struct listnode *node, *nnode; struct ecommunity *ecom; + struct vrf_route_target *l3rt; uint32_t seqnum; struct list *vrf_export_rtl = NULL; @@ -807,12 +848,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn); if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, - ecom)) + l3rt)) bgp_attr_set_ecommunity( attr, ecommunity_merge( bgp_attr_get_ecommunity(attr), - ecom)); + l3rt->ecom)); } } @@ -4266,7 +4307,8 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf) */ 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); + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, + true); } /* @@ -4283,7 +4325,8 @@ static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf) */ 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); + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, + true); } static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) @@ -4526,10 +4569,41 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, } } -void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) +static void rt_list_remove_node(struct list *rt_list, + struct ecommunity *ecomdel, bool is_l3) { - struct listnode *node, *nnode, *node_to_del; - struct ecommunity *ecom, *ecom_auto; + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct vrf_route_target *l3rt = NULL; + struct ecommunity *ecom = NULL; + + /* remove the RT from the RT list */ + + if (is_l3) { + for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { + if (ecommunity_match(l3rt->ecom, ecomdel)) { + evpn_vrf_rt_del(l3rt); + node_to_del = node; + break; + } + } + } else { + for (ALL_LIST_ELEMENTS(rt_list, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomdel)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + } + + if (node_to_del) + list_delete_node(rt_list, node_to_del); +} + +void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, + bool is_l3) +{ + struct ecommunity *ecom_auto; struct ecommunity_val eval; if (bgp->advertise_autort_rfc8365) @@ -4538,18 +4612,9 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) ecom_auto = ecommunity_new(); ecommunity_add_val(ecom_auto, &eval, false, false); - 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; - break; - } - } - - if (node_to_del) - list_delete_node(rtl, node_to_del); + /* Remove rt */ + rt_list_remove_node(rtl, ecom_auto, is_l3); ecommunity_free(&ecom_auto); } @@ -4557,6 +4622,10 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd) { + struct vrf_route_target *newrt; + + newrt = evpn_vrf_rt_new(ecomadd); + /* uninstall routes from vrf */ if (is_l3vni_live(bgp_vrf)) uninstall_routes_for_vrf(bgp_vrf); @@ -4568,7 +4637,7 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, 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); + listnode_add_sort(bgp_vrf->vrf_import_rtl, newrt); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); /* map VRF to its RTs and install routes matching the new RTs */ @@ -4581,9 +4650,6 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, 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; - /* uninstall routes from vrf */ if (is_l3vni_live(bgp_vrf)) uninstall_routes_for_vrf(bgp_vrf); @@ -4591,17 +4657,8 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, /* Cleanup the RT to VRF mapping */ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); - /* 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); + /* Remove rt */ + rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true); assert(bgp_vrf->vrf_import_rtl); /* fallback to auto import rt, if this was the last RT */ @@ -4621,11 +4678,15 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd) { + struct vrf_route_target *newrt; + + newrt = evpn_vrf_rt_new(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); + listnode_add_sort(bgp_vrf->vrf_export_rtl, newrt); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); if (is_l3vni_live(bgp_vrf)) @@ -4635,20 +4696,8 @@ void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, 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); + /* Remove rt */ + rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true); /* * Temporary assert to make SA happy. @@ -5100,11 +5149,11 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) uint32_t i = 0; struct ecommunity_val *eval = NULL; struct listnode *node = NULL, *nnode = NULL; - struct ecommunity *ecom = NULL; + struct vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { - for (i = 0; i < ecom->size; i++) { - eval = (struct ecommunity_val *)(ecom->val + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { + for (i = 0; i < l3rt->ecom->size; i++) { + eval = (struct ecommunity_val *)(l3rt->ecom->val + (i * ECOMMUNITY_SIZE)); map_vrf_to_rt(bgp_vrf, eval); @@ -5120,14 +5169,14 @@ void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf) uint32_t i; struct ecommunity_val *eval; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { - for (i = 0; i < ecom->size; i++) { + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { + for (i = 0; i < l3rt->ecom->size; i++) { struct vrf_irt_node *irt; struct ecommunity_val eval_tmp; - eval = (struct ecommunity_val *)(ecom->val + eval = (struct ecommunity_val *)(l3rt->ecom->val + (i * ECOMMUNITY_SIZE)); /* If using "automatic" RT, we only care about the @@ -6000,12 +6049,12 @@ void bgp_evpn_init(struct bgp *bgp) "BGP VRF Import RT Hash"); bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = - (int (*)(void *, void *))bgp_evpn_route_target_cmp; - bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm; + (int (*)(void *, void *))evpn_vrf_route_target_cmp; + bgp->vrf_import_rtl->del = evpn_vrf_rt_del; bgp->vrf_export_rtl = list_new(); bgp->vrf_export_rtl->cmp = - (int (*)(void *, void *))bgp_evpn_route_target_cmp; - bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm; + (int (*)(void *, void *))evpn_vrf_route_target_cmp; + bgp->vrf_export_rtl->del = evpn_vrf_rt_del; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = vni_list_cmp; /* By default Duplicate Address Dection is enabled. diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 64fdc2970..f8cd20f24 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -194,6 +194,17 @@ struct evpn_remote_ip { struct list *macip_path_list; }; +/* + * Wrapper struct for l3 RT's + */ +struct vrf_route_target { + /* flags based on config to determine how RTs are handled */ + uint8_t flags; +#define BGP_VRF_RT_AUTO (1 << 0) + + struct ecommunity *ecom; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); @@ -590,7 +601,8 @@ extern struct zclient *zclient; extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi, safi_t safi, bool add); -extern void evpn_rt_delete_auto(struct bgp *, vni_t, struct list *); +extern void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, + bool is_l3); extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd); extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index a0d39c30c..500fa6ea8 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -373,7 +373,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, char buf1[INET6_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; char buf2[ETHER_ADDR_STRLEN]; @@ -434,8 +434,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, if (!json) vty_out(vty, " Import Route Target:\n"); - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) @@ -452,8 +452,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, else vty_out(vty, " Export Route Target:\n"); - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) @@ -928,7 +928,7 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, char rt_buf[25]; char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; if (!bgp->l3vni) return; @@ -971,8 +971,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, prefix_rd2str(&bgp->vrf_prd, buf2, RD_ADDRSTRLEN)); } - for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) { @@ -999,8 +999,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, if (json) json_object_object_add(json_vni, "importRTs", json_import_rtl); - for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) { @@ -2009,12 +2009,12 @@ DEFUN(no_evpnrt5_network, static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl, false); } static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl, false); } /* @@ -5698,18 +5698,35 @@ DEFUN (no_bgp_evpn_vni_rd_without_val, * Loop over all extended-communities in the route-target list rtl and * return 1 if we find ecomtarget */ -static int bgp_evpn_rt_matches_existing(struct list *rtl, - struct ecommunity *ecomtarget) +static bool bgp_evpn_rt_matches_existing(struct list *rtl, + struct ecommunity *ecomtarget) { - struct listnode *node, *nnode; + struct listnode *node; struct ecommunity *ecom; - for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { + for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) { if (ecommunity_match(ecom, ecomtarget)) - return 1; + return true; } - return 0; + return false; +} + +/* + * L3 RT version of above. + */ +static bool bgp_evpn_vrf_rt_matches_existing(struct list *rtl, + struct ecommunity *ecomtarget) +{ + struct listnode *node; + struct vrf_route_target *l3rt; + + for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) { + if (ecommunity_match(l3rt->ecom, ecomtarget)) + return true; + } + + return false; } /* display L3VNI related info for a VRF instance */ @@ -5730,7 +5747,7 @@ DEFUN (show_bgp_vrf_l3vni_info, struct bgp *bgp = NULL; struct listnode *node = NULL; struct bgpevpn *vpn = NULL; - struct ecommunity *ecom = NULL; + struct vrf_route_target *l3rt; json_object *json = NULL; json_object *json_vnis = NULL; json_object *json_export_rts = NULL; @@ -5780,13 +5797,13 @@ DEFUN (show_bgp_vrf_l3vni_info, 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)); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt)) + vty_out(vty, "%s ", ecommunity_str(l3rt->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)); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) + vty_out(vty, "%s ", ecommunity_str(l3rt->ecom)); vty_out(vty, "\n"); vty_out(vty, " RD: %s\n", prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN)); @@ -5811,17 +5828,19 @@ DEFUN (show_bgp_vrf_l3vni_info, json_object_object_add(json, "l2vnis", json_vnis); /* export rts */ - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt)) json_object_array_add( json_export_rts, - json_object_new_string(ecommunity_str(ecom))); + json_object_new_string( + ecommunity_str(l3rt->ecom))); json_object_object_add(json, "export-rts", json_export_rts); /* import rts */ - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) json_object_array_add( json_import_rts, - json_object_new_string(ecommunity_str(ecom))); + json_object_new_string( + ecommunity_str(l3rt->ecom))); json_object_object_add(json, "import-rts", json_import_rts); json_object_string_add( json, "rd", @@ -5837,12 +5856,14 @@ static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) { /* Do nothing if we already have this route-target */ if (is_import) { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecom)) + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl, + ecom)) bgp_evpn_configure_import_rt_for_vrf(bgp, ecom); else return -1; } else { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecom)) + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl, + ecom)) bgp_evpn_configure_export_rt_for_vrf(bgp, ecom); else return -1; @@ -5855,12 +5876,14 @@ static int del_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) { /* Verify we already have this route-target */ if (is_import) { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecom)) + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl, + ecom)) return -1; bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecom); } else { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecom)) + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl, + ecom)) return -1; bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecom); @@ -6532,12 +6555,12 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, - ecom)) { + l3rt)) { ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); vty_out(vty, " route-target import %s\n", ecom_str); XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } @@ -6547,12 +6570,12 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, - ecom)) { + l3rt)) { ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); vty_out(vty, " route-target export %s\n", ecom_str); XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } -- cgit v1.2.3 From 58d8948cf43073ee0ea617ecadf5518372fef12c Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Thu, 25 Feb 2021 16:27:07 -0500 Subject: bgpd: evpn L3 RT auto config and wildcard implementation Implement forcing L3 auto derivation via configs even when manually RTs are set. This will allow both to coexist in BGP RTs. Without using auto config command, it will remove auto derived RTs when you manually configure your own. To allow both, use the auto command ond import/export/both. Implement '*' wildcard import L3 RTs so we can import a route into any AS. This is necessary to avoid a user from having to configure an L3 RT for every AS they care to import evpn route from. Signed-off-by: Stephen Worley --- bgpd/bgp_evpn.c | 378 ++++++++++++++++++++++++++++++++---------------- bgpd/bgp_evpn_private.h | 8 +- bgpd/bgp_evpn_vty.c | 161 +++++++++++++++++++-- bgpd/bgpd.h | 6 +- 4 files changed, 408 insertions(+), 145 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d2cb39656..340219b5d 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -387,7 +387,7 @@ static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom) * just retain the local-admin field. */ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, - struct ecommunity_val *src) + const struct ecommunity_val *src) { uint8_t type; @@ -403,33 +403,55 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, } /* - * Map one RT to specified VRF. - * bgp_vrf = BGP vrf instance + * Converts the RT to Ecommunity Value and adjusts masking based + * on flags set for RT. */ -static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval) +static void vrf_rt2ecom_val(struct ecommunity_val *to_eval, + const struct vrf_route_target *l3rt, int iter) { - struct vrf_irt_node *irt = NULL; - struct ecommunity_val eval_tmp; + const struct ecommunity_val *eval; - /* If using "automatic" RT, + eval = (const struct ecommunity_val *)(l3rt->ecom->val + + (iter * ECOMMUNITY_SIZE)); + /* If using "automatic" or "wildcard *" RT, * we only care about the local-admin sub-field. * This is to facilitate using L3VNI(VRF-VNI) - * as the RT for EBGP peering too. + * as the RT for EBGP peering too and simplify + * configurations by allowing any ASN via '*'. */ - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); - if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) - mask_ecom_global_admin(&eval_tmp, eval); + memcpy(to_eval, eval, ECOMMUNITY_SIZE); - irt = lookup_vrf_import_rt(&eval_tmp); - if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) - /* Already mapped. */ - return; + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO) || + CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) + mask_ecom_global_admin(to_eval, eval); +} - if (!irt) - irt = vrf_import_rt_new(&eval_tmp); +/* + * Map one RT to specified VRF. + * bgp_vrf = BGP vrf instance + */ +static void map_vrf_to_rt(struct bgp *bgp_vrf, struct vrf_route_target *l3rt) +{ + uint32_t i = 0; + + for (i = 0; i < l3rt->ecom->size; i++) { + struct vrf_irt_node *irt = NULL; + struct ecommunity_val eval_tmp; + + /* Adjust masking for value */ + vrf_rt2ecom_val(&eval_tmp, l3rt, i); - /* Add VRF to the list for this RT. */ - listnode_add(irt->vrfs, bgp_vrf); + irt = lookup_vrf_import_rt(&eval_tmp); + + if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + return; /* Already mapped. */ + + if (!irt) + irt = vrf_import_rt_new(&eval_tmp); + + /* Add VRF to the list for this RT. */ + listnode_add(irt->vrfs, bgp_vrf); + } } /* @@ -437,12 +459,28 @@ static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval) * VRFs for this RT, then the RT hash is deleted. * bgp_vrf: BGP VRF specific instance */ -static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt) +static void unmap_vrf_from_rt(struct bgp *bgp_vrf, + struct vrf_route_target *l3rt) { - /* Delete VRF from list for this RT. */ - listnode_delete(irt->vrfs, bgp_vrf); - if (!listnode_head(irt->vrfs)) { - vrf_import_rt_free(irt); + uint32_t i; + + for (i = 0; i < l3rt->ecom->size; i++) { + struct vrf_irt_node *irt; + struct ecommunity_val eval_tmp; + + /* Adjust masking for value */ + vrf_rt2ecom_val(&eval_tmp, l3rt, i); + + irt = lookup_vrf_import_rt(&eval_tmp); + + if (!irt) + return; /* Not mapped */ + + /* Delete VRF from list for this RT. */ + listnode_delete(irt->vrfs, bgp_vrf); + + if (!listnode_head(irt->vrfs)) + vrf_import_rt_free(irt); } } @@ -531,10 +569,12 @@ static void bgp_evpn_get_rmac_nexthop(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, vni_t vni, struct list *rtl) +static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl, + bool is_l3) { struct ecommunity_val eval; struct ecommunity *ecomadd; + struct ecommunity *ecom; struct vrf_route_target *l3rt; struct vrf_route_target *newrt; bool ecom_found = false; @@ -546,15 +586,29 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) ecomadd = ecommunity_new(); ecommunity_add_val(ecomadd, &eval, false, false); - for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) - if (ecommunity_cmp(ecomadd, l3rt->ecom)) { - ecom_found = true; - break; - } + + if (is_l3) { + for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) + if (ecommunity_cmp(ecomadd, l3rt->ecom)) { + ecom_found = true; + break; + } + } else { + for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) + if (ecommunity_cmp(ecomadd, ecom)) { + ecom_found = true; + break; + } + } if (!ecom_found) { - newrt = evpn_vrf_rt_new(ecomadd); - listnode_add_sort(rtl, newrt); + if (is_l3) { + newrt = evpn_vrf_rt_new(ecomadd); + /* Label it as autoderived */ + SET_FLAG(newrt->flags, BGP_VRF_RT_AUTO); + listnode_add_sort(rtl, newrt); + } else + listnode_add_sort(rtl, ecomadd); } else ecommunity_free(&ecomadd); } @@ -4292,13 +4346,14 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf) { struct bgp *bgp_evpn = NULL; - form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); - UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, true); /* Map RT to VRF */ bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) return; + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); } @@ -4316,8 +4371,7 @@ static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf) */ 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); + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, true); } /* @@ -4576,8 +4630,6 @@ static void rt_list_remove_node(struct list *rt_list, struct vrf_route_target *l3rt = NULL; struct ecommunity *ecom = NULL; - /* remove the RT from the RT list */ - if (is_l3) { for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { if (ecommunity_match(l3rt->ecom, ecomdel)) { @@ -4596,6 +4648,7 @@ static void rt_list_remove_node(struct list *rt_list, } } + if (node_to_del) list_delete_node(rt_list, node_to_del); } @@ -4608,71 +4661,154 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, if (bgp->advertise_autort_rfc8365) vni |= EVPN_AUTORT_VXLAN; + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecom_auto = ecommunity_new(); ecommunity_add_val(ecom_auto, &eval, false, false); - /* Remove rt */ rt_list_remove_node(rtl, ecom_auto, is_l3); ecommunity_free(&ecom_auto); } -void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, - struct ecommunity *ecomadd) +static void evpn_vrf_rt_routes_map(struct bgp *bgp_vrf) { - struct vrf_route_target *newrt; - - newrt = evpn_vrf_rt_new(ecomadd); + /* map VRFs to its RTs and install routes matching this new RT */ + if (is_l3vni_live(bgp_vrf)) { + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + install_routes_for_vrf(bgp_vrf); + } +} +static void evpn_vrf_rt_routes_unmap(struct bgp *bgp_vrf) +{ /* uninstall routes from vrf */ if (is_l3vni_live(bgp_vrf)) uninstall_routes_for_vrf(bgp_vrf); /* Cleanup the RT to VRF mapping */ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); +} - /* Remove auto generated RT */ - evpn_auto_rt_import_delete_for_vrf(bgp_vrf); +static bool rt_list_has_cfgd_rt(struct list *rt_list) +{ + struct listnode *node = NULL, *nnode = NULL; + struct vrf_route_target *l3rt = NULL; + + for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { + if (!CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + return true; + } + + return false; +} + +static void unconfigure_import_rt_for_vrf_fini(struct bgp *bgp_vrf) +{ + if (!bgp_vrf->vrf_import_rtl) + return; /* this should never fail */ + + if (!is_l3vni_live(bgp_vrf)) + return; /* Nothing to do if no vni */ + + /* fall back to auto-generated RT if this was the last RT */ + if (list_isempty(bgp_vrf->vrf_import_rtl)) + evpn_auto_rt_import_add_for_vrf(bgp_vrf); +} + +static void unconfigure_export_rt_for_vrf_fini(struct bgp *bgp_vrf) +{ + + if (!bgp_vrf->vrf_export_rtl) + return; /* this should never fail */ + + if (!is_l3vni_live(bgp_vrf)) + return; /* Nothing to do if no vni */ + + /* fall back to auto-generated RT if this was the last RT */ + if (list_isempty(bgp_vrf->vrf_export_rtl)) + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} + +void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd, + bool is_wildcard) +{ + struct vrf_route_target *newrt; + + newrt = evpn_vrf_rt_new(ecomadd); + + if (is_wildcard) + SET_FLAG(newrt->flags, BGP_VRF_RT_WILD); + + evpn_vrf_rt_routes_unmap(bgp_vrf); + + /* Remove auto generated RT if not configured */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) + 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, newrt); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); - /* map VRF to its RTs and install routes matching the new RTs */ - if (is_l3vni_live(bgp_vrf)) { - bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - install_routes_for_vrf(bgp_vrf); - } + evpn_vrf_rt_routes_map(bgp_vrf); +} + +void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ + if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) + return; /* Already configured */ + + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD); + + if (!is_l3vni_live(bgp_vrf)) + return; /* Wait for VNI before adding rts */ + + evpn_vrf_rt_routes_unmap(bgp_vrf); + + evpn_auto_rt_import_add_for_vrf(bgp_vrf); + + evpn_vrf_rt_routes_map(bgp_vrf); } void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel) { - /* uninstall routes from vrf */ - if (is_l3vni_live(bgp_vrf)) - uninstall_routes_for_vrf(bgp_vrf); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + return; /* Already un-configured */ - /* Cleanup the RT to VRF mapping */ - bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + evpn_vrf_rt_routes_unmap(bgp_vrf); /* Remove rt */ rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true); - assert(bgp_vrf->vrf_import_rtl); - /* fallback to auto import rt, if this was the last RT */ - if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) { + if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_import_rtl)) UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); - if (is_l3vni_live(bgp_vrf)) - evpn_auto_rt_import_add_for_vrf(bgp_vrf); - } - /* map VRFs to its RTs and install routes matching this new RT */ - if (is_l3vni_live(bgp_vrf)) { - bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - install_routes_for_vrf(bgp_vrf); - } + unconfigure_import_rt_for_vrf_fini(bgp_vrf); + + evpn_vrf_rt_routes_map(bgp_vrf); +} + +void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) + return; /* Already un-configured */ + + evpn_vrf_rt_routes_unmap(bgp_vrf); + + /* remove auto-generated RT */ + evpn_auto_rt_import_delete_for_vrf(bgp_vrf); + + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD); + + unconfigure_import_rt_for_vrf_fini(bgp_vrf); + + evpn_vrf_rt_routes_map(bgp_vrf); } void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, @@ -4682,42 +4818,60 @@ void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, newrt = evpn_vrf_rt_new(ecomadd); - /* remove auto-generated RT */ - evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + /* Remove auto generated RT if not configured */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) + 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, newrt); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); if (is_l3vni_live(bgp_vrf)) bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); } +void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ + if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) + return; /* Already configured */ + + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD); + + if (!is_l3vni_live(bgp_vrf)) + return; /* Wait for VNI before adding rts */ + + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} + void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel) { + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) + return; /* Already un-configured */ + /* Remove rt */ rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true); - /* - * Temporary assert to make SA happy. - * The ALL_LIST_ELEMENTS macro above has a NULL check - * which means that SA is going to complain about - * the list_isempty call, which doesn't NULL check. - * So until we get this situation cleaned up, here - * we are. - */ - assert(bgp_vrf->vrf_export_rtl); - - /* fall back to auto-generated RT if this was the last RT */ - if (list_isempty(bgp_vrf->vrf_export_rtl)) { + if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_export_rtl)) UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); - if (is_l3vni_live(bgp_vrf)) - evpn_auto_rt_export_add_for_vrf(bgp_vrf); - } - if (is_l3vni_live(bgp_vrf)) - bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + unconfigure_export_rt_for_vrf_fini(bgp_vrf); +} + +void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) + return; /* Already un-configured */ + + /* remove auto-generated RT */ + evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD); + + unconfigure_export_rt_for_vrf_fini(bgp_vrf); } /* @@ -5146,19 +5300,11 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, */ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) { - uint32_t i = 0; - struct ecommunity_val *eval = NULL; - struct listnode *node = NULL, *nnode = NULL; + struct listnode *node, *nnode; struct vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { - for (i = 0; i < l3rt->ecom->size; i++) { - eval = (struct ecommunity_val *)(l3rt->ecom->val - + (i - * ECOMMUNITY_SIZE)); - map_vrf_to_rt(bgp_vrf, eval); - } - } + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) + map_vrf_to_rt(bgp_vrf, l3rt); } /* @@ -5166,37 +5312,13 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) */ void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf) { - uint32_t i; - struct ecommunity_val *eval; struct listnode *node, *nnode; struct vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { - for (i = 0; i < l3rt->ecom->size; i++) { - struct vrf_irt_node *irt; - struct ecommunity_val eval_tmp; - - eval = (struct ecommunity_val *)(l3rt->ecom->val - + (i - * ECOMMUNITY_SIZE)); - /* If using "automatic" RT, we only care about the - * local-admin sub-field. - * This is to facilitate using VNI as the RT for EBGP - * peering too. - */ - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); - if (!CHECK_FLAG(bgp_vrf->vrf_flags, - BGP_VRF_IMPORT_RT_CFGD)) - mask_ecom_global_admin(&eval_tmp, eval); - - irt = lookup_vrf_import_rt(&eval_tmp); - if (irt) - unmap_vrf_from_rt(bgp_vrf, irt); - } - } + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) + unmap_vrf_from_rt(bgp_vrf, l3rt); } - /* * Map the RTs (configured or automatically derived) of a VNI to the VNI. * The mapping will be used during route processing. @@ -5258,7 +5380,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->vni, vpn->import_rtl); + form_auto_rt(bgp, vpn->vni, vpn->import_rtl, false); UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD); /* Map RT to VNI */ @@ -5270,7 +5392,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->vni, vpn->export_rtl); + form_auto_rt(bgp, vpn->vni, vpn->export_rtl, false); UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD); } @@ -5675,12 +5797,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, } /* Map auto derive or configured RTs */ - if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) || + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) evpn_auto_rt_import_add_for_vrf(bgp_vrf); else bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD) || + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) evpn_auto_rt_export_add_for_vrf(bgp_vrf); /* auto derive RD */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index f8cd20f24..fdbffa95d 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -201,6 +201,7 @@ struct vrf_route_target { /* flags based on config to determine how RTs are handled */ uint8_t flags; #define BGP_VRF_RT_AUTO (1 << 0) +#define BGP_VRF_RT_WILD (1 << 1) struct ecommunity *ecom; }; @@ -605,12 +606,17 @@ extern void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, bool is_l3); extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd); +extern void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel); +extern void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, - struct ecommunity *ecomadd); + struct ecommunity *ecomadd, + bool is_wildcard); +extern void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel); +extern void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf); extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_handle_autort_change(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 500fa6ea8..13a63f153 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -5852,13 +5852,15 @@ DEFUN (show_bgp_vrf_l3vni_info, return CMD_SUCCESS; } -static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) +static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import, + bool is_wildcard) { /* Do nothing if we already have this route-target */ if (is_import) { if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl, ecom)) - bgp_evpn_configure_import_rt_for_vrf(bgp, ecom); + bgp_evpn_configure_import_rt_for_vrf(bgp, ecom, + is_wildcard); else return -1; } else { @@ -5897,12 +5899,37 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, bool is_import) { int ret = CMD_SUCCESS; + bool is_wildcard = false; struct ecommunity *ecom = NULL; for (int i = rt_idx; i < argc; i++) { + is_wildcard = false; + + /* + * Special handling for wildcard '*' here. + * + * Let's just convert it to 0 here so we dont have to modify + * the ecommunity parser. + */ + if ((argv[i]->arg)[0] == '*') { + if (!is_import) { + vty_out(vty, + "%% Wildcard '*' only applicable for import\n"); + ret = CMD_WARNING; + continue; + } + + (argv[i]->arg)[0] = '0'; + is_wildcard = true; + } + ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + /* Put it back as was */ + if (is_wildcard) + (argv[i]->arg)[0] = '*'; + if (!ecom) { vty_out(vty, "%% Malformed Route Target list\n"); ret = CMD_WARNING; @@ -5912,7 +5939,7 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, ecommunity_str(ecom); if (is_add) { - if (add_rt(bgp, ecom, is_import) != 0) { + if (add_rt(bgp, ecom, is_import, is_wildcard) != 0) { vty_out(vty, "%% RT specified already configured for this VRF: %s\n", argv[i]->arg); @@ -5943,7 +5970,7 @@ DEFUN (bgp_evpn_vrf_rt, "import and export\n" "import\n" "export\n" - "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)\n") { int ret = CMD_SUCCESS; int tmp_ret = CMD_SUCCESS; @@ -5964,6 +5991,11 @@ DEFUN (bgp_evpn_vrf_rt, return CMD_WARNING_CONFIG_FAILED; } + if (strmatch(argv[2]->arg, "auto")) { + vty_out(vty, "%% `auto` cannot be configured via list\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Add/update the import route-target */ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true); @@ -5989,9 +6021,30 @@ DEFUN (bgp_evpn_vrf_rt_auto, "export\n" "Automatically derive route target\n") { - // TODO: auto - vty_out(vty, "AUTO TODO\n"); - return CMD_WARNING_CONFIG_FAILED; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + 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_CONFIG_FAILED; + } + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + bgp_evpn_configure_import_auto_rt_for_vrf(bgp); + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + bgp_evpn_configure_export_auto_rt_for_vrf(bgp); + + return CMD_SUCCESS; } DEFUN (no_bgp_evpn_vrf_rt, @@ -6010,7 +6063,7 @@ DEFUN (no_bgp_evpn_vrf_rt, int rt_type; if (!bgp) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; if (!strcmp(argv[2]->arg, "import")) rt_type = RT_TYPE_IMPORT; @@ -6023,24 +6076,29 @@ DEFUN (no_bgp_evpn_vrf_rt, return CMD_WARNING_CONFIG_FAILED; } + if (!strcmp(argv[3]->arg, "auto")) { + vty_out(vty, "%% `auto` cannot be unconfigured via list\n"); + return CMD_WARNING_CONFIG_FAILED; + } + 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; + return CMD_WARNING_CONFIG_FAILED; } } 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; + return CMD_WARNING_CONFIG_FAILED; } } 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; + return CMD_WARNING_CONFIG_FAILED; } } @@ -6069,9 +6127,51 @@ DEFUN (no_bgp_evpn_vrf_rt_auto, "export\n" "Automatically derive route target\n") { - // TODO: auto - vty_out(vty, "AUTO TODO\n"); - return CMD_WARNING_CONFIG_FAILED; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + 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_CONFIG_FAILED; + } + + if (rt_type == RT_TYPE_IMPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Import AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } else if (rt_type == RT_TYPE_EXPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } else if (rt_type == RT_TYPE_BOTH) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD) && + !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Import/Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + bgp_evpn_unconfigure_import_auto_rt_for_vrf(bgp); + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + bgp_evpn_unconfigure_export_auto_rt_for_vrf(bgp); + + return CMD_SUCCESS; } DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd, @@ -6559,13 +6659,36 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " route-target import %s\n", ecom_str); + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) { + char *vni_str = NULL; + + vni_str = strchr(ecom_str, ':') + 1; + + if (!vni_str) + continue; /* This should never happen */ + + vty_out(vty, " route-target import *:%s\n", + vni_str); + + } else + vty_out(vty, " route-target import %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } } + /* import route-target auto */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) + vty_out(vty, " route-target import auto\n"); + /* export route-target */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { char *ecom_str; @@ -6574,12 +6697,20 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); vty_out(vty, " route-target export %s\n", ecom_str); XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } } + + /* export route-target auto */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) + vty_out(vty, " route-target export auto\n"); } void bgp_ethernetvpn_init(void) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 8348b37b8..30d89c108 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -719,8 +719,10 @@ struct bgp { #define BGP_VRF_AUTO (1 << 0) #define BGP_VRF_IMPORT_RT_CFGD (1 << 1) #define BGP_VRF_EXPORT_RT_CFGD (1 << 2) -#define BGP_VRF_RD_CFGD (1 << 3) -#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4) +#define BGP_VRF_IMPORT_AUTO_RT_CFGD (1 << 3) /* retain auto when cfgd */ +#define BGP_VRF_EXPORT_AUTO_RT_CFGD (1 << 4) /* retain auto when cfgd */ +#define BGP_VRF_RD_CFGD (1 << 5) +#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6) /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; -- cgit v1.2.3 From 24df3379881fc7bec6c23294ca5b8c33fcecab63 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Wed, 3 Mar 2021 12:56:27 -0500 Subject: bgpd: add route-map to `no` advertise ipvX * cmd Add route-map as a possible word for the `no` form of `advertise ipvX *** [route-map WORD] cmd. Before this patch the cmd was only accepted if `no` form was given without route-map WORD. So if you just copypaste the original version of the cmd, it would fail. Signed-off-by: Stephen Worley --- bgpd/bgp_evpn_vty.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 13a63f153..d703830f2 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4024,11 +4024,13 @@ DEFUN (bgp_evpn_advertise_type5, DEFUN (no_bgp_evpn_advertise_type5, no_bgp_evpn_advertise_type5_cmd, - "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", NO_STR "Advertise prefix routes\n" BGP_AFI_HELP_STR - BGP_SAFI_HELP_STR) + BGP_SAFI_HELP_STR + "route-map for filtering specific routes\n" + "Name of the route map\n") { struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ int idx_afi = 0; -- cgit v1.2.3 From 6eb8350586c7eb2afa1ed20381c1febd296e38ed Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 9 Mar 2021 18:59:09 -0500 Subject: bgpd,lib: route-map/plist matching via type-2/5 routes Implement the ability to match type-2 and type-5 routes via a route-map and a prefix-list. Add some library code to convert an evpn prefix into a general ipv4/ipv6 prefix for type-2 and type-5 routes. evpn prefix is really just another subtype of prefix so all the info needed can be extracted right there. Add a special handler to bgp_routemap for evpn type routes when applying the outbound route-map. This calls the library code to convert the evpn_prefix to a ipv4/ipv6 prefix and run it through the plist code. In this we assume type-2 routes are a /32. Signed-off-by: Stephen Worley --- bgpd/bgp_routemap.c | 18 +++++++++++++++++ lib/prefix.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/prefix.h | 1 + 3 files changed, 76 insertions(+) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 33f68c9e8..3d7442f45 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -642,6 +642,20 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, return RMAP_NOMATCH; } +static enum route_map_cmd_result_t +route_match_prefix_list_evpn(afi_t afi, struct prefix_list *plist, + const struct prefix *p) +{ + /* Convert to match a general plist */ + struct prefix new; + + if (evpn_prefix2prefix(p, &new)) + return RMAP_NOMATCH; + + return (prefix_list_apply(plist, &new) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + static enum route_map_cmd_result_t route_match_address_prefix_list(void *rule, afi_t afi, const struct prefix *prefix, void *object) @@ -655,6 +669,10 @@ route_match_address_prefix_list(void *rule, afi_t afi, if (prefix->family == AF_FLOWSPEC) return route_match_prefix_list_flowspec(afi, plist, prefix); + + else if (prefix->family == AF_EVPN) + return route_match_prefix_list_evpn(afi, plist, prefix); + return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); } diff --git a/lib/prefix.c b/lib/prefix.c index e64b10bf2..a3b8e5c82 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1404,6 +1404,63 @@ bool ipv4_unicast_valid(const struct in_addr *addr) return true; } +static int ipaddr2prefix(const struct ipaddr *ip, uint16_t prefixlen, + struct prefix *p) +{ + switch (ip->ipa_type) { + case (IPADDR_V4): + p->family = AF_INET; + p->u.prefix4 = ip->ipaddr_v4; + p->prefixlen = prefixlen; + break; + case (IPADDR_V6): + p->family = AF_INET6; + p->u.prefix6 = ip->ipaddr_v6; + p->prefixlen = prefixlen; + break; + case (IPADDR_NONE): + p->family = AF_UNSPEC; + break; + } + + return 0; +} + +/* + * Convert type-2 and type-5 evpn route prefixes into the more + * general ipv4/ipv6 prefix types so we can match prefix lists + * and such. + */ +int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to) +{ + const struct evpn_addr *addr; + + if (evpn->family != AF_EVPN) + return -1; + + addr = &evpn->u.prefix_evpn; + + switch (addr->route_type) { + case (2): + if (IS_IPADDR_V4(&addr->macip_addr.ip)) + ipaddr2prefix(&addr->macip_addr.ip, 32, to); + else if (IS_IPADDR_V6(&addr->macip_addr.ip)) + ipaddr2prefix(&addr->macip_addr.ip, 128, to); + else + return -1; /* mac only? */ + + break; + case (5): + ipaddr2prefix(&addr->prefix_addr.ip, + addr->prefix_addr.ip_prefix_length, to); + break; + default: + return -1; + } + + return 0; +} + printfrr_ext_autoreg_p("EA", printfrr_ea); static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) diff --git a/lib/prefix.h b/lib/prefix.h index 7b2f88987..026e525f6 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -510,6 +510,7 @@ extern char *esi_to_str(const esi_t *esi, char *buf, int size); extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); extern bool ipv4_unicast_valid(const struct in_addr *addr); +extern int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to); static inline int ipv6_martian(const struct in6_addr *addr) { -- cgit v1.2.3 From 0f33e6dfca3528bdd1b079e2f54df493f565481a Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Fri, 7 May 2021 15:17:58 -0400 Subject: doc: add doc for EVPN L3 Route-Targets Add doc for EVPN L3 Route-Targets. Signed-off-by: Stephen Worley --- doc/user/bgp.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index e31bfe7bf..0feb0a4ab 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2869,6 +2869,20 @@ sysctl configurations: For more information, see ``man 7 arp``. +.. _bgp-evpn-l3-route-targets: + +EVPN L3 Route-Targets +^^^^^^^^^^^^^^^^^^^^^ + +.. clicmd:: route-target + +Modifty the route-target set for EVPN advertised type-2/type-5 routes. +RTLIST is a list of any of matching +``(A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)`` where ``*`` indicates wildcard +matching for the AS number. It will be set to match any AS number. This is +useful in datacenter deployments with Downstream VNI. ``auto`` is used to +retain the autoconfigure that is default behavior for L3 RTs. + .. _bgp-evpn-advertise-pip: EVPN advertise-PIP -- cgit v1.2.3 From a33b4d3f4c0bd4c0df222d388f2f6a20717717c6 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Wed, 12 May 2021 11:58:37 -0400 Subject: lib: handle type2/5 routes in optimized route-map Handle matching type2/5 evpn routes via lookup in the optimized route-maps used by plists. Convert the evpn_prefix to ipv4/v6 prefix to perform longest matching on in the tree. Signed-off-by: Stephen Worley --- lib/routemap.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/routemap.c b/lib/routemap.c index 9529b7941..7c733821c 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1820,7 +1820,24 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, struct route_map_index *index = NULL, *best_index = NULL; struct route_map_index *head_index = NULL; struct route_table *table = NULL; - unsigned char family = prefix->family; + struct prefix conv; + unsigned char family; + + /* + * Handling for matching evpn_routes in the prefix table. + * + * We convert type2/5 prefix to ipv4/6 prefix to do longest + * prefix matching on. + */ + if (prefix->family == AF_EVPN) { + if (evpn_prefix2prefix(prefix, &conv) != 0) + return NULL; + + prefix = &conv; + } + + + family = prefix->family; if (family == AF_INET) table = map->ipv4_prefix_table; -- cgit v1.2.3 From 133c000cc2fd943b49d1f11c9c81048337326181 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Fri, 4 Jun 2021 16:29:46 -0400 Subject: doc: fix typo in route-target Fix small typo in route-target doc. Signed-off-by: Stephen Worley --- doc/user/bgp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 0feb0a4ab..024884f8b 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2876,7 +2876,7 @@ EVPN L3 Route-Targets .. clicmd:: route-target -Modifty the route-target set for EVPN advertised type-2/type-5 routes. +Modify the route-target set for EVPN advertised type-2/type-5 routes. RTLIST is a list of any of matching ``(A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)`` where ``*`` indicates wildcard matching for the AS number. It will be set to match any AS number. This is -- cgit v1.2.3 From 5ad4fc6ce9b2e8b2161484a5fb86410976cc8fd9 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Fri, 11 Mar 2022 13:28:30 -0500 Subject: lib: use evpn type enum for prefix conversion Use the evpn type enum for the evpn_prefix2prefix conversion. Signed-off-by: Stephen Worley --- lib/prefix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/prefix.c b/lib/prefix.c index a3b8e5c82..4642f14d3 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1441,7 +1441,7 @@ int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to) addr = &evpn->u.prefix_evpn; switch (addr->route_type) { - case (2): + case BGP_EVPN_MAC_IP_ROUTE: if (IS_IPADDR_V4(&addr->macip_addr.ip)) ipaddr2prefix(&addr->macip_addr.ip, 32, to); else if (IS_IPADDR_V6(&addr->macip_addr.ip)) @@ -1450,7 +1450,7 @@ int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to) return -1; /* mac only? */ break; - case (5): + case BGP_EVPN_IP_PREFIX_ROUTE: ipaddr2prefix(&addr->prefix_addr.ip, addr->prefix_addr.ip_prefix_length, to); break; -- cgit v1.2.3 From a5d7012ca0c04c97bd15b67e9f6e9211203b7c31 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Tue, 23 Aug 2022 12:22:08 -0400 Subject: bgpd: use DEFPY for new vrf rt auto commands Switch to using DEFPY for new vrf rt auto commands. Signed-off-by: Stephen Worley --- bgpd/bgp_evpn_vty.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index d703830f2..0b3d60103 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -6014,9 +6014,9 @@ DEFUN (bgp_evpn_vrf_rt, return ret; } -DEFUN (bgp_evpn_vrf_rt_auto, +DEFPY (bgp_evpn_vrf_rt_auto, bgp_evpn_vrf_rt_auto_cmd, - "route-target auto", + "route-target $type auto", "Route Target\n" "import and export\n" "import\n" @@ -6029,11 +6029,11 @@ DEFUN (bgp_evpn_vrf_rt_auto, if (!bgp) return CMD_WARNING_CONFIG_FAILED; - if (!strcmp(argv[1]->arg, "import")) + if (strmatch(type, "import")) rt_type = RT_TYPE_IMPORT; - else if (!strcmp(argv[1]->arg, "export")) + else if (strmatch(type, "export")) rt_type = RT_TYPE_EXPORT; - else if (!strcmp(argv[1]->arg, "both")) + else if (strmatch(type, "both")) rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); @@ -6119,9 +6119,9 @@ DEFUN (no_bgp_evpn_vrf_rt, return ret; } -DEFUN (no_bgp_evpn_vrf_rt_auto, +DEFPY (no_bgp_evpn_vrf_rt_auto, no_bgp_evpn_vrf_rt_auto_cmd, - "no route-target auto", + "no route-target $type auto", NO_STR "Route Target\n" "import and export\n" @@ -6135,11 +6135,11 @@ DEFUN (no_bgp_evpn_vrf_rt_auto, if (!bgp) return CMD_WARNING_CONFIG_FAILED; - if (!strcmp(argv[2]->arg, "import")) + if (strmatch(type, "import")) rt_type = RT_TYPE_IMPORT; - else if (!strcmp(argv[2]->arg, "export")) + else if (strmatch(type, "export")) rt_type = RT_TYPE_EXPORT; - else if (!strcmp(argv[2]->arg, "both")) + else if (strmatch(type, "both")) rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); -- cgit v1.2.3