summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c3
-rw-r--r--bgpd/bgp_attr.h3
-rw-r--r--bgpd/bgp_attr_evpn.c29
-rw-r--r--bgpd/bgp_attr_evpn.h2
-rw-r--r--bgpd/bgp_ecommunity.c20
-rw-r--r--bgpd/bgp_evpn.c1560
-rw-r--r--bgpd/bgp_evpn.h27
-rw-r--r--bgpd/bgp_evpn_private.h197
-rw-r--r--bgpd/bgp_evpn_vty.c822
-rw-r--r--bgpd/bgp_main.c1
-rw-r--r--bgpd/bgp_memory.c1
-rw-r--r--bgpd/bgp_memory.h1
-rw-r--r--bgpd/bgp_route.c8
-rw-r--r--bgpd/bgp_route.h5
-rw-r--r--bgpd/bgp_vty.c39
-rw-r--r--bgpd/bgp_zebra.c91
-rw-r--r--bgpd/bgpd.c66
-rw-r--r--bgpd/bgpd.h41
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c6
-rw-r--r--lib/command.h2
-rw-r--r--lib/command_match.c6
-rw-r--r--lib/grammar_sandbox.c18
-rw-r--r--lib/log.c2
-rw-r--r--lib/nexthop.h1
-rw-r--r--lib/prefix.c5
-rw-r--r--lib/prefix.h10
-rw-r--r--lib/vrf.c11
-rw-r--r--lib/vrf.h1
-rw-r--r--lib/zclient.c14
-rw-r--r--lib/zclient.h6
-rw-r--r--lib/zebra.h1
-rw-r--r--zebra/connected.c4
-rw-r--r--zebra/kernel_socket.c8
-rw-r--r--zebra/main.c7
-rw-r--r--zebra/redistribute.c2
-rw-r--r--zebra/rib.h3
-rw-r--r--zebra/rt_netlink.c4
-rw-r--r--zebra/zebra_ns.c3
-rw-r--r--zebra/zebra_ns.h3
-rw-r--r--zebra/zebra_rib.c27
-rw-r--r--zebra/zebra_vrf.c6
-rw-r--r--zebra/zebra_vrf.h4
-rw-r--r--zebra/zebra_vty.c282
-rw-r--r--zebra/zebra_vxlan.c2656
-rw-r--r--zebra/zebra_vxlan.h30
-rw-r--r--zebra/zebra_vxlan_null.c24
-rw-r--r--zebra/zebra_vxlan_private.h156
-rw-r--r--zebra/zserv.c49
-rw-r--r--zebra/zserv.h2
49 files changed, 5848 insertions, 421 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 5b917fc814..206d91bfe9 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1876,6 +1876,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky);
attr->sticky = sticky;
+ /* Extract the Rmac, if any */
+ bgp_attr_rmac(attr, &attr->rmac);
+
return BGP_ATTR_PARSE_PROCEED;
}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index f694f01adb..1de1bee0f9 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -182,6 +182,9 @@ struct attr {
/* EVPN MAC Mobility sequence number, if any. */
u_int32_t mm_seqnum;
+
+ /* EVPN local router-mac */
+ struct ethaddr rmac;
};
/* rmap_change_flags definition */
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 300c9ddb50..eaa4e329d4 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -105,6 +105,35 @@ char *ecom_mac2str(char *ecom_mac)
return prefix_mac2str((struct ethaddr *)en, NULL, 0);
}
+/* Fetch router-mac from extended community */
+void bgp_attr_rmac(struct attr *attr,
+ struct ethaddr *rmac)
+{
+ int i = 0;
+ struct ecommunity *ecom;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return;
+
+ /* If there is a router mac extended community, set RMAC in attr */
+ for (i = 0; i < ecom->size; i++) {
+ u_char *pnt = NULL;
+ u_char type = 0;
+ u_char sub_type = 0;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (!(type == ECOMMUNITY_ENCODE_EVPN &&
+ sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC))
+ continue;
+
+ memcpy(rmac, pnt, ETH_ALEN);
+ }
+}
+
/*
* Fetch and return the sequence number from MAC Mobility extended
* community, if present, else 0.
diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h
index 15d9e126e4..8b55cb3002 100644
--- a/bgpd/bgp_attr_evpn.h
+++ b/bgpd/bgp_attr_evpn.h
@@ -59,7 +59,7 @@ extern void bgp_add_routermac_ecom(struct attr *attr,
struct ethaddr *routermac);
extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag,
struct prefix *dst);
-
+extern void bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac);
extern u_int32_t bgp_attr_mac_mobility_seqnum(struct attr *attr,
u_char *sticky);
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index e19f516505..9caf38d569 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -695,19 +695,19 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
} else if (type == ECOMMUNITY_ENCODE_EVPN) {
if (filter == ECOMMUNITY_ROUTE_TARGET)
continue;
- if (*pnt == ECOMMUNITY_SITE_ORIGIN) {
- char macaddr[6];
+ if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
+ struct ethaddr rmac;
pnt++;
- memcpy(&macaddr, pnt, 6);
+ memcpy(&rmac, pnt, ETH_ALEN);
len = sprintf(
str_buf + str_pnt,
- "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
- (uint8_t)macaddr[0],
- (uint8_t)macaddr[1],
- (uint8_t)macaddr[2],
- (uint8_t)macaddr[3],
- (uint8_t)macaddr[4],
- (uint8_t)macaddr[5]);
+ "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)rmac.octet[0],
+ (uint8_t)rmac.octet[1],
+ (uint8_t)rmac.octet[2],
+ (uint8_t)rmac.octet[3],
+ (uint8_t)rmac.octet[4],
+ (uint8_t)rmac.octet[5]);
} else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
u_int32_t seqnum;
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index ca2e1a54d4..12cc425bd3 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -93,6 +93,131 @@ static int vni_hash_cmp(const void *p1, const void *p2)
}
/*
+ * Make vrf import route target hash key.
+ */
+static unsigned int vrf_import_rt_hash_key_make(void *p)
+{
+ struct vrf_irt_node *irt = p;
+ char *pnt = irt->rt.val;
+ unsigned int key = 0;
+ int c = 0;
+
+ key += pnt[c];
+ key += pnt[c + 1];
+ key += pnt[c + 2];
+ key += pnt[c + 3];
+ key += pnt[c + 4];
+ key += pnt[c + 5];
+ key += pnt[c + 6];
+ key += pnt[c + 7];
+
+ return key;
+}
+
+/*
+ * Comparison function for vrf import rt hash
+ */
+static int vrf_import_rt_hash_cmp(const void *p1, const void *p2)
+{
+ const struct vrf_irt_node *irt1 = p1;
+ const struct vrf_irt_node *irt2 = p2;
+
+ if (irt1 == NULL && irt2 == NULL)
+ return 1;
+
+ if (irt1 == NULL || irt2 == NULL)
+ return 0;
+
+ return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
+}
+
+/*
+ * Create a new vrf import_rt in default instance
+ */
+static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_def = NULL;
+ struct vrf_irt_node *irt;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt new - def instance not created yet");
+ return NULL;
+ }
+
+ irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT,
+ sizeof(struct vrf_irt_node));
+ if (!irt)
+ return NULL;
+
+ irt->rt = *rt;
+ irt->vrfs = list_new();
+
+ /* Add to hash */
+ if (!hash_get(bgp_def->vrf_import_rt_hash, irt, hash_alloc_intern)) {
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+ return NULL;
+ }
+
+ return irt;
+}
+
+/*
+ * Free the vrf import rt node
+ */
+static void vrf_import_rt_free(struct vrf_irt_node *irt)
+{
+ struct bgp *bgp_def = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt free - def instance not created yet");
+ return;
+ }
+
+ hash_release(bgp_def->vrf_import_rt_hash, irt);
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+}
+
+/*
+ * Function to lookup Import RT node - used to map a RT to set of
+ * VNIs importing routes with that RT.
+ */
+static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_def = NULL;
+ struct vrf_irt_node *irt;
+ struct vrf_irt_node tmp;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt lookup - def instance not created yet");
+ return NULL;
+ }
+
+ memset(&tmp, 0, sizeof(struct vrf_irt_node));
+ memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
+ irt = hash_lookup(bgp_def->vrf_import_rt_hash, &tmp);
+ return irt;
+}
+
+/*
+ * Is specified VRF present on the RT's list of "importing" VRFs?
+ */
+static int is_vrf_present_in_irt_vrfs(struct list *vrfs,
+ struct bgp *bgp_vrf)
+{
+ struct listnode *node = NULL, *nnode = NULL;
+ struct bgp *tmp_bgp_vrf = NULL;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (tmp_bgp_vrf == bgp_vrf)
+ return 1;
+ }
+ return 0;
+}
+
+/*
* Make import route target hash key.
*/
static unsigned int import_rt_hash_key_make(void *p)
@@ -247,6 +372,57 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
}
/*
+ * Map one RT to specified VRF.
+ * bgp_vrf = BGP vrf instance
+ */
+static void map_vrf_to_rt(struct bgp *bgp_vrf,
+ struct ecommunity_val *eval)
+{
+ struct vrf_irt_node *irt = NULL;
+ struct ecommunity_val eval_tmp;
+
+ /* If using "automatic" RT,
+ * we only care about the local-admin sub-field.
+ * This is to facilitate using L3VNI(VRF-VNI)
+ * as the RT for EBGP peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ /* Already mapped. */
+ return;
+
+ if (!irt) {
+ irt = vrf_import_rt_new(&eval_tmp);
+ assert(irt);
+ }
+
+ /* Add VRF to the list for this RT. */
+ listnode_add(irt->vrfs, bgp_vrf);
+}
+
+/*
+ * Unmap specified VRF from specified RT. If there are no other
+ * VRFs for this RT, then the RT hash is deleted.
+ * bgp_vrf: BGP VRF specific instance
+ */
+static void unmap_vrf_from_rt(struct bgp *bgp_vrf,
+ struct vrf_irt_node *irt)
+{
+ /* Delete VRF from list for this RT. */
+ listnode_delete(irt->vrfs, bgp_vrf);
+ if (!listnode_head(irt->vrfs)) {
+ list_delete_and_null(&irt->vrfs);
+ vrf_import_rt_free(irt);
+ }
+}
+
+/*
* Map one RT to specified VNI.
*/
static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn,
@@ -301,12 +477,12 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn,
* VNIs but the same across routers (in the same AS) for a particular
* VNI.
*/
-static void form_auto_rt(struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl)
+static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
{
struct ecommunity_val eval;
struct ecommunity *ecomadd;
- encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval);
@@ -426,19 +602,75 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Build extended communities for EVPN prefix route.
+ */
+static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_rmac;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_rmac;
+ bgp_encap_types tnl_type;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ struct list *vrf_export_rtl = NULL;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (u_int8_t *)eval.val;
+
+ /* Add Encap */
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* Add the export RTs for L3VNI/VRF */
+ vrf_export_rtl = bgp_vrf->vrf_export_rtl;
+ if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ ecom);
+ }
+
+ /* add the router mac extended community */
+ if (!is_zero_mac(&attr->rmac)) {
+ memset(&ecom_rmac, 0, sizeof(ecom_rmac));
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecom_rmac.size = 1;
+ ecom_rmac.val = (uint8_t *)eval_rmac.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_rmac);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/*
* Build extended communities for EVPN route. RT and ENCAP are
* applicable to all routes.
+ * TODO: currently kernel doesnt support ipv6 routes with ipv4 nexthops.
+ * This means that we can't do symmetric routing for ipv6 hosts routes
+ * in the same way as ipv4 host routes.
+ * We wont attach l3-vni related RTs for ipv6 routes.
+ * For now, We will only adevrtise ipv4 host routes
+ * with L3-VNI related ext-comm.
*/
-static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr)
+static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
+ afi_t afi)
{
struct ecommunity ecom_encap;
struct ecommunity ecom_sticky;
+ struct ecommunity ecom_rmac;
struct ecommunity_val eval;
struct ecommunity_val eval_sticky;
+ struct ecommunity_val eval_rmac;
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
struct ecommunity *ecom;
u_int32_t seqnum;
+ struct list *vrf_export_rtl = NULL;
/* Encap */
tnl_type = BGP_ENCAP_TYPE_VXLAN;
@@ -450,10 +682,24 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr)
/* Add Encap */
attr->ecommunity = ecommunity_dup(&ecom_encap);
- /* Add the export RTs */
+ /* Add the export RTs for L2VNI */
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
+ */
+ if (afi == AFI_IP) {
+ vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
+ if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
+ ecom))
+ attr->ecommunity =
+ ecommunity_merge(attr->ecommunity,
+ ecom);
+ }
+ }
+
if (attr->sticky) {
seqnum = 0;
memset(&ecom_sticky, 0, sizeof(ecom_sticky));
@@ -464,6 +710,15 @@ 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)) {
+ memset(&ecom_rmac, 0, sizeof(ecom_rmac));
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecom_rmac.size = 1;
+ ecom_rmac.val = (uint8_t *)eval_rmac.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_rmac);
+ }
+
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
}
@@ -699,6 +954,123 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn)
return local_ri->attr->sticky;
}
+static int update_evpn_type5_route_entry(struct bgp *bgp_def,
+ struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi, struct bgp_node *rn,
+ struct attr *attr, int *route_changed)
+{
+ struct attr *attr_new = NULL;
+ struct bgp_info *ri = NULL;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ struct bgp_info *local_ri = NULL;
+ struct bgp_info *tmp_ri = NULL;
+
+ *route_changed = 0;
+ /* locate the local route entry if any */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
+ if (tmp_ri->peer == bgp_def->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ }
+
+ /* create a new route entry if one doesnt exist.
+ Otherwise see if route attr has changed
+ */
+ if (!local_ri) {
+
+ /* route has changed as this is the first entry */
+ *route_changed = 1;
+
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+
+ /* create the route info from attribute */
+ ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp_def->peer_self, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+
+ /* L3-VNI goes in the label2 field */
+ bgp_info_extra_get(ri);
+ vni2label(bgp_vrf->l3vni, &label);
+ memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES);
+
+ /* add the route entry to route node*/
+ bgp_info_add(rn, ri);
+ } else {
+
+ tmp_ri = local_ri;
+ if (!attrhash_cmp(tmp_ri->attr, attr)) {
+
+ /* attribute changed */
+ *route_changed = 1;
+
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+ bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, tmp_ri);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_ri->attr);
+ tmp_ri->attr = attr_new;
+ tmp_ri->uptime = bgp_clock();
+ }
+ }
+ return 0;
+}
+
+/* update evpn type-5 route entry */
+static int update_evpn_type5_route(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct bgp_node *rn = NULL;
+ struct bgp *bgp_def = NULL;
+ int route_changed = 0;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* build path attribute for this route */
+ memset(&attr, 0, sizeof(struct attr));
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = bgp_vrf->originator_ip;
+ attr.mp_nexthop_global_in = bgp_vrf->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
+
+ /* Setup RT and encap extended community */
+ build_evpn_type5_route_extcomm(bgp_vrf, &attr);
+
+ /* get the route node in global table */
+ rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi,
+ (struct prefix *)evp,
+ &bgp_vrf->vrf_prd);
+ assert(rn);
+
+ /* create or update the route entry within the route node */
+ update_evpn_type5_route_entry(bgp_def, bgp_vrf,
+ afi, safi,
+ rn, &attr, &route_changed);
+
+ /* schedule for processing and unlock node */
+ if (route_changed) {
+ bgp_process(bgp_def, rn, afi, safi);
+ bgp_unlock_node(rn);
+ }
+
+ /* uninten temporary */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
/*
* Create or update EVPN route entry. This could be in the VNI route table
* or the global route table.
@@ -834,11 +1206,14 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0;
- attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
+ bgpevpn_get_rmac(vpn, &attr.rmac);
vni2label(vpn->vni, &(attr.label));
/* Set up RT and ENCAP extended community. */
- build_evpn_route_extcomm(vpn, &attr);
+ build_evpn_route_extcomm(vpn, &attr,
+ IS_EVPN_PREFIX_IPADDR_V4(p) ?
+ AFI_IP : AFI_IP6);
/* First, create (or fetch) route node within the VNI. */
/* NOTE: There is no RD here. */
@@ -880,6 +1255,58 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
+/* Delete EVPN type5 route entry from global table */
+static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
+ struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi,
+ struct bgp_node *rn,
+ struct bgp_info **ri)
+{
+ struct bgp_info *tmp_ri = NULL;
+
+ *ri = NULL;
+
+ /* find the matching route entry */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
+ if (tmp_ri->peer == bgp_def->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ *ri = tmp_ri;
+
+ /* Mark route for delete. */
+ if (tmp_ri)
+ bgp_info_delete(rn, tmp_ri);
+}
+
+/* Delete EVPN type5 route */
+static int delete_evpn_type5_route(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_node *rn = NULL;
+ struct bgp_info *ri = NULL;
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* locate the global route entry for this type-5 prefix */
+ rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi,
+ (struct prefix *)evp, &bgp_vrf->vrf_prd);
+ if (!rn)
+ return 0;
+
+ delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri);
+ if (ri)
+ bgp_process(bgp_def, rn, afi, safi);
+ bgp_unlock_node(rn);
+ return 0;
+}
+
/*
* Delete EVPN route entry. This could be in the VNI route table
* or the global route table.
@@ -967,12 +1394,16 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
struct bgp_info *ri;
struct attr attr;
struct attr attr_sticky;
+ struct attr attr_ip6;
+ struct attr attr_sticky_ip6;
struct attr *attr_new;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
memset(&attr, 0, sizeof(struct attr));
memset(&attr_sticky, 0, sizeof(struct attr));
+ memset(&attr_ip6, 0, sizeof(struct attr));
+ memset(&attr_sticky_ip6, 0, sizeof(struct attr));
/* Build path-attribute - all type-2 routes for this VNI will share the
* same path attribute.
@@ -982,14 +1413,29 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
attr.nexthop = vpn->originator_ip;
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ bgpevpn_get_rmac(vpn, &attr.rmac);
attr_sticky.nexthop = vpn->originator_ip;
attr_sticky.mp_nexthop_global_in = vpn->originator_ip;
attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr_sticky.sticky = 1;
+ bgpevpn_get_rmac(vpn, &attr_sticky.rmac);
+ bgp_attr_default_set(&attr_ip6, BGP_ORIGIN_IGP);
+ bgp_attr_default_set(&attr_sticky_ip6, BGP_ORIGIN_IGP);
+ attr_ip6.nexthop = vpn->originator_ip;
+ attr_ip6.mp_nexthop_global_in = vpn->originator_ip;
+ attr_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ bgpevpn_get_rmac(vpn, &attr_ip6.rmac);
+ attr_sticky_ip6.nexthop = vpn->originator_ip;
+ attr_sticky_ip6.mp_nexthop_global_in = vpn->originator_ip;
+ attr_sticky_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_sticky_ip6.sticky = 1;
+ bgpevpn_get_rmac(vpn, &attr_sticky_ip6.rmac);
/* Set up RT, ENCAP and sticky MAC extended community. */
- build_evpn_route_extcomm(vpn, &attr);
- build_evpn_route_extcomm(vpn, &attr_sticky);
+ build_evpn_route_extcomm(vpn, &attr, AFI_IP);
+ build_evpn_route_extcomm(vpn, &attr_sticky, AFI_IP);
+ build_evpn_route_extcomm(vpn, &attr_ip6, AFI_IP6);
+ build_evpn_route_extcomm(vpn, &attr_sticky_ip6, AFI_IP6);
/* Walk this VNI's route table and update local type-2 routes. For any
* routes updated, update corresponding entry in the global table too.
@@ -1003,12 +1449,24 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
- if (evpn_route_is_sticky(bgp, rn))
- update_evpn_route_entry(bgp, vpn, afi, safi, rn,
- &attr_sticky, 0, 1, &ri, 0);
- else
- update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr,
- 0, 1, &ri, 0);
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (evpn_route_is_sticky(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_sticky, 0, 1,
+ &ri, 0);
+ else
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr, 0, 1, &ri, 0);
+ } else {
+ if (evpn_route_is_sticky(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_sticky_ip6, 0, 1,
+ &ri, 0);
+ else
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_ip6, 0, 1,
+ &ri, 0);
+ }
/* If a local route exists for this prefix, we need to update
* the global routing table too.
@@ -1222,6 +1680,100 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Install route entry into the VRF routing table and invoke route selection.
+ */
+static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr *attr_new;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ char buf[PREFIX_STRLEN];
+ char buf1[PREFIX_STRLEN];
+
+ memset(pp, 0, sizeof(struct prefix));
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, pp);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug("installing evpn prefix %s as ip prefix %s in vrf %s",
+ prefix2str(evp, buf, sizeof(buf)),
+ prefix2str(pp, buf1, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ /* Create (or fetch) route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else
+ return 0;
+
+ /* Check if route entry is already present. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Create new route with its attribute. */
+ ri = info_make(parent_ri->type, parent_ri->sub_type, 0,
+ parent_ri->peer, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(ri);
+ ri->extra->parent = parent_ri;
+ if (parent_ri->extra)
+ memcpy(&ri->extra->label, &parent_ri->extra->label,
+ BGP_LABEL_BYTES);
+ bgp_info_add(rn, ri);
+ } else {
+ if (attrhash_cmp(ri->attr, parent_ri->attr)
+ && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) {
+ bgp_unlock_node(rn);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, ri);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&ri->attr);
+ ri->attr = attr_new;
+ ri->uptime = bgp_clock();
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, rn, afi, safi);
+
+ return ret;
+}
+
+/*
* Install route entry into the VNI routing table and invoke route selection.
*/
static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
@@ -1288,6 +1840,73 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Uninstall route entry from the VRF routing table and send message
+ * to zebra, if appropriate.
+ */
+static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ char buf[PREFIX_STRLEN];
+ char buf1[PREFIX_STRLEN];
+
+ memset(pp, 0, sizeof(struct prefix));
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, pp);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug("uninstalling evpn prefix %s as ip prefix %s in vrf %s",
+ prefix2str(evp, buf, sizeof(buf)),
+ prefix2str(pp, buf1, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ /* Locate route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ } else {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ }
+
+ if (!rn)
+ return 0;
+
+ /* Find matching route entry. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri)
+ return 0;
+
+ /* Mark entry for deletion */
+ bgp_info_delete(rn, ri);
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, rn, afi, safi);
+
+ /* Unlock route node. */
+ bgp_unlock_node(rn);
+
+ return ret;
+}
+
+/*
* Uninstall route entry from the VNI routing table and send message
* to zebra, if appropriate.
*/
@@ -1327,6 +1946,73 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Given a route entry and a VRF, see if this route entry should be
+ * imported into the VRF i.e., RTs match.
+ */
+static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
+ struct bgp_info *ri)
+{
+ struct attr *attr = ri->attr;
+ struct ecommunity *ecom;
+ int i;
+
+ assert(attr);
+ /* Route should have valid RT to be even considered. */
+ if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* For each extended community RT, see if it matches this VNI. If any RT
+ * matches, we're done.
+ */
+ for (i = 0; i < ecom->size; i++) {
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct vrf_irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* See if this RT matches specified VNIs import RTs */
+ irt = lookup_vrf_import_rt(eval);
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+
+ /* Also check for non-exact match. In this, we mask out the AS
+ * and
+ * only check on the local-admin sub-field. This is to
+ * facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS
+ || type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ mask_ecom_global_admin(&eval_tmp, eval);
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ }
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
* Given a route entry and a VNI, see if this route entry should be
* imported into the VNI i.e., RTs match.
*/
@@ -1394,6 +2080,88 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Install or uninstall mac-ip routes are appropriate for this
+ * particular VRF.
+ */
+static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf,
+ int install)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rd_rn, *rn;
+ struct bgp_table *table;
+ struct bgp_info *ri;
+ int ret;
+ char buf[PREFIX_STRLEN];
+ struct bgp *bgp_def = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VRF. Note that we need to loop through all global
+ * routes to determine which route matches the import rt on vrf
+ */
+ for (rd_rn = bgp_table_top(bgp_def->rib[afi][safi]); rd_rn;
+ rd_rn = bgp_route_next(rd_rn)) {
+ table = (struct bgp_table *)(rd_rn->info);
+ if (!table)
+ continue;
+
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ /* if not mac-ip route skip this route */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ continue;
+
+ /* if not a mac+ip route skip this route */
+ if (!(IS_EVPN_PREFIX_IPADDR_V4(evp) ||
+ IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ continue;
+
+ for (ri = rn->info; ri; ri = ri->next) {
+ /* Consider "valid" remote routes applicable for
+ * this VRF.
+ */
+ if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID)
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (is_route_matching_for_vrf(bgp_vrf, ri)) {
+ if (install)
+ ret =
+ install_evpn_route_entry_in_vrf(
+ bgp_vrf, evp, ri);
+ else
+ ret =
+ uninstall_evpn_route_entry_in_vrf(
+ bgp_vrf, evp, ri);
+
+ if (ret) {
+ zlog_err(
+ "Failed to %s EVPN %s route in VRF %s",
+ install ? "install"
+ : "uninstall",
+ prefix2str(evp, buf,
+ sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
* Install or uninstall routes of specified type that are appropriate for this
* particular VNI.
*/
@@ -1467,6 +2235,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
return 0;
}
+/* Install any existing remote routes applicable for this VRF into VRF RIB. This
+ * is invoked upon l3vni-add or l3vni import rt change
+ */
+static int install_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 1);
+ return 0;
+}
+
/*
* Install any existing remote routes applicable for this VNI into its
* routing table. This is invoked when a VNI becomes "live" or its Import
@@ -1488,6 +2265,13 @@ static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
1);
}
+/* uninstall routes from l3vni vrf. */
+static int uninstall_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 0);
+ return 0;
+}
+
/*
* Uninstall any existing remote routes for this VNI. One scenario in which
* this is invoked is upon an import RT change.
@@ -1510,6 +2294,51 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
+ * Install or uninstall route in matching VRFs (list).
+ */
+static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
+ safi_t safi, struct prefix_evpn *evp,
+ struct bgp_info *ri,
+ struct list *vrfs, int install)
+{
+ char buf[PREFIX2STR_BUFFER];
+ struct bgp *bgp_vrf;
+ struct listnode *node, *nnode;
+
+ /* Only type-2/type-5 routes go into a VRF */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ return 0;
+
+ /* if it is type-2 route and not a mac+ip route skip this route */
+ if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) &&
+ !(IS_EVPN_PREFIX_IPADDR_V4(evp) || IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
+ int ret;
+
+ if (install)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+ else
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+
+ if (ret) {
+ zlog_err("%u: Failed to %s prefix %s in VRF %s",
+ bgp_def->vrf_id,
+ install ? "install" : "uninstall",
+ prefix2str(evp, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
* Install or uninstall route in matching VNIs (list).
*/
static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
@@ -1559,9 +2388,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
assert(attr);
- /* Only type-2 and type-3 routes go into a L2 VNI. */
+ /* Only type-2 and type-3 and type-5 are supported currently */
if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
- || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE))
+ || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
return 0;
/* If we don't have Route Target, nothing much to do. */
@@ -1572,15 +2402,16 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (!ecom || !ecom->size)
return -1;
- /* For each extended community RT, see which VNIs match and import
- * the route into matching VNIs.
+ /* For each extended community RT, see which VNIs/VRFs match and import
+ * the route into matching VNIs/VRFs.
*/
for (i = 0; i < ecom->size; i++) {
u_char *pnt;
u_char type, sub_type;
struct ecommunity_val *eval;
struct ecommunity_val eval_tmp;
- struct irt_node *irt;
+ struct irt_node *irt; /* import rt for l2vni */
+ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
@@ -1591,34 +2422,106 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
- /* Are we interested in this RT? */
+ /* Import route into matching l2-vnis (type-2/type-3 routes go
+ * into l2vni table)
+ */
irt = lookup_import_rt(bgp, eval);
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
- /* Also check for non-exact match. In this, we mask out the AS
- * and
- * only check on the local-admin sub-field. This is to
- * facilitate using
+ /* Import route into matching l3-vnis (type-2/type-5 routes go
+ * into l3vni/vrf table)
+ */
+ vrf_irt = lookup_vrf_import_rt(eval);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri,
+ vrf_irt->vrfs, import);
+
+ /* Also check for non-exact match. In this,
+ * we mask out the AS and
+ * only check on the local-admin sub-field.
+ * This is to facilitate using
* VNI as the RT for EBGP peering too.
*/
irt = NULL;
+ vrf_irt = NULL;
if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_import_rt(bgp, &eval_tmp);
+ vrf_irt = lookup_vrf_import_rt(&eval_tmp);
}
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp,
+ ri, vrf_irt->vrfs,
+ import);
}
return 0;
}
+/* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
+ * routes */
+static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
+{
+ /* delete all ipv4 routes and withdraw from peers */
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* delete all ipv6 routes and withdraw from peers */
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+}
+
+/* update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5
+ * routes */
+static void update_advertise_vrf_routes(struct bgp *bgp_vrf)
+{
+ /* update all ipv4 routes */
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* update all ipv6 routes */
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+}
+
+/*
+ * update and advertise local routes for a VRF as type-5 routes.
+ * This is invoked upon RD change for a VRF. Note taht the processing is only
+ * done in the global route table using the routes which already exist in the
+ * VRF routing table
+ */
+static void update_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* derive the RD for the VRF based on new router-id */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* update advertise ipv4|ipv6 routes as type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
+/*
+ * Delete and withdraw all type-5 routes for the RD corresponding to VRF.
+ * This is invoked upon VRF RD change. The processing is done only from global
+ * table.
+ */
+static void withdraw_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* delete/withdraw ipv4|ipv6 routes as type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
+}
+
/*
* Update and advertise local routes for a VNI. Invoked upon router-id
* change. Note that the processing is done only on the global route table
@@ -1973,6 +2876,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
/* Make EVPN prefix. */
memset(&p, 0, sizeof(struct prefix_evpn));
p.family = AF_EVPN;
+ p.prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
/* Additional information outside of prefix - ESI and GW IP */
@@ -2007,14 +2911,12 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
pfx += 4;
memcpy(&evpn.gw_ip.ipv4, pfx, 4);
pfx += 4;
- p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV4;
} else {
SET_IPADDR_V6(&p.prefix.ip);
memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16);
pfx += 16;
memcpy(&evpn.gw_ip.ipv6, pfx, 16);
pfx += 16;
- p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6;
}
label_pnt = (mpls_label_t *)pfx;
@@ -2045,10 +2947,13 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p,
return;
p_evpn_p = &(p->u.prefix_evpn);
+ /* len denites the total len of IP and GW-IP in the route
+ IP and GW-IP have to be both ipv4 or ipv6
+ */
if (IS_IPADDR_V4(&p_evpn_p->ip))
- len = 8; /* ipv4 */
+ len = 8; /* IP and GWIP are both ipv4 */
else
- len = 32; /* ipv6 */
+ len = 32; /* IP and GWIP are both ipv6 */
/* 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);
@@ -2109,30 +3014,311 @@ static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp)
bgp_evpn_free(bgp, vpn);
}
+/*
+ * Derive AUTO import RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* Map RT to VRF */
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+}
+
+/*
+ * Delete AUTO import RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+}
+
+/*
+ * Derive AUTO export RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
+{
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+/*
+ * Delete AUTO export RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+
+ /* update all type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* update all type-2 routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
+}
/*
* Public functions.
*/
+/* withdraw type-5 route corresponding to ip prefix */
+void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ build_type5_prefix_from_ip_prefix(&evp, &rn->p);
+ ret = delete_evpn_type5_route(bgp_vrf, &evp);
+ if (ret) {
+ zlog_err(
+ "%u failed to delete type-5 route for prefix %s in vrf %s",
+ bgp_vrf->vrf_id,
+ prefix2str(&rn->p, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+}
+
+/* withdraw all type-5 routes for an address family */
+void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_node *rn = NULL;
+
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
+ bgp_evpn_withdraw_type5_route(bgp_vrf, rn, afi, safi);
+
+}
+
+/* advertise ip prefix as type-5 route*/
+void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ if (!rn->info)
+ return;
+
+ /* only advertise subnet routes as type-5 */
+ if (is_host_route(&rn->p))
+ return;
+
+ build_type5_prefix_from_ip_prefix(&evp, &rn->p);
+ ret = update_evpn_type5_route(bgp_vrf, &evp);
+ if (ret) {
+ zlog_err(
+ "%u failed to create type-5 route for prefix %s in vrf %s",
+ bgp_vrf->vrf_id,
+ prefix2str(&rn->p, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+}
+
+/* advertise all type-5 routes for an address family */
+void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_node *rn = NULL;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
+ bgp_evpn_advertise_type5_route(bgp_vrf, rn, afi, safi);
+}
+
+void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni,
+ struct list *rtl)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom, *ecom_auto;
+ struct ecommunity_val eval;
+
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+
+ ecom_auto = ecommunity_new();
+ ecommunity_add_val(ecom_auto, &eval);
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecom_auto)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(rtl, node_to_del);
+
+ ecommunity_free(&ecom_auto);
+}
+
+void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* uninstall routes from vrf */
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* Remove auto generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+
+ /* Add the newly configured RT to RT list */
+ listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* map VRF to its RTs */
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+
+ /* install routes matching the new VRF */
+ install_routes_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* uninstall routes from vrf */
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
+
+ /* fallback to auto import rt, if this was the last RT */
+ if (list_isempty(bgp_vrf->vrf_import_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ }
+
+ /* map VRFs to its RTs */
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+
+ /* install routes matching this new RT */
+ install_routes_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* remove auto-generated RT */
+ evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+
+ /* Add the new RT to the RT list */
+ listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+
+}
+
+void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* Remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
+
+ /* fall back to auto-generated RT if this was the last RT */
+ if (bgp_vrf->vrf_export_rtl && list_isempty(bgp_vrf->vrf_export_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+ }
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
/*
* Handle change to BGP router id. This is invoked twice by the change
* handler, first before the router id has been changed and then after
* the router id has been changed. The first invocation will result in
- * local routes for all VNIs being deleted and withdrawn and the next
+ * local routes for all VNIs/VRF being deleted and withdrawn and the next
* will result in the routes being re-advertised.
*/
void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw)
{
- if (withdraw)
+ if (withdraw) {
+
+ /* delete and withdraw all the type-5 routes
+ stored in the global table for this vrf
+ */
+ withdraw_router_id_vrf(bgp);
+
+ /* delete all the VNI routes (type-2/type-3) routes for all the
+ * L2-VNIs
+ */
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *,
void *))withdraw_router_id_vni,
bgp);
- else
+ } else {
+
+ /* advertise all routes in the vrf as type-5 routes with the new
+ * RD
+ */
+ update_router_id_vrf(bgp);
+
+ /* advertise all the VNI routes (type-2/type-3) routes with the
+ * new RD
+ */
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *,
void *))update_router_id_vni,
bgp);
+ }
}
/*
@@ -2143,6 +3329,15 @@ int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn)
return update_routes_for_vni(bgp, vpn);
}
+void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf,
+ int withdraw)
+{
+ if (withdraw)
+ delete_withdraw_vrf_routes(bgp_vrf);
+ else
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
/*
* Handle change to RD. This is invoked twice by the change handler,
* first before the RD has been changed and then after the RD has
@@ -2287,6 +3482,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
inet_ntop(family, &p->prefix.ip.ip.addr, buf2,
PREFIX2STR_BUFFER));
}
+ } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ snprintf(buf, len, "[%d]:[0]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.ip_prefix_length,
+ IS_EVPN_PREFIX_IPADDR_V4(p) ?
+ inet_ntoa(p->prefix.ip.ipaddr_v4) :
+ inet6_ntoa(p->prefix.ip.ipaddr_v6));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",
@@ -2455,6 +3657,65 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
return 0;
}
+/*
+ * Map the RTs (configured or automatically derived) of a VRF to the VRF.
+ * The mapping will be used during route processing.
+ * bgp_def: default bgp instance
+ * bgp_vrf: specific bgp vrf instance on which RT is configured
+ */
+void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
+{
+ int i = 0;
+ struct ecommunity_val *eval = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+ struct ecommunity *ecom = NULL;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ map_vrf_to_rt(bgp_vrf, eval);
+ }
+ }
+}
+
+/*
+ * Unmap the RTs (configured or automatically derived) of a VRF from the VRF.
+ */
+void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
+{
+ int i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ struct vrf_irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ /* If using "automatic" RT, we only care about the
+ * local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP
+ * peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt)
+ unmap_vrf_from_rt(bgp_vrf, irt);
+ }
+ }
+}
+
+
/*
* Map the RTs (configured or automatically derived) of a VNI to the VNI.
@@ -2517,7 +3778,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn, vpn->import_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
/* Map RT to VNI */
@@ -2529,7 +3790,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn, vpn->export_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
}
@@ -2537,6 +3798,20 @@ void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
* Derive RD automatically for VNI using passed information - it
* is of the form RouterId:unique-id-for-vni.
*/
+void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
+{
+ char buf[100];
+
+ bgp->vrf_prd.family = AF_UNSPEC;
+ bgp->vrf_prd.prefixlen = 64;
+ sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), bgp->vrf_rd_id);
+ str2prefix_rd(buf, &bgp->vrf_prd);
+}
+
+/*
+ * Derive RD automatically for VNI using passed information - it
+ * is of the form RouterId:unique-id-for-vni.
+ */
void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn)
{
char buf[100];
@@ -2566,7 +3841,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni)
* Create a new vpn - invoked upon configuration or zebra notification.
*/
struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip)
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id)
{
struct bgpevpn *vpn;
@@ -2580,13 +3856,14 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
/* Set values - RD and RT set to defaults. */
vpn->vni = vni;
vpn->originator_ip = originator_ip;
+ vpn->tenant_vrf_id = tenant_vrf_id;
/* Initialize route-target import and export lists */
vpn->import_rtl = list_new();
vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
vpn->export_rtl = list_new();
vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
- bf_assign_index(bgp->rd_idspace, vpn->rd_id);
+ bf_assign_index(bm->rd_idspace, vpn->rd_id);
derive_rd_rt_for_vni(bgp, vpn);
/* Initialize EVPN route table. */
@@ -2597,6 +3874,10 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
XFREE(MTYPE_BGP_EVPN, vpn);
return NULL;
}
+
+ /* add to l2vni list on corresponding vrf */
+ bgpevpn_link_to_l3vni(vpn);
+
QOBJ_REG(vpn, bgpevpn);
return vpn;
}
@@ -2609,11 +3890,12 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ bgpevpn_unlink_from_l3vni(vpn);
bgp_table_unlock(vpn->route_table);
bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
list_delete_and_null(&vpn->import_rtl);
list_delete_and_null(&vpn->export_rtl);
- bf_release_index(bgp->rd_idspace, vpn->rd_id);
+ bf_release_index(bm->rd_idspace, vpn->rd_id);
hash_release(bgp->vnihash, vpn);
QOBJ_UNREG(vpn);
XFREE(MTYPE_BGP_EVPN, vpn);
@@ -2772,6 +4054,160 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
return 0;
}
+static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
+ struct bgp *bgp_vrf)
+{
+ struct bgpevpn *vpn = NULL;
+ struct bgp *bgp_def = NULL;
+
+ bgp_def = bgp_get_default();
+ assert(bgp_def);
+
+ vpn = (struct bgpevpn *)backet->data;
+ if (vpn->tenant_vrf_id == bgp_vrf->vrf_id)
+ bgpevpn_link_to_l3vni(vpn);
+}
+
+int bgp_evpn_local_l3vni_add(vni_t l3vni,
+ vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct in_addr originator_ip)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ as_t as = 0;
+
+ /* get the default instamce - required to get the AS number for VRF
+ * auto-creatio
+ */
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("Cannot process L3VNI %u ADD - default BGP instance not yet created",
+ l3vni);
+ return -1;
+ }
+ as = bgp_def->as;
+
+ /* if the BGP vrf instance doesnt exist - create one */
+ bgp_vrf = bgp_lookup_by_name(vrf_id_to_name(vrf_id));
+ if (!bgp_vrf) {
+
+ int ret = 0;
+
+ ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
+ BGP_INSTANCE_TYPE_VRF);
+ switch (ret) {
+ case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET:
+ zlog_err("'bgp multiple-instance' not present\n");
+ return -1;
+ case BGP_ERR_AS_MISMATCH:
+ zlog_err("BGP is already running; AS is %u\n", as);
+ return -1;
+ case BGP_ERR_INSTANCE_MISMATCH:
+ zlog_err("BGP instance name and AS number mismatch\n");
+ return -1;
+ }
+
+ /* mark as auto created */
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO);
+ }
+
+ /* associate with l3vni */
+ bgp_vrf->l3vni = l3vni;
+
+ /* set the router mac - to be used in mac-ip routes for this vrf */
+ memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr));
+
+ /* set the originator ip */
+ bgp_vrf->originator_ip = originator_ip;
+
+ /* auto derive RD/RT */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* link all corresponding l2vnis */
+ hash_iterate(bgp_def->vnihash,
+ (void (*)(struct hash_backet *, void *))
+ 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);
+
+ /* advertise type-5 routes if needed */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* install all remote routes belonging to this l3vni into correspondng
+ * vrf */
+ install_routes_for_vrf(bgp_vrf);
+
+ return 0;
+}
+
+int bgp_evpn_local_l3vni_del(vni_t l3vni,
+ vrf_id_t vrf_id)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp vrf instance */
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf) {
+ zlog_err("Cannot process L3VNI %u Del - Could not find BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("Cannot process L3VNI %u Del - Could not find default BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will
+ * take care of uninstalling the routes from zebra
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* delete/withdraw all type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
+
+ /* remove the l3vni from vrf instance */
+ bgp_vrf->l3vni = 0;
+
+ /* remove the Rmac from the BGP vrf */
+ memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr));
+
+ /* delete RD/RT */
+ if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) {
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ list_delete_all_node(bgp_vrf->vrf_import_rtl);
+ }
+ if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl)) {
+ list_delete_all_node(bgp_vrf->vrf_export_rtl);
+ }
+
+ /* update all corresponding local mac-ip routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
+
+
+ /* Delete the instance if it was autocreated */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
+ bgp_delete(bgp_vrf);
+
+ return 0;
+}
+
/*
* Handle del of a local VNI.
*/
@@ -2815,7 +4251,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
* about is change to local-tunnel-ip.
*/
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip)
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
@@ -2828,6 +4265,17 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
/* Lookup VNI. If present and no change, exit. */
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (vpn) {
+
+ /* update tenant_vrf_id if required */
+ if (vpn->tenant_vrf_id != tenant_vrf_id) {
+ bgpevpn_unlink_from_l3vni(vpn);
+ vpn->tenant_vrf_id = tenant_vrf_id;
+ bgpevpn_link_to_l3vni(vpn);
+
+ /* update all routes with new export RT for VRFs */
+ update_routes_for_vni(bgp, vpn);
+ }
+
if (is_vni_live(vpn)
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
/* Probably some other param has changed that we don't
@@ -2840,7 +4288,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);
+ vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id);
if (!vpn) {
zlog_err(
"%u: Failed to allocate VNI entry for VNI %u - at Add",
@@ -2907,10 +4355,19 @@ void bgp_evpn_cleanup(struct bgp *bgp)
if (bgp->import_rt_hash)
hash_free(bgp->import_rt_hash);
bgp->import_rt_hash = NULL;
+ if (bgp->vrf_import_rt_hash)
+ hash_free(bgp->vrf_import_rt_hash);
+ bgp->vrf_import_rt_hash = NULL;
if (bgp->vnihash)
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
- bf_free(bgp->rd_idspace);
+ if (bgp->vrf_import_rtl)
+ list_delete_and_null(&bgp->vrf_import_rtl);
+ if (bgp->vrf_export_rtl)
+ list_delete_and_null(&bgp->vrf_export_rtl);
+ if (bgp->l2vnis)
+ list_delete_and_null(&bgp->l2vnis);
+ bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
}
/*
@@ -2918,7 +4375,7 @@ void bgp_evpn_cleanup(struct bgp *bgp)
* Create
* VNI hash table
* hash for RT to VNI
- * unique rd id space for auto derivation of RD for VNIs
+ * assign a unique rd id for auto derivation of vrf_prd
*/
void bgp_evpn_init(struct bgp *bgp)
{
@@ -2927,7 +4384,24 @@ void bgp_evpn_init(struct bgp *bgp)
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
- bf_init(bgp->rd_idspace, UINT16_MAX);
- /*assign 0th index in the bitfield, so that we start with id 1*/
- bf_assign_zero_index(bgp->rd_idspace);
+ bgp->vrf_import_rt_hash =
+ hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp,
+ "BGP VRF Import RT Hash");
+ bgp->vrf_import_rtl = list_new();
+ bgp->vrf_import_rtl->cmp =
+ (int (*)(void *, void *))evpn_route_target_cmp;
+
+ bgp->vrf_export_rtl = list_new();
+ bgp->vrf_export_rtl->cmp =
+ (int (*)(void *, void *))evpn_route_target_cmp;
+ bgp->l2vnis = list_new();
+ bgp->l2vnis->cmp =
+ (int (*)(void *, void *))vni_hash_cmp;
+ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+}
+
+void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
+{
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
}
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index 985f41f586..9400916845 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -22,9 +22,29 @@
#define _QUAGGA_BGP_EVPN_H
#include "vxlan.h"
+#include "bgpd.h"
#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */
+static inline int is_evpn_enabled(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_default();
+ return bgp ? bgp->advertise_all_vni : 0;
+}
+
+extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
+ struct bgp_node *rn,
+ afi_t afi, safi_t safi);
+extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf,
+ struct bgp_node *rn,
+ afi_t afi, safi_t safi);
+extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf);
extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw);
extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len);
extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len);
@@ -45,9 +65,14 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni,
extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
struct ethaddr *mac, struct ipaddr *ip,
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);
+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,
- struct in_addr originator_ip);
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
extern void bgp_evpn_init(struct bgp *bgp);
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index a58f73f4bc..2d52e1995d 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -30,9 +30,10 @@
#define RT_ADDRSTRLEN 28
-/* EVPN prefix lengths. */
+/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */
#define EVPN_TYPE_2_ROUTE_PREFIXLEN 224
#define EVPN_TYPE_3_ROUTE_PREFIXLEN 224
+#define EVPN_TYPE_5_ROUTE_PREFIXLEN 224
/* EVPN route types. */
typedef enum {
@@ -53,6 +54,7 @@ typedef enum {
*/
struct bgpevpn {
vni_t vni;
+ vrf_id_t tenant_vrf_id;
u_int32_t flags;
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
@@ -96,10 +98,99 @@ struct irt_node {
struct list *vnis;
};
+/* Mapping of Import RT to VRFs.
+ * The Import RTs of all VRFss are maintained in a hash table with each
+ * RT linking to all VRFs that will import routes matching this RT.
+ */
+struct vrf_irt_node {
+ /* RT */
+ struct ecommunity_val rt;
+
+ /* List of VNIs importing routes matching this RT. */
+ struct list *vrfs;
+};
+
+
#define RT_TYPE_IMPORT 1
#define RT_TYPE_EXPORT 2
#define RT_TYPE_BOTH 3
+static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
+{
+ return (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_RD_CFGD));
+}
+
+static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf,
+ struct prefix_rd *prd)
+{
+ return (memcmp(&bgp_vrf->vrf_prd.val, prd->val, ECOMMUNITY_SIZE) == 0);
+}
+
+static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return 0;
+
+ return bgp_vrf->l3vni;
+}
+
+static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ memset(rmac, 0, sizeof(struct ethaddr));
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return;
+ memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
+}
+
+static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return NULL;
+
+ return bgp_vrf->vrf_export_rtl;
+}
+
+static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return NULL;
+
+ return bgp_vrf->vrf_import_rtl;
+}
+
+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)
+ return;
+ listnode_delete(bgp_vrf->l2vnis, vpn);
+}
+
+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)
+ return;
+ listnode_add_sort(bgp_vrf->l2vnis, vpn);
+}
+
static inline int is_vni_configured(struct bgpevpn *vpn)
{
return (CHECK_FLAG(vpn->flags, VNI_FLAG_CFGD));
@@ -157,6 +248,15 @@ static inline vni_t label2vni(mpls_label_t *label)
return vni;
}
+static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *rmac)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+ memcpy(&eval->val[2], rmac, ETH_ALEN);
+}
+
static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq,
struct ecommunity_val *eval)
{
@@ -171,6 +271,44 @@ static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq,
eval->val[7] = seq & 0xff;
}
+static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = evp->prefix.ip_prefix_length;
+ memcpy(&(ip->u.prefix4),
+ &(evp->prefix.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = evp->prefix.ip_prefix_length;
+ memcpy(&(ip->u.prefix6),
+ &(evp->prefix.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
+static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = IPV4_MAX_BITLEN;
+ memcpy(&(ip->u.prefix4),
+ &(evp->prefix.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&(ip->u.prefix6),
+ &(evp->prefix.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
struct ethaddr *mac,
struct ipaddr *ip)
@@ -185,6 +323,31 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
memcpy(&p->prefix.ip, ip, sizeof(*ip));
}
+static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
+ struct prefix *ip_prefix)
+{
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (ip_prefix->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4,
+ sizeof(struct in_addr));
+ } else {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6,
+ sizeof(struct in6_addr));
+ }
+
+ memset(evp, 0, sizeof(struct prefix_evpn));
+ evp->family = AF_EVPN;
+ evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
+ evp->prefix.ip_prefix_length = ip_prefix->prefixlen;
+ evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+ evp->prefix.ip.ipa_type = ip.ipa_type;
+ memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr));
+}
+
static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
struct in_addr originator_ip)
{
@@ -196,13 +359,41 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
p->prefix.ip.ipaddr_v4 = originator_ip;
}
+static inline int advertise_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi)
+{
+ if (!bgp_vrf->l3vni)
+ return 0;
+
+ if (afi == AFI_IP &&
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
+ return 1;
+
+ if (afi == AFI_IP6 &&
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
+ return 1;
+
+ return 0;
+}
+extern void evpn_rt_delete_auto(struct bgp*, vni_t, struct list*);
+extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
+extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
struct bgpevpn *vpn);
+extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw);
extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,
int withdraw);
extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn);
extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf);
+extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf);
extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn);
extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp,
struct bgpevpn *vpn);
@@ -211,8 +402,10 @@ extern void bgp_evpn_derive_auto_rt_import(struct bgp *bgp,
extern void bgp_evpn_derive_auto_rt_export(struct bgp *bgp,
struct bgpevpn *vpn);
extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp);
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);
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 4114c45221..b463896c49 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -53,6 +53,117 @@ struct vni_walk_ctx {
};
#if defined(HAVE_CUMULUS)
+static void display_vrf_import_rt(struct vty *vty,
+ struct vrf_irt_node *irt,
+ json_object *json)
+{
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_as eas;
+ struct ecommunity_ip eip;
+ struct listnode *node, *nnode;
+ struct bgp *tmp_bgp_vrf = NULL;
+ json_object *json_rt = NULL;
+ json_object *json_vrfs = NULL;
+ char rt_buf[RT_ADDRSTRLEN];
+
+ if (json) {
+ json_rt = json_object_new_object();
+ json_vrfs = json_object_new_array();
+ }
+
+ pnt = (u_char *)&irt->rt.val;
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ return;
+
+ memset(&eas, 0, sizeof(eas));
+ switch (type) {
+ case ECOMMUNITY_ENCODE_AS:
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ pnt = ptr_get_be32(pnt, &eas.val);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_IP:
+ memcpy(&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip),
+ eip.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_AS4:
+ pnt = ptr_get_be32(pnt, &eas.val);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (!json) {
+ vty_out(vty,
+ "\nList of VRFs importing routes with this route-target:\n");
+ }
+
+ for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (json)
+ json_object_array_add(
+ json_vrfs,
+ json_object_new_string(
+ vrf_id_to_name(
+ tmp_bgp_vrf->vrf_id)));
+ else
+ vty_out(vty, " %s\n",
+ vrf_id_to_name(tmp_bgp_vrf->vrf_id));
+ }
+
+ if (json) {
+ json_object_object_add(json_rt, "vrfs", json_vrfs);
+ json_object_object_add(json, rt_buf, json_rt);
+ }
+}
+
+static void show_vrf_import_rt_entry(struct hash_backet *backet,
+ void *args[])
+{
+ json_object *json = NULL;
+ struct vty *vty = NULL;
+ struct vrf_irt_node *irt = (struct vrf_irt_node *)backet->data;
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ display_vrf_import_rt(vty, irt, json);
+}
+
static void display_import_rt(struct vty *vty, struct irt_node *irt,
json_object *json)
{
@@ -252,6 +363,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
vty_out(vty, " (known to the kernel)");
vty_out(vty, "\n");
+ vty_out(vty, " Tenant-Vrf: %s\n",
+ vrf_id_to_name(vpn->tenant_vrf_id));
vty_out(vty, " RD: %s\n",
prefix_rd2str(&vpn->prd, buf1, sizeof(buf1)));
vty_out(vty, " Originator IP: %s\n",
@@ -298,6 +411,53 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
json_object_object_add(json, "exportRts", json_export_rtl);
}
+static void evpn_show_vrf_routes(struct vty *vty,
+ struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int header = 1;
+ u_int32_t prefix_cnt, path_cnt;
+ struct bgp_table *table;
+
+ prefix_cnt = path_cnt = 0;
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+
+ table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ char prefix_str[BUFSIZ];
+
+ bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
+ sizeof(prefix_str));
+
+ if (rn->info) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp_def, NULL);
+ header = 0;
+ }
+ prefix_cnt++;
+ }
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (ri = rn->info; ri; ri = ri->next) {
+ route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
+ path_cnt++;
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes exist for this VRF");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)",
+ prefix_cnt, path_cnt);
+}
+
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json)
@@ -450,7 +610,7 @@ static void show_vni_entry(struct hash_backet *backet, void *args[])
} else {
vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni,
inet_ntoa(vpn->originator_ip),
- prefix_rd2str(&vpn->prd, buf2, sizeof(buf2)));
+ prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN));
}
for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
@@ -502,6 +662,9 @@ static void show_vni_entry(struct hash_backet *backet, void *args[])
break;
}
+ if (!json)
+ vty_out(vty, "%-37s", vrf_id_to_name(vpn->tenant_vrf_id));
+
if (json) {
char vni_str[VNI_STR_LEN];
@@ -1166,40 +1329,15 @@ DEFUN(no_evpnrt5_network,
}
#if defined(HAVE_CUMULUS)
-static void evpn_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn,
- struct list *rtl)
-{
- struct listnode *node, *nnode, *node_to_del;
- struct ecommunity *ecom, *ecom_auto;
- struct ecommunity_val eval;
-
- encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
-
- ecom_auto = ecommunity_new();
- ecommunity_add_val(ecom_auto, &eval);
- node_to_del = NULL;
-
- for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
- if (ecommunity_match(ecom, ecom_auto)) {
- ecommunity_free(&ecom);
- node_to_del = node;
- }
- }
-
- if (node_to_del)
- list_delete_node(rtl, node_to_del);
-
- ecommunity_free(&ecom_auto);
-}
static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn, vpn->import_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
}
static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn, vpn->export_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl);
}
/*
@@ -1352,6 +1490,46 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Configure RD for VRF
+ */
+static void evpn_configure_vrf_rd(struct bgp *bgp_vrf,
+ struct prefix_rd *rd)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* update RD */
+ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd));
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
+/*
+ * Unconfigure RD for VRF
+ */
+static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* fall back to default RD */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
+/*
* Configure RD for a VNI (vty handler)
*/
static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn,
@@ -1403,7 +1581,10 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (!vpn) {
- vpn = bgp_evpn_new(bgp, vni, bgp->router_id);
+ /* tenant vrf will be updated when we get local_vni_add from
+ * zebra
+ */
+ vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0);
if (!vpn) {
zlog_err(
"%u: Failed to allocate VNI entry for VNI %u - at Config",
@@ -1453,6 +1634,25 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
+ * Display import RT mapping to VRFs (vty handler)
+ * bgp_def: default bgp instance
+ */
+static void evpn_show_vrf_import_rts(struct vty *vty,
+ struct bgp *bgp_def,
+ json_object *json)
+{
+ void *args[2];
+
+ args[0] = vty;
+ args[1] = json;
+
+ hash_iterate(bgp_def->vrf_import_rt_hash,
+ (void (*)(struct hash_backet *, void *))
+ show_vrf_import_rt_entry,
+ args);
+}
+
+/*
* Display import RT mapping to VNIs (vty handler)
*/
static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp,
@@ -1778,11 +1978,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
/* RD header and legend - once overall. */
if (rd_header && !json) {
vty_out(vty,
- "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:"
- "[MAC]\n");
+ "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n");
vty_out(vty,
- "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:"
- "[OrigIP]\n\n");
+ "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty,
+ "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
rd_header = 0;
}
@@ -2014,8 +2214,9 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp,
} else {
vty_out(vty, "Number of VNIs: %u\n", num_vnis);
vty_out(vty, "Flags: * - Kernel\n");
- vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI",
- "Orig IP", "RD", "Import RT", "Export RT");
+ vty_out(vty, " %-10s %-15s %-21s %-25s %-25s %-37s\n", "VNI",
+ "Orig IP", "RD", "Import RT",
+ "Export RT", "Tenant-Vrf");
}
args[0] = vty;
@@ -2239,6 +2440,115 @@ DEFUN (no_bgp_evpn_advertise_all_vni,
return CMD_SUCCESS;
}
+DEFUN (bgp_evpn_advertise_type5,
+ bgp_evpn_advertise_type5_cmd,
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
+ vty_out(vty,
+ "%%only ipv4 or ipv6 address families are supported");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%%only ipv4 unicast or ipv6 unicast are supported");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+
+ /* if we are already advertising ipv4 prefix as type-5
+ * nothing to do
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
+ SET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
+ bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
+ }
+ } else {
+
+ /* if we are already advertising ipv6 prefix as type-5
+ * nothing to do
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
+ SET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
+ bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_type5,
+ no_bgp_evpn_advertise_type5_cmd,
+ "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ NO_STR
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
+ vty_out(vty,
+ "%%only ipv4 or ipv6 address families are supported");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%%only ipv4 unicast or ipv6 unicast are supported");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+
+ /* if we are already advertising ipv4 prefix as type-5
+ * nothing to do
+ */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
+ }
+ } else {
+
+ /* if we are already advertising ipv6 prefix as type-5
+ * nothing to do
+ */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
/*
* Display VNI information - for all or a specific VNI
*/
@@ -2278,7 +2588,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
? "Enabled"
: "Disabled");
json_object_string_add(json, "advertiseAllVnis",
- bgp->advertise_all_vni
+ is_evpn_enabled()
? "Enabled"
: "Disabled");
} else {
@@ -2288,8 +2598,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
/* Display all VNIs */
vty_out(vty, "Advertise All VNI flag: %s\n",
- bgp->advertise_all_vni ? "Enabled"
- : "Disabled");
+ is_evpn_enabled() ? "Enabled" : "Disabled");
}
evpn_show_all_vnis(vty, bgp, json);
@@ -2530,6 +2839,33 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
}
/*
+ * Display per-VRF EVPN routing table.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd,
+ "show bgp l2vpn evpn route vrf VRFNAME",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
+ "VRF\n"
+ "VRF Name\n")
+{
+ int vrf_idx = 6;
+ char *vrf_name = NULL;
+ struct bgp *bgp_vrf = NULL;
+
+ vrf_name = argv[vrf_idx]->arg;
+ bgp_vrf = bgp_lookup_by_name(vrf_name);
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ evpn_show_vrf_routes(vty, bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
+/*
* Display per-VNI EVPN routing table.
*/
DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
@@ -2787,6 +3123,42 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all,
/*
* Display EVPN import route-target hash table
*/
+DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt,
+ show_bgp_l2vpn_evpn_vrf_import_rt_cmd,
+ "show bgp l2vpn evpn vrf-import-rt [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vrf import route target\n"
+ JSON_STR)
+{
+ u_char uj = 0;
+ struct bgp *bgp_def = NULL;
+ json_object *json = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ evpn_show_vrf_import_rts(vty, bgp_def, json);
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display EVPN import route-target hash table
+ */
DEFUN(show_bgp_l2vpn_evpn_import_rt,
show_bgp_l2vpn_evpn_import_rt_cmd,
"show bgp l2vpn evpn import-rt [json]",
@@ -2978,6 +3350,91 @@ DEFUN_NOSH (exit_vni,
return CMD_SUCCESS;
}
+DEFUN (bgp_evpn_vrf_rd,
+ bgp_evpn_vrf_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* If same as existing value, there is nothing more to do. */
+ if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd))
+ return CMD_SUCCESS;
+
+ /* Configure or update the RD. */
+ evpn_configure_vrf_rd(bgp_vrf, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd,
+ no_bgp_evpn_vrf_rd_cmd,
+ "no rd ASN:NN_OR_IP-ADDRESS:NN",
+ NO_STR
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[2]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) {
+ vty_out(vty,
+ "%% RD specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd_without_val,
+ no_bgp_evpn_vrf_rd_without_val_cmd,
+ "no rd",
+ NO_STR
+ "Route Distinguisher\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_evpn_vni_rd,
bgp_evpn_vni_rd_cmd,
"rd ASN:NN_OR_IP-ADDRESS:NN",
@@ -3084,6 +3541,279 @@ static int bgp_evpn_rt_matches_existing(struct list *rtl,
return 0;
}
+/* display L3VNI related info for a VRF instance */
+DEFUN (show_bgp_vrf_l3vni_info,
+ show_bgp_vrf_l3vni_info_cmd,
+ "show bgp vrf VRFNAME l3vni info [json]",
+ SHOW_STR
+ BGP_STR
+ "show bgp vrf\n"
+ "VRF Name\n"
+ "L3-VNI\n"
+ "L3-VNI info\n"
+ JSON_STR)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ int idx_vrf = 3;
+ const char *name = NULL;
+ struct bgp *bgp = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct ecommunity *ecom = NULL;
+ json_object *json = NULL;
+ json_object *json_vnis = NULL;
+ json_object *json_export_rts = NULL;
+ json_object *json_import_rts = NULL;
+ u_char uj = use_json(argc, argv);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vnis = json_object_new_array();
+ json_export_rts = json_object_new_array();
+ json_import_rts = json_object_new_array();
+ }
+
+ name = argv[idx_vrf]->arg;
+ bgp = bgp_lookup_by_name(name);
+ if (!bgp) {
+ if (!uj)
+ vty_out(vty, "BGP instance for VRF %s not found",
+ name);
+ else {
+ json_object_string_add(json, "warning",
+ "BGP instance not found");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json));
+ json_object_free(json);
+ }
+ return CMD_WARNING;
+ }
+
+ if (!json) {
+ vty_out(vty, "BGP VRF: %s\n", name);
+ vty_out(vty, " Local-Ip: %s\n",
+ inet_ntoa(bgp->originator_ip));
+ vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
+ vty_out(vty, " Rmac: %s\n",
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ vty_out(vty, " L2-VNI List:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ vty_out(vty, "%u ", vpn->vni);
+ vty_out(vty, "\n");
+ vty_out(vty, " Export-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " Import-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " RD: %s\n",
+ prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN));
+ } else {
+ json_object_string_add(json, "vrf", name);
+ json_object_string_add(json, "local-ip",
+ inet_ntoa(bgp->originator_ip));
+ json_object_int_add(json, "l3vni", bgp->l3vni);
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&bgp->rmac, buf,
+ sizeof(buf)));
+ /* list of l2vnis */
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ json_object_array_add(json_vnis,
+ json_object_new_int(vpn->vni));
+ json_object_object_add(json, "l2vnis", json_vnis);
+
+ /* export rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ json_object_array_add(json_export_rts,
+ json_object_new_string(
+ ecommunity_str(ecom)));
+ json_object_object_add(json, "export-rts", json_export_rts);
+
+ /* import rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ json_object_array_add(json_import_rts,
+ json_object_new_string(
+ ecommunity_str(ecom)));
+ json_object_object_add(json, "import-rts", json_import_rts);
+ json_object_string_add(
+ json, "rd",
+ prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN));
+
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+/* import/export rt for l3vni-vrf */
+DEFUN (bgp_evpn_vrf_rt,
+ bgp_evpn_vrf_rt_cmd,
+ "route-target <both|import|export> RT",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ int rt_type;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[1]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[1]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[1]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Add/update the import route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this import route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomadd))
+ bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd);
+ }
+
+ /* Add/update the export route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this export route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomadd))
+ bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rt,
+ no_bgp_evpn_vrf_rt_cmd,
+ "no route-target <both|import|export> RT",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int rt_type, found_ecomdel;
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[2]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[2]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[2]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_BOTH) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
+ && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import/Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomdel) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomdel);
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_BOTH) {
+ found_ecomdel = 0;
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (!found_ecomdel) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
DEFUN (bgp_evpn_vni_rt,
bgp_evpn_vni_rt_cmd,
@@ -3306,6 +4036,12 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
if (bgp->advertise_gw_macip)
vty_out(vty, " advertise-default-gw\n");
+
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
+ vty_out(vty, " advertise ipv4 unicast\n");
+
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
+ vty_out(vty, " advertise ipv6 unicast\n");
}
void bgp_ethernetvpn_init(void)
@@ -3333,6 +4069,8 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd);
/* "show bgp l2vpn evpn" commands. */
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
@@ -3341,11 +4079,13 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);
/* "show bgp evpn" commands. */
install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd);
@@ -3358,6 +4098,7 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
@@ -3368,6 +4109,11 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd);
install_element(BGP_EVPN_VNI_NODE,
&bgp_evpn_advertise_default_gw_vni_cmd);
install_element(BGP_EVPN_VNI_NODE,
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 7dd4253b2e..a720d31a76 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -221,6 +221,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status)
#endif
bgp_zebra_destroy();
+ bf_free(bm->rd_idspace);
list_delete_and_null(&bm->bgp);
memset(bm, 0, sizeof(*bm));
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 37054ce425..64543ff019 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -117,4 +117,5 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
+DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT")
DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index 35b83a0401..fae98329c6 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -113,5 +113,6 @@ DECLARE_MTYPE(LCOMMUNITY_VAL)
DECLARE_MTYPE(BGP_EVPN)
DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
+DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT)
DECLARE_MTYPE(BGP_EVPN_MACIP)
#endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 81b605ad79..c691233c57 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2222,6 +2222,14 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
}
}
+ /* advertise/withdraw type-5 routes */
+ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+ if (new_select)
+ bgp_evpn_advertise_type5_route(bgp, rn, afi, safi);
+ else if (old_select)
+ bgp_evpn_withdraw_type5_route(bgp, rn, afi, safi);
+ }
+
/* Clear any route change flags. */
bgp_zebra_clear_route_change_flags(rn);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 085de3fabb..ae4759aad0 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -73,9 +73,12 @@ struct bgp_info_extra {
/* Nexthop reachability check. */
u_int32_t igpmetric;
- /* MPLS label. */
+ /* MPLS label - L2VNI */
mpls_label_t label;
+ /* MPLS label - L3-VNI */
+ mpls_label_t label2;
+
#if ENABLE_BGP_VNC
union {
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 50e5bcf096..cff5c1b2e3 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -58,6 +58,7 @@
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_bfd.h"
#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_evpn.h"
static struct peer_group *listen_range_exists(struct bgp *bgp,
struct prefix *range, int exact);
@@ -873,6 +874,8 @@ DEFUN_NOSH (router_bgp,
*/
}
+ /* unset the auto created flag as the user config is now present */
+ UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO);
VTY_PUSH_CONTEXT(BGP_NODE, bgp);
return CMD_SUCCESS;
@@ -909,6 +912,12 @@ DEFUN (no_router_bgp,
"%% Multiple BGP processes are configured\n");
return CMD_WARNING_CONFIG_FAILED;
}
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
} else {
as = strtoul(argv[idx_asn]->arg, NULL, 10);
@@ -921,6 +930,12 @@ DEFUN (no_router_bgp,
vty_out(vty, "%% Can't find BGP instance\n");
return CMD_WARNING_CONFIG_FAILED;
}
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
}
bgp_delete(bgp);
@@ -6450,6 +6465,7 @@ DEFUN (show_bgp_vrfs,
"Show BGP VRFs\n"
JSON_STR)
{
+ char buf[ETHER_ADDR_STRLEN];
struct list *inst = bm->bgp;
struct listnode *node;
struct bgp *bgp;
@@ -6457,8 +6473,6 @@ DEFUN (show_bgp_vrfs,
json_object *json = NULL;
json_object *json_vrfs = NULL;
int count = 0;
- static char header[] =
- "Type Id RouterId #PeersCfg #PeersEstb Name";
if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
vty_out(vty, "BGP Multiple Instance is not enabled\n");
@@ -6484,7 +6498,10 @@ DEFUN (show_bgp_vrfs,
count++;
if (!uj && count == 1)
- vty_out(vty, "%s\n", header);
+ vty_out(vty,
+ "%4s %-5s %-16s %9s %10s %-37s %-10s %-15s\n",
+ "Type", "Id", "routerId", "#PeersVfg",
+ "#PeersEstb", "Name", "L3-VNI", "Rmac");
peers_cfg = peers_estb = 0;
if (uj)
@@ -6518,11 +6535,17 @@ DEFUN (show_bgp_vrfs,
json_object_int_add(json_vrf, "numEstablishedPeers",
peers_estb);
+ json_object_int_add(json_vrf, "l3vni", bgp->l3vni);
+ json_object_string_add(json_vrf, "rmac",
+ prefix_mac2str(&bgp->rmac, buf,
+ sizeof(buf)));
json_object_object_add(json_vrfs, name, json_vrf);
} else
- vty_out(vty, "%4s %-5d %-16s %9u %10u %s\n", type,
- vrf_id_ui, inet_ntoa(bgp->router_id), peers_cfg,
- peers_estb, name);
+ vty_out(vty,
+ "%4s %-5d %-16s %9u %10u %-37s %-10u %-15s\n",
+ type, vrf_id_ui, inet_ntoa(bgp->router_id),
+ peers_cfg, peers_estb, name, bgp->l3vni,
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
}
if (uj) {
@@ -7685,7 +7708,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
}
if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
- if (p->bgp->advertise_all_vni)
+ if (is_evpn_enabled())
json_object_boolean_true_add(
json_addr, "advertiseAllVnis");
}
@@ -7957,7 +7980,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
/* advertise-vni-all */
if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
- if (p->bgp->advertise_all_vni)
+ if (is_evpn_enabled())
vty_out(vty, " advertise-all-vni\n");
}
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index b2db54ef79..30e3c6f31d 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -999,6 +999,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
/* Make Zebra API structure. */
memset(&api, 0, sizeof(api));
+ memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
@@ -1015,6 +1016,13 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (info->sub_type == BGP_ROUTE_AGGREGATE)
zapi_route_set_blackhole(&api, BLACKHOLE_NULL);
+ /* If it is an EVPN route mark as such.
+ * Currently presence of rmac in attr denotes
+ * this is an EVPN type-2 route
+ */
+ if (!is_zero_mac(&(info->attr->rmac)))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
@@ -1072,7 +1080,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv4 = *nexthop;
- api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ /* EVPN type-2 routes are
+ programmed as onlink on l3-vni SVI
+ */
+ if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ api_nh->type = NEXTHOP_TYPE_IPV4;
} else {
ifindex_t ifindex;
struct in6_addr *nexthop;
@@ -1126,8 +1141,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
}
- if (mpinfo->extra
- && bgp_is_valid_label(&mpinfo->extra->label)) {
+ if (mpinfo->extra && bgp_is_valid_label(&mpinfo->extra->label)
+ && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
has_valid_label = 1;
label = label_pton(&mpinfo->extra->label);
@@ -1137,7 +1152,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
valid_nh_count++;
}
- if (has_valid_label)
+ /* if this is a evpn route we don't have to include the label */
+ if (has_valid_label &&
+ !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
if (info->sub_type != BGP_ROUTE_AGGREGATE)
@@ -1179,7 +1196,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
sizeof(nh_buf));
label_buf[0] = '\0';
- if (has_valid_label)
+ if (has_valid_label &&
+ !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
sprintf(label_buf, "label %u",
api_nh->labels[0]);
zlog_debug(" nhop [%d]: %s %s", i + 1, nh_buf,
@@ -1233,11 +1251,19 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi)
return;
memset(&api, 0, sizeof(api));
+ memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = peer->bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
api.prefix = *p;
+ /* If it is an EVPN route mark as such.
+ * Currently presence of rmac in attr denotes
+ * this is an EVPN type-2 route
+ */
+ if (!is_zero_mac(&(info->attr->rmac)))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
if (peer->sort == BGP_PEER_IBGP) {
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
@@ -1551,7 +1577,7 @@ void bgp_zebra_instance_register(struct bgp *bgp)
/* For default instance, register to learn about VNIs, if appropriate.
*/
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
- && bgp->advertise_all_vni)
+ && is_evpn_enabled())
bgp_zebra_advertise_all_vni(bgp, 1);
}
@@ -1570,7 +1596,7 @@ void bgp_zebra_instance_deregister(struct bgp *bgp)
/* For default instance, unregister learning about VNIs, if appropriate.
*/
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
- && bgp->advertise_all_vni)
+ && is_evpn_enabled())
bgp_zebra_advertise_all_vni(bgp, 0);
/* Deregister for router-id, interfaces, redistributed routes. */
@@ -1676,6 +1702,39 @@ 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)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ vni_t l3vni = 0;
+ struct ethaddr rmac;
+ struct in_addr originator_ip;
+ struct stream *s;
+
+ memset(&rmac, 0, sizeof(struct ethaddr));
+ memset(&originator_ip, 0, sizeof(struct in_addr));
+ s = zclient->ibuf;
+ l3vni = stream_getl(s);
+ if (cmd == ZEBRA_L3VNI_ADD) {
+ stream_get(&rmac, s, sizeof(struct ethaddr));
+ originator_ip.s_addr = stream_get_ipv4(s);
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
+ (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id),
+ l3vni,
+ prefix_mac2str(&rmac, buf, sizeof(buf)));
+
+ if (cmd == ZEBRA_L3VNI_ADD)
+ bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
+ else
+ bgp_evpn_local_l3vni_del(l3vni, vrf_id);
+
+ return 0;
+}
+
static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
@@ -1683,23 +1742,29 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
vni_t vni;
struct bgp *bgp;
struct in_addr vtep_ip;
+ vrf_id_t tenant_vrf_id = VRF_DEFAULT;
s = zclient->ibuf;
vni = stream_getl(s);
- if (command == ZEBRA_VNI_ADD)
+ if (command == ZEBRA_VNI_ADD) {
vtep_ip.s_addr = stream_get_ipv4(s);
+ stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
+ }
+
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx VNI %s VRF %u VNI %u",
- (command == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id,
- vni);
+ zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
+ (command == ZEBRA_VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id),
+ vni, vrf_id_to_name(tenant_vrf_id));
if (command == ZEBRA_VNI_ADD)
return bgp_evpn_local_vni_add(
- bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id);
+ bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id,
+ tenant_vrf_id);
else
return bgp_evpn_local_vni_del(bgp, vni);
}
@@ -1783,6 +1848,8 @@ void bgp_zebra_init(struct thread_master *master)
zclient->local_vni_del = bgp_zebra_process_local_vni;
zclient->local_macip_add = bgp_zebra_process_local_macip;
zclient->local_macip_del = bgp_zebra_process_local_macip;
+ zclient->local_l3vni_add = bgp_zebra_process_local_l3vni;
+ zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
}
void bgp_zebra_destroy(void)
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index d76d5b0b7e..5356cfd2fc 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -79,7 +79,7 @@
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_io.h"
-
+#include "bgpd/bgp_ecommunity.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
@@ -218,7 +218,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id)
return 0;
/* EVPN uses router id in RD, withdraw them */
- if (bgp->advertise_all_vni)
+ if (is_evpn_enabled())
bgp_evpn_handle_router_id_update(bgp, TRUE);
IPV4_ADDR_COPY(&bgp->router_id, id);
@@ -235,7 +235,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id)
}
/* EVPN uses router id in RD, update them */
- if (bgp->advertise_all_vni)
+ if (is_evpn_enabled())
bgp_evpn_handle_router_id_update(bgp, FALSE);
return 0;
@@ -1151,15 +1151,13 @@ struct peer *peer_new(struct bgp *bgp)
* - We RX a BGP_UPDATE where the attributes alone are just
* under BGP_MAX_PACKET_SIZE
* - The user configures an outbound route-map that does many as-path
- * prepends or adds many communities. At most they can have
- * CMD_ARGC_MAX
- * args in a route-map so there is a finite limit on how large they
- * can
- * make the attributes.
+ * prepends or adds many communities. At most they can have
+ * CMD_ARGC_MAX args in a route-map so there is a finite limit on how
+ * large they can make the attributes.
*
* Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid
- * bounds
- * checking for every single attribute as we construct an UPDATE.
+ * bounds checking for every single attribute as we construct an
+ * UPDATE.
*/
peer->obuf_work =
stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
@@ -3145,6 +3143,9 @@ int bgp_delete(struct bgp *bgp)
bgp->name);
}
+ /* unmap from RT list */
+ bgp_evpn_vrf_delete(bgp);
+
/* Stop timers. */
if (bgp->t_rmap_def_originate_eval) {
BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval);
@@ -7083,6 +7084,11 @@ int bgp_config_write(struct vty *vty)
/* BGP configuration. */
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /* skip all auto created vrf as they dont have user config */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
+ continue;
+
/* Router bgp ASN */
vty_out(vty, "router bgp %u", bgp->as);
@@ -7307,6 +7313,38 @@ int bgp_config_write(struct vty *vty)
if (bgp_option_check(BGP_OPT_CONFIG_CISCO))
vty_out(vty, " no auto-summary\n");
+ /* import route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target import %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ /* export route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target export %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
/* IPv4 unicast configuration. */
bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
@@ -7367,6 +7405,13 @@ void bgp_master_init(struct thread_master *master)
bgp_process_queue_init();
+ /* init the rd id space.
+ assign 0th index in the bitfield,
+ so that we start with id 1
+ */
+ bf_init(bm->rd_idspace, UINT16_MAX);
+ bf_assign_zero_index(bm->rd_idspace);
+
/* Enable multiple instances by default. */
bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE);
@@ -7531,6 +7576,7 @@ void bgp_terminate(void)
*/
/* reverse bgp_master_init */
bgp_close();
+
if (bm->listen_sockets)
list_delete_and_null(&bm->listen_sockets);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 20b99c171e..8e843eb659 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -36,6 +36,7 @@
#include "defaults.h"
#include "bgp_memory.h"
#include "bitfield.h"
+#include "vxlan.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@@ -136,6 +137,9 @@ struct bgp_master {
/* clang-format off */
#define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */
+ /* Id space for automatic RD derivation for an EVI/VRF */
+ bitfield_t rd_idspace;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp_master)
@@ -408,8 +412,41 @@ struct bgp {
/* Hash table of Import RTs to EVIs */
struct hash *import_rt_hash;
- /* Id space for automatic RD derivation for an EVI */
- bitfield_t rd_idspace;
+ /* Hash table of VRF import RTs to VRFs */
+ struct hash *vrf_import_rt_hash;
+
+ /* L3-VNI corresponding to this vrf */
+ vni_t l3vni;
+
+ /* router-mac to be used in mac-ip routes for this vrf */
+ struct ethaddr rmac;
+
+ /* originator ip - to be used as NH for type-5 routes */
+ struct in_addr originator_ip;
+
+ /* vrf flags */
+ uint32_t vrf_flags;
+#define BGP_VRF_AUTO (1 << 0)
+#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1)
+#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2)
+#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
+#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
+#define BGP_VRF_RD_CFGD (1 << 5)
+
+ /* unique ID for auto derivation of RD for this vrf */
+ uint16_t vrf_rd_id;
+
+ /* RD for this VRF */
+ struct prefix_rd vrf_prd;
+
+ /* import rt list for the vrf instance */
+ struct list *vrf_import_rtl;
+
+ /* export rt list for the vrf instance */
+ struct list *vrf_export_rtl;
+
+ /* list of corresponding l2vnis (struct bgpevpn) */
+ struct list *l2vnis;
QOBJ_FIELDS
};
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
index 7f2dbe7f9d..e3cb739464 100644
--- a/bgpd/rfapi/bgp_rfapi_cfg.c
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -4097,7 +4097,8 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp)
}
}
- if (hc->default_rd.prefixlen || hc->default_response_lifetime
+ if (hc->default_rd.prefixlen
+ || hc->default_response_lifetime != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT
|| hc->default_rt_import_list || hc->default_rt_export_list
|| hc->nve_groups_sequential->count) {
@@ -4125,7 +4126,8 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp)
buf,
sizeof(buf)));
}
- if (hc->default_response_lifetime) {
+ if (hc->default_response_lifetime
+ != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) {
vty_out(vty, " response-lifetime ");
if (hc->default_response_lifetime != UINT32_MAX)
vty_out(vty, "%d",
diff --git a/lib/command.h b/lib/command.h
index 42dd1c5325..fa8323bf2d 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -190,7 +190,7 @@ struct cmd_node {
#define CMD_NOT_MY_INSTANCE 14
/* Argc max counts. */
-#define CMD_ARGC_MAX 25
+#define CMD_ARGC_MAX 256
/* Turn off these macros when uisng cpp with extract.pl */
#ifndef VTYSH_EXTRACT_PL
diff --git a/lib/command_match.c b/lib/command_match.c
index c60373f910..f6b07a0b20 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -28,8 +28,6 @@
DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack")
-#define MAXDEPTH 256
-
#ifdef TRACE_MATCHER
#define TM 1
#else
@@ -84,7 +82,7 @@ static enum match_type match_mac(const char *, bool);
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
struct list **argv, const struct cmd_element **el)
{
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
enum matcher_rv status;
*argv = NULL;
@@ -200,7 +198,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline,
/* check history/stack of tokens
* this disallows matching the same one more than once if there is a
* circle in the graph (used for keyword arguments) */
- if (n == MAXDEPTH)
+ if (n == CMD_ARGC_MAX)
return MATCHER_NO_MATCH;
if (!token->allowrepeat)
for (size_t s = 0; s < n; s++)
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 66b042ad97..79c951dd69 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -33,8 +33,6 @@
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
-#define MAXDEPTH 64
-
/** headers **/
void grammar_sandbox_init(void);
void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
@@ -262,7 +260,7 @@ DEFUN (grammar_test_show,
{
check_nodegraph();
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
stack, 0);
return CMD_SUCCESS;
@@ -277,8 +275,8 @@ DEFUN (grammar_test_dot,
{
check_nodegraph();
- struct graph_node *stack[MAXDEPTH];
- struct graph_node *visited[MAXDEPTH * MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
+ struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX];
size_t vpos = 0;
FILE *ofd = fopen(argv[2]->arg, "w");
@@ -334,7 +332,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack,
return;
}
- if (++stackpos == MAXDEPTH)
+ if (++stackpos == CMD_ARGC_MAX)
return;
for (i = 0; i < vector_active(gn->to); i++) {
@@ -354,7 +352,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack,
static struct list *cmd_graph_permutations(struct graph *graph)
{
char accumulate[2048] = "";
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
struct list *rv = list_new();
rv->cmp = cmd_permute_cmp;
@@ -532,7 +530,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
vty_out(vty, " ?'%s'", tok->desc);
vty_out(vty, " ");
- if (stackpos == MAXDEPTH) {
+ if (stackpos == CMD_ARGC_MAX) {
vty_out(vty, " -aborting! (depth limit)\n");
return;
}
@@ -586,7 +584,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
if (visited[i] == start)
return;
visited[(*visitpos)++] = start;
- if ((*visitpos) == MAXDEPTH * MAXDEPTH)
+ if ((*visitpos) == CMD_ARGC_MAX * CMD_ARGC_MAX)
return;
snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
@@ -626,7 +624,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
}
fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color);
- if (stackpos == MAXDEPTH)
+ if (stackpos == CMD_ARGC_MAX)
return;
stack[stackpos++] = start;
diff --git a/lib/log.c b/lib/log.c
index 7589934b69..bf65ac7c7d 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -945,6 +945,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
+ DESC_ENTRY(ZEBRA_L3VNI_ADD),
+ DESC_ENTRY(ZEBRA_L3VNI_DEL),
DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD),
DESC_ENTRY(ZEBRA_REMOTE_VTEP_DEL),
DESC_ENTRY(ZEBRA_MACIP_ADD),
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 20b0cd5227..a727f37057 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -80,6 +80,7 @@ struct nexthop {
#define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */
#define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */
#define NEXTHOP_FLAG_DUPLICATE (1 << 6) /* nexthop duplicates another active one */
+#define NEXTHOP_FLAG_EVPN_RVTEP (1 << 7) /* EVPN remote vtep nexthop */
#define NEXTHOP_IS_ACTIVE(flags) \
(CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \
&& !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE))
diff --git a/lib/prefix.c b/lib/prefix.c
index 10f77bda87..9f13cb8bb1 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -301,7 +301,7 @@ static const struct in6_addr maskbytes6[] = {
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
-static int is_zero_mac(const struct ethaddr *mac)
+int is_zero_mac(struct ethaddr *mac)
{
int i = 0;
@@ -1043,10 +1043,11 @@ static const char *prefixevpn2str(const struct prefix *p, char *str, int size)
family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
? AF_INET
: AF_INET6;
- snprintf(str, size, "[%d]:[%u][%s]/%d",
+ snprintf(str, size, "[%d]:[%u][%s/%d]/%d",
p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag,
inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf,
PREFIX2STR_BUFFER),
+ p->u.prefix_evpn.ip_prefix_length,
p->prefixlen);
} else {
sprintf(str, "Unsupported EVPN route type %d",
diff --git a/lib/prefix.h b/lib/prefix.h
index 0732cf1290..7e947ea48a 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -348,6 +348,7 @@ extern void masklen2ip6(const int, struct in6_addr *);
extern const char *inet6_ntoa(struct in6_addr);
+extern int is_zero_mac(struct ethaddr *mac);
extern int prefix_str2mac(const char *str, struct ethaddr *mac);
extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size);
@@ -398,4 +399,13 @@ static inline int is_default_prefix(const struct prefix *p)
return 0;
}
+static inline int is_host_route(struct prefix *p)
+{
+ if (p->family == AF_INET)
+ return (p->prefixlen == IPV4_MAX_BITLEN);
+ else if (p->family == AF_INET6)
+ return (p->prefixlen == IPV6_MAX_BITLEN);
+ return 0;
+}
+
#endif /* _ZEBRA_PREFIX_H */
diff --git a/lib/vrf.c b/lib/vrf.c
index 056f778a3e..3c34b95262 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -225,6 +225,17 @@ static void vrf_disable(struct vrf *vrf)
(*vrf_master.vrf_disable_hook)(vrf);
}
+const char *vrf_id_to_name(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (vrf)
+ return vrf->name;
+
+ return "n/a";
+}
+
vrf_id_t vrf_name_to_id(const char *name)
{
struct vrf *vrf;
diff --git a/lib/vrf.h b/lib/vrf.h
index e93e993776..9afca4c6fb 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -103,6 +103,7 @@ extern struct vrf_name_head vrfs_by_name;
extern struct vrf *vrf_lookup_by_id(vrf_id_t);
extern struct vrf *vrf_lookup_by_name(const char *);
extern struct vrf *vrf_get(vrf_id_t, const char *);
+extern const char *vrf_id_to_name(vrf_id_t vrf_id);
extern vrf_id_t vrf_name_to_id(const char *);
#define VRF_GET_ID(V, NAME) \
diff --git a/lib/zclient.c b/lib/zclient.c
index 655e4e1a80..4177ce1a71 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -912,6 +912,8 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api)
stream_putl(s, api->flags);
stream_putc(s, api->message);
stream_putc(s, api->safi);
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_put(s, &(api->rmac), sizeof(struct ethaddr));
/* Put prefix information. */
stream_putc(s, api->prefix.family);
@@ -1032,6 +1034,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETL(s, api->flags);
STREAM_GETC(s, api->message);
STREAM_GETC(s, api->safi);
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_get(&(api->rmac), s, sizeof(struct ethaddr));
/* Prefix. */
STREAM_GETC(s, api->prefix.family);
@@ -2205,6 +2209,16 @@ static int zclient_read(struct thread *thread)
(*zclient->local_vni_del)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_L3VNI_ADD:
+ if (zclient->local_l3vni_add)
+ (*zclient->local_l3vni_add)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_L3VNI_DEL:
+ if (zclient->local_l3vni_del)
+ (*zclient->local_l3vni_del)(command, zclient, length,
+ vrf_id);
+ break;
case ZEBRA_MACIP_ADD:
if (zclient->local_macip_add)
(*zclient->local_macip_add)(command, zclient, length,
diff --git a/lib/zclient.h b/lib/zclient.h
index de58044671..cc34fd9d2c 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -112,6 +112,8 @@ typedef enum {
ZEBRA_ADVERTISE_ALL_VNI,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
+ ZEBRA_L3VNI_ADD,
+ ZEBRA_L3VNI_DEL,
ZEBRA_REMOTE_VTEP_ADD,
ZEBRA_REMOTE_VTEP_DEL,
ZEBRA_MACIP_ADD,
@@ -200,6 +202,8 @@ struct zclient {
int (*fec_update)(int, struct zclient *, uint16_t);
int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
@@ -277,6 +281,8 @@ struct zapi_route {
u_int32_t mtu;
vrf_id_t vrf_id;
+
+ struct ethaddr rmac;
};
/* Zebra IPv4 route message API. */
diff --git a/lib/zebra.h b/lib/zebra.h
index fa5fa89f77..1eb0c56252 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -409,6 +409,7 @@ extern const char *zserv_command_string(unsigned int command);
#define ZEBRA_FLAG_STATIC 0x40
#define ZEBRA_FLAG_SCOPE_LINK 0x100
#define ZEBRA_FLAG_FIB_OVERRIDE 0x200
+#define ZEBRA_FLAG_EVPN_ROUTE 0x400
/* ZEBRA_FLAG_BLACKHOLE was 0x04 */
/* ZEBRA_FLAG_REJECT was 0x80 */
diff --git a/zebra/connected.c b/zebra/connected.c
index 9c0a3af8e3..7b949c5041 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -396,10 +396,10 @@ void connected_down(struct interface *ifp, struct connected *ifc)
* head.
*/
rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, false);
+ &p, NULL, &nh, 0, 0, false, NULL);
rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
- 0, &p, NULL, &nh, 0, 0, false);
+ 0, &p, NULL, &nh, 0, 0, false, NULL);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index d45a502543..9fd7bb1c24 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1039,7 +1039,7 @@ void rtm_read(struct rt_msghdr *rtm)
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true);
+ NULL, 0, 0, true, NULL);
if (!nh.type) {
nh.type = NEXTHOP_TYPE_IPV4;
@@ -1054,7 +1054,7 @@ void rtm_read(struct rt_msghdr *rtm)
else
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true);
+ &nh, 0, 0, true, NULL);
}
if (dest.sa.sa_family == AF_INET6) {
/* One day we might have a debug section here like one in the
@@ -1085,7 +1085,7 @@ void rtm_read(struct rt_msghdr *rtm)
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true);
+ NULL, 0, 0, true, NULL);
if (!nh.type) {
nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX
@@ -1102,7 +1102,7 @@ void rtm_read(struct rt_msghdr *rtm)
else
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true);
+ &nh, 0, 0, true, NULL);
}
}
diff --git a/zebra/main.c b/zebra/main.c
index 5eb8e75b87..e26c8e3d69 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -300,7 +300,6 @@ int main(int argc, char **argv)
zebra_if_init();
zebra_debug_init();
router_id_cmd_init();
- zebra_vty_init();
access_list_init();
prefix_list_init();
#if defined(HAVE_RTADV)
@@ -322,6 +321,12 @@ int main(int argc, char **argv)
* routing socket. */
zebra_ns_init();
+ /*
+ * Initialize show/config command after the vrf initialization is
+ * complete
+ */
+ zebra_vty_init();
+
#if defined(HANDLE_ZAPI_FUZZING)
if (fuzzing) {
zserv_read_file(fuzzing);
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index cbf77765a3..3c6a2a7daf 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -570,7 +570,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re)
rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE,
re->table, re->flags, &p, NULL, re->nexthop,
- zebrad.rtm_table_default, re->metric, false);
+ zebrad.rtm_table_default, re->metric, false, NULL);
return 0;
}
diff --git a/zebra/rib.h b/zebra/rib.h
index 804372f18e..c7e83480ca 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -305,7 +305,8 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *,
extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
u_short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- u_int32_t table_id, u_int32_t metric, bool fromkernel);
+ u_int32_t table_id, u_int32_t metric, bool fromkernel,
+ struct ethaddr *rmac);
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t,
union g_addr *,
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 910f9b3d93..a77814668d 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -543,13 +543,13 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
memcpy(&nh.gate, gate, sz);
rib_delete(afi, SAFI_UNICAST, vrf_id,
proto, 0, flags, &p, NULL, &nh,
- table, metric, true);
+ table, metric, true, NULL);
} else {
/* XXX: need to compare the entire list of nexthops
* here for NLM_F_APPEND stupidity */
rib_delete(afi, SAFI_UNICAST, vrf_id,
proto, 0, flags, &p, NULL, NULL,
- table, metric, true);
+ table, metric, true, NULL);
}
}
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 602dc5ea41..5ede948e0a 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -30,6 +30,7 @@
#include "zebra_vrf.h"
#include "zebra_memory.h"
#include "rt.h"
+#include "zebra_vxlan.h"
DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
@@ -49,6 +50,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
#endif
zns->if_table = route_table_init();
+ zebra_vxlan_ns_init(zns);
kernel_init(zns);
interface_list(zns);
route_read(zns);
@@ -61,6 +63,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
struct zebra_ns *zns = (struct zebra_ns *)(*info);
route_table_finish(zns->if_table);
+ zebra_vxlan_ns_disable(zns);
#if defined(HAVE_RTADV)
rtadv_terminate(zns);
#endif
diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h
index 6cfba93e50..0c340d8d59 100644
--- a/zebra/zebra_ns.h
+++ b/zebra/zebra_ns.h
@@ -49,6 +49,9 @@ struct zebra_ns {
struct route_table *if_table;
+ /* L3-VNI hash table (for EVPN). Only in default instance */
+ struct hash *l3vni_table;
+
#if defined(HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 83834899c8..41e14459b1 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -50,6 +50,7 @@
#include "zebra/zebra_rnh.h"
#include "zebra/interface.h"
#include "zebra/connected.h"
+#include "zebra/zebra_vxlan.h"
DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
@@ -259,7 +260,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
/*Pending: need to think if null ifp here is ok during bootup?
There was a crash because ifp here was coming to be NULL */
if (ifp)
- if (connected_is_unnumbered(ifp)) {
+ if (connected_is_unnumbered(ifp) ||
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
}
@@ -839,7 +841,9 @@ static unsigned nexthop_active_check(struct route_node *rn,
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
- if (nexthop_active(AFI_IP, re, nexthop, set, rn))
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN_RVTEP))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else if (nexthop_active(AFI_IP, re, nexthop, set, rn))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -2314,7 +2318,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
u_short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- u_int32_t table_id, u_int32_t metric, bool fromkernel)
+ u_int32_t table_id, u_int32_t metric, bool fromkernel,
+ struct ethaddr *rmac)
{
struct route_table *table;
struct route_node *rn;
@@ -2467,6 +2472,22 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
return;
}
+
+ if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
+ struct nexthop *tmp_nh;
+
+ for (ALL_NEXTHOPS(re->nexthop, tmp_nh)) {
+ struct ipaddr vtep_ip;
+
+ memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ vtep_ip.ipa_type = IPADDR_V4;
+ memcpy(&(vtep_ip.ipaddr_v4),
+ &(tmp_nh->gate.ipv4),
+ sizeof(struct in_addr));
+ zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac,
+ &vtep_ip, p);
+ }
+ }
rib_delnode(rn, same);
}
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index 0d0a8dd747..1ae9eac61f 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -250,6 +250,10 @@ static int zebra_vrf_delete(struct vrf *vrf)
route_table_finish(zvrf->rnh_table[afi]);
route_table_finish(zvrf->import_check_table[afi]);
}
+
+ /* cleanup evpn states for vrf */
+ zebra_vxlan_vrf_delete(zvrf);
+
list_delete_all_node(zvrf->rid_all_sorted_list);
list_delete_all_node(zvrf->rid_lo_sorted_list);
XFREE(MTYPE_ZEBRA_VRF, zvrf);
@@ -474,6 +478,8 @@ static int vrf_config_write(struct vty *vty)
if (strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
+ if (zvrf->l3vni)
+ vty_out(vty, " vni %u\n", zvrf->l3vni);
vty_out(vty, "!\n");
}
}
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index 1dfc0b3eb8..c7a0717ee8 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -24,6 +24,7 @@
#include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h>
+#include <lib/vxlan.h>
/* MPLS (Segment Routing) global block */
typedef struct mpls_srgb_t_ {
@@ -114,6 +115,9 @@ struct zebra_vrf {
*/
int advertise_gw_macip;
+ /* l3-vni info */
+ vni_t l3vni;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index ccd0c703d4..82b0157ad3 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -49,6 +49,7 @@
#include "zebra/zserv.h"
#include "zebra/router-id.h"
#include "zebra/ipforward.h"
+#include "zebra/zebra_vxlan_private.h"
extern int allow_delete;
@@ -65,6 +66,14 @@ static void vty_show_ip_route_summary(struct vty *vty,
static void vty_show_ip_route_summary_prefix(struct vty *vty,
struct route_table *table);
+/*
+ * special macro to allow us to get the correct zebra_vrf
+ */
+#define ZEBRA_DECLVAR_CONTEXT(A, B) \
+ struct vrf *A = VTY_GET_CONTEXT(vrf); \
+ struct zebra_vrf *B = \
+ (vrf) ? vrf->info : NULL; \
+
/* VNI range as per RFC 7432 */
#define CMD_VNI_RANGE "(1-16777215)"
@@ -1896,6 +1905,110 @@ DEFUN (show_vrf,
return CMD_SUCCESS;
}
+DEFUN (vrf_vni_mapping,
+ vrf_vni_mapping_cmd,
+ "vni " CMD_VNI_RANGE,
+ "VNI\n"
+ "VNI-ID\n")
+{
+ int ret = 0;
+
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+ char err[ERR_STR_SZ];
+
+ assert(vrf);
+ assert(zvrf);
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+ if (ret != 0) {
+ vty_out(vty, "%s\n", err);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_vrf_vni_mapping,
+ no_vrf_vni_mapping_cmd,
+ "no vni " CMD_VNI_RANGE,
+ NO_STR
+ "VNI\n"
+ "VNI-ID")
+{
+ int ret = 0;
+ char err[ERR_STR_SZ];
+ vni_t vni = strtoul(argv[2]->arg, NULL, 10);
+
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+
+ assert(vrf);
+ assert(zvrf);
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+ if (ret != 0) {
+ vty_out(vty, "%s\n", err);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* show vrf */
+DEFUN (show_vrf_vni,
+ show_vrf_vni_cmd,
+ "show vrf vni [json]",
+ SHOW_STR
+ "VRF\n"
+ "VNI\n"
+ JSON_STR)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ json_object *json = NULL;
+ json_object *json_vrfs = NULL;
+ u_char uj = use_json(argc, argv);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vrfs = json_object_new_array();
+ }
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ if (!zvrf->l3vni)
+ continue;
+
+ if (!uj) {
+ vty_out(vty, "vrf: %s VNI: %u",
+ zvrf_name(zvrf),
+ zvrf->l3vni);
+ vty_out(vty, "\n");
+ } else {
+ json_object *json_vrf = NULL;
+
+ json_vrf = json_object_new_object();
+ json_object_string_add(json_vrf, "vrf",
+ zvrf_name(zvrf));
+ json_object_int_add(json_vrf, "l3vni",
+ zvrf->l3vni);
+ json_object_array_add(json_vrfs, json_vrf);
+ }
+ }
+
+ if (uj) {
+ json_object_object_add(json, "vrfs", json_vrfs);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_vni,
show_evpn_vni_cmd,
"show evpn vni [json]",
@@ -1931,6 +2044,161 @@ DEFUN (show_evpn_vni_vni,
return CMD_SUCCESS;
}
+DEFUN (show_evpn_l3vni,
+ show_evpn_l3vni_cmd,
+ "show evpn l3vni [json]",
+ SHOW_STR
+ "EVPN\n"
+ "L3 VNI\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_l3vnis(vty, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_l3vni_vni,
+ show_evpn_l3vni_vni_cmd,
+ "show evpn l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "L3 VxLAN Network Identifier\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t vni;
+ u_char uj = use_json(argc, argv);
+
+ vni = strtoul(argv[3]->arg, NULL, 10);
+ zebra_vxlan_print_l3vni(vty, vni, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni_mac,
+ show_evpn_rmac_l3vni_mac_cmd,
+ "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ "MAC\n"
+ "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n"
+ JSON_STR)
+{
+ vni_t l3vni = 0;
+ struct ethaddr mac;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!prefix_str2mac(argv[6]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+ zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni,
+ show_evpn_rmac_l3vni_cmd,
+ "show evpn rmac l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t l3vni = 0;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni_all,
+ show_evpn_rmac_l3vni_all_cmd,
+ "show evpn rmac l3vni all [json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC addresses\n"
+ "L3-VNI\n"
+ "All VNIs\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_rmacs_all_l3vni(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni_ip,
+ show_evpn_nh_l3vni_ip_cmd,
+ "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote Vteps\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ "Ip address\n"
+ "Host address (ipv4 or ipv6)\n"
+ JSON_STR)
+{
+ vni_t l3vni;
+ struct ipaddr ip;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ if (str2ipaddr(argv[6]->arg, &ip) != 0) {
+ if (!uj)
+ vty_out(vty, "%% Malformed Neighbor address\n");
+ return CMD_WARNING;
+ }
+ zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni,
+ show_evpn_nh_l3vni_cmd,
+ "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote Vteps\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t l3vni;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ zebra_vxlan_print_nh_l3vni(vty, l3vni, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni_all,
+ show_evpn_nh_l3vni_all_cmd,
+ "show evpn next-hops l3vni all [json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote VTEPs\n"
+ "L3-VNI\n"
+ "All VNIs\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_nh_all_l3vni(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_mac_vni,
show_evpn_mac_vni_cmd,
"show evpn mac vni " CMD_VNI_RANGE "[json]",
@@ -2601,6 +2869,7 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &no_zebra_packet_process_cmd);
install_element(VIEW_NODE, &show_vrf_cmd);
+ install_element(VIEW_NODE, &show_vrf_vni_cmd);
install_element(VIEW_NODE, &show_route_cmd);
install_element(VIEW_NODE, &show_route_detail_cmd);
install_element(VIEW_NODE, &show_route_summary_cmd);
@@ -2626,6 +2895,14 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_evpn_vni_cmd);
install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd);
@@ -2635,4 +2912,9 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd);
install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd);
+
+ install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &no_vrf_vni_mapping_cmd);
+
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 9c70b55a1a..0ef1802367 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -48,7 +48,9 @@
#include "zebra/zebra_l2.h"
#include "lib/json.h"
+DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
+DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
@@ -61,6 +63,10 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
void **args);
+static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
+ json_object *json);
+static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
+ json_object *json);
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt);
static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt);
@@ -91,11 +97,46 @@ static int zvni_neigh_send_del_to_client(vni_t vni,
struct ethaddr *macaddr, u_char flags);
static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n);
-static zebra_vni_t *zvni_map_svi(struct interface *ifp,
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
struct interface *br_if);
static struct interface *zvni_map_to_svi(vlanid_t vid,
struct interface *br_if);
+/* l3-vni next-hop neigh related APIs */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip);
+static void *zl3vni_nh_alloc(void *p);
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac);
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+
+/* l3-vni rmac related APIs */
+static void zl3vni_print_rmac_hash(struct hash_backet *, void *);
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static void *zl3vni_rmac_alloc(void *p);
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac);
+
+/* l3-vni related APIs*/
+static int is_vni_l3(vni_t);
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
+static void *zl3vni_alloc(void *p);
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
+static int zl3vni_del(zebra_l3vni_t *zl3vni);
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t);
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
+
static unsigned int mac_hash_keymake(void *p);
static int mac_cmp(const void *p1, const void *p2);
static void *zvni_mac_alloc(void *p);
@@ -385,6 +426,80 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
json_object_object_add(json, vni_str, json_vni);
}
+/* print a specific next hop for an l3vni */
+static void zl3vni_print_nh(zebra_neigh_t *n,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "Ip: %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ vty_out(vty, " RMAC: %s\n",
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "ip",
+ ipaddr2str(&(n->ip), buf2,
+ sizeof(buf2)));
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&n->emac, buf2,
+ sizeof(buf2)));
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
+/* Print a specific RMAC entry */
+static void zl3vni_print_rmac(zebra_mac_t *zrmac,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[PREFIX_STRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "MAC: %s\n",
+ prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1)));
+ vty_out(vty, " Remote VTEP: %s\n",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "Rmac",
+ prefix_mac2str(&zrmac->macaddr,
+ buf1,
+ sizeof(buf1)));
+ json_object_string_add(json, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
/*
* Print a specific MAC entry.
*/
@@ -603,6 +718,234 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt)
}
}
+static void zl3vni_print_nh_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ struct nh_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json_vni = NULL;
+ struct json_object *json_nh = NULL;
+ zebra_neigh_t *n = NULL;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ wctx = (struct nh_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json_vni = wctx->json;
+ if (json_vni)
+ json_nh = json_object_new_object();
+ n = (zebra_neigh_t *)backet->data;
+ if (!n)
+ return;
+
+ if (!json_vni) {
+ vty_out(vty, "%-15s %-17s %6d\n",
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)),
+ listcount(n->host_list));
+ } else {
+ json_object_string_add(json_nh, "nexthop-ip",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ json_object_string_add(json_nh, "rmac",
+ prefix_mac2str(&n->emac, buf1,
+ sizeof(buf1)));
+ json_object_int_add(json_nh, "refCnt", listcount(n->host_list));
+ json_object_object_add(json_vni,
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ json_nh);
+ }
+}
+
+static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ uint32_t num_nh = 0;
+ struct nh_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_nh = hashcount(zl3vni->nh_table);
+ if (!num_nh)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #Next-Hops %u\n\n",
+ zl3vni->vni, num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numNh", num_nh);
+
+ memset(&wctx, 0, sizeof(struct nh_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #MACs %u\n\n",
+ zl3vni->vni, num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numRmacs", num_rmacs);
+
+ /* assign per-vni to wctx->json object to fill macs
+ * under the vni. Re-assign primary json object to fill
+ * next vni information.
+ */
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ struct rmac_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json = NULL;
+ struct json_object *json_rmac = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ wctx = (struct rmac_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json = wctx->json;
+ if (json)
+ json_rmac = json_object_new_object();
+ zrmac = (zebra_mac_t *)backet->data;
+ if (!zrmac)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-17s %-21s %-6d\n",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip),
+ listcount(zrmac->host_list));
+ } else {
+ json_object_string_add(json_rmac, "rmac",
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)));
+ json_object_string_add(json_rmac, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ json_object_int_add(json_rmac, "refcnt",
+ listcount(zrmac->host_list));
+ json_object_object_add(json,
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)),
+ json_rmac);
+ }
+}
+
+/* print a specific L3 VNI entry */
+static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ zebra_vni_t *zvni = NULL;
+ json_object *json_vni_list = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+
+ vty = ctx[0];
+ json = ctx[1];
+
+ if (!json) {
+ vty_out(vty, "VNI: %u\n", zl3vni->vni);
+ vty_out(vty, " Local Vtep Ip: %s",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ vty_out(vty, " Vxlan-Intf: %s\n",
+ zl3vni_vxlan_if_name(zl3vni));
+ vty_out(vty, " SVI-If: %s\n",
+ zl3vni_svi_if_name(zl3vni));
+ vty_out(vty, " State: %s\n",
+ zl3vni_state2str(zl3vni));
+ vty_out(vty, " Vrf: %s\n",
+ zl3vni_vrf_name(zl3vni));
+ vty_out(vty, " Rmac: %s\n",
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ vty_out(vty, " L2-VNIs: ");
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni))
+ vty_out(vty, "%u ", zvni->vni);
+ vty_out(vty, "\n");
+ } else {
+ json_vni_list = json_object_new_array();
+ json_object_int_add(json, "vni", zl3vni->vni);
+ json_object_string_add(json, "local-vtep-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json, "vxlan-intf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json, "svi-if",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json, "vrf",
+ zl3vni_vrf_name(zl3vni));
+ json_object_string_add(json, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
+ json_object_array_add(json_vni_list,
+ json_object_new_int(zvni->vni));
+ }
+ json_object_object_add(json, "l2-vnis", json_vni_list);
+ }
+}
+
/*
* Print a specific VNI entry.
*/
@@ -619,10 +962,14 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt)
vty = ctxt[0];
json = ctxt[1];
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty, "VNI: %u\n", zvni->vni);
- else
+ vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id));
+ } else {
json_object_int_add(json, "vni", zvni->vni);
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zvni->vrf_id));
+ }
if (!zvni->vxlan_if) { // unexpected
if (json == NULL)
@@ -682,6 +1029,56 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt)
}
}
+/* print a L3 VNI hash entry */
+static void zl3vni_print_hash(struct hash_backet *backet,
+ void *ctx[])
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ vty = (struct vty *)ctx[0];
+ json = (json_object *)ctx[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ zl3vni->vni,
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zl3vni_vxlan_if_name(zl3vni),
+ zl3vni_svi_if_name(zl3vni),
+ zl3vni_state2str(zl3vni),
+ zl3vni_vrf_name(zl3vni),
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ } else {
+ char vni_str[VNI_STR_LEN];
+
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "vni", zl3vni->vni);
+ json_object_string_add(json_vni, "local-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json_vni, "vxlan-if",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json_vni, "svi-if",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json_vni, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json_vni, "vrf",
+ zl3vni_vrf_name(zl3vni));
+ json_object_string_add(json_vni, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ json_object_object_add(json, vni_str, json_vni);
+ }
+
+}
+
/*
* Print a VNI hash entry - called for display of all VNIs.
*/
@@ -714,10 +1111,12 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[])
num_macs = num_valid_macs(zvni);
num_neigh = hashcount(zvni->neigh_table);
if (json == NULL)
- vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni,
+ vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n",
+ zvni->vni,
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh,
- num_vteps);
+ num_vteps,
+ vrf_id_to_name(zvni->vrf_id));
else {
char vni_str[VNI_STR_LEN];
snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
@@ -753,11 +1152,11 @@ static int zvni_macip_send_msg_to_client(vni_t vni,
struct ipaddr *ip, u_char flags,
u_int16_t cmd)
{
- struct zserv *client;
- struct stream *s;
- int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ int ipa_len;
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
@@ -785,12 +1184,13 @@ static int zvni_macip_send_msg_to_client(vni_t vni,
stream_putc(s, flags); /* sticky mac/gateway mac */
+
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s",
+ "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s",
(cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), vni,
@@ -1000,7 +1400,7 @@ static void zvni_process_neigh_on_local_mac_add(zebra_vni_t *zvni,
if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now ACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
@@ -1040,7 +1440,7 @@ static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
@@ -1077,7 +1477,7 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
@@ -1302,12 +1702,12 @@ static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
struct ethaddr *macaddr, struct ipaddr *ip)
{
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
zif = zvni->vxlan_if->info;
if (!zif)
@@ -1353,7 +1753,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)));
@@ -1370,10 +1770,10 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
struct ipaddr *ip)
{
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
/* If the neigh entry is not present nothing to do*/
n = zvni_neigh_lookup(zvni, ip);
@@ -1395,7 +1795,7 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
ipaddr2str(ip, buf2, sizeof(buf2)));
@@ -1747,7 +2147,8 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp,
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
*/
-static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if)
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
+ struct interface *br_if)
{
struct zebra_ns *zns;
struct route_node *rn;
@@ -2133,13 +2534,15 @@ static int zvni_send_add_to_client(zebra_vni_t *zvni)
zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT);
stream_putl(s, zvni->vni);
stream_put_in_addr(s, &zvni->local_vtep_ip);
+ stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send VNI_ADD %u %s to %s",
+ zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s",
zvni->vni, inet_ntoa(zvni->local_vtep_ip),
+ vrf_id_to_name(zvni->vrf_id),
zebra_route_string(client->proto));
client->vniadd_cnt++;
@@ -2189,10 +2592,9 @@ static void zvni_build_hash_table()
/* Walk VxLAN interfaces and create VNI hash. */
zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ vni_t vni;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
- zebra_vni_t *zvni;
- vni_t vni;
ifp = (struct interface *)rn->info;
if (!ifp)
@@ -2200,39 +2602,83 @@ static void zvni_build_hash_table()
zif = ifp->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
- vxl = &zif->l2info.vxl;
+ vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Create VNI hash for intf %s(%u) VNI %u local IP %s",
- ifp->name, ifp->ifindex, vni,
- inet_ntoa(vxl->vtep_ip));
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- /* VNI hash entry is not expected to exist. */
- zvni = zvni_lookup(vni);
- if (zvni) {
- zlog_err(
- "VNI hash already present for IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- continue;
- }
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u",
+ ifp->name, ifp->ifindex, vni);
- zvni = zvni_add(vni);
- if (!zvni) {
- zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
- /* Inform BGP if interface is up and mapped to bridge. */
- if (if_is_operative(ifp) && zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ /*
+ * we need to associate with SVI.
+ * we can associate with svi-if only after association
+ * with vxlan-intf is complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %s",
+ ifp->name, ifp->ifindex, vni,
+ inet_ntoa(vxl->vtep_ip));
+
+ /* VNI hash entry is not expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (zvni) {
+ zlog_err(
+ "VNI hash already present for IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ continue;
+ }
+
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+
+ /* Inform BGP if intf is up and mapped to bridge. */
+ if (if_is_operative(ifp) && zif->brslave_info.br_if)
+ zvni_send_add_to_client(zvni);
+ }
}
}
@@ -2351,12 +2797,18 @@ static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip)
*/
static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
{
- zebra_vni_t *zvni;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
zvni = (zebra_vni_t *)backet->data;
if (!zvni)
return;
+ /* remove from l3-vni list */
+ zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
/* Free up all neighbors and MACs, if any. */
zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
@@ -2368,9 +2820,1288 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
zvni_del(zvni);
}
+/* cleanup L3VNI */
+static void zl3vni_cleanup_all(struct hash_backet *backet,
+ void *args)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+}
+
+static int is_host_present_in_host_list(struct list *list,
+ struct prefix *host)
+{
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, node, p)) {
+ if (prefix_same(p, host))
+ return 1;
+ }
+ return 0;
+}
+
+static void host_list_add_host(struct list *list,
+ struct prefix *host)
+{
+ struct prefix *p = NULL;
+
+ p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix));
+ memcpy(p, host, sizeof(struct prefix));
+
+ listnode_add_sort(list, p);
+}
+
+static void host_list_delete_host(struct list *list,
+ struct prefix *host)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct prefix *p = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nnode, p)) {
+ if (prefix_same(p, host)) {
+ XFREE(MTYPE_HOST_PREFIX, p);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(list, node_to_del);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp;
+ zebra_mac_t *pmac;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.macaddr, rmac, ETH_ALEN);
+ pmac = hash_lookup(zl3vni->rmac_table, &tmp);
+
+ return pmac;
+}
+
+/*
+ * Callback to allocate RMAC hash entry.
+ */
+static void *zl3vni_rmac_alloc(void *p)
+{
+ const zebra_mac_t *tmp_rmac = p;
+ zebra_mac_t *zrmac;
+
+ zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
+ *zrmac = *tmp_rmac;
+
+ return ((void *)zrmac);
+}
+
+/*
+ * Add RMAC entry to l3-vni
+ */
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp_rmac;
+ zebra_mac_t *zrmac = NULL;
+
+ memset(&tmp_rmac, 0, sizeof(zebra_mac_t));
+ memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN);
+ zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc);
+ assert(zrmac);
+
+ zrmac->host_list = list_new();
+ zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE);
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC);
+
+ return zrmac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ zebra_mac_t *tmp_rmac;
+
+ if (zrmac->host_list)
+ list_delete_and_null(&zrmac->host_list);
+ zrmac->host_list = NULL;
+
+ tmp_rmac = hash_release(zl3vni->rmac_table, zrmac);
+ if (tmp_rmac)
+ XFREE(MTYPE_MAC, tmp_rmac);
+
+ return 0;
+}
+
+/*
+ * Install remote RMAC into the kernel.
+ */
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr,
+ zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/*
+ * Uninstall remote RMAC from the kernel.
+ */
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ if (!zl3vni->vxlan_if) {
+ zlog_err(
+ "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ zl3vni->vni, zl3vni);
+ return -1;
+ }
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/* handle rmac add */
+static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+
+ zrmac = zl3vni_rmac_add(zl3vni, rmac);
+ if (!zrmac) {
+ zlog_warn(
+ "Failed to add RMAC %s L3VNI %u Remote VTEP %s",
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni, ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)));
+ return -1;
+ }
+ memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
+ zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
+
+ /* install rmac in kernel */
+ zl3vni_rmac_install(zl3vni, zrmac);
+ }
+
+ if (!is_host_present_in_host_list(zrmac->host_list, host_prefix))
+ host_list_add_host(zrmac->host_list, host_prefix);
+ return 0;
+}
+
+
+/* handle rmac delete */
+static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac)
+ return -1;
+
+ host_list_delete_host(zrmac->host_list, host_prefix);
+ if (list_isempty(zrmac->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+
+ /* del the rmac entry */
+ zl3vni_rmac_del(zl3vni, zrmac);
+ }
+ return 0;
+}
+
+/*
+ * Look up nh hash entry on a l3-vni.
+ */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t tmp;
+ zebra_neigh_t *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(zl3vni->nh_table, &tmp);
+
+ return n;
+}
+
+
+/*
+ * Callback to allocate NH hash entry on L3-VNI.
+ */
+static void *zl3vni_nh_alloc(void *p)
+{
+ const zebra_neigh_t *tmp_n = p;
+ zebra_neigh_t *n;
+
+ n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+/*
+ * Add neighbor entry.
+ */
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ zebra_neigh_t tmp_n;
+ zebra_neigh_t *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(zebra_neigh_t));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
+ assert(n);
+
+ n->host_list = list_new();
+ n->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ memcpy(&n->emac, mac, ETH_ALEN);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH);
+
+ return n;
+}
+
+/*
+ * Delete neighbor entry.
+ */
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ zebra_neigh_t *tmp_n;
+
+ if (n->host_list)
+ list_delete_and_null(&n->host_list);
+ n->host_list = NULL;
+
+ tmp_n = hash_release(zl3vni->nh_table, n);
+ if (tmp_n)
+ XFREE(MTYPE_NEIGH, tmp_n);
+
+ return 0;
+}
+
+/*
+ * Install remote nh as neigh into the kernel.
+ */
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac);
+}
+
+/*
+ * Uninstall remote nh from the kernel.
+ */
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_del_neigh(zl3vni->svi_if, &n->ip);
+}
+
+/* add remote vtep as a neigh entry */
+static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh) {
+ nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac);
+ if (!nh) {
+
+ zlog_warn(
+ "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)",
+ ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)),
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni);
+ return -1;
+ }
+
+ /* install the nh neigh in kernel */
+ zl3vni_nh_install(zl3vni, nh);
+ }
+
+ if (!is_host_present_in_host_list(nh->host_list, host_prefix))
+ host_list_add_host(nh->host_list, host_prefix);
+
+ return 0;
+}
+
+/* handle nh neigh delete */
+static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh)
+ return -1;
+
+ host_list_delete_host(nh->host_list, host_prefix);
+ if (list_isempty(nh->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_nh_uninstall(zl3vni, nh);
+
+ /* delete the nh entry */
+ zl3vni_nh_del(zl3vni, nh);
+ }
+
+ return 0;
+}
+
+/* handle neigh update from kernel - the only thing of interest is to
+ * readd stale entries.
+ */
+static int zl3vni_local_nh_add_update(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip, u_int16_t state)
+{
+#ifdef GNU_LINUX
+ zebra_neigh_t *n = NULL;
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n)
+ return 0;
+
+ /* all next hop neigh are remote and installed by frr.
+ * If the kernel has aged this entry, re-install.
+ */
+ if (state & NUD_STALE)
+ zl3vni_nh_install(zl3vni, n);
+#endif
+ return 0;
+}
+
+/* handle neigh delete from kernel */
+static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t *n = NULL;
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n)
+ return 0;
+
+ /* all next hop neigh are remote and installed by frr.
+ * If we get an age out notification for these neigh entries, we have to
+ * install it back
+ */
+ zl3vni_nh_install(zl3vni, n);
+
+ return 0;
+}
+
+/*
+ * Hash function for L3 VNI.
+ */
+static unsigned int l3vni_hash_keymake(void *p)
+{
+ const zebra_l3vni_t *zl3vni = p;
+
+ return jhash_1word(zl3vni->vni, 0);
+}
+
+/*
+ * Compare 2 L3 VNI hash entries.
+ */
+static int l3vni_hash_cmp(const void *p1, const void *p2)
+{
+ const zebra_l3vni_t *zl3vni1 = p1;
+ const zebra_l3vni_t *zl3vni2 = p2;
+
+ return (zl3vni1->vni == zl3vni2->vni);
+}
+
+/*
+ * Callback to allocate L3 VNI hash entry.
+ */
+static void *zl3vni_alloc(void *p)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ const zebra_l3vni_t *tmp_l3vni = p;
+
+ zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t));
+ zl3vni->vni = tmp_l3vni->vni;
+ return ((void *)zl3vni);
+}
+
+/*
+ * Look up L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t tmp_l3vni;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+ memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_l3vni.vni = vni;
+ zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni);
+
+ return zl3vni;
+}
+
+/*
+ * Add L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
+{
+ zebra_l3vni_t tmp_zl3vni;
+ struct zebra_ns *zns = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_zl3vni.vni = vni;
+
+ zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc);
+ assert(zl3vni);
+
+ zl3vni->vrf_id = vrf_id;
+ zl3vni->svi_if = NULL;
+ zl3vni->vxlan_if = NULL;
+ zl3vni->l2vnis = list_new();
+ zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
+
+ /* Create hash table for remote RMAC */
+ zl3vni->rmac_table =
+ hash_create(mac_hash_keymake, mac_cmp,
+ "Zebra L3-VNI RMAC-Table");
+
+ /* Create hash table for neighbors */
+ zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp,
+ "Zebra L3-VNI next-hop table");
+
+ return zl3vni;
+}
+
+/*
+ * Delete L3 VNI hash entry.
+ */
+static int zl3vni_del(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t *tmp_zl3vni;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ /* free the list of l2vnis */
+ list_delete_and_null(&zl3vni->l2vnis);
+ zl3vni->l2vnis = NULL;
+
+ /* Free the rmac table */
+ hash_free(zl3vni->rmac_table);
+ zl3vni->rmac_table = NULL;
+
+ /* Free the nh table */
+ hash_free(zl3vni->nh_table);
+ zl3vni->nh_table = NULL;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni);
+ if (tmp_zl3vni)
+ XFREE(MTYPE_ZL3VNI, tmp_zl3vni);
+
+ return 0;
+}
+
+static int is_vni_l3(vni_t vni)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni)
+ return 1;
+ return 0;
+}
+
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns = NULL;
+ struct route_node *rn = NULL;
+ struct interface *ifp = NULL;
+
+ /* loop through all vxlan-interface */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ ifp = (struct interface *)rn->info;
+ if (!ifp)
+ continue;
+
+ zif = ifp->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+
+ vxl = &zif->l2info.vxl;
+ if (vxl->vni == zl3vni->vni) {
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ return ifp;
+ }
+ }
+
+ return NULL;
+}
+
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */
+ struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */
+
+ if (!zl3vni->vxlan_if)
+ return NULL;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return NULL;
+
+ vxl = &zif->l2info.vxl;
+
+ return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
+}
+
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
+{
+ struct zebra_vrf *zvrf = NULL;
+
+ zvrf = zebra_vrf_lookup_by_id(vrf_id);
+ if (!zvrf)
+ return NULL;
+
+ return zl3vni_lookup(zvrf->l3vni);
+}
+
+/*
+ * Map SVI and associated bridge to a VNI. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ */
+static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp,
+ struct interface *br_if)
+{
+ int found = 0;
+ vlanid_t vid = 0;
+ u_char bridge_vlan_aware = 0;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_ns *zns = NULL;
+ struct route_node *rn = NULL;
+ struct zebra_if *zif = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_l2info_bridge *br = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!br_if)
+ return NULL;
+
+ /* Make sure the linked interface is a bridge. */
+ if (!IS_ZEBRA_IF_BRIDGE(br_if))
+ return NULL;
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert(zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+ if (bridge_vlan_aware) {
+ struct zebra_l2info_vlan *vl;
+
+ if (!IS_ZEBRA_IF_VLAN(ifp))
+ return NULL;
+
+ zif = ifp->info;
+ assert(zif);
+ vl = &zif->l2info.vl;
+ vid = vl->vid;
+ }
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ if (!tmp_if)
+ continue;
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative(tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware || vxl->access_vlan == vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ zl3vni = zl3vni_lookup(vxl->vni);
+ return zl3vni;
+}
+
+/*
+ * Inform BGP about l3-vni.
+ */
+static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+ struct ethaddr rmac;
+ char buf[ETHER_ADDR_STRLEN];
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ /* get the rmac */
+ memset(&rmac, 0, sizeof(struct ethaddr));
+ zl3vni_get_rmac(zl3vni, &rmac);
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zserv_create_header(s, ZEBRA_L3VNI_ADD,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+ stream_put(s, &rmac, sizeof(struct ethaddr));
+ stream_put_in_addr(s, &zl3vni->local_vtep_ip);
+
+ /* 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",
+ zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ prefix_mac2str(&rmac, buf, sizeof(buf)),
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zebra_route_string(client->proto));
+
+ client->l3vniadd_cnt++;
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Inform BGP about local l3-VNI deletion.
+ */
+static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zserv_create_header(s, ZEBRA_L3VNI_DEL,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send L3_VNI_DEL %u VRF %s to %s",
+ zl3vni->vni,
+ vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ zebra_route_string(client->proto));
+
+ client->l3vnidel_cnt++;
+ return zebra_server_send_message(client);
+}
+
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
+{
+ 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);
+}
+
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
+{
+ 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);
+}
+
+static void zvni_add_to_l3vni_list(struct hash_backet *backet,
+ void *ctxt)
+{
+ zebra_vni_t *zvni = (zebra_vni_t *) backet->data;
+ zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt;
+
+ if (zvni->vrf_id == zl3vni_vrf_id(zl3vni))
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+}
+
+/*
+ * handle transition of vni from l2 to l3 and vice versa
+ */
+static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf,
+ vni_t vni, int add)
+{
+ zebra_vni_t *zvni = NULL;
+
+ /* There is a possibility that VNI notification was already received
+ * from kernel and we programmed it as L2-VNI
+ * In such a case we need to delete this L2-VNI first, so
+ * that it can be reprogrammed as L3-VNI in the system. It is also
+ * possible that the vrf-vni mapping is removed from FRR while the vxlan
+ * interface is still present in kernel. In this case to keep it
+ * symmetric, we will delete the l3-vni and reprogram it as l2-vni
+ */
+ if (add) {
+ /* Locate hash entry */
+ zvni = zvni_lookup(vni);
+ if (!zvni)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L2-VNI %u - transition to L3-VNI",
+ vni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, VNI %u",
+ zvni, zvni->vni);
+ return -1;
+ }
+ } else {
+ /* TODO_MITESH: This needs to be thought through. We don't have
+ * enough information at this point to reprogram the vni as
+ * l2-vni. One way is to store the required info in l3-vni and
+ * used it solely for this purpose
+ */
+ }
+
+ return 0;
+}
+
+/* delete and uninstall rmac hash entry */
+static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zrmac = (zebra_mac_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+ zl3vni_rmac_del(zl3vni, zrmac);
+}
+
+/* delete and uninstall nh hash entry */
+static void zl3vni_del_nh_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_neigh_t *n = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ n = (zebra_neigh_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_nh_uninstall(zl3vni, n);
+ zl3vni_nh_del(zl3vni, n);
+}
/* Public functions */
+/* handle evpn route in vrf table */
+void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return;
+
+ /* add the next hop neighbor */
+ zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
+
+ /* add the rmac */
+ zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix);
+}
+
+/* handle evpn vrf route delete */
+void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni)
+ return;
+
+ /* delete the next hop entry */
+ zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix);
+
+ /* delete the rmac entry */
+ zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix);
+}
+
+void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ethaddr *rmac,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_mac_t *zrmac = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u doesnt exist\n",
+ l3vni);
+ return;
+ }
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% Requested RMAC doesnt exist in L3-VNI %u",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_rmac(zrmac, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+ num_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of Remote RMACs known for this VNI: %u\n",
+ num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json, "numRmacs", num_rmacs);
+
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_rmac_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ipaddr *ip,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_neigh_t *n = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% Requested next-hop not present for L3-VNI %u",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_nh(n, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ u_int32_t num_nh;
+ struct nh_walk_ctx wctx;
+ json_object *json = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+
+ num_nh = hashcount(zl3vni->nh_table);
+ if (!num_nh)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of NH Neighbors known for this VNI: %u\n",
+ num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json, "numNh", num_nh);
+
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_nh_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return;
+}
+
+/*
+ * Display L3 VNI information (VTY command handler).
+ */
+void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json)
+{
+ void *args[2];
+ json_object *json = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ zl3vni_print(zl3vni, (void *)args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display L3 VNI hash table (VTY command handler).
+ */
+void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json)
+{
+ u_int32_t num_vnis;
+ void *args[2];
+ json_object *json = NULL;
+ struct zebra_ns *zns = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ num_vnis = hashcount(zns->l3vni_table);
+ if (!num_vnis) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_object_int_add(json, "numVnis", num_vnis);
+ } else {
+ vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis);
+ vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State",
+ "VRF", "Rmac");
+ }
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *, void *))zl3vni_print_hash,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
/*
* Display Neighbors for a VNI (VTY command handler).
*/
@@ -2789,9 +4520,9 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
vty_out(vty, "Advertise gateway mac-ip: %s\n",
zvrf->advertise_gw_macip ? "Yes" : "No");
vty_out(vty, "Number of VNIs: %u\n", num_vnis);
- vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI",
+ vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI",
"VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
- "# Remote VTEPs");
+ "# Remote VTEPs", "VRF");
}
args[0] = vty;
args[1] = json;
@@ -2816,18 +4547,27 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
int zebra_vxlan_local_neigh_del(struct interface *ifp,
struct interface *link_if, struct ipaddr *ip)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
char buf[INET6_ADDRSTRLEN];
char buf2[ETHER_ADDR_STRLEN];
- zebra_mac_t *zmac;
+ zebra_neigh_t *n = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_mac_t *zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_del(zl3vni, ip);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
+
if (!zvni->vxlan_if) {
zlog_err(
"VNI %u hash %p doesn't have intf upon local neighbor DEL",
@@ -2836,7 +4576,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp,
}
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u",
+ zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u",
ipaddr2str(ip, buf, sizeof(buf)),
ifp->name, ifp->ifindex, zvni->vni);
@@ -2891,23 +4631,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
struct ethaddr *macaddr, u_int16_t state,
u_char ext_learned)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
- zebra_mac_t *zmac, *old_zmac;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_vni_t *zvni = NULL;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL, *old_zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_add_update(zl3vni, ip, state);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
- "%s-> VNI %u",
+ "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, state, ext_learned ? "ext-learned " : "",
@@ -3015,12 +4762,12 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
/* Inform BGP. */
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u",
+ zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)),
zvni->vni);
-
ZEBRA_NEIGH_SET_ACTIVE(n);
+
return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0);
}
@@ -3045,6 +4792,10 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, u_short length,
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
s = client->ibuf;
while (l < length) {
@@ -3189,6 +4940,10 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, u_short length,
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
if (!EVPN_ENABLED(zvrf)) {
zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback",
__PRETTY_FUNCTION__);
@@ -3898,14 +5653,14 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT),
svi_if_zif->link_ifindex);
- zvni = zvni_map_svi(svi_if, svi_if_link);
+ zvni = zvni_from_svi(svi_if, svi_if_link);
}
} else if (IS_ZEBRA_IF_BRIDGE(svi_if)) {
/*
* If it is a vlan unaware bridge then svi is the bridge
* itself
*/
- zvni = zvni_map_svi(svi_if, svi_if);
+ zvni = zvni_from_svi(svi_if, svi_if);
}
} else if (IS_ZEBRA_IF_VLAN(ifp)) {
struct zebra_if *svi_if_zif =
@@ -3917,9 +5672,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex);
if (svi_if_zif && svi_if_link)
- zvni = zvni_map_svi(ifp, svi_if_link);
+ zvni = zvni_from_svi(ifp, svi_if_link);
} else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
- zvni = zvni_map_svi(ifp, ifp);
+ zvni = zvni_from_svi(ifp, ifp);
}
if (!zvni)
@@ -3958,56 +5713,108 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
}
/*
- * Handle SVI interface going down. At this point, this is a NOP since
- * the kernel deletes the neighbor entries on this SVI (if any).
+ * Handle SVI interface going down.
+ * SVI can be associated to either L3-VNI or L2-VNI.
+ * For L2-VNI: At this point, this is a NOP since
+ * the kernel deletes the neighbor entries on this SVI (if any).
+ * We only need to update the vrf corresponding to zvni.
+ * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete
+ * from bgp
*/
int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
+
+ /* process l3-vni down */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* remove association with svi-if */
+ zl3vni->svi_if = NULL;
+ } else {
+ zebra_vni_t *zvni = NULL;
+
+ /* since we dont have svi corresponding to zvni, we associate it
+ * to default vrf. Note: the corresponding neigh entries on the
+ * SVI would have already been deleted */
+ zvni = zvni_from_svi(ifp, link_if);
+ if (zvni) {
+ zvni->vrf_id = VRF_DEFAULT;
+
+ /* update the tenant vrf in BGP */
+ zvni_send_add_to_client(zvni);
+ }
+ }
return 0;
}
/*
- * Handle SVI interface coming up. This may or may not be of interest,
- * but if this is a SVI on a VxLAN bridge, we need to install any remote
- * neighbor entries (which will be used for EVPN ARP suppression).
+ * Handle SVI interface coming up.
+ * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni
+ * vxlan intf).
+ * For L2-VNI: we need to install any remote neighbors entried (used for
+ * apr-suppression)
+ * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI
*/
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
- zebra_vni_t *zvni;
- struct neigh_walk_ctx n_wctx;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
- zvni = zvni_map_svi(ifp, link_if);
- if (!zvni)
- return 0;
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
- if (!zvni->vxlan_if) {
- zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
- zvni->vni, zvni);
- return -1;
- }
+ /* associate with svi */
+ zl3vni->svi_if = ifp;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors",
- ifp->name, ifp->ifindex, zvni->vni);
+ /* process oper-up */
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process SVI up for l2-vni */
+ struct neigh_walk_ctx n_wctx;
- /* Install any remote neighbors for this VNI. */
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+ zvni = zvni_from_svi(ifp, link_if);
+ if (!zvni)
+ return 0;
+
+ if (!zvni->vxlan_if) {
+ zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors",
+ ifp->name, ifp->ifindex, zvni->vni,
+ vrf_id_to_name(ifp->vrf_id));
+
+ /* update the vrf information for l2-vni and inform bgp */
+ zvni->vrf_id = ifp->vrf_id;
+ zvni_send_add_to_client(zvni);
+
+ /* Install any remote neighbors for this VNI. */
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table,
+ zvni_install_neigh_hash,
+ &n_wctx);
+ }
return 0;
}
/*
- * Handle VxLAN interface down - update BGP if required, and do
- * internal cleanup.
+ * Handle VxLAN interface down
*/
int zebra_vxlan_if_down(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4018,31 +5825,55 @@ int zebra_vxlan_if_down(struct interface *ifp)
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is DOWN",
- ifp->name, ifp->ifindex, vni);
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
- assert(zvni->vxlan_if == ifp);
+ /* process-if-down for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete this VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
- /* Free up all neighbors and MACs, if any. */
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ } else {
+ /* process if-down for l2-vni */
+ zebra_vni_t *zvni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L2-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+
+ /* Delete this VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MACs, if any. */
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 1);
+ }
return 0;
}
@@ -4051,10 +5882,9 @@ int zebra_vxlan_if_down(struct interface *ifp)
*/
int zebra_vxlan_if_up(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4065,26 +5895,66 @@ int zebra_vxlan_if_up(struct interface *ifp)
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is UP",
- ifp->name, ifp->ifindex, vni);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ /* Handle L3-VNI add */
+ zebra_l3vni_t *zl3vni = NULL;
- assert(zvni->vxlan_if == ifp);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
- /* If part of a bridge, inform BGP about this VNI. */
- /* Also, read and populate local MACs and neighbors. */
- if (zif->brslave_info.br_if) {
- zvni_send_add_to_client(zvni);
- zvni_read_mac_neigh(zvni, ifp);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ /* we need to associate with SVI, if any, we can associate with
+ * svi-if only after association with vxlan-intf is complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+ /* Handle L2-VNI add */
+
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L2-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ /* If part of a bridge, inform BGP about this VNI. */
+ /* Also, read and populate local MACs and neighbors. */
+ if (zif->brslave_info.br_if) {
+ zvni_send_add_to_client(zvni);
+ zvni_read_mac_neigh(zvni, ifp);
+ }
}
return 0;
@@ -4096,10 +5966,9 @@ int zebra_vxlan_if_up(struct interface *ifp)
*/
int zebra_vxlan_if_del(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4110,34 +5979,69 @@ int zebra_vxlan_if_del(struct interface *ifp)
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del VNI %u intf %s(%u)",
- vni, ifp->name, ifp->ifindex);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return 0;
- }
+ /* process if-del for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L3-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
- /* Free up all neighbors and MAC, if any. */
- zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ /* process oper-down for l3-vni */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
- /* Delete the hash entry. */
- if (zvni_del(zvni)) {
- zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
- zvni, ifp->name, ifp->ifindex, zvni->vni);
- return -1;
+ /* remove the association with vxlan_if */
+ memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr));
+ zl3vni->vxlan_if = NULL;
+ } else {
+
+ /* process if-del for l2-vni*/
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L2-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* remove from l3-vni list */
+ zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
+ zvni, ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
}
return 0;
@@ -4148,10 +6052,9 @@ int zebra_vxlan_if_del(struct interface *ifp)
*/
int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4162,79 +6065,129 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- /* Update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to find VNI hash on update, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Update VNI %u intf %s(%u) VLAN %u local IP %s "
- "master %u chg 0x%x",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
- zif->brslave_info.bridge_ifindex, chgflags);
-
- /* Removed from bridge? Cleanup and return */
- if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
- && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
- /* Delete from client, remove all remote VTEPs */
- /* Also, free up all MACs and neighbors. */
- zvni_send_del_to_client(zvni->vni);
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
- zvni_vtep_del_all(zvni, 1);
- return 0;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex, chgflags);
+
+ /* Removed from bridge? Cleanup and return */
+ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ return 0;
+ }
- /* Handle other changes. */
- if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- /* Remove all existing local neighbors and MACs for this VNI
- * (including from BGP)
+ /* access-vlan change - process oper down, associate with new
+ * svi_if and then process oper up again
*/
- zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- }
+ if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ if (if_is_operative(ifp)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zl3vni->svi_if = NULL;
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(
+ zl3vni);
+ }
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* if we have a valid new master, process l3-vni oper up */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) {
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ }
+ } else {
+ zebra_vni_t *zvni = NULL;
- /* Take further actions needed. Note that if we are here, there is a
- * change of interest.
- */
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- return 0;
+ /* Update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Inform BGP, if there is a change of interest. */
- if (chgflags
- & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
- zvni_send_add_to_client(zvni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex, chgflags);
+
+ /* Removed from bridge? Cleanup and return */
+ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
+ /* Delete from client, remove all remote VTEPs */
+ /* Also, free up all MACs and neighbors. */
+ zvni_send_del_to_client(zvni->vni);
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zvni_vtep_del_all(zvni, 1);
+ return 0;
+ }
- /* If there is a valid new master or a VLAN mapping change, read and
- * populate local MACs and neighbors. Also, reinstall any remote MACs
- * and neighbors for this VNI (based on new VLAN).
- */
- if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
- zvni_read_mac_neigh(zvni, ifp);
- else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- struct mac_walk_ctx m_wctx;
- struct neigh_walk_ctx n_wctx;
+ /* Handle other changes. */
+ if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ /* Remove all existing local neigh and MACs for this VNI
+ * (including from BGP)
+ */
+ zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ }
- zvni_read_mac_neigh(zvni, ifp);
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
- hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+ /* Take further actions needed.
+ * Note that if we are here, there is a change of interest.
+ */
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return 0;
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
- &n_wctx);
+ /* Inform BGP, if there is a change of interest. */
+ if (chgflags
+ & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
+ zvni_send_add_to_client(zvni);
+
+ /* If there is a valid new master or a VLAN mapping change,
+ * read and populate local MACs and neighbors.
+ * Also, reinstall any remote MACs and neighbors
+ * for this VNI (based on new VLAN).
+ */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ zvni_read_mac_neigh(zvni, ifp);
+ else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ struct mac_walk_ctx m_wctx;
+ struct neigh_walk_ctx n_wctx;
+
+ zvni_read_mac_neigh(zvni, ifp);
+
+ memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ m_wctx.zvni = zvni;
+ hash_iterate(zvni->mac_table,
+ zvni_install_mac_hash,
+ &m_wctx);
+
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
+ &n_wctx);
+ }
}
return 0;
@@ -4245,10 +6198,9 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
*/
int zebra_vxlan_if_add(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4259,37 +6211,200 @@ int zebra_vxlan_if_add(struct interface *ifp)
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
- zif->brslave_info.bridge_ifindex);
+ if (is_vni_l3(vni)) {
- /* Create or update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zvni = zvni_add(vni);
- if (!zvni) {
+ /* process if-add for l3-vni*/
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /*
+ * we expect the l3-vni has entry to be present here.
+ * The only place l3-vni is created in zebra is vrf-vni mapping
+ * command. This might change when we have the switchd support
+ * for l3-vxlan interface.
+ */
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
+
+ /* Associate with SVI, if any. We can associate with svi-if only
+ * after association with vxlan_if is complete */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process if-add for l2-vni */
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ /* Create or update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u",
+ vni,
+ vlan_if ? vrf_id_to_name(vlan_if->vrf_id) :
+ "Default",
+ ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return 0;
+
+ /* Inform BGP */
+ zvni_send_add_to_client(zvni);
+
+ /* Read and populate local MACs and neighbors */
+ zvni_read_mac_neigh(zvni, ifp);
+ }
+
+ return 0;
+}
+
+int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
+ vni_t vni,
+ char *err, int err_str_sz,
+ int add)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_default = NULL;
+
+ zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+ if (!zvrf_default)
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("vrf %s vni %u %s",
+ zvrf_name(zvrf),
+ vni,
+ add ? "ADD" : "DEL");
+
+ if (add) {
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
+
+ /* check if the vni is already present under zvrf */
+ if (zvrf->l3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured under the vrf");
return -1;
}
+
+ /* check if this VNI is already present in the system */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured as L3-VNI");
+ return -1;
+ }
+
+ /* add the L3-VNI to the global table */
+ zl3vni = zl3vni_add(vni, zvrf_id(zvrf));
+ if (!zl3vni) {
+ snprintf(err, err_str_sz,
+ "Could not add L3-VNI");
+ return -1;
+ }
+
+ /* associate the vrf with vni */
+ zvrf->l3vni = vni;
+
+ /* associate with vxlan-intf;
+ * we need to associate with the vxlan-intf first
+ */
+ zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
+
+ /* associate with corresponding SVI interface, we can associate
+ * with svi-if only after vxlan interface association is
+ * complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ /* formulate l2vni list */
+ hash_iterate(zvrf_default->vni_table,
+ zvni_add_to_l3vni_list, zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ snprintf(err, err_str_sz, "VNI doesn't exist");
+ return -1;
+ }
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table,
+ zl3vni_del_rmac_hash_entry,
+ zl3vni);
+
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table,
+ zl3vni_del_nh_hash_entry,
+ zl3vni);
+
+ zvrf->l3vni = 0;
+ zl3vni_del(zl3vni);
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
}
+ return 0;
+}
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ zl3vni = zl3vni_from_vrf(zvrf_id(zvrf));
+ if (!zl3vni)
return 0;
- /* Inform BGP */
- zvni_send_add_to_client(zvni);
-
- /* Read and populate local MACs and neighbors */
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zl3vni_del(zl3vni);
+ zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0);
return 0;
}
@@ -4410,8 +6525,9 @@ stream_failure:
int zebra_vxlan_advertise_all_vni(struct zserv *client,
u_short length, struct zebra_vrf *zvrf)
{
- struct stream *s;
- int advertise;
+ struct stream *s = NULL;
+ int advertise = 0;
+ struct zebra_ns *zns = NULL;
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("EVPN VNI Adv for non-default VRF %u",
@@ -4449,6 +6565,13 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client,
* kernel and free entries.
*/
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+
+ /* cleanup all l3vnis */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return -1;
+
+ hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL);
}
stream_failure:
@@ -4475,3 +6598,28 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
hash_free(zvrf->vni_table);
}
+
+/* init the l3vni table */
+void zebra_vxlan_ns_init(struct zebra_ns *zns)
+{
+ zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
+ "Zebra VRF L3 VNI table");
+}
+
+/* free l3vni table */
+void zebra_vxlan_ns_disable(struct zebra_ns *zns)
+{
+ hash_free(zns->l3vni_table);
+}
+
+/* get the l3vni svi ifindex */
+ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return 0;
+
+ return zl3vni->svi_if->ifindex;
+}
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 290d19bcf3..b7def6acf8 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -51,6 +51,13 @@ is_evpn_enabled()
#define VNI_STR_LEN 32
+extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
+extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
+extern void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni,
+ struct ipaddr *ip, u_char uj);
+extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
+ struct ethaddr *rmac,
+ u_char use_json);
extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, u_char use_json);
extern void zebra_vxlan_print_macs_all_vni(struct vty *vty,
@@ -84,6 +91,16 @@ extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, u_char use_json);
extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
u_char use_json);
+extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, u_char
+ use_json);
+extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, u_char use_json);
+extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, u_char
+ use_json);
+extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, u_char use_json);
+extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni,
+ u_char use_json);
+extern void zebra_vxlan_print_l3vnis(struct vty *vty,
+ u_char use_json);
extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
int add);
@@ -129,7 +146,20 @@ extern int zebra_vxlan_advertise_gw_macip(struct zserv *client,
extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
u_short length,
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);
extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
extern void zebra_vxlan_close_tables(struct zebra_vrf *);
+extern void zebra_vxlan_ns_init(struct zebra_ns *zns);
+extern void zebra_vxlan_ns_disable(struct zebra_ns *zns);
+extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *ip,
+ struct prefix *host_prefix);
+extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix);
#endif /* _ZEBRA_VXLAN_H */
diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c
index bbed5ddb05..db828c337e 100644
--- a/zebra/zebra_vxlan_null.c
+++ b/zebra/zebra_vxlan_null.c
@@ -83,6 +83,30 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf)
{
}
+void zebra_vxlan_print_rmacs_l3vni(struct vty*, vni_t, u_char)
+{
+}
+
+void zebra_vxlan_print_rmacs_all_l3vni(struct vty*, u_char)
+{
+}
+
+void zebra_vxlan_print_nh_l3vni(struct vty*, vni_t, u_char)
+{
+}
+
+void zebra_vxlan_print_nh_all_l3vni(struct vty*, u_char)
+{
+}
+
+void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni)
+{
+}
+
+void zebra_vxlan_print_l3vnis(struct vty *vty)
+{
+}
+
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
return 0;
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index fa7d0e9457..ef6f9b99cb 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -30,12 +30,16 @@
#include "if.h"
#include "linklist.h"
+#include "zebra_vxlan.h"
+
+#define ERR_STR_SZ 256
/* definitions */
typedef struct zebra_vni_t_ zebra_vni_t;
typedef struct zebra_vtep_t_ zebra_vtep_t;
typedef struct zebra_mac_t_ zebra_mac_t;
typedef struct zebra_neigh_t_ zebra_neigh_t;
+typedef struct zebra_l3vni_t_ zebra_l3vni_t;
/*
* VTEP info
@@ -75,6 +79,9 @@ struct zebra_vni_t_ {
/* Local IP */
struct in_addr local_vtep_ip;
+ /* tenant VRF, if any */
+ vrf_id_t vrf_id;
+
/* List of local or remote MAC */
struct hash *mac_table;
@@ -82,6 +89,128 @@ struct zebra_vni_t_ {
struct hash *neigh_table;
};
+/* L3 VNI hash table */
+struct zebra_l3vni_t_ {
+
+ /* VNI key */
+ vni_t vni;
+
+ /* vrf_id */
+ vrf_id_t vrf_id;
+
+ /* Local IP */
+ struct in_addr local_vtep_ip;
+
+ /* kernel interface for l3vni */
+ struct interface *vxlan_if;
+
+ /* SVI interface corresponding to the l3vni */
+ struct interface *svi_if;
+
+ /* list of L2 VNIs associated with the L3 VNI */
+ struct list *l2vnis;
+
+ /* list of remote router-macs */
+ struct hash *rmac_table;
+
+ /* list of remote vtep-ip neigh */
+ struct hash *nh_table;
+};
+
+/* get the vx-intf name for l3vni */
+static inline const char *zl3vni_vxlan_if_name(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None";
+}
+
+/* get the svi intf name for l3vni */
+static inline const char *zl3vni_svi_if_name(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->svi_if ? zl3vni->svi_if->name : "None";
+}
+
+/* get the vrf name for l3vni */
+static inline const char *zl3vni_vrf_name(zebra_l3vni_t *zl3vni)
+{
+ return vrf_id_to_name(zl3vni->vrf_id);
+}
+
+/* get the rmac string */
+static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf,
+ int size)
+{
+ char *ptr;
+
+ if (!buf)
+ ptr = (char *)XMALLOC(MTYPE_TMP,
+ ETHER_ADDR_STRLEN * sizeof(char));
+ else {
+ assert(size >= ETHER_ADDR_STRLEN);
+ ptr = buf;
+ }
+
+ if (zl3vni->svi_if)
+ snprintf(ptr, (ETHER_ADDR_STRLEN),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)zl3vni->svi_if->hw_addr[0],
+ (uint8_t)zl3vni->svi_if->hw_addr[1],
+ (uint8_t)zl3vni->svi_if->hw_addr[2],
+ (uint8_t)zl3vni->svi_if->hw_addr[3],
+ (uint8_t)zl3vni->svi_if->hw_addr[4],
+ (uint8_t)zl3vni->svi_if->hw_addr[5]);
+ else
+ snprintf(ptr, ETHER_ADDR_STRLEN, "None");
+
+ return ptr;
+}
+
+/*
+ * l3-vni is oper up when:
+ * 0. if EVPN is enabled (advertise-all-vni cfged)
+ * 1. it is associated to a vxlan-intf
+ * 2. Associated vxlan-intf is oper up
+ * 3. it is associated to an SVI
+ * 4. associated SVI is oper up
+ */
+static inline int is_l3vni_oper_up(zebra_l3vni_t *zl3vni)
+{
+ return (is_evpn_enabled() && zl3vni &&
+ (zl3vni->vrf_id != VRF_UNKNOWN) &&
+ zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if) &&
+ zl3vni->svi_if && if_is_operative(zl3vni->svi_if));
+}
+
+static inline const char *zl3vni_state2str(zebra_l3vni_t *zl3vni)
+{
+ if (!zl3vni)
+ return NULL;
+
+ if (is_l3vni_oper_up(zl3vni))
+ return "Up";
+ else
+ return "Down";
+
+ return NULL;
+}
+
+static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->vrf_id;
+}
+
+static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ if (!zl3vni)
+ return;
+
+ if (!is_l3vni_oper_up(zl3vni))
+ return;
+
+ if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if))
+ memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN);
+}
+
/*
* MAC hash table.
*
@@ -103,6 +232,7 @@ struct zebra_mac_t_ {
#define ZEBRA_MAC_REMOTE 0x02
#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */
#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */
+#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */
/* Local or remote info. */
union {
@@ -116,6 +246,9 @@ struct zebra_mac_t_ {
/* List of neigh associated with this mac */
struct list *neigh_list;
+
+ /* list of hosts pointing to this remote RMAC */
+ struct list *host_list;
};
/*
@@ -141,6 +274,11 @@ struct mac_walk_ctx {
struct json_object *json; /* Used for JSON Output */
};
+struct rmac_walk_ctx {
+ struct vty *vty;
+ struct json_object *json;
+};
+
enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 };
#define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE
@@ -175,11 +313,15 @@ struct zebra_neigh_t_ {
u_int32_t flags;
#define ZEBRA_NEIGH_LOCAL 0x01
#define ZEBRA_NEIGH_REMOTE 0x02
+#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */
enum zebra_neigh_state state;
/* Remote VTEP IP - applicable only for remote neighbors. */
struct in_addr r_vtep_ip;
+
+ /* list of hosts pointing to this remote NH entry */
+ struct list *host_list;
};
/*
@@ -206,4 +348,18 @@ struct neigh_walk_ctx {
struct json_object *json; /* Used for JSON Output */
};
+/* context for neigh hash walk - update l3vni and rmac */
+struct neigh_l3info_walk_ctx {
+
+ zebra_vni_t *zvni;
+ zebra_l3vni_t *zl3vni;
+ int add;
+};
+
+struct nh_walk_ctx {
+
+ struct vty *vty;
+ struct json_object *json;
+};
+
#endif /* _ZEBRA_VXLAN_PRIVATE_H */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 684ba49e7d..7eded89f6d 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -1132,23 +1132,26 @@ static int zread_route_add(struct zserv *client, u_short length,
struct route_entry *re;
struct nexthop *nexthop = NULL;
int i, ret;
+ vrf_id_t vrf_id = 0;
s = client->ibuf;
if (zapi_route_decode(s, &api) < 0)
return -1;
/* Allocate new route. */
+ vrf_id = zvrf_id(zvrf);
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->type = api.type;
re->instance = api.instance;
re->flags = api.flags;
re->uptime = time(NULL);
- re->vrf_id = zvrf_id(zvrf);
+ re->vrf_id = vrf_id;
re->table = zvrf->table_id;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
+ ifindex_t ifindex = 0;
switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -1159,11 +1162,42 @@ static int zread_route_add(struct zserv *client, u_short length,
nexthop = route_entry_nexthop_ipv4_add(
re, &api_nh->gate.ipv4, NULL);
break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX: {
+
+ struct ipaddr vtep_ip;
+
+ memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ if (CHECK_FLAG(api.flags,
+ ZEBRA_FLAG_EVPN_ROUTE)) {
+ ifindex =
+ get_l3vni_svi_ifindex(vrf_id);
+ } else {
+ ifindex = api_nh->ifindex;
+ }
+
nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL,
- api_nh->ifindex);
+ ifindex);
+
+ /* if this an EVPN route entry,
+ program the nh as neigh
+ */
+ if (CHECK_FLAG(api.flags,
+ ZEBRA_FLAG_EVPN_ROUTE)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_EVPN_RVTEP);
+ vtep_ip.ipa_type = IPADDR_V4;
+ memcpy(&(vtep_ip.ipaddr_v4),
+ &(api_nh->gate.ipv4),
+ sizeof(struct in_addr));
+ zebra_vxlan_evpn_vrf_route_add(
+ vrf_id,
+ &api.rmac,
+ &vtep_ip,
+ &api.prefix);
+ }
break;
+ }
case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add(
re, &api_nh->gate.ipv6);
@@ -1265,7 +1299,7 @@ static int zread_route_del(struct zserv *client, u_short length,
rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance,
api.flags, &api.prefix, src_p, NULL, zvrf->table_id,
- api.metric, false);
+ api.metric, false, &api.rmac);
/* Stats */
switch (api.prefix.family) {
@@ -1466,7 +1500,7 @@ static int zread_ipv4_delete(struct zserv *client, u_short length,
table_id = zvrf->table_id;
rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, NULL, NULL, table_id, 0, false);
+ api.flags, &p, NULL, NULL, table_id, 0, false, NULL);
client->v4_route_del_cnt++;
stream_failure:
@@ -1884,7 +1918,8 @@ static int zread_ipv6_delete(struct zserv *client, u_short length,
src_pp = NULL;
rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, src_pp, NULL, client->rtm_table, 0, false);
+ api.flags, &p, src_pp, NULL, client->rtm_table, 0, false,
+ NULL);
client->v6_route_del_cnt++;
@@ -2979,6 +3014,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client)
vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt);
vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt);
vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt);
+ vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt);
+ vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt);
vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt);
vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt);
diff --git a/zebra/zserv.h b/zebra/zserv.h
index c4b4e20df2..63db9d9eb9 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -110,6 +110,8 @@ struct zserv {
u_int32_t bfd_client_reg_cnt;
u_int32_t vniadd_cnt;
u_int32_t vnidel_cnt;
+ u_int32_t l3vniadd_cnt;
+ u_int32_t l3vnidel_cnt;
u_int32_t macipadd_cnt;
u_int32_t macipdel_cnt;