From 9c97bc44468a03c58b62ffe789ee93561f8a846e Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Tue, 29 Dec 2020 17:39:16 -0800 Subject: [PATCH 01/14] bgpd: Data structure for gateway IP overlay Index "struct bgp_route_evpn" is used to store an overlay index. Add a "type" for overlay index. Signed-off-by: Ameya Dharkar --- bgpd/bgp_attr_evpn.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 6fdf73fd1e..102509fdd7 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -30,7 +30,21 @@ union gw_addr { struct in6_addr ipv6; }; +enum overlay_index_type { + OVERLAY_INDEX_TYPE_NONE, + OVERLAY_INDEX_GATEWAY_IP, + OVERLAY_INDEX_ESI, + OVERLAY_INDEX_MAC, +}; + +/* + * Structure to store ovrelay index for EVPN type-5 route + * This structure stores ESI and Gateway IP overlay index. + * MAC overlay index is stored in the RMAC attribute. + */ struct bgp_route_evpn { + enum overlay_index_type type; + esi_t eth_s_id; union gw_addr gw_ip; }; From d0a4ee6010a6ba66aee396c2fe56e4ec6b45a553 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Fri, 21 May 2021 02:04:00 -0700 Subject: [PATCH 02/14] bgpd: Add "set evpn gateway-ip" clause for route-map - Add following set clause for route-maps "set evpn gateway-ip A.B.C.D|X:X::X:X" - When this route-map is applied as outboubd policy in BGP, it will set the gateway-ip in BGP attribute For EVPN type-5 routes. Example configuration: route-map RMAP-EVPN_GWIP permit 5 set evpn gateway-ip ipv4 50.0.2.12 set evpn gateway-ip ipv6 50:0:2::12 router bgp 101 bgp router-id 10.100.0.1 neighbor 10.0.1.2 remote-as 102 ! address-family l2vpn evpn neighbor 10.0.1.2 activate neighbor 10.0.1.2 route-map RMAP-EVPN_GWIP out advertise-all-vni exit-address-family Signed-off-by: Ameya Dharkar --- bgpd/bgp_routemap.c | 213 ++++++++++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 14 +++ bgpd/bgp_routemap_nb.h | 8 ++ bgpd/bgp_routemap_nb_config.c | 104 +++++++++++++++++ lib/routemap.h | 4 + lib/routemap_cli.c | 10 ++ yang/frr-bgp-route-map.yang | 32 +++++ 7 files changed, 385 insertions(+) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 921a3e0dd9..ca1407b86a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1084,6 +1084,71 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = { route_match_rd_free }; +static enum route_map_cmd_result_t +route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object) +{ + struct ipaddr *gw_ip = rule; + struct bgp_path_info *path; + struct prefix_evpn *evp; + + if (prefix->family != AF_EVPN) + return RMAP_OKAY; + + evp = (struct prefix_evpn *)prefix; + if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE) + return RMAP_OKAY; + + if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4) + || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16)) + return RMAP_OKAY; + + path = object; + + /* Set gateway-ip value. */ + path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr, + IPADDRSZ(gw_ip)); + + return RMAP_OKAY; +} + +/* + * Route map `evpn gateway-ip' compile function. + * Given string is converted to struct ipaddr structure + */ +static void *route_set_evpn_gateway_ip_compile(const char *arg) +{ + struct ipaddr *gw_ip = NULL; + int ret; + + gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr)); + + ret = str2ipaddr(arg, gw_ip); + if (ret < 0) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); + return NULL; + } + return gw_ip; +} + +/* Free route map's compiled `evpn gateway_ip' value. */ +static void route_set_evpn_gateway_ip_free(void *rule) +{ + struct ipaddr *gw_ip = rule; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); +} + +/* Route map commands for set evpn gateway-ip ipv4. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = { + "evpn gateway-ip ipv4", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + +/* Route map commands for set evpn gateway-ip ipv6. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = { + "evpn gateway-ip ipv6", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + /* Route map commands for VRF route leak with source vrf matching */ static enum route_map_cmd_result_t route_match_vrl_source_vrf(void *rule, const struct prefix *prefix, @@ -4066,6 +4131,148 @@ DEFUN_YANG (no_match_evpn_rd, return nb_cli_apply_changes(vty, NULL); } +DEFUN_YANG (set_evpn_gw_ip_ipv4, + set_evpn_gw_ip_ipv4_cmd, + "set evpn gateway-ip ipv4 A.B.C.D", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv4, + no_set_evpn_gw_ip_ipv4_cmd, + "no set evpn gateway-ip ipv4 A.B.C.D", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (set_evpn_gw_ip_ipv6, + set_evpn_gw_ip_ipv6_cmd, + "set evpn gateway-ip ipv6 X:X::X:X", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv6 address\n" + "Gateway IP address in IPv6 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv6, + no_set_evpn_gw_ip_ipv6_cmd, + "no set evpn gateway-ip ipv6 X:X::X:X", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + DEFPY_YANG(match_vrl_source_vrf, match_vrl_source_vrf_cmd, "match source-vrf NAME$vrf_name", @@ -6123,6 +6330,8 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd); route_map_install_set(&route_set_table_id_cmd); route_map_install_set(&route_set_srte_color_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); @@ -6166,6 +6375,10 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_evpn_rd_cmd); install_element(RMAP_NODE, &match_evpn_default_route_cmd); install_element(RMAP_NODE, &no_match_evpn_default_route_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd); install_element(RMAP_NODE, &match_vrl_source_vrf_cmd); install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index b165c5d0ee..1254591b87 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -371,6 +371,20 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy, + } + }, { .xpath = NULL, }, diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a15f521513..f0e492eb61 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -130,6 +130,14 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_mod int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args); #ifdef __cplusplus } diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index ff08c16a82..e541d117be 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2637,3 +2637,107 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec { return lib_route_map_entry_set_destroy(args); } + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv4"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv6"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/lib/routemap.h b/lib/routemap.h index 5b6b64eaeb..4d76ae1536 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -366,6 +366,10 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:ipv4-vpn-address")) #define IS_SET_BGP_IPV4_NH(A) \ (strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6")) /* Prototypes. */ extern void route_map_init(void); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index e11b9eea74..bf982cfa2b 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1248,6 +1248,16 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:ipv4-nexthop")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(action)) { + vty_out(vty, " set evpn gateway-ip ipv4 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(action)) { + vty_out(vty, " set evpn gateway-ip ipv6 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6")); } } diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index ca60c8f7b6..1c990b5ed9 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -300,6 +300,18 @@ module frr-bgp-route-map { "Set BGP large community list (for deletion)"; } + identity set-evpn-gateway-ip-ipv4 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv4"; + } + + identity set-evpn-gateway-ip-ipv6 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv6"; + } + grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; @@ -816,5 +828,25 @@ module frr-bgp-route-map { type bgp-filter:bgp-list-name; } } + case evpn-gateway-ip-ipv4 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv4')"; + description + "Set EVPN gateway IP overlay index IPv4"; + leaf evpn-gateway-ip-ipv4 { + type inet:ipv4-address; + } + } + case evpn-gateway-ip-ipv6 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv6')"; + description + "Set EVPN gateway IP overlay index IPv6"; + leaf evpn-gateway-ip-ipv6 { + type inet:ipv6-address; + } + } } } From d4a88de3bb32f76573af09f6758c43773b335bf6 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 4 Jan 2021 18:31:11 -0800 Subject: [PATCH 03/14] bgpd: CLI to advertise gateway IP overlay index Adds gateway-ip option to advertise ipv4/ipv6 unicast CLI. dev(config-router-af)# advertise unicast gateway-ip Specify EVPN Overlay Index route-map route-map for filtering specific routes When gateway-ip is specified, gateway IP field of EVPN RT-5 NLRI is filled with the BGP nexthop of the vrf prefix being advertised. No support for ESI overlay index yet. Test cases: 1) advertise ipv4 unicast 2) advertise ipv4 unicast gateway-ip 3) advertise ipv6 unicast 4) advertise ipv6 unicast gateway-ip 5) Modify from no-overlay-index to gateway-ip 6) Modify from gateway-ip to no-overlay-index 7) CLI with route-map and modify route-map Author: Sri Mohana Singamsetty Signed-off-by: Sri Mohana Singamsetty --- bgpd/bgp_evpn.h | 16 +-- bgpd/bgp_evpn_vty.c | 236 +++++++++++++++++++++++++++++++++++-------- bgpd/bgp_evpn_vty.h | 4 + bgpd/bgp_nb_config.c | 64 ++++++++---- bgpd/bgpd.h | 18 ++-- 5 files changed, 265 insertions(+), 73 deletions(-) diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 83a6dd84c8..90e088c3eb 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -63,14 +63,18 @@ static inline int advertise_type5_routes(struct bgp *bgp_vrf, if (!bgp_vrf->l3vni) return 0; - if (afi == AFI_IP && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) + if ((afi == AFI_IP) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)))) return 1; - if (afi == AFI_IP6 && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) + if ((afi == AFI_IP6) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)))) return 1; return 0; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index fb3ba2c0ec..fccaf9166c 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -59,6 +59,17 @@ struct vni_walk_ctx { int detail; }; +int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx, + enum overlay_index_type *oly) +{ + *oly = OVERLAY_INDEX_TYPE_NONE; + if (argv_find(argv, argc, "gateway-ip", oly_idx)) { + if (oly) + *oly = OVERLAY_INDEX_GATEWAY_IP; + } + return 1; +} + static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, json_object *json) { @@ -3784,10 +3795,11 @@ DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet, DEFUN (bgp_evpn_advertise_type5, bgp_evpn_advertise_type5_cmd, - "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map WORD]", "Advertise prefix routes\n" BGP_AFI_HELP_STR BGP_SAFI_HELP_STR + "advertise gateway IP overlay index\n" "route-map for filtering specific routes\n" "Name of the route map\n") { @@ -3799,9 +3811,14 @@ DEFUN (bgp_evpn_advertise_type5, safi_t safi = 0; int ret = 0; int rmap_changed = 0; + enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE; + int idx_oly = 0; + bool adv_flag_changed = false; argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly); + ret = argv_find(argv, argc, "route-map", &idx_rmap); if (ret) { if (!bgp_vrf->adv_cmd_rmap[afi][safi].name) @@ -3826,39 +3843,149 @@ DEFUN (bgp_evpn_advertise_type5, return CMD_WARNING; } - if (afi == AFI_IP) { - - /* if we are already advertising ipv4 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); - } else { - - /* if we are already advertising ipv6 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + if ((oly != OVERLAY_INDEX_TYPE_NONE) + && (oly != OVERLAY_INDEX_GATEWAY_IP)) { + vty_out(vty, "%%Unknown overlay-index type specified"); + return CMD_WARNING; } - if (rmap_changed) { + if (afi == AFI_IP) { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv4 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else { + + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } + } else { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv6 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else { + + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } + } + + if ((rmap_changed) || (adv_flag_changed)) { + + /* If either of these are changed, then FRR needs to + * withdraw already advertised type5 routes. + */ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); - if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_vrf->adv_cmd_rmap[afi][safi].name); - route_map_counter_decrement( + if (rmap_changed) { + if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_vrf->adv_cmd_rmap[afi][safi].name); + route_map_counter_decrement( bgp_vrf->adv_cmd_rmap[afi][safi].map); - bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; - bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + } } } @@ -3912,22 +4039,30 @@ DEFUN (no_bgp_evpn_advertise_type5, /* if we are not advertising ipv4 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); } } else { /* if we are not advertising ipv6 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); } } @@ -6103,21 +6238,40 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " flooding disable\n"); if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) vty_out(vty, " advertise ipv4 unicast route-map %s\n", bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv4 unicast\n"); + vty_out(vty, + " advertise ipv4 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv4 unicast gateway-ip route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv4 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) - vty_out(vty, " advertise ipv6 unicast route-map %s\n", + vty_out(vty, + " advertise ipv6 unicast route-map %s\n", bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv6 unicast\n"); + vty_out(vty, + " advertise ipv6 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv6 unicast gateway-ip route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv6 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 33f6e4f1b6..137365ddbf 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -28,6 +28,10 @@ extern void bgp_ethernetvpn_init(void); #define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n" #define EVPN_HELP_STR "Ethernet Virtual Private Network\n" +extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, + int *oly_idx, + enum overlay_index_type *oly); + /* Parse type from "type ", return -1 on failure */ extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc); diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 5189d7ba8f..ab22aee1ca 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -182,24 +182,52 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args) for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - (bgp == bgp_get_evpn() && - (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || - (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { + if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || (bgp == bgp_get_evpn() + && (CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) + || (tmp_bgp->vnihash + && hashcount(tmp_bgp->vnihash))) { snprintf( args->errmsg, args->errmsg_len, "Cannot delete default BGP instance. Dependent VRF instances exist\n"); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b18cd4ca77..f2c8ece024 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -510,16 +510,18 @@ struct bgp { uint16_t af_flags[AFI_MAX][SAFI_MAX]; #define BGP_CONFIG_DAMPENING (1 << 0) /* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */ -#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1) -#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6) /* import/export between address families */ -#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5) -#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6) +#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7) +#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8) /* vrf-route leaking flags */ -#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7) -#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8) +#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9) +#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10) /* BGP per AF peer count */ uint32_t af_peer_count[AFI_MAX][SAFI_MAX]; From 6c995628c1d175d255c2a02f108813090da4c4e2 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Sun, 10 Jan 2021 18:32:34 -0800 Subject: [PATCH 04/14] bgpd: Generate and advertise gateway IP overlay index with EVPN RT-5 Gateway IP overlay index is generated for EVPN RT-5 when following CLI is configured. router bgp 100 vrf vrf-blue address-family l2vpn evpn advertise ipv4 unicast gateway-ip advertise ipv6 unicast gateway-ip BGP nexthop of the VRF IP/IPv6 route is set as the gateway IP of the corresponding EVPN RT-5 Signed-off-by: Ameya Dharkar --- bgpd/bgp_debug.c | 26 ++++++++++++++++++++++++-- bgpd/bgp_debug.h | 13 +++++++------ bgpd/bgp_evpn.c | 29 +++++++++++++++++++++++++---- bgpd/bgp_mac.c | 4 ++-- bgpd/bgp_route.c | 30 +++++++++++++++--------------- bgpd/bgp_updgrp_packet.c | 3 ++- 6 files changed, 75 insertions(+), 30 deletions(-) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 8f286e66df..856afb05f8 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2680,10 +2680,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size) { char rd_buf[RD_ADDRSTRLEN]; char tag_buf[30]; + char overlay_index_buf[INET6_ADDRSTRLEN + 14]; + const struct prefix_evpn *evp; + /* ' with addpath ID ' 17 * max strlen of uint32 + 10 * +/- (just in case) + 1 @@ -2701,6 +2705,23 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u", addpath_id); + overlay_index_buf[0] = '\0'; + if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) { + char obuf[INET6_ADDRSTRLEN]; + + obuf[0] = '\0'; + evp = pu.evp; + if (is_evpn_prefix_ipaddr_v4(evp)) + inet_ntop(AF_INET, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + else if (is_evpn_prefix_ipaddr_v6(evp)) + inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + + snprintf(overlay_index_buf, sizeof(overlay_index_buf), + " gateway IP %s", obuf); + } + tag_buf[0] = '\0'; if (bgp_labeled_safi(safi) && num_labels) { @@ -2720,9 +2741,10 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, } if (prd) - snprintfrr(str, size, "RD %s %pFX%s%s %s %s", + snprintfrr(str, size, "RD %s %pFX%s%s%s %s %s", prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), pu.p, - tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); + overlay_index_buf, tag_buf, pathid_buf, afi2str(afi), + safi2str(safi)); else if (safi == SAFI_FLOWSPEC) { char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; const struct prefix_fs *fs = pu.fs; diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index fa8da1c345..d847fb84e7 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -37,7 +37,8 @@ #define DUMP_DETAIL 32 /* RD + Prefix + Path-Id */ -#define BGP_PRD_PATH_STRLEN (PREFIX_STRLEN + RD_ADDRSTRLEN + 20) +#define BGP_PRD_PATH_STRLEN \ + (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34) extern int dump_open; extern int dump_update; @@ -179,11 +180,11 @@ extern bool bgp_debug_update(struct peer *peer, const struct prefix *p, extern bool bgp_debug_bestpath(struct bgp_dest *dest); extern bool bgp_debug_zebra(const struct prefix *p); -extern const char * -bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd, - union prefixconstptr pu, mpls_label_t *label, - uint32_t num_labels, int addpath_valid, - uint32_t addpath_id, char *str, int size); +extern const char *bgp_debug_rdpfxpath2str( + afi_t afi, safi_t safi, const struct prefix_rd *prd, + union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, + int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size); const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data, size_t datalen); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d8e57419ee..b29825afc5 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1261,7 +1261,8 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, /* update evpn type-5 route entry */ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, - struct attr *src_attr) + struct attr *src_attr, afi_t src_afi, + safi_t src_safi) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1315,6 +1316,26 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + if (src_afi == AFI_IP6 && + CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) { + if (src_attr && + !IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv6, + &src_attr->mp_nexthop_global, + sizeof(struct in6_addr)); + } + } else if (src_afi == AFI_IP && + CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) { + if (src_attr && src_attr->nexthop.s_addr != 0) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv4, + &src_attr->nexthop, sizeof(struct in_addr)); + } + } + /* Setup RT and encap extended community */ build_evpn_type5_route_extcomm(bgp_vrf, &attr); @@ -4078,7 +4099,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */ stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); - if (attr) + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI) stream_put(s, &attr->esi, sizeof(esi_t)); else stream_put(s, 0, sizeof(esi_t)); @@ -4088,7 +4109,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr); else stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16); - if (attr) { + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { const struct bgp_route_evpn *evpn_overlay = bgp_attr_get_evpn_overlay(attr); @@ -4301,7 +4322,7 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct prefix_evpn evp; build_type5_prefix_from_ip_prefix(&evp, p); - ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr); + ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi); if (ret) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: Failed to create type-5 route for prefix %pFX", diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 3d7bc08ac5..02b7e64869 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -200,8 +200,8 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, AFI_L2VPN, SAFI_EVPN, &prd, p, label_pnt, num_labels, pi->addpath_rx_id ? 1 : 0, - pi->addpath_rx_id, pfx_buf, - sizeof(pfx_buf)); + pi->addpath_rx_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s skip update of %s marked as removed", peer->host, pfx_buf); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8438621f68..c539bccb62 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3867,7 +3867,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); @@ -3893,7 +3893,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s...duplicate ignored", @@ -3920,8 +3920,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s, flapped quicker than processing", peer->host, pfx_buf); @@ -3934,7 +3934,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4207,8 +4207,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4353,8 +4353,8 @@ filtered: } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s", peer->host, pfx_buf, reason); } @@ -4438,8 +4438,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s withdrawing route %s not in adj-in", peer->host, pfx_buf); @@ -4458,8 +4458,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Logging. */ if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host, pfx_buf); } @@ -4479,8 +4479,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } else if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s Can't find the route %s", peer->host, pfx_buf); } diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 6418decd16..038ef4f798 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -862,6 +862,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, label_pnt, num_labels, addpath_encode, addpath_tx_id, + &adv->baa->attr->evpn_overlay, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s", subgrp->update_group->id, subgrp->id, @@ -1031,7 +1032,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0, addpath_encode, addpath_tx_id, - pfx_buf, sizeof(pfx_buf)); + NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable", subgrp->update_group->id, subgrp->id, pfx_buf); From 8304dabfab9063fe5720e8e714476ed2e9127540 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Sun, 10 Jan 2021 19:49:11 -0800 Subject: [PATCH 05/14] bgpd: EVPN route type-5 gateway IP show command Display gateway IP attribute in show command "show bgp l2vpn evpn route type prefix [json]" dev# sh bgp l2vpn evpn 100.0.0.21 BGP routing table entry for 10.100.0.2:1000:[5]:[0]:[32]:[100.0.0.21] Paths: (1 available, best #1) Advertised to non peer-group peers: 10.0.1.1 Route [5]:[0]:[32]:[100.0.0.21] VNI 1000 Gateway IP 50.0.2.21 203 10.100.0.2 from 0.0.0.0 (10.100.0.2) Origin IGP, metric 0, valid, sourced, local, best (First path received) Extended Community: ET:8 RT:102:1000 Rmac:72:48:54:da:7f:13 Last update: Mon Jun 29 12:29:05 2020 Signed-off-by: Ameya Dharkar --- bgpd/bgp_route.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index c539bccb62..4ec8ac305a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -9647,6 +9647,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_nexthop_global = json_object_new_object(); } + if (safi == SAFI_EVPN) { + if (!json_paths) + vty_out(vty, " Route %pRN", bn); + } + if (path->extra) { char tag_buf[30]; @@ -9658,12 +9663,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } if (safi == SAFI_EVPN) { if (!json_paths) { - vty_out(vty, " Route %pFX", - (struct prefix_evpn *) - bgp_dest_get_prefix(bn)); if (tag_buf[0] != '\0') vty_out(vty, " VNI %s", tag_buf); - vty_out(vty, "\n"); } else { if (tag_buf[0]) json_object_string_add(json_path, "VNI", @@ -9710,6 +9711,27 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } + if (safi == SAFI_EVPN + && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + char gwip_buf[INET6_ADDRSTRLEN]; + + if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)&bn->p)) + inet_ntop(AF_INET, &attr->evpn_overlay.gw_ip.ipv4, + gwip_buf, sizeof(gwip_buf)); + else + inet_ntop(AF_INET6, &attr->evpn_overlay.gw_ip.ipv6, + gwip_buf, sizeof(gwip_buf)); + + if (json_paths) + json_object_string_add(json_path, "gatewayIP", + gwip_buf); + else + vty_out(vty, " Gateway IP %s", gwip_buf); + } + + if (safi == SAFI_EVPN) + vty_out(vty, "\n"); + /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { From 66ff60895ade27c0209444c58a2070a5cdbb385e Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Sun, 10 Jan 2021 21:40:42 -0800 Subject: [PATCH 06/14] bgpd: Parse EVPN RT-5 NLRI and store gateway IP for EVPN route While installing this route in the EVPN table, make sure all the conditions mentioned in the draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11 are met. Draft mentions following conditions: - ESI and gateway IP cannot be both nonzero at the same time. - ESI, gateway IP, RMAC and VNI label all cannot be 0 at the same time. If the received EVPN RT-5 route does not meet these conditions, the route is treated as withdraw. Signed-off-by: Ameya Dharkar --- bgpd/bgp_attr_evpn.c | 1 + bgpd/bgp_evpn.c | 44 +++++++++++++++++++++++++++++++------- bgpd/bgp_route.c | 50 +++++++++++--------------------------------- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 1df646c346..0e341a8c6b 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi) return false; } + diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index b29825afc5..ae365fa13e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -3972,7 +3972,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, mpls_label_t label; /* holds the VNI as in the packet */ int ret; afi_t gw_afi; - bool is_valid_update = false; + bool is_valid_update = true; /* Type-5 route should be 34 or 58 bytes: * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16), @@ -4001,9 +4001,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Additional information outside of prefix - ESI and GW IP */ memset(&evpn, 0, sizeof(evpn)); - /* Fetch ESI */ + /* Fetch ESI overlay index */ if (attr) - memcpy(&attr->esi, pfx, sizeof(esi_t)); + memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t)); pfx += ESI_BYTES; /* Fetch Ethernet Tag. */ @@ -4052,25 +4052,53 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, * field */ + /* + * An update containing a non-zero gateway IP and a non-zero ESI + * at the same time is should be treated as withdraw + */ + if (bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && !is_zero_gw_ip(&evpn.gw_ip, gw_afi)) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.", + peer->host); + is_valid_update = false; + } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id)) + evpn.type = OVERLAY_INDEX_ESI; + else if (!is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + evpn.type = OVERLAY_INDEX_GATEWAY_IP; if (attr) { - is_valid_update = true; - if (is_zero_mac(&attr->rmac) && - is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + if (is_zero_mac(&attr->rmac) + && !bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && is_zero_gw_ip(&evpn.gw_ip, gw_afi) && label == 0) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero", + peer->host); is_valid_update = false; + } if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac)) is_valid_update = false; } /* Process the route. */ - if (is_valid_update) + if (attr && is_valid_update) ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, 0, &evpn); - else + else { + if (!is_valid_update) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(attr, attr_str, BUFSIZ); + zlog_warn( + "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw", + peer->hostname, peer->bgp->vrf_id, &p, + attr_str); + } ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, &evpn); + } return ret; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 4ec8ac305a..794da3b9c1 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3442,23 +3442,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, return new; } -static void overlay_index_update(struct attr *attr, - union gw_addr *gw_ip) -{ - if (!attr) - return; - if (gw_ip == NULL) { - struct bgp_route_evpn eo; - - memset(&eo, 0, sizeof(eo)); - bgp_attr_set_evpn_overlay(attr, &eo); - } else { - struct bgp_route_evpn eo = {.gw_ip = *gw_ip}; - - bgp_attr_set_evpn_overlay(attr, &eo); - } -} - static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path, union gw_addr *gw_ip) { @@ -3641,6 +3624,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (has_valid_label) assert(label != NULL); + /* Update overlay index of the attribute */ + if (afi == AFI_L2VPN && evpn) + memcpy(&attr->evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); + /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ if (!soft_reconfig @@ -3816,12 +3804,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(&new_attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } - /* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following * condition : * Suppress fib is enabled @@ -3856,10 +3838,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, && (!has_valid_label || memcmp(&(bgp_path_info_extra_get(pi))->label, label, num_labels * sizeof(mpls_label_t)) - == 0) - && (overlay_index_equal( - afi, pi, - evpn == NULL ? NULL : &evpn->gw_ip))) { + == 0)) { if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { @@ -3867,7 +3846,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, NULL, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); @@ -3893,7 +3872,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, NULL, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s...duplicate ignored", @@ -3920,7 +3899,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, NULL, + addpath_id ? 1 : 0, addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s, flapped quicker than processing", @@ -3934,7 +3913,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, NULL, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4207,7 +4186,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, NULL, + addpath_id ? 1 : 0, addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4239,11 +4218,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(new->attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } /* Nexthop reachability check. */ if (((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) @@ -4353,7 +4327,7 @@ filtered: } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, NULL, + addpath_id ? 1 : 0, addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s", peer->host, pfx_buf, reason); From a2299abae82c41ea7aaa303dde91bfceee1a88b3 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Sun, 10 Jan 2021 22:32:41 -0800 Subject: [PATCH 07/14] bgpd: Import received EVPN RT-5 prefix with gateway IP in BGP VRF The IP/IPv6 prefix carried with EVPN RT-5 is imported in the BGP vrf according to the attached route targets. If the prefix carries a gateway IP overlay index, this gateway IP should be installed as the nexthop of the route imported in the BGP vrf. This route in vrf will be marked as VALID only if the nexthop is resolved in the SVI network. To receive runtime reachability information for the nexthop, register it with the nexthop tracking module. Send this route to zebra after processing. Signed-off-by: Ameya Dharkar --- bgpd/bgp_evpn.c | 59 +++++++++++++++++++++++++++++++++++++++++++----- bgpd/bgp_nht.c | 8 +++++-- bgpd/bgp_zebra.c | 41 ++++++++++++++++++++++++++------- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index ae365fa13e..d00a882c1a 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -53,6 +53,7 @@ #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_nht.h" /* * Definitions and external declarations. @@ -2402,6 +2403,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, bool new_pi = false; bool use_l3nhg = false; bool is_l3nhg_active = false; + char buf1[INET6_ADDRSTRLEN]; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); @@ -2432,10 +2434,36 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * make sure to set the flag for next hop attribute. */ attr = *parent_pi->attr; - if (afi == AFI_IP6) - evpn_convert_nexthop_to_ipv6(&attr); - else - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) { + if (afi == AFI_IP6) + evpn_convert_nexthop_to_ipv6(&attr); + else + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } else { + + /* + * If gateway IP overlay index is specified in the NLRI of + * EVPN RT-5, this gateway IP should be used as the nexthop + * for the prefix in the VRF + */ + if (bgp_debug_zebra(NULL)) { + zlog_debug( + "Install gateway IP %s as nexthop for prefix %pFX in vrf %s", + inet_ntop(pp->family, &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)), pp, + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + if (afi == AFI_IP6) { + memcpy(&attr.mp_nexthop_global, + &attr.evpn_overlay.gw_ip.ipv6, + sizeof(struct in6_addr)); + attr.mp_nexthop_len = IPV6_MAX_BYTELEN; + } else { + attr.nexthop = attr.evpn_overlay.gw_ip.ipv4; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg, &is_l3nhg_active, NULL); @@ -2481,8 +2509,27 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, pi->attr = attr_new; pi->uptime = bgp_clock(); } - /* as it is an importation, change nexthop */ - bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + + /* Gateway IP nexthop should be resolved */ + if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi, + NULL, 0)) + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + else { + if (BGP_DEBUG(nht, NHT)) { + inet_ntop(pp->family, + &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)); + zlog_debug("%s: gateway IP NH unresolved", + buf1); + } + bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); + } + } else { + + /* as it is an importation, change nexthop */ + bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + } /* Link path to evpn nexthop */ bgp_evpn_path_nh_add(bgp_vrf, pi); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 4b4a3716e6..638c72ae67 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -942,6 +942,9 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) * In case of unicast routes that were imported from vpn * and that have labels, they are valid only if there are * nexthops with labels + * + * If the nexthop is EVPN gateway-IP, + * do not check for a valid label. */ bool bnc_is_valid_nexthop = false; @@ -950,8 +953,9 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED && path->extra && - path->extra->num_labels) { - + path->extra->num_labels && + path->attr->evpn_overlay.type != + OVERLAY_INDEX_GATEWAY_IP) { bnc_is_valid_nexthop = bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c2c114d2c9..b32f319655 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1058,9 +1058,19 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, * connected routes leaked into a VRF. */ if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV4; + else { + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1085,9 +1095,19 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->vrf_id = nh_bgp->vrf_id; if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV6; + else { + api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1392,8 +1412,13 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api_nh->label_num = 1; api_nh->labels[0] = label; } - memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), - sizeof(struct ethaddr)); + + if (is_evpn + && mpinfo->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP) + memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), + sizeof(struct ethaddr)); + api_nh->weight = nh_weight; if (mpinfo->extra From 9daa5d471aeb1a6a89f3fa942c4ed6bea963358d Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 00:14:05 -0800 Subject: [PATCH 08/14] bgpd, zebra: Add svi_interface to zebra VNI and bgp EVPN structures SVI ifindex for L2VNI is required in BGP to perform EVPN type-5 to type-2 recusrsive resolution using gateway IP overlay index. Program this svi_ifindex in struct zebra_vni_t as well as in struct bgpevpn Changes include: 1. Add svi_if field to struct zebra_evpn_t 2. Add svi_ifindex field to struct bgpevpn 3. When SVI (bridge or VLAN) is bound to a VxLAN interface, store it in the zebra_evpn_t structure. 4. Add this SVI ifindex to ZEBRA_VNI_ADD 5. Store svi_ifindex in struct bgpevpn Signed-off-by: Ameya Dharkar --- bgpd/bgp_evpn.c | 16 +++++++++++----- bgpd/bgp_evpn.h | 3 ++- bgpd/bgp_evpn_private.h | 4 +++- bgpd/bgp_evpn_vty.c | 7 ++++++- bgpd/bgp_zebra.c | 13 ++++++++----- zebra/zebra_evpn.c | 26 ++++++++++++++++++++++---- zebra/zebra_evpn.h | 3 +++ zebra/zebra_vxlan.c | 12 ++++++++++++ 8 files changed, 67 insertions(+), 17 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d00a882c1a..6fb7045296 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -5230,7 +5230,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; @@ -5244,6 +5245,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, vpn->originator_ip = originator_ip; vpn->tenant_vrf_id = tenant_vrf_id; vpn->mcast_grp = mcast_grp; + vpn->svi_ifindex = svi_ifindex; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); @@ -5699,6 +5701,7 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + vpn->svi_ifindex = 0; /* * tunnel is no longer active, del tunnel ip address from tip_hash */ @@ -5719,8 +5722,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) - + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -5732,13 +5735,16 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip) && IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp) - && vpn->tenant_vrf_id == tenant_vrf_id) + && vpn->tenant_vrf_id == tenant_vrf_id + && vpn->svi_ifindex == svi_ifindex) /* Probably some other param has changed that we don't * care about. */ return 0; bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); + vpn->svi_ifindex = svi_ifindex; + /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { bgpevpn_unlink_from_l3vni(vpn); @@ -5762,7 +5768,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id, - mcast_grp); + mcast_grp, svi_ifindex); if (!vpn) { flog_err( EC_BGP_VNI, diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 90e088c3eb..617ed31be1 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -202,7 +202,8 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index debed9f68b..3983f35e50 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -62,6 +62,7 @@ RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, struct bgpevpn { vni_t vni; vrf_id_t tenant_vrf_id; + ifindex_t svi_ifindex; uint32_t flags; #define VNI_FLAG_CFGD 0x1 /* VNI is user configured */ #define VNI_FLAG_LIVE 0x2 /* VNI is "live" */ @@ -612,7 +613,8 @@ extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni); extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index fccaf9166c..4c6225be94 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -531,6 +531,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else json_object_string_add(json, "advertiseSviMacIp", "Disabled"); + json_object_string_add( + json, "sviInterface", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } else { vty_out(vty, "VNI: %d", vpn->vni); if (is_vni_live(vpn)) @@ -564,6 +567,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else vty_out(vty, " Advertise-svi-macip : %s\n", "Disabled"); + vty_out(vty, " SVI interface : %s\n", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } if (!json) @@ -2290,7 +2295,7 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) /* tenant vrf will be updated when we get local_vni_add from * zebra */ - vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp); + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0); if (!vpn) { flog_err( EC_BGP_VNI, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index b32f319655..73ce2d9ae8 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2830,6 +2830,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) struct in_addr vtep_ip = {INADDR_ANY}; vrf_id_t tenant_vrf_id = VRF_DEFAULT; struct in_addr mcast_grp = {INADDR_ANY}; + ifindex_t svi_ifindex = 0; s = zclient->ibuf; vni = stream_getl(s); @@ -2837,6 +2838,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); + stream_get(&svi_ifindex, s, sizeof(ifindex_t)); } bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2844,16 +2846,17 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (cmd == ZEBRA_VNI_ADD) ? "add" : "del", - vrf_id_to_name(vrf_id), vni, - vrf_id_to_name(tenant_vrf_id)); + zlog_debug( + "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), vni, + vrf_id_to_name(tenant_vrf_id), svi_ifindex); if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id, - tenant_vrf_id, mcast_grp); + tenant_vrf_id, mcast_grp, svi_ifindex); else return bgp_evpn_local_vni_del(bgp, vni); } diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 30f4a44769..816f46bac9 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -134,6 +134,10 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) if (json == NULL) { vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " SVI interface: %s\n", + (zevpn->svi_if ? zevpn->svi_if->name : "")); + vty_out(vty, " SVI ifIndex: %u\n", + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0)); vty_out(vty, " Local VTEP IP: %pI4\n", &zevpn->local_vtep_ip); vty_out(vty, " Mcast group: %pI4\n", @@ -142,6 +146,12 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) json_object_string_add(json, "vxlanInterface", zevpn->vxlan_if->name); json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + if (zevpn->svi_if) { + json_object_string_add(json, "sviInterface", + zevpn->svi_if->name); + json_object_int_add(json, "sviIfindex", + zevpn->svi_if->ifindex); + } json_object_string_add(json, "vtepIp", inet_ntop(AF_INET, &zevpn->local_vtep_ip, buf, sizeof(buf))); @@ -1048,6 +1058,8 @@ int zebra_evpn_del(zebra_evpn_t *zevpn) zvrf = zebra_vrf_get_evpn(); assert(zvrf); + zevpn->svi_if = NULL; + /* Free the neighbor hash table. */ hash_free(zevpn->neigh_table); zevpn->neigh_table = NULL; @@ -1075,6 +1087,7 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) { struct zserv *client; struct stream *s; + ifindex_t svi_index; int rc; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); @@ -1082,6 +1095,8 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) if (!client) return 0; + svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0; + s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); @@ -1089,15 +1104,18 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) stream_put_in_addr(s, &zevpn->local_vtep_ip); stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ stream_put_in_addr(s, &zevpn->mcast_grp); + stream_put(s, &svi_index, sizeof(ifindex_t)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni, - &zevpn->local_vtep_ip, - vrf_id_to_name(zevpn->vrf_id), - zebra_route_string(client->proto)); + zlog_debug( + "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s", + zevpn->vni, &zevpn->local_vtep_ip, + vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id, + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0), + zebra_route_string(client->proto)); client->vniadd_cnt++; rc = zserv_send_message(client, s); diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 27392ec85c..ee9e1406e4 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -98,6 +98,9 @@ struct zebra_evpn_t_ { /* Corresponding VxLAN interface. */ struct interface *vxlan_if; + /* Corresponding SVI interface. */ + struct interface *svi_if; + /* List of remote VTEPs */ zebra_vtep_t *vteps; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 09eb78917c..99df49a46b 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1012,6 +1012,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns, vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf( vlan_if->vrf_id); @@ -4527,6 +4528,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) zevpn = zebra_evpn_from_svi(ifp, link_if); if (zevpn) { + zevpn->svi_if = NULL; zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ @@ -4582,6 +4584,7 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ + zevpn->svi_if = ifp; zevpn->vrf_id = ifp->vrf_id; if (if_is_operative(zevpn->vxlan_if)) @@ -4792,6 +4795,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) @@ -4894,6 +4898,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) struct zebra_l2info_vxlan *vxl = NULL; zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4983,6 +4988,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ + zevpn->svi_if = NULL; zebra_evpn_send_del_to_client(zevpn); zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); @@ -5012,6 +5018,11 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) zebra_evpn_es_set_base_evpn(zevpn); } zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) + zevpn->svi_if = vlan_if; + /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -5131,6 +5142,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) From 021b6596652452ad8a3d3dd401164b17f9121519 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 03:51:56 -0800 Subject: [PATCH 09/14] bgpd: EVPN route type-5 to type-2 recursive resolution using gateway IP When EVPN prefix route with a gateway IP overlay index is imported into the IP vrf at the ingress PE, BGP nexthop of this route is set to the gateway IP. For this vrf route to be valid, following conditions must be met. - Gateway IP nexthop of this route should be L3 reachable, i.e., this route should be resolved in RIB. - A remote MAC/IP route should be present for the gateway IP address in the EVI(L2VPN table). To check for the first condition, gateway IP is registered with nht (nexthop tracking) to receive the reachability notifications for this IP from zebra RIB. If the gateway IP is reachable, zebra sends the reachability information (i.e., nexthop interface) for the gateway IP. This nexthop interface should be the SVI interface. Now, to find out type-2 route corresponding to the gateway IP, we need to fetch the VNI for the above SVI. To do this VNI lookup effitiently, define a hashtable of struct bgpevpn with svi_ifindex as key. struct hash *vni_svi_hash; An EVI instance is added to vni_svi_hash if its svi_ifindex is nonzero. Using this hash, we obtain struct bgpevpn corresponding to the gateway IP. For gateway IP overlay index recursive lookup, once we find the correct EVI, we have to lookup its route table for a MAC/IP prefix. As we have to iterate the entire route table for every lookup, this lookup is expensive. We can optimize this lookup by adding all the remote IP addresses in a hash table. Following hash table is defined for this purpose in struct bgpevpn Struct hash *remote_ip_hash; When a MAC/IP route is installed in the EVI table, it is also added to remote_ip_hash. It is possible to have multiple MAC/IP routes with the same IP address because of host move scenarios. Thus, for every address addr in remote_ip_hash, we maintain list of all the MAC/IP routes having addr as their IP address. Following structure defines an address in remote_ip_hash. struct evpn_remote_ip { struct ipaddr addr; struct list *macip_path_list; }; A Boolean field is added to struct bgp_nexthop_cache to indicate that the nexthop is EVPN gateway IP overlay index. bool is_evpn_gwip_nexthop; A flag BGP_NEXTHOP_EVPN_INCOMPLETE is added to struct bgp_nexthop_cache. This flag is set when the gateway IP is L3 reachable but not yet resolved by a MAC/IP route. Following table explains the combination of L3 and L2 reachability w.r.t. BGP_NEXTHOP_VALID and BGP_NEXTHOP_EVPN_INCOMPLETE flags * | MACIP resolved | MACIP unresolved *----------------|----------------|------------------ * L3 reachable | VALID = 1 | VALID = 0 * | INCOMPLETE = 0 | INCOMPLETE = 1 * ---------------|----------------|-------------------- * L3 unreachable | VALID = 0 | VALID = 0 * | INCOMPLETE = 0 | INCOMPLETE = 0 Procedure that we use to check if the gateway IP is resolvable by a MAC/IP route: - Find the EVI/L2VRF that belongs to the nexthop SVI using vni_svi_hash. - Check if the gateway IP is present in remote_ip_hash in this EVI. When the gateway IP is L3 reachable and it is also resolved by a MAC/IP route, unset BGP_NEXTHOP_EVPN_INCOMPLETE flag and set BGP_NEXTHOP_VALID flag. Signed-off-by: Ameya Dharkar --- bgpd/bgp_evpn.c | 433 +++++++++++++++++++++++++++++++++++++++- bgpd/bgp_evpn.h | 5 + bgpd/bgp_evpn_private.h | 15 ++ bgpd/bgp_evpn_vty.c | 57 ++++++ bgpd/bgp_memory.c | 1 + bgpd/bgp_memory.h | 2 + bgpd/bgp_nexthop.c | 14 ++ bgpd/bgp_nexthop.h | 27 +++ bgpd/bgp_nht.c | 56 +++++- bgpd/bgp_nht.h | 1 + bgpd/bgpd.h | 8 + 11 files changed, 608 insertions(+), 11 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 6fb7045296..1db552f11d 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -66,6 +66,24 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn); +static unsigned int vni_svi_hash_key_make(const void *p); +static bool vni_svi_hash_cmp(const void *p1, const void *p2); +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve); +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args); +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args); static struct in_addr zero_vtep_ip; /* @@ -2220,6 +2238,7 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) dest = bgp_route_next(dest)) { for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_evpn_remote_ip_hash_del(vpn, pi); bgp_path_info_delete(dest, pi); bgp_path_info_reap(dest, pi); } @@ -2633,6 +2652,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, pi->uptime = bgp_clock(); } + /* Add this route to remote IP hashtable */ + bgp_evpn_remote_ip_hash_add(vpn, pi); + /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2761,6 +2783,8 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!pi) return 0; + bgp_evpn_remote_ip_hash_del(vpn, pi); + /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); @@ -5266,6 +5290,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, return NULL; } + bgp_evpn_remote_ip_hash_init(vpn); + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + /* add to l2vni list on corresponding vrf */ bgpevpn_link_to_l3vni(vpn); @@ -5283,6 +5310,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgp_evpn_remote_ip_hash_destroy(vpn); bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); @@ -5290,6 +5318,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) list_delete(&vpn->import_rtl); list_delete(&vpn->export_rtl); bf_release_index(bm->rd_idspace, vpn->rd_id); + hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -5701,6 +5730,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = 0; /* * tunnel is no longer active, del tunnel ip address from tip_hash @@ -5743,13 +5774,53 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); - vpn->svi_ifindex = svi_ifindex; + if (vpn->svi_ifindex != svi_ifindex) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * for old SVI + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = svi_ifindex; + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * for new SVI + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_link_nexthop, + vpn); + } /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * in old tenant vrf + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); bgpevpn_unlink_from_l3vni(vpn); vpn->tenant_vrf_id = tenant_vrf_id; bgpevpn_link_to_l3vni(vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * in new tenant vrf + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_link_nexthop, + vpn); } /* If tunnel endpoint IP has changed, update (and delete prior @@ -5870,6 +5941,8 @@ void bgp_evpn_cleanup(struct bgp *bgp) hash_free(bgp->vrf_import_rt_hash); bgp->vrf_import_rt_hash = NULL; + hash_free(bgp->vni_svi_hash); + bgp->vni_svi_hash = NULL; hash_free(bgp->vnihash); bgp->vnihash = NULL; @@ -5888,6 +5961,9 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); + bgp->vni_svi_hash = + hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp, + "BGP VNI hash based on SVI ifindex"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); @@ -5980,3 +6056,358 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) return false; } + +static void *bgp_evpn_remote_ip_hash_alloc(void *p) +{ + const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p; + struct evpn_remote_ip *ip; + + ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip)); + *ip = *key; + ip->macip_path_list = list_new(); + + return ip; +} + +static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p) +{ + const struct evpn_remote_ip *ip = p; + const struct ipaddr *addr = &ip->addr; + + if (IS_IPADDR_V4(addr)) + return jhash_1word(addr->ipaddr_v4.s_addr, 0); + + return jhash2(addr->ipaddr_v6.s6_addr32, + array_size(addr->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2) +{ + const struct evpn_remote_ip *ip1 = p1; + const struct evpn_remote_ip *ip2 = p2; + + return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0); +} + +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn) +{ + vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make, + bgp_evpn_remote_ip_hash_cmp, + "BGP EVPN remote IP hash"); +} + +static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + + list_delete(&ip->macip_path_list); + + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); +} + +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) +{ + if (vpn->remote_ip_hash == NULL) + return; + + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free, + vpn); + + hash_free(vpn->remote_ip_hash); + vpn->remote_ip_hash = NULL; +} + +/* Add a remote MAC/IP route to hash table */ +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip) { + if (listnode_lookup(ip->macip_path_list, pi) != NULL) + return; + (void)listnode_add(ip->macip_path_list, pi); + return; + } + + ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc); + if (!ip) + return; + + (void)listnode_add(ip->macip_path_list, pi); + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +/* Delete a remote MAC/IP route from hash table */ +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip == NULL) + return; + + listnode_delete(ip->macip_path_list, pi); + + if (ip->macip_path_list->count == 0) { + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); + } +} + +static void show_remote_ip_entry(struct hash_bucket *bucket, void *args) +{ + char buf[INET6_ADDRSTRLEN]; + char buf2[EVPN_ROUTE_STRLEN]; + struct prefix_evpn *evp; + + struct listnode *node = NULL; + struct bgp_path_info *pi = NULL; + struct vty *vty = (struct vty *)args; + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + + vty_out(vty, " Remote IP: %s\n", + ipaddr2str(&ip->addr, buf, sizeof(buf))); + vty_out(vty, " Linked MAC/IP routes:\n"); + for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) { + evp = (struct prefix_evpn *)&pi->net->p; + prefix2str(evp, buf2, sizeof(buf2)); + vty_out(vty, " %s\n", buf2); + } +} + +void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "VNI: %u\n", vpn->vni); + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))show_remote_ip_entry, + vty); + vty_out(vty, "\n"); +} + +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); +} + +static unsigned int vni_svi_hash_key_make(const void *p) +{ + const struct bgpevpn *vpn = p; + + return jhash_1word(vpn->svi_ifindex, 0); +} + +static bool vni_svi_hash_cmp(const void *p1, const void *p2) +{ + const struct bgpevpn *vpn1 = p1; + const struct bgpevpn *vpn2 = p2; + + return (vpn1->svi_ifindex == vpn2->svi_ifindex); +} + +static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp, + ifindex_t svi) +{ + struct bgpevpn *vpn; + struct bgpevpn tmp; + + memset(&tmp, 0, sizeof(struct bgpevpn)); + tmp.svi_ifindex = svi; + vpn = hash_lookup(bgp->vni_svi_hash, &tmp); + return vpn; +} + +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern); +} + +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_release(bgp->vni_svi_hash, vpn); +} + +void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *evpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni); +} + +/* + * This function is called for a bgp_nexthop_cache entry when the nexthop is + * gateway IP overlay index. + * This function returns true if there is a remote MAC/IP route for the gateway + * IP in the EVI of the nexthop SVI. + */ +bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc) +{ + struct bgp *bgp_evpn = NULL; + struct bgpevpn *vpn = NULL; + struct evpn_remote_ip tmp; + struct prefix *p; + + if (!bnc->nexthop || bnc->nexthop->ifindex == 0) + return false; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return false; + + /* + * Gateway IP is resolved by nht over SVI interface. + * Use this SVI to find corresponding EVI(L2 context) + */ + vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex); + if (!vpn) + return false; + + if (vpn->bgp_vrf != bnc->bgp) + return false; + + /* + * Check if the gateway IP is present in the EVI remote_ip_hash table + * which stores all the remote IP addresses received via MAC/IP routes + * in this EVI + */ + memset(&tmp, 0, sizeof(struct evpn_remote_ip)); + + p = &bnc->prefix; + if (p->family == AF_INET) { + tmp.addr.ipa_type = IPADDR_V4; + memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4), + sizeof(struct in_addr)); + } else if (p->family == AF_INET6) { + tmp.addr.ipa_type = IPADDR_V6; + memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6), + sizeof(struct in6_addr)); + } else + return false; + + if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL) + return false; + + return true; +} + +/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */ +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve) +{ + afi_t afi; + struct prefix p; + struct bgp_nexthop_cache *bnc; + struct bgp_nexthop_cache_head *tree = NULL; + + if (!vpn->bgp_vrf || vpn->svi_ifindex == 0) + return; + + memset(&p, 0, sizeof(struct prefix)); + + if (addr->ipa_type == IPADDR_V4) { + afi = AFI_IP; + p.family = AF_INET; + memcpy(&(p.u.prefix4), &(addr->ipaddr_v4), + sizeof(struct in_addr)); + p.prefixlen = IPV4_MAX_BITLEN; + } else if (addr->ipa_type == IPADDR_V6) { + afi = AFI_IP6; + p.family = AF_INET6; + memcpy(&(p.u.prefix6), &(addr->ipaddr_v6), + sizeof(struct in6_addr)); + p.prefixlen = IPV6_MAX_BITLEN; + } else + return; + + tree = &vpn->bgp_vrf->nexthop_cache_table[afi]; + bnc = bnc_find(tree, &p, 0); + + if (!bnc || !bnc->is_evpn_gwip_nexthop) + return; + + if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex) + return; + + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf, sizeof(buf)); + zlog_debug("%s(%u): vni %u mac/ip %s for NH %s", + vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id, + vpn->vni, (resolve ? "add" : "delete"), buf); + } + + /* + * MAC/IP route or SVI or tenant vrf being added to EVI. + * Set nexthop as valid only if it is already L3 reachable + */ + if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } + + /* MAC/IP route or SVI or tenant vrf being deleted from EVI */ + if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) { + bnc->flags &= ~BGP_NEXTHOP_VALID; + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } +} + diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 617ed31be1..f91b81f10a 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -211,4 +211,9 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); +extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, + void *args); +extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args); +extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc); + #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 3983f35e50..c46f34bf74 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -103,6 +103,15 @@ struct bgpevpn { struct list *import_rtl; struct list *export_rtl; + /* + * EVPN route that uses gateway IP overlay index as its nexthop + * needs to do a recursive lookup. + * A remote MAC/IP entry should be present for the gateway IP. + * Maintain a hash of the addresses received via remote MAC/IP routes + * for efficient gateway IP recursive lookup in this EVI + */ + struct hash *remote_ip_hash; + /* Route table for EVPN routes for * this VNI. */ struct bgp_table *route_table; @@ -179,6 +188,12 @@ struct bgp_evpn_info { bool is_anycast_mac; }; +/* This structure defines an entry in remote_ip_hash */ +struct evpn_remote_ip { + struct ipaddr addr; + struct list *macip_path_list; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4c6225be94..c334992d9a 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4360,6 +4360,61 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash, + show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd, + "show bgp l2vpn evpn vni remote-ip-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "Remote IP hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vnihash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_remote_ip_hash, + vty); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash, + show_bgp_l2vpn_evpn_vni_svi_hash_cmd, + "show bgp l2vpn evpn vni-svi-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vni-svi-hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vni_svi_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_vni_svi_hash, + vty); + + return CMD_SUCCESS; +} + DEFPY(show_bgp_l2vpn_evpn_es_evi, show_bgp_l2vpn_evpn_es_evi_cmd, "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]", @@ -6396,6 +6451,8 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index eb85936f0f..196e56a937 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -144,3 +144,4 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie"); DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service"); DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id"); DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function"); +DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index c5ba371498..0021fa9c3d 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN); DECLARE_MTYPE(BGP_SRV6_SID); DECLARE_MTYPE(BGP_SRV6_FUNCTION); +DECLARE_MTYPE(EVPN_REMOTE_IP); + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c417dda398..e7bad42f97 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->metric, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + bgp_show_nexthops_detail(vty, bgp, bnc); + } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) { + vty_out(vty, + " %s overlay index unresolved [IGP metric %d], #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->metric, bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); bgp_show_nexthops_detail(vty, bgp, bnc); } else { @@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) vty_out(vty, " Must be Connected\n"); diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index fe0a9646a0..16c2b6c65a 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -56,6 +56,10 @@ struct bgp_nexthop_cache { time_t last_update; uint16_t flags; +/* + * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop + * is RIB reachable as well as MAC/IP is present + */ #define BGP_NEXTHOP_VALID (1 << 0) #define BGP_NEXTHOP_REGISTERED (1 << 1) #define BGP_NEXTHOP_CONNECTED (1 << 2) @@ -64,11 +68,29 @@ struct bgp_nexthop_cache { #define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5) #define BGP_NEXTHOP_LABELED_VALID (1 << 6) +/* + * This flag is added for EVPN gateway IP nexthops. + * If the nexthop is RIB reachable, but a MAC/IP is not yet + * resolved, this flag is set. + * Following table explains the combination of L3 and L2 reachability w.r.t. + * VALID and INCOMPLETE flags + * + * | MACIP resolved | MACIP unresolved + *----------------|----------------|------------------ + * L3 reachable | VALID = 1 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 1 + * ---------------|----------------|-------------------- + * L3 unreachable | VALID = 0 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 0 + */ +#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7) + uint16_t change_flags; #define BGP_NEXTHOP_CHANGED (1 << 0) #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) /* Back pointer to the cache tree this entry belongs to. */ struct bgp_nexthop_cache_head *tree; @@ -79,6 +101,11 @@ struct bgp_nexthop_cache { LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; + + /* This flag is set to TRUE for a bnc that is gateway IP overlay index + * nexthop. + */ + bool is_evpn_gwip_nexthop; }; extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 638c72ae67..742ef217d9 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); -static void evaluate_paths(struct bgp_nexthop_cache *bnc); static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); static int bgp_nht_ifp_initial(struct thread *thread); @@ -244,6 +243,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, } } + if (pi && is_route_parent_evpn(pi)) + bnc->is_evpn_gwip_nexthop = true; + if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -331,7 +333,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 1; else if (safi == SAFI_UNICAST && pi && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels) { + && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { return bgp_isvalid_labeled_nexthop(bnc); } else return (bgp_isvalid_nexthop(bnc)); @@ -387,6 +389,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; + bool evpn_resolved = false; bnc->last_update = bgp_clock(); bnc->change_flags = 0; @@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, if (!bnc->nexthop_num) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); - bnc->flags |= BGP_NEXTHOP_VALID; + if (!bnc->is_evpn_gwip_nexthop) + bnc->flags |= BGP_NEXTHOP_VALID; bnc->metric = nhr->metric; bnc->nexthop_num = nhr->nexthop_num; @@ -488,7 +492,40 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, } bnc_nexthop_free(bnc); bnc->nexthop = nhlist_head; + + /* + * Gateway IP nexthop is L3 reachable. Mark it as + * BGP_NEXTHOP_VALID only if it is recursively resolved with a + * remote EVPN RT-2. + * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE. + * When its mapping with EVPN RT-2 is established, unset + * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID. + */ + if (bnc->is_evpn_gwip_nexthop) { + evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc); + + if (BGP_DEBUG(nht, NHT)) { + char buf2[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf2, sizeof(buf2)); + zlog_debug( + "EVPN gateway IP %s recursive MAC/IP lookup %s", + buf2, + (evpn_resolved ? "successful" + : "failed")); + } + + if (evpn_resolved) { + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + } else { + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags &= ~BGP_NEXTHOP_VALID; + } + } } else { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; bnc->flags &= ~BGP_NEXTHOP_VALID; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; bnc->nexthop_num = nhr->nexthop_num; @@ -694,6 +731,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE); } } } @@ -888,7 +926,7 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, * RETURNS: * void. */ -static void evaluate_paths(struct bgp_nexthop_cache *bnc) +void evaluate_paths(struct bgp_nexthop_cache *bnc) { struct bgp_dest *dest; struct bgp_path_info *path; @@ -950,12 +988,10 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) bool bnc_is_valid_nexthop = false; bool path_valid = false; - if (safi == SAFI_UNICAST && - path->sub_type == BGP_ROUTE_IMPORTED && - path->extra && - path->extra->num_labels && - path->attr->evpn_overlay.type != - OVERLAY_INDEX_GATEWAY_IP) { + if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED + && path->extra && path->extra->num_labels + && (path->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 9268b225ca..e006aa4469 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -90,6 +90,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp); */ extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); +extern void evaluate_paths(struct bgp_nexthop_cache *bnc); /* APIs for setting up and allocating L3 nexthop group ids */ extern uint32_t bgp_l3nhg_id_alloc(void); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f2c8ece024..a43a21e167 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -640,6 +640,14 @@ struct bgp { /* EVI hash table */ struct hash *vnihash; + /* + * VNI hash table based on SVI ifindex as its key. + * We use SVI ifindex as key to lookup a VNI table for gateway IP + * overlay index recursive lookup. + * For this purpose, a hashtable is added which optimizes this lookup. + */ + struct hash *vni_svi_hash; + /* EVPN enable - advertise gateway macip routes */ int advertise_gw_macip; From dc6cef732ecb72e4e3d112ed8ddfd859f35ac465 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Tue, 11 May 2021 15:26:29 -0700 Subject: [PATCH 10/14] bgpd: Add CLI for overlay index recursive resolution Gateway IP overlay index of the remote type-5 route is resolved recursively using remote type-2 route. For the purpose of this recursive resolution, for each L2VNI, we build a hash table of the remote IP addresses received by remote type-2 routes. For the topologies where overlay index resolution is not needed, we do not need to build this remote-ip-hash. Thus, make the recursive resolution of the overlay index conditional on "enable-resolve-overlay-index" configuration. router bgp 65001 bgp router-id 192.168.100.1 neighbor 10.0.1.2 remote-as 65002 ! address-family l2vpn evpn neighbor 10.0.1.2 activate advertise-all-vni enable-resolve-overlay-index----------> New configuration exit-address-family Gateway IP overlay index will be resolved only if this configuration is present. Signed-off-by: Ameya Dharkar --- bgpd/bgp_evpn.c | 86 +++++++++++++++++++++++++++++++++++++-------- bgpd/bgp_evpn.h | 14 ++++++++ bgpd/bgp_evpn_vty.c | 44 +++++++++++++++++++++++ bgpd/bgpd.h | 9 +++++ 4 files changed, 139 insertions(+), 14 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 1db552f11d..e08e5b9799 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -72,6 +72,10 @@ static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, struct bgp_path_info *pi); static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg); static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); @@ -5780,9 +5784,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, * Unresolve all the gateway IP nexthops for this VNI * for old SVI */ - hash_iterate(vpn->remote_ip_hash, - (void (*)(struct hash_bucket *, - void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, vpn); bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); vpn->svi_ifindex = svi_ifindex; @@ -5792,9 +5797,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, * Resolve all the gateway IP nexthops for this VNI * for new SVI */ - hash_iterate(vpn->remote_ip_hash, - (void (*)(struct hash_bucket *, - void *))bgp_evpn_remote_ip_hash_link_nexthop, + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, vpn); } @@ -5805,9 +5811,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, * Unresolve all the gateway IP nexthops for this VNI * in old tenant vrf */ - hash_iterate(vpn->remote_ip_hash, - (void (*)(struct hash_bucket *, - void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, vpn); bgpevpn_unlink_from_l3vni(vpn); vpn->tenant_vrf_id = tenant_vrf_id; @@ -5817,9 +5824,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, * Resolve all the gateway IP nexthops for this VNI * in new tenant vrf */ - hash_iterate(vpn->remote_ip_hash, - (void (*)(struct hash_bucket *, - void *))bgp_evpn_remote_ip_hash_link_nexthop, + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, vpn); } @@ -6091,6 +6099,9 @@ static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2) static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn) { + if (!evpn_resolve_overlay_index()) + return; + vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make, bgp_evpn_remote_ip_hash_cmp, "BGP EVPN remote IP hash"); @@ -6111,7 +6122,7 @@ static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args) static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) { - if (vpn->remote_ip_hash == NULL) + if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL) return; hash_iterate(vpn->remote_ip_hash, @@ -6130,6 +6141,13 @@ static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, struct evpn_remote_ip *ip; struct prefix_evpn *evp; + if (!evpn_resolve_overlay_index()) + return; + + if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED + || !CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + return; + evp = (struct prefix_evpn *)&pi->net->p; if (evp->family != AF_EVPN @@ -6163,6 +6181,9 @@ static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, struct evpn_remote_ip *ip; struct prefix_evpn *evp; + if (!evpn_resolve_overlay_index()) + return; + evp = (struct prefix_evpn *)&pi->net->p; if (evp->family != AF_EVPN @@ -6184,6 +6205,17 @@ static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, } } +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg) +{ + if (!evpn_resolve_overlay_index()) + return; + + hash_iterate(vpn->remote_ip_hash, func, arg); +} + static void show_remote_ip_entry(struct hash_bucket *bucket, void *args) { char buf[INET6_ADDRSTRLEN]; @@ -6211,7 +6243,8 @@ void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args) struct vty *vty = (struct vty *)args; vty_out(vty, "VNI: %u\n", vpn->vni); - hash_iterate(vpn->remote_ip_hash, + bgp_evpn_remote_ip_hash_iterate( + vpn, (void (*)(struct hash_bucket *, void *))show_remote_ip_entry, vty); vty_out(vty, "\n"); @@ -6300,6 +6333,9 @@ bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc) struct evpn_remote_ip tmp; struct prefix *p; + if (!evpn_resolve_overlay_index()) + return false; + if (!bnc->nexthop || bnc->nexthop->ifindex == 0) return false; @@ -6411,3 +6447,25 @@ static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, } } +void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct bgp_dest *dest; + struct bgp_path_info *pi; + + bgp_evpn_remote_ip_hash_init(vpn); + + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) + bgp_evpn_remote_ip_hash_add(vpn, pi); +} + +void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + + bgp_evpn_remote_ip_hash_destroy(vpn); +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index f91b81f10a..eec746e3be 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -156,6 +156,14 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) return true; } +static inline bool evpn_resolve_overlay_index(void) +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_evpn(); + return bgp ? bgp->resolve_overlay_index : false; +} + extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct attr *src_attr, afi_t afi, @@ -215,5 +223,11 @@ extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args); extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args); extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc); +extern void +bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg); +extern void +bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg); #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index c334992d9a..adb7148f24 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3302,6 +3302,28 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) bgp_evpn_cleanup_on_disable(bgp); } +/* Set resolve overlay index flag */ +static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set) +{ + if (set == bgp->resolve_overlay_index) + return; + + if (set) { + bgp->resolve_overlay_index = true; + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_set, + NULL); + } else { + hash_iterate( + bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_unset, + NULL); + bgp->resolve_overlay_index = false; + } +} + /* * EVPN - use RFC8365 to auto-derive RT */ @@ -4117,6 +4139,23 @@ DEFPY (bgp_evpn_ead_evi_tx_disable, return CMD_SUCCESS; } +DEFPY (bgp_evpn_enable_resolve_overlay_index, + bgp_evpn_enable_resolve_overlay_index_cmd, + "[no$no] enable-resolve-overlay-index", + NO_STR + "Enable Recursive Resolution of type-5 route overlay index\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (bgp != bgp_get_evpn()) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true); + return CMD_SUCCESS; +} + DEFPY (bgp_evpn_advertise_pip_ip_mac, bgp_evpn_advertise_pip_ip_mac_cmd, "[no$no] advertise-pip [ip [mac ]]", @@ -6252,6 +6291,9 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->evpn_info->advertise_svi_macip) vty_out(vty, " advertise-svi-ip\n"); + if (bgp->resolve_overlay_index) + vty_out(vty, " enable-resolve-overlay-index\n"); + if (bgp_mh_info->host_routes_use_l3nhg != BGP_EVPN_MH_USE_ES_L3NHG_DEF) { if (bgp_mh_info->host_routes_use_l3nhg) @@ -6440,6 +6482,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd); + install_element(BGP_EVPN_NODE, + &bgp_evpn_enable_resolve_overlay_index_cmd); /* test commands */ install_element(BGP_EVPN_NODE, &test_es_add_cmd); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a43a21e167..776f4b0a21 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -693,6 +693,15 @@ struct bgp { /* Hash table of EVPN nexthops maintained per-tenant-VRF */ struct hash *evpn_nh_table; + /* + * Flag resolve_overlay_index is used for recursive resolution + * procedures for EVPN type-5 route's gateway IP overlay index. + * When this flag is set, we build remote-ip-hash for + * all L2VNIs and resolve overlay index nexthops using this hash. + * Overlay index nexthops remain unresolved if this flag is not set. + */ + bool resolve_overlay_index; + /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) From 1b09e77e4d0752e6e29affbd440dbb82dcee5044 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 11:08:43 -0800 Subject: [PATCH 11/14] Zebra: FPM support for gateway IP overlay Index FPM sends VNI to the data plane with the EVPN prefix. For pure type-5 EVPN route, nexthop interface of EVPN prefix is L3VNI SVI. Thus, we encode L3VNI corresponding to the nexthop vrf with rtmsg for this prefix. For EVPN type-5 route with gateway IP overlay index, we supporting asymmetric IRB. Thus, nexthop interface is L2VNI SVI. So, instead of fetching vrf VNI, fetch VNI corresponding to the nexthop SVI and encode it in the rtmsg for EVPN prefix. Signed-off-by: Ameya Dharkar --- zebra/zebra_fpm_netlink.c | 33 +++++++++++++++++++++++++++------ zebra/zebra_vxlan.c | 21 +++++++++++++++++++++ zebra/zebra_vxlan_private.h | 1 + 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index cebd576365..efbd078a52 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -42,6 +42,7 @@ #include "zebra/zebra_fpm_private.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/interface.h" /* * af_addr_size @@ -164,7 +165,10 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, { struct netlink_nh_info nhi; union g_addr *src; - zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf = NULL; + struct interface *ifp = NULL, *link_if = NULL; + struct zebra_if *zif = NULL; + vni_t vni = 0; memset(&nhi, 0, sizeof(nhi)); src = NULL; @@ -199,12 +203,29 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN; - zl3vni = zl3vni_from_vrf(nexthop->vrf_id); - if (zl3vni && is_l3vni_oper_up(zl3vni)) { - - /* Add VNI to VxLAN encap info */ - nhi.encap_info.vxlan_encap.vni = zl3vni->vni; + /* Extract VNI id for the nexthop SVI interface */ + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (zvrf) { + ifp = if_lookup_by_index_per_ns(zvrf->zns, + nexthop->ifindex); + if (ifp) { + zif = (struct zebra_if *)ifp->info; + if (zif) { + if (IS_ZEBRA_IF_BRIDGE(ifp)) + link_if = ifp; + else if (IS_ZEBRA_IF_VLAN(ifp)) + link_if = + if_lookup_by_index_per_ns( + zvrf->zns, + zif->link_ifindex); + if (link_if) + vni = vni_id_from_svi(ifp, + link_if); + } + } } + + nhi.encap_info.vxlan_encap.vni = vni; } /* diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 99df49a46b..2f3ea7475a 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1842,6 +1842,27 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, return zl3vni; } +vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if) +{ + vni_t vni = 0; + zebra_evpn_t *zevpn = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* Check if an L3VNI belongs to this SVI interface. + * If not, check if an L2VNI belongs to this SVI interface. + */ + zl3vni = zl3vni_from_svi(ifp, br_if); + if (zl3vni) + vni = zl3vni->vni; + else { + zevpn = zebra_evpn_from_svi(ifp, br_if); + if (zevpn) + vni = zevpn->vni; + } + + return vni; +} + static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) { diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 0556c4adce..84ac76b3b9 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -224,6 +224,7 @@ extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni); extern zebra_l3vni_t *zl3vni_lookup(vni_t vni); +extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if); DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)); From 40f4507dbdd6ca85f1e373ab3ac0fc175d42271e Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 11:21:24 -0800 Subject: [PATCH 12/14] doc: Update documentation for gateway IP CLI Signed-off-by: Ameya Dharkar --- doc/user/bgp.rst | 109 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 0e01b8c3e4..c3670186bf 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2696,6 +2696,115 @@ remote VTEP. Note that you should not enable both the advertise-svi-ip and the advertise-default-gw at the same time. +.. _bgp-evpn-overlay-index-gateway-ip: + +EVPN Overlay Index Gateway IP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11 +explains the use of overlay indexes for recursive route resolution for EVPN +type-5 route. + +We support gateway IP overlay index. +A gateway IP, advertised with EVPN prefix route, is used to find an EVPN MAC/IP +route with its IP field same as the gateway IP. This MAC/IP entry provides the +nexthop VTEP and the tunnel information required for the VxLAN encapsulation. + +Functionality: + +:: + + . +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4 | | EVPN | | | | + ======+ Host1 +------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + +Consider above topology where prefix SN1 is connected behind host1. Host1 +advertises SN1 to PE1 over BGP IPv4 session. PE1 advertises SN1 to PE2 using +EVPN type-5 route with host1 IP as the gateway IP. PE1 also advertises +Host1 MAC/IP as type-2 route which is used to resolve host1 gateway IP. + +PE2 receives this type-5 route and imports it into the vrf based on route +targets. BGP prefix imported into the vrf uses gateway IP as its BGP nexthop. +This route is installed into zebra if following conditions are satisfied: +1. Gateway IP nexthop is L3 reachable. +2. PE2 has received EVPN type-2 route with IP field set to gateway IP. + +Topology requirements: +1. This feature is supported for asymmetric routing model only. While + sending packets to SN1, ingress PE (PE2) performs routing and + egress PE (PE1) performs only bridging. +2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge + interface associated with L2VNI is an L3 interface. i.e., this interface is + configured with an address in the L2VNI subnet. Note that the gateway IP + should also have an address in the same subnet. +3. As this feature works in asymmetric routing model, all L2VNIs and corresponding + VxLAN and bridge interfaces should be present at all the PEs. +4. L3VNI configuration is required to generate and import EVPN type-5 routes. + L3VNI VxLAN and bridge interfaces also should be present. + +A PE can use one of the following two mechanisms to advertise an EVPN type-5 +route with gateway IP. + +1. CLI to add gateway IP while generating EVPN type-5 route from a BGP IPv4/IPv6 +prefix: + +.. index:: advertise unicast [gateway-ip] +.. clicmd:: [no] advertise unicast [gateway-ip] + +When this CLI is configured for a BGP vrf under L2VPN EVPN address family, EVPN +type-5 routes are generated for BGP prefixes in the vrf. Nexthop of the BGP +prefix becomes the gateway IP of the corresponding type-5 route. + +If the above command is configured without the "gateway-ip" keyword, type-5 +routes are generated without overlay index. + +2. Add gateway IP to EVPN type-5 route using a route-map: + +.. index:: set evpn gateway-ip +.. clicmd:: [no] set evpn gateway-ip + +When route-map with above set clause is applied as outbound policy in BGP, it +will set the gateway-ip in EVPN type-5 NLRI. + +Example configuration: + +.. code-block:: frr + + router bgp 100 + neighbor 192.168.0.1 remote-as 101 + ! + address-family ipv4 l2vpn evpn + neighbor 192.168.0.1 route-map RMAP out + exit-address-family + ! + route-map RMAP permit 10 + set evpn gateway-ip 10.0.0.1 + set evpn gateway-ip 10::1 + +A PE that receives a type-5 route with gateway IP overlay index should have +"enable-resolve-overlay-index" configuration enabled to recursively resolve the +overlay index nexthop and install the prefix into zebra. + +.. index:: enable-resolve-overlay-index +.. clicmd:: [no] enable-resolve-overlay-index + +Example configuration: + +.. code-block:: frr + + router bgp 65001 + bgp router-id 192.168.100.1 + no bgp ebgp-requires-policy + neighbor 10.0.1.2 remote-as 65002 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family + ! + EVPN Multihoming ^^^^^^^^^^^^^^^^ From fb651da9e2e231a54881c2894037467881831b6b Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 20:08:59 -0800 Subject: [PATCH 13/14] tools: Add EVPN show commands to support bundle Signed-off-by: Ameya Dharkar --- tools/etc/frr/support_bundle_commands.conf | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 0d64320488..732470f828 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -31,7 +31,29 @@ show bgp ipv6 statistics show bgp martian next-hop show bgp nexthop +show bgp vrf all summary +show bgp vrf all ipv4 +show bgp vrf all ipv6 +show bgp vrf all neighbors + show bgp evpn route +show bgp l2vpn evpn route vni all +show bgp l2vpn evpn vni +show bgp l2vpn evpn import-rt +show bgp l2vpn evpn vrf-import-rt +show bgp l2vpn evpn all overlay +show bgp l2vpn evpn summary +show bgp l2vpn evpn route detail +show bgp l2vpn evpn vni remote-ip-hash +show bgp l2vpn evpn vni-svi-hash + +show evpn +show evpn arp-cache vni all detail +show evpn mac vni all detail +show evpn next-hops vni all +show evpn rmac vni all +show evpn vni detail + CMD_LIST_END # Zebra Support Bundle Command List From eb3859f2f4e47a2e0f6100211c6ef9de79fdd259 Mon Sep 17 00:00:00 2001 From: Ameya Dharkar Date: Mon, 11 Jan 2021 20:15:13 -0800 Subject: [PATCH 14/14] tests: Topotest for BGP EVPN Overlay Index Gateway IP feature Following functionality is covered: +--------+ BGP +--------+ BGP +--------+ +--------+ SN1 | | IPv4/v6 | | EVPN | | | | ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 + | | | | | | | | +--------+ +--------+ +--------+ +--------+ Host1 is connected to PE1 and host2 is connected to PE2 Host1 and PE1 have IPv4/v6 BGP sessions. PE1 and PE2 gave EVPN session. Host1 advertises IPv4/v6 prefixes to PE1. PE1 advertises these prefixes to PE2 as EVPN type-5 routes. Gateway IP for these EVPN type-5 routes is host1 IP. Host1 MAC/IP is advertised by PE1 as EVPN type-2 route Following testcases are covered: TC_1: Check BGP and zebra states for above topology at PE1 and PE2. TC_2: Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2 Advertise the prefixes again. Check states. TC_3: Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2. Enable VxLAN interface again. Check states. Signed-off-by: Ameya Dharkar --- .../PE1/bgp_vni_routes_base.json | 192 +++++++++ .../PE1/bgp_vni_routes_no_rt2.json | 8 + .../PE1/bgp_vni_routes_no_rt5.json | 192 +++++++++ .../PE1/bgp_vrf_ipv4_base.json | 27 ++ .../PE1/bgp_vrf_ipv4_no_rt2.json | 27 ++ .../PE1/bgp_vrf_ipv4_no_rt5.json | 6 + .../PE1/bgp_vrf_ipv6_base.json | 27 ++ .../PE1/bgp_vrf_ipv6_no_rt2.json | 27 ++ .../PE1/bgp_vrf_ipv6_no_rt5.json | 6 + .../PE1/bgpd.conf | 30 ++ .../PE1/zebra.conf | 14 + .../PE1/zebra_vrf_ipv4_base.json | 56 +++ .../PE1/zebra_vrf_ipv4_no_rt2.json | 56 +++ .../PE1/zebra_vrf_ipv4_no_rt5.json | 29 ++ .../PE1/zebra_vrf_ipv6_base.json | 55 +++ .../PE1/zebra_vrf_ipv6_no_rt2.json | 55 +++ .../PE1/zebra_vrf_ipv6_no_rt5.json | 29 ++ .../PE2/bgp_vni_routes_base.json | 192 +++++++++ .../PE2/bgp_vni_routes_no_rt2.json | 68 ++++ .../PE2/bgp_vni_routes_no_rt5.json | 192 +++++++++ .../PE2/bgp_vrf_ipv4_base.json | 27 ++ .../PE2/bgp_vrf_ipv4_no_rt2.json | 27 ++ .../PE2/bgp_vrf_ipv4_no_rt5.json | 6 + .../PE2/bgp_vrf_ipv6_base.json | 28 ++ .../PE2/bgp_vrf_ipv6_no_rt2.json | 28 ++ .../PE2/bgp_vrf_ipv6_no_rt5.json | 6 + .../PE2/bgpd.conf | 14 + .../PE2/zebra.conf | 14 + .../PE2/zebra_vrf_ipv4_base.json | 56 +++ .../PE2/zebra_vrf_ipv4_no_rt2.json | 29 ++ .../PE2/zebra_vrf_ipv4_no_rt5.json | 29 ++ .../PE2/zebra_vrf_ipv6_base.json | 56 +++ .../PE2/zebra_vrf_ipv6_no_rt2.json | 29 ++ .../PE2/zebra_vrf_ipv6_no_rt5.json | 29 ++ .../__init__.py | 0 .../host1/bgpd.conf | 18 + .../host1/zebra.conf | 4 + .../host2/bgpd.conf | 1 + .../host2/zebra.conf | 4 + .../test_bgp_evpn_overlay_index_gateway.py | 385 ++++++++++++++++++ 40 files changed, 2078 insertions(+) create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf create mode 100644 tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf create mode 100755 tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json new file mode 100644 index 0000000000..2eeebad4b0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000000..419bcc3dd9 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json @@ -0,0 +1,8 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000000..2eeebad4b0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000000..833f98657b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..833f98657b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..4a292bddbe --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000000..3dc3fcf9cb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..3dc3fcf9cb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..6c11d894eb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf new file mode 100644 index 0000000000..63aa99a832 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 101 + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 102 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.11 remote-as 111 + neighbor 50:0:1::11 remote-as 111 + ! + address-family ipv4 unicast + no neighbor 50:0:1::11 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 50:0:1::11 activate + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast gateway-ip + advertise ipv6 unicast gateway-ip + exit-address-family \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf new file mode 100644 index 0000000000..99a2e89ef3 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.2/32 10.0.1.2 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.1/32 +interface PE1-eth0 + ip address 10.0.1.1/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000000..2dcf35d91b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..2dcf35d91b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..9c3091dc50 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000000..229c927656 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..229c927656 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json new file mode 100644 index 0000000000..7b8d38e492 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000000..6273b3e728 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json @@ -0,0 +1,68 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000000..7b8d38e492 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000000..c03d70195f --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..7f1b8d2ef4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..52e4311635 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000000..1d90c9c798 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..a0e63c6e25 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..789fe69b28 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } } \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf new file mode 100644 index 0000000000..59fee15dfc --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 102 + bgp router-id 10.100.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.1 remote-as 101 + ! + address-family l2vpn evpn + neighbor 10.0.1.1 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.2 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf new file mode 100644 index 0000000000..b78cdcc512 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.1/32 10.0.1.1 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.2/32 +interface PE2-eth0 + ip address 10.0.1.2/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000000..b3a3640be4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..996fe52f44 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..996fe52f44 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000000..d5be22a2ba --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json @@ -0,0 +1,56 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50:0:1::11", + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf new file mode 100644 index 0000000000..7608ec95c3 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 111 + bgp router-id 10.100.0.11 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.1 remote-as 101 + neighbor 50:0:1::1 remote-as 101 + ! + address-family ipv4 unicast + network 100.0.0.21/32 + no neighbor 50:0:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + network 100::21/128 + neighbor 50:0:1::1 activate + exit-address-family + + diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf new file mode 100644 index 0000000000..c8c832e9d8 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.11/24 + ipv6 address 50:0:1::11/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf new file mode 100644 index 0000000000..9135545c58 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.21/24 + ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py new file mode 100755 index 0000000000..fbce2809e0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality +Following functionality is covered: + + +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4/v6 | | EVPN | | | | + ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + Host1 is connected to PE1 and host2 is connected to PE2 + Host1 and PE1 have IPv4/v6 BGP sessions. + PE1 and PE2 gave EVPN session. + Host1 advertises IPv4/v6 prefixes to PE1. + PE1 advertises these prefixes to PE2 as EVPN type-5 routes. + Gateway IP for these EVPN type-5 routes is host1 IP. + Host1 MAC/IP is advertised by PE1 as EVPN type-2 route + +Following testcases are covered: +TC_1: +Check BGP and zebra states for above topology at PE1 and PE2. + +TC_2: +Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2 +Advertise the prefixes again. Check states. + +TC_3: +Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2. +Enable VxLAN interface again. Check states. +""" + +import os +import sys +import json +from functools import partial +import pytest +import time +import platform + +#Current Working Directory +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + step, + write_test_header, + write_test_footer, + generate_support_bundle, +) + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +#Global variables +PES = ['PE1', 'PE2'] +HOSTS = ['host1', 'host2'] +PE_SUFFIX = {'PE1': '1', 'PE2': '2'} +HOST_SUFFIX = {'host1': '1', 'host2': '2'} +TRIGGERS = ["base", "no_rt5", "no_rt2"] + + +class TemplateTopo(Topo): + """Test topology builder""" + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers and add links. + + # Create routers + for pe in PES: + tgen.add_router(pe) + for host in HOSTS: + tgen.add_router(host) + + krel = platform.release() + logger.info('Kernel version ' + krel) + + #Add links + tgen.add_link(tgen.gears['PE1'], tgen.gears['PE2'], 'PE1-eth0', 'PE2-eth0') + tgen.add_link(tgen.gears['PE1'], tgen.gears['host1'], 'PE1-eth1', 'host1-eth0') + tgen.add_link(tgen.gears['PE2'], tgen.gears['host2'], 'PE2-eth1', 'host2-eth0') + + +def setup_module(mod): + "Sets up the pytest environment" + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(kernelv)) + return + + if topotest.version_cmp(kernelv, '4.15') == 0: + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + else: + l3mdev_accept = 0 + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + tgen.start_topology() + + # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes + for (name, host) in tgen.gears.items(): + if name not in HOSTS: + continue + + host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name]) + host.run("ip link set dev {}-eth0 down").format(name) + host.run("ip link set dev {0}-eth0 address {1}".format(name, host_mac)) + host.run("ip link set dev {}-eth0 up").format(name) + + # Configure PE VxLAN and Bridge interfaces + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name]) + bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name]) + bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name]) + + pe.run("ip link add vrf-blue type vrf table 10") + pe.run("ip link set dev vrf-blue up") + pe.run("ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br100 type bridge stp_state 0") + pe.run("ip link set dev vxlan100 master br100") + pe.run("ip link set dev {}-eth1 master br100".format(name)) + pe.run("ip addr add {} dev br100".format(bridge_ip)) + pe.run("ip link set up dev br100") + pe.run("ip link set up dev vxlan100") + pe.run("ip link set up dev {}-eth1".format(name)) + pe.run("ip link set dev br100 master vrf-blue") + pe.run("ip -6 addr add {} dev br100".format(bridge_ipv6)) + + pe.run("ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br1000 type bridge stp_state 0") + pe.run("ip link set dev vxlan1000 master br100") + pe.run("ip link set up dev br1000") + pe.run("ip link set up dev vxlan1000") + pe.run("ip link set dev br1000 master vrf-blue") + + pe.run("sysctl -w net.ipv4.ip_forward=1") + pe.run("sysctl -w net.ipv6.conf.all.forwarding=1") + pe.run("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept)) + pe.run("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) + + # For all registred routers, load the zebra configuration file + for (name, router) in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + logger.info("Running setup_module() done") + topotest.sleep(200) + + +def teardown_module(mod): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def evpn_gateway_ip_show_op_check(trigger=" "): + """ + This function checks CLI O/P for commands mentioned in show_commands for a given trigger + :param trigger: Should be a trigger present in TRIGGERS + :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure) + """ + tgen = get_topogen() + + if trigger not in TRIGGERS: + return "Unexpected trigger", "Unexpected trigger {}".format(trigger) + + show_commands = {'bgp_vni_routes': 'show bgp l2vpn evpn route vni 100 json', + 'bgp_vrf_ipv4' : 'show bgp vrf vrf-blue ipv4 json', + 'bgp_vrf_ipv6' : 'show bgp vrf vrf-blue ipv6 json', + 'zebra_vrf_ipv4': 'show ip route vrf vrf-blue json', + 'zebra_vrf_ipv6': 'show ipv6 route vrf vrf-blue json'} + + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + + for (cmd_key, command) in show_commands.items(): + expected_op_file = "{0}/{1}/{2}_{3}.json".format(CWD, name, cmd_key, trigger) + expected_op = json.loads(open(expected_op_file).read()) + + test_func = partial(topotest.router_json_cmp, pe, command, expected_op) + ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command) + if result is not None: + return result, assertmsg + + return None, "Pass" + + +def test_evpn_gateway_ip_basic_topo(request): + """ + Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2") + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt5(request): + """ + Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + h1 = tgen.gears['host1'] + + step("Withdraw type-5 routes") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "no network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "no network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Advertise type-5 routes again") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt2(request): + """ + Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + + step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes") + + pe1 = tgen.gears['PE1'] + + pe1.run('ip link set dev vxlan100 down') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") + + pe1.run('ip link set dev vxlan100 up') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_memory_leak(): + """Run the memory leak test and report results""" + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))