zebra, bgp: Support type-5 routes with asymmetric routing

Asymmetric routing is an ideal choice when all VLANs are cfged on all leafs.
It simplifies the routing configuration and
eliminates potential need for advertising subnet routes.
However, we need to reach the Internet or global destinations
or to do subnet-based routing between PODs or DCs.
This requires EVPN type-5 routes but those routes require L3 VNI configuration.

This task is to support EVPN type-5 routes for prefix-based routing in
conjunction with asymmetric routing within the POD/DC.
It is done by providing an option to use the L3 VNI only for prefix routes,
so that type-2 routes (host routes) will only use the L2 VNI.

Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
This commit is contained in:
Mitesh Kanjariya 2018-02-06 14:28:22 -08:00 committed by mitesh
parent 5651675e43
commit c48d9f5f85
11 changed files with 147 additions and 41 deletions

View File

@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
/* Add the export RTs for L3VNI - currently only supported for IPV4 host
* routes
/*
* only attach l3-vni export rts for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP) {
if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
ecommunity_merge(attr->ecommunity, &ecom_sticky);
}
if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
/*
* only attach l3-vni rmac for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP && !is_zero_mac(&attr->rmac) &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
ecom_rmac.size = 1;
@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* The VNI goes into the 'label' field of the route */
vni2label(vpn->vni, &label[0]);
/* Type-2 routes may carry a second VNI - the L3-VNI */
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
/* Type-2 routes may carry a second VNI - the L3-VNI.
* Only attach second label if we are advertising two labels for
* type-2 routes.
*/
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
@ -1209,6 +1220,24 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
&& !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
route_change = 0;
else {
/*
* The attributes have changed, type-2 routes needs to
* be advertised with right labels.
*/
vni2label(vpn->vni, &label[0]);
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
if (l3vni) {
vni2label(l3vni, &label[1]);
num_labels++;
}
}
memcpy(&tmp_ri->extra->label, label, sizeof(label));
tmp_ri->extra->num_labels = num_labels;
/* The attribute has changed. */
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
@ -4214,7 +4243,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
int bgp_evpn_local_l3vni_add(vni_t l3vni,
vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip)
struct in_addr originator_ip,
int filter)
{
struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
struct bgp *bgp_def = NULL; /* default bgp instance */
@ -4266,6 +4296,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
/* set the originator ip */
bgp_vrf->originator_ip = originator_ip;
/* set the right filter - are we using l3vni only for prefix routes? */
if (filter)
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
/* auto derive RD/RT */
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
@ -4279,9 +4313,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
link_l2vni_hash_to_l3vni,
bgp_vrf);
/* updates all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* Only update all corresponding type-2 routes if we are advertising two
* labels along with type-2 routes
*/
if (!filter)
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* advertise type-5 routes if needed */
update_advertise_vrf_routes(bgp_vrf);
@ -4340,9 +4377,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
}
/* update all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
update_routes_for_vni(bgp_def, vpn);
}
}
/* Delete the instance if it was autocreated */
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))

View File

@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
u_char flags);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip);
struct in_addr originator_ip,
int filter);
extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
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,

View File

@ -61,6 +61,8 @@ struct bgpevpn {
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if
needed for this VPN */
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
@ -179,9 +181,13 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
if (!bgp_vrf)
return;
listnode_delete(bgp_vrf->l2vnis, vpn);
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
if (bgp_vrf->l2vnis)
listnode_delete(bgp_vrf->l2vnis, vpn);
}
static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
@ -189,9 +195,16 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
if (!bgp_vrf)
return;
listnode_add_sort(bgp_vrf->l2vnis, vpn);
/* check if we are advertising two labels for this vpn */
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
if (bgp_vrf->l2vnis)
listnode_add_sort(bgp_vrf->l2vnis, vpn);
}
static inline int is_vni_configured(struct bgpevpn *vpn)

View File

