diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 5007fafc29..4963ea64d0 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -348,6 +348,7 @@ struct attr { #define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5) #define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6) #define BATTR_RMAP_LINK_BW_SET (1 << 7) +#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8) /* Router Reflector related structure. */ struct cluster_list { diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 61f1b295ca..297623365a 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -61,22 +61,51 @@ static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc) && bnc->nexthop_num > 0)); } -static int bgp_isvalid_labeled_nexthop(struct bgp_nexthop_cache *bnc) +static int bgp_isvalid_nexthop_for_mplsovergre(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) +{ + struct interface *ifp = NULL; + struct nexthop *nexthop; + + for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { + ifp = if_lookup_by_index( + bnc->ifindex ? bnc->ifindex : nexthop->ifindex, + bnc->bgp->vrf_id); + if (ifp && (ifp->ll_type == ZEBRA_LLT_IPGRE || + ifp->ll_type == ZEBRA_LLT_IP6GRE)) + break; + } + } + if (!ifp) + return false; + + if (CHECK_FLAG(path->attr->rmap_change_flags, + BATTR_RMAP_L3VPN_ACCEPT_GRE)) + return true; + + return false; +} + +static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) { /* - * In the case of MPLS-VPN, the label is learned from LDP or other + * - In the case of MPLS-VPN, the label is learned from LDP or other * protocols, and nexthop tracking is enabled for the label. * The value is recorded as BGP_NEXTHOP_LABELED_VALID. - * In the case of SRv6-VPN, we need to track the reachability to the + * - In the case of SRv6-VPN, we need to track the reachability to the * SID (in other words, IPv6 address). As in MPLS, we need to record * the value as BGP_NEXTHOP_SID_VALID. However, this function is * currently not implemented, and this function assumes that all * Transit routes for SRv6-VPN are valid. + * - Otherwise check for mpls-gre acceptance */ - return (bgp_zebra_num_connects() == 0 - || (bnc && bnc->nexthop_num > 0 - && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) - || bnc->bgp->srv6_enabled))); + return (bgp_zebra_num_connects() == 0 || + (bnc && (bnc->nexthop_num > 0 && + (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || + bnc->bgp->srv6_enabled || + bgp_isvalid_nexthop_for_mplsovergre(bnc, path))))); } static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) @@ -359,11 +388,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, */ if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) return 1; - else if (safi == SAFI_UNICAST && pi - && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { - return bgp_isvalid_labeled_nexthop(bnc); - } else + else if (safi == SAFI_UNICAST && pi && + pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra && + pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) + return bgp_isvalid_nexthop_for_mpls(bnc, pi); + else return (bgp_isvalid_nexthop(bnc)); } @@ -1063,7 +1092,8 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) && (path->attr->evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = - bgp_isvalid_labeled_nexthop(bnc) ? true : false; + bgp_isvalid_nexthop_for_mpls(bnc, path) ? true + : false; } else { if (bgp_update_martian_nexthop( bnc->bgp, afi, safi, path->type, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 33f68c9e88..40bbdccff4 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1953,6 +1953,57 @@ static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = { route_set_ip_nexthop_free }; +/* `set l3vpn next-hop encapsulation l3vpn gre' */ + +/* Set nexthop to object */ +struct rmap_l3vpn_nexthop_encapsulation_set { + uint8_t protocol; +}; + +static enum route_map_cmd_result_t +route_set_l3vpn_nexthop_encapsulation(void *rule, const struct prefix *prefix, + void *object) +{ + struct rmap_l3vpn_nexthop_encapsulation_set *rins = rule; + struct bgp_path_info *path; + + path = object; + + if (rins->protocol != IPPROTO_GRE) + return RMAP_OKAY; + + SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_L3VPN_ACCEPT_GRE); + return RMAP_OKAY; +} + +/* Route map `l3vpn nexthop encapsulation' compile function. */ +static void *route_set_l3vpn_nexthop_encapsulation_compile(const char *arg) +{ + struct rmap_l3vpn_nexthop_encapsulation_set *rins; + + rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, + sizeof(struct rmap_l3vpn_nexthop_encapsulation_set)); + + /* XXX ALL GRE modes are accepted for now: gre or ip6gre */ + rins->protocol = IPPROTO_GRE; + + return rins; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void route_set_l3vpn_nexthop_encapsulation_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for l3vpn next-hop encapsulation set. */ +static const struct route_map_rule_cmd + route_set_l3vpn_nexthop_encapsulation_cmd = { + "l3vpn next-hop encapsulation", + route_set_l3vpn_nexthop_encapsulation, + route_set_l3vpn_nexthop_encapsulation_compile, + route_set_l3vpn_nexthop_encapsulation_free}; + /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ @@ -5290,6 +5341,34 @@ DEFUN_YANG (no_set_distance, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_l3vpn_nexthop_encapsulation, set_l3vpn_nexthop_encapsulation_cmd, + "[no] set l3vpn next-hop encapsulation gre", + NO_STR SET_STR + "L3VPN operations\n" + "Next hop Information\n" + "Encapsulation options (for BGP only)\n" + "Accept L3VPN traffic over GRE encapsulation\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']"; + const char *xpath_value = + "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation"; + enum nb_operation operation; + + if (no) + operation = NB_OP_DESTROY; + else + operation = NB_OP_CREATE; + + nb_cli_enqueue_change(vty, xpath, operation, NULL); + if (operation == NB_OP_DESTROY) + return nb_cli_apply_changes(vty, NULL); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "gre"); + + return nb_cli_apply_changes(vty, NULL); +} + DEFUN_YANG (set_local_pref, set_local_pref_cmd, "set local-preference WORD", @@ -6835,6 +6914,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_ecommunity_none_cmd); route_map_install_set(&route_set_tag_cmd); route_map_install_set(&route_set_label_index_cmd); + route_map_install_set(&route_set_l3vpn_nexthop_encapsulation_cmd); install_element(RMAP_NODE, &match_peer_cmd); install_element(RMAP_NODE, &match_peer_local_cmd); @@ -6937,6 +7017,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_ipx_vpn_nexthop_cmd); install_element(RMAP_NODE, &set_originator_id_cmd); install_element(RMAP_NODE, &no_set_originator_id_cmd); + install_element(RMAP_NODE, &set_l3vpn_nexthop_encapsulation_cmd); route_map_install_match(&route_match_ipv6_address_cmd); route_map_install_match(&route_match_ipv6_next_hop_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 585596e1aa..2117334f7f 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -406,6 +406,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy, + } + }, { .xpath = NULL, }, diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a01adf7d5d..cd7a70dbcf 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -150,6 +150,10 @@ 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); +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_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 b87877b1e0..585c2a3ff0 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2922,3 +2922,56 @@ int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( return NB_OK; } + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/l3vpn-nexthop-encapsulation + */ +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_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 = "l3vpn next-hop encapsulation"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, + "l3vpn next-hop encapsulation", 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_l3vpn_nexthop_encapsulation_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/doc/user/bgp.rst b/doc/user/bgp.rst index 033b753639..905c9861b3 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2720,6 +2720,25 @@ are reached using *core* MPLS labels which are distributed using LDP or BGP labeled unicast. *bgpd* also supports inter-VRF route leaking. +L3VPN over GRE interfaces +^^^^^^^^^^^^^^^^^^^^^^^^^ + +In MPLS-VPN or SRv6-VPN, an L3VPN next-hop entry requires that the path +chosen respectively contains a labelled path or a valid SID IPv6 address. +Otherwise the L3VPN entry will not be installed. It is possible to ignore +that check when the path chosen by the next-hop uses a GRE interface, and +there is a route-map configured at inbound side of ipv4-vpn or ipv6-vpn +address family with following syntax: + +.. clicmd:: set l3vpn next-hop encapsulation gre + +The incoming BGP L3VPN entry is accepted, provided that the next hop of the +L3VPN entry uses a path that takes the GRE tunnel as outgoing interface. The +remote endpoint should be configured just behind the GRE tunnel; remote +device configuration may vary depending whether it acts at edge endpoint or +not: in any case, the expectation is that incoming MPLS traffic received at +this endpoint should be considered as a valid path for L3VPN. + .. _bgp-vrf-route-leaking: VRF Route Leaking diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 05c9eeb755..5e222576ca 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -339,6 +339,9 @@ Route Map Set Command Set the color of a SR-TE Policy to be applied to a learned route. The SR-TE Policy is uniquely determined by the color and the BGP nexthop. +.. clicmd:: set l3vpn next-hop encapsulation gre + + Accept L3VPN traffic over GRE encapsulation. .. _route-map-call-command: diff --git a/lib/routemap.h b/lib/routemap.h index ad391981e0..0152e820d0 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -387,6 +387,8 @@ DECLARE_QOBJ_TYPE(route_map); (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")) +#define IS_SET_BGP_L3VPN_NEXTHOP_ENCAPSULATION(A) \ + (strmatch(A, "frr-bgp-route-map:set-l3vpn-nexthop-encapsulation")) enum ecommunity_lb_type { EXPLICIT_BANDWIDTH, diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index ff98a14c41..42c7a05d18 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1258,6 +1258,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6")); + } else if (IS_SET_BGP_L3VPN_NEXTHOP_ENCAPSULATION(action)) { + vty_out(vty, " set l3vpn next-hop encapsulation %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation")); } } diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index eaa7891f0c..fcfd14e4fe 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -330,6 +330,12 @@ module frr-bgp-route-map { "Set EVPN gateway IP overlay index IPv6"; } + identity set-l3vpn-nexthop-encapsulation { + base frr-route-map:rmap-set-type; + description + "Accept L3VPN traffic over other than LSP encapsulation"; + } + grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; @@ -902,5 +908,21 @@ module frr-bgp-route-map { type inet:ipv6-address; } } + case l3vpn-nexthop-encapsulation { + 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-l3vpn-nexthop-encapsulation')"; + description + "Accept L3VPN traffic over other than LSP encapsulation"; + leaf l3vpn-nexthop-encapsulation { + type enumeration { + enum "gre" { + value 0; + description + "GRE protocol"; + } + } + } + } } }