@ -3897,6 +3897,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
vty_out(vty, " Rmac: %s\n",
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
vty_out(vty, " VNI Filter: %s\n",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
vty_out(vty, " L2-VNI List:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
@ -3922,6 +3926,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
json_object_string_add(json, "rmac",
prefix_mac2str(&bgp->rmac, buf,
sizeof(buf)));
json_object_string_add(json, "vniFilter",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
? "prefix-routes-only" : "none");
/* list of l2vnis */
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
json_object_array_add(json_vnis,

View File

@ -1731,6 +1731,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
int filter = 0;
char buf[ETHER_ADDR_STRLEN];
vni_t l3vni = 0;
struct ethaddr rmac;
@ -1744,17 +1745,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
if (cmd == ZEBRA_L3VNI_ADD) {
stream_get(&rmac, s, sizeof(struct ethaddr));
originator_ip.s_addr = stream_get_ipv4(s);
stream_get(&filter, s, sizeof(int));
}
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s",
(cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
vrf_id_to_name(vrf_id),
l3vni,
prefix_mac2str(&rmac, buf, sizeof(buf)));
prefix_mac2str(&rmac, buf, sizeof(buf)),
filter ? "prefix-routes-only" : "none");
if (cmd == ZEBRA_L3VNI_ADD)
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip,
filter);
else
bgp_evpn_local_l3vni_del(l3vni, vrf_id);

View File

@ -435,6 +435,7 @@ struct bgp {
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
#define BGP_VRF_RD_CFGD (1 << 5)
#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;

View File

@ -598,7 +598,10 @@ static int vrf_config_write(struct vty *vty)
if (vrf_is_user_cfged(vrf)) {
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
if (zvrf->l3vni)
vty_out(vty, " vni %u\n", zvrf->l3vni);
vty_out(vty, " vni %u%s\n",
zvrf->l3vni,
is_l3vni_for_prefix_routes_only(zvrf->l3vni) ?
" prefix-routes-only" :"");
vty_out(vty, "!\n");
}

View File

@ -2338,20 +2338,26 @@ DEFUN (show_vrf,
DEFUN (default_vrf_vni_mapping,
default_vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE,
"vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to the DEFAULT VRF\n"
"VNI-ID\n")
"VNI-ID\n"
"Prefix routes only \n")
{
int ret = 0;
char err[ERR_STR_SZ];
struct zebra_vrf *zvrf = NULL;
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
int filter = 0;
zvrf = vrf_info_lookup(VRF_DEFAULT);
if (!zvrf)
return CMD_WARNING;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
if (argc == 3)
filter = 1;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2376,7 +2382,7 @@ DEFUN (no_default_vrf_vni_mapping,
if (!zvrf)
return CMD_WARNING;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2387,11 +2393,13 @@ DEFUN (no_default_vrf_vni_mapping,
DEFUN (vrf_vni_mapping,
vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE,
"vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to tenant VRF\n"
"VNI-ID\n")
"VNI-ID\n"
"prefix-routes-only\n")
{
int ret = 0;
int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
@ -2400,9 +2408,13 @@ DEFUN (vrf_vni_mapping,
assert(vrf);
assert(zvrf);
if (argc == 3)
filter = 1;
/* Mark as having FRR configuration */
vrf_set_user_cfged(vrf);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2427,7 +2439,7 @@ DEFUN (no_vrf_vni_mapping,
assert(vrf);
assert(zvrf);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;

View File

@ -928,6 +928,9 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
zl3vni_svi_if_name(zl3vni));
vty_out(vty, " State: %s\n",
zl3vni_state2str(zl3vni));
vty_out(vty, " VNI Filter: %s\n",
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
vty_out(vty, " Router MAC: %s\n",
zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
vty_out(vty, " L2 VNIs: ");
@ -951,6 +954,10 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
json_object_string_add(json, "routerMac",
zl3vni_rmac2str(zl3vni, buf,
sizeof(buf)));
json_object_string_add(json, "vniFilter",
CHECK_FLAG(zl3vni->filter,
PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
json_object_array_add(json_vni_list,
json_object_new_int(zvni->vni));
@ -3613,15 +3620,19 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
stream_putl(s, zl3vni->vni);
stream_put(s, &rmac, sizeof(struct ethaddr));
stream_put_in_addr(s, &zl3vni->local_vtep_ip);
stream_put(s, &zl3vni->filter, sizeof(int));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
zlog_debug(
"Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s",
zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
prefix_mac2str(&rmac, buf, sizeof(buf)),
inet_ntoa(zl3vni->local_vtep_ip),
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none",
zebra_route_string(client->proto));
client->l3vniadd_cnt++;
@ -3666,10 +3677,6 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("L3-VNI %u is UP - send add to BGP",
zl3vni->vni);
/* send l3vni add to BGP */
zl3vni_send_add_to_client(zl3vni);
}
@ -3679,10 +3686,6 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("L3-VNI %u is Down - Send del to BGP",
zl3vni->vni);
/* send l3-vni del to BGP*/
zl3vni_send_del_to_client(zl3vni);
}
@ -3835,6 +3838,17 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
/* Public functions */
int is_l3vni_for_prefix_routes_only(vni_t vni)
{
zebra_l3vni_t *zl3vni = NULL;
zl3vni = zl3vni_lookup(vni);
if (!zl3vni)
return 0;
return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
}
/* handle evpn route in vrf table */
void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
struct ethaddr *rmac,
@ -6432,7 +6446,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
vni_t vni,
char *err, int err_str_sz,
int add)
int filter, int add)
{
zebra_l3vni_t *zl3vni = NULL;
struct zebra_vrf *zvrf_default = NULL;
@ -6477,6 +6491,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
/* associate the vrf with vni */
zvrf->l3vni = vni;
/* set the filter in l3vni to denote if we are using l3vni only
* for prefix routes
*/
if (filter)
SET_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY);
/* associate with vxlan-intf;
* we need to associate with the vxlan-intf first
*/

View File

@ -52,6 +52,7 @@ is_evpn_enabled()
#define VNI_STR_LEN 32
extern int is_l3vni_for_prefix_routes_only(vni_t vni);
extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf);
@ -154,7 +155,7 @@ extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
struct zebra_vrf *zvrf);
extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
char *err,
int err_str_sz, int add);
int err_str_sz, int filter, int add);
extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
extern void zebra_vxlan_close_tables(struct zebra_vrf *);
extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *);

View File

@ -101,6 +101,9 @@ struct zebra_l3vni_t_ {
/* vrf_id */
vrf_id_t vrf_id;
uint32_t filter;
#define PREFIX_ROUTES_ONLY (1 << 0) /* l3-vni used for prefix routes only */
/* Local IP */
struct in_addr local_vtep_ip;