summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/Makefile.am2
-rw-r--r--bgpd/bgp_attr.c3
-rw-r--r--bgpd/bgp_ecommunity.c15
-rw-r--r--bgpd/bgp_evpn.c1169
-rw-r--r--bgpd/bgp_evpn.h4
-rw-r--r--bgpd/bgp_evpn_private.h88
-rw-r--r--bgpd/bgp_evpn_vty.c406
-rw-r--r--bgpd/bgp_memory.c2
-rw-r--r--bgpd/bgp_memory.h3
-rw-r--r--bgpd/bgp_network.c4
-rw-r--r--bgpd/bgp_route.c15
-rw-r--r--bgpd/bgp_vty.c50
-rw-r--r--bgpd/bgp_zebra.c36
-rw-r--r--bgpd/bgpd.c97
-rw-r--r--bgpd/bgpd.h10
-rw-r--r--doc/user/overview.rst28
-rw-r--r--doc/user/zebra.rst30
-rw-r--r--isisd/isis_te.c77
-rw-r--r--lib/log.c2
-rw-r--r--lib/prefix.c69
-rw-r--r--lib/prefix.h15
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/vrf.c2
-rw-r--r--lib/zclient.c10
-rw-r--r--lib/zclient.h6
-rw-r--r--ospf6d/subdir.am2
-rw-r--r--ospfd/subdir.am2
-rw-r--r--ripd/subdir.am2
-rw-r--r--zebra/redistribute.c33
-rw-r--r--zebra/subdir.am2
30 files changed, 1978 insertions, 208 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index 8a410adca1..6761389100 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -116,7 +116,7 @@ module_LTLIBRARIES += bgpd_snmp.la
endif
bgpd_snmp_la_SOURCES = bgp_snmp.c
-bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
bgpd_snmp_la_LIBADD = ../lib/libfrrsnmp.la
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index e714e17bde..2c52b57b36 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1177,7 +1177,6 @@ static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer,
* not right.
* So do the checks later, i.e. here
*/
- struct bgp *bgp = peer->bgp;
struct aspath *aspath;
/* Confederation sanity check. */
@@ -1192,7 +1191,7 @@ static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer,
}
/* First AS check for EBGP. */
- if (bgp != NULL && bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS)) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_ENFORCE_FIRST_AS)) {
if (peer->sort == BGP_PEER_EBGP
&& !aspath_firstas_check(attr->aspath, peer->as)) {
zlog_err("%s incorrect first AS (must be %u)",
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 20f5e15d69..8c5356c998 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -801,6 +801,21 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
len = sprintf(
str_buf + str_pnt,
"FS:marking %u", *(pnt+5));
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
+ struct ethaddr mac;
+
+ pnt++;
+ memcpy(&mac, pnt, ETH_ALEN);
+ len = sprintf(
+ str_buf + str_pnt,
+ "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)mac.octet[0],
+ (uint8_t)mac.octet[1],
+ (uint8_t)mac.octet[2],
+ (uint8_t)mac.octet[3],
+ (uint8_t)mac.octet[4],
+ (uint8_t)mac.octet[5]);
} else
unk_ecom = 1;
} else {
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 9f3fb3498c..991b70c9ca 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -53,13 +53,15 @@
extern struct zclient *zclient;
DEFINE_QOBJ_TYPE(bgpevpn)
+DEFINE_QOBJ_TYPE(evpnes)
/*
* Static function declarations
*/
-static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
- afi_t afi, safi_t safi, struct bgp_node *rn,
+static void delete_evpn_route_entry(struct bgp *bgp,
+ afi_t afi, safi_t safi,
+ struct bgp_node *rn,
struct bgp_info **ri);
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
@@ -67,6 +69,47 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
* Private functions.
*/
+/* compare two IPV4 VTEP IPs */
+static int evpn_vtep_ip_cmp(const void *p1, const void *p2)
+{
+ const struct in_addr *ip1 = p1;
+ const struct in_addr *ip2 = p2;
+
+ if (!ip1 && !ip2)
+ return 1;
+ if (!ip1 || !ip2)
+ return 0;
+ return (ip1->s_addr == ip2->s_addr);
+}
+
+/*
+ * Make hash key for ESI.
+ */
+static unsigned int esi_hash_keymake(void *p)
+{
+ struct evpnes *pes = p;
+ const void *pnt = (void *)pes->esi.val;
+
+ return jhash(pnt, ESI_BYTES, 0xa5a5a55a);
+}
+
+/*
+ * Compare two ESIs.
+ */
+static int esi_cmp(const void *p1, const void *p2)
+{
+ const struct evpnes *pes1 = p1;
+ const struct evpnes *pes2 = p2;
+
+ if (pes1 == NULL && pes2 == NULL)
+ return 1;
+
+ if (pes1 == NULL || pes2 == NULL)
+ return 0;
+
+ return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0);
+}
+
/*
* Make vni hash key.
*/
@@ -593,6 +636,40 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Build extended community for EVPN ES (type-4) route
+ */
+static void build_evpn_type4_route_extcomm(struct evpnes *es,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_es_rt;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_es_rt;
+ bgp_encap_types tnl_type;
+ struct ethaddr mac;
+
+ /* 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 = (uint8_t *)eval.val;
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* ES import RT */
+ memset(&mac, 0, sizeof(struct ethaddr));
+ memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
+ es_get_system_mac(&es->esi, &mac);
+ encode_es_rt_extcomm(&eval_es_rt, &mac);
+ ecom_es_rt.size = 1;
+ ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
+ attr->ecommunity =
+ ecommunity_merge(attr->ecommunity, &ecom_es_rt);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/*
* Build extended communities for EVPN prefix route.
*/
static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
@@ -833,7 +910,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
(struct prefix *)&rn->p, &vpn->prd);
if (global_rn) {
/* Delete route entry in the global EVPN table. */
- delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
/* Schedule for processing - withdraws to peers happen from
* this table.
@@ -847,6 +924,164 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
bgp_info_delete(rn, old_local);
}
+static struct in_addr *es_vtep_new(struct in_addr vtep)
+{
+ struct in_addr *ip;
+
+ ip = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(struct in_addr));
+ if (!ip)
+ return NULL;
+
+ ip->s_addr = vtep.s_addr;
+ return ip;
+}
+
+static void es_vtep_free(struct in_addr *ip)
+{
+ XFREE(MTYPE_BGP_EVPN_ES_VTEP, ip);
+}
+
+/* check if VTEP is already part of the list */
+static int is_vtep_present_in_list(struct list *list,
+ struct in_addr vtep)
+{
+ struct listnode *node = NULL;
+ struct in_addr *tmp;
+
+ for (ALL_LIST_ELEMENTS_RO(list, node, tmp)) {
+ if (tmp->s_addr == vtep.s_addr)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Best path for ES route was changed,
+ * update the list of VTEPs for this ES
+ */
+static int evpn_es_install_vtep(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p,
+ struct in_addr rvtep)
+{
+ struct in_addr *vtep_ip;
+
+ if (is_vtep_present_in_list(es->vtep_list, rvtep))
+ return 0;
+
+
+ vtep_ip = es_vtep_new(rvtep);
+ if (vtep_ip)
+ listnode_add_sort(es->vtep_list, vtep_ip);
+ return 0;
+}
+
+/*
+ * Best path for ES route was changed,
+ * update the list of VTEPs for this ES
+ */
+static int evpn_es_uninstall_vtep(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p,
+ struct in_addr rvtep)
+{
+ struct listnode *node, *nnode, *node_to_del = NULL;
+ struct in_addr *tmp;
+
+ for (ALL_LIST_ELEMENTS(es->vtep_list, node, nnode, tmp)) {
+ if (tmp->s_addr == rvtep.s_addr) {
+ es_vtep_free(tmp);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(es->vtep_list, node_to_del);
+
+ return 0;
+}
+
+/*
+ * Calculate the best path for a ES(type-4) route.
+ */
+static int evpn_es_route_select_install(struct bgp *bgp,
+ struct evpnes *es,
+ struct bgp_node *rn)
+{
+ int ret = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_info *old_select; /* old best */
+ struct bgp_info *new_select; /* new best */
+ struct bgp_info_pair old_and_new;
+
+ /* Compute the best path. */
+ bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
+ old_select = old_and_new.old;
+ new_select = old_and_new.new;
+
+ /*
+ * If the best path hasn't changed - see if something needs to be
+ * updated
+ */
+ if (old_select && old_select == new_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED
+ && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
+ && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)
+ && !bgp->addpath_tx_used[afi][safi]) {
+ if (bgp_zebra_has_route_changed(rn, old_select)) {
+ ret = evpn_es_install_vtep(bgp, es,
+ (struct prefix_evpn *)&rn->p,
+ old_select->attr->nexthop);
+ }
+ UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);
+ bgp_zebra_clear_route_change_flags(rn);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR);
+
+ /*
+ * bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(rn);
+
+ if (old_select)
+ bgp_info_unset_flag(rn, old_select, BGP_INFO_SELECTED);
+ if (new_select) {
+ bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED);
+ bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED);
+ UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG);
+ }
+
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && new_select->sub_type == BGP_ROUTE_IMPORTED) {
+ ret = evpn_es_install_vtep(bgp, es,
+ (struct prefix_evpn *)&rn->p,
+ new_select->attr->nexthop);
+ } else {
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED)
+ ret = evpn_es_uninstall_vtep(
+ bgp, es, (struct prefix_evpn *)&rn->p,
+ old_select->attr->nexthop);
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(rn);
+
+ /* Reap old select bgp_info, if it has been removed */
+ if (old_select && CHECK_FLAG(old_select->flags, BGP_INFO_REMOVED))
+ bgp_info_reap(rn, old_select);
+
+ return ret;
+}
+
/*
* Calculate the best path for an EVPN route. Install/update best path in zebra,
* if appropriate.
@@ -991,6 +1226,177 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn)
return local_ri->attr->sticky;
}
+/*
+ * create or update EVPN type4 route entry.
+ * This could be in the ES table or the global table.
+ * TODO: handle remote ES (type4) routes as well
+ */
+static int update_evpn_type4_route_entry(struct bgp *bgp,
+ struct evpnes *es,
+ afi_t afi, safi_t safi,
+ struct bgp_node *rn,
+ struct attr *attr,
+ int add,
+ struct bgp_info **ri,
+ int *route_changed)
+{
+ char buf[ESI_STR_LEN];
+ char buf1[INET6_ADDRSTRLEN];
+ struct bgp_info *tmp_ri = NULL;
+ struct bgp_info *local_ri = NULL; /* local route entry if any */
+ struct bgp_info *remote_ri = NULL; /* remote route entry if any */
+ struct attr *attr_new = NULL;
+ struct prefix_evpn *evp = NULL;
+
+ *ri = NULL;
+ *route_changed = 1;
+ evp = (struct prefix_evpn *)&rn->p;
+
+ /* locate the local and remote entries if any */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
+ if (tmp_ri->peer == bgp->peer_self &&
+ tmp_ri->type == ZEBRA_ROUTE_BGP &&
+ tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ if (tmp_ri->type == ZEBRA_ROUTE_BGP &&
+ tmp_ri->sub_type == BGP_ROUTE_IMPORTED &&
+ CHECK_FLAG(tmp_ri->flags, BGP_INFO_VALID))
+ remote_ri = tmp_ri;
+ }
+
+ /* we don't expect to see a remote_ri at this point.
+ * An ES route has esi + vtep_ip as the key,
+ * We shouldn't see the same route from any other vtep.
+ */
+ if (remote_ri) {
+ zlog_err(
+ "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote",
+ bgp->vrf_id,
+ esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)),
+ ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
+ return -1;
+ }
+
+ if (!local_ri && !add)
+ return 0;
+
+ /* create or update the entry */
+ if (!local_ri) {
+
+ /* Add or update attribute to hash */
+ attr_new = bgp_attr_intern(attr);
+
+ /* Create new route with its attribute. */
+ tmp_ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC,
+ 0, bgp->peer_self, attr_new, rn);
+ SET_FLAG(tmp_ri->flags, BGP_INFO_VALID);
+
+ /* add the newly created path to the route-node */
+ bgp_info_add(rn, tmp_ri);
+ } else {
+ tmp_ri = local_ri;
+ if (attrhash_cmp(tmp_ri->attr, attr)
+ && !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+ *route_changed = 0;
+ else {
+ /* 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 back the route entry. */
+ *ri = tmp_ri;
+ return 0;
+}
+
+/* update evpn es (type-4) route */
+static int update_evpn_type4_route(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p)
+{
+ int ret = 0;
+ int route_changed = 0;
+ char buf[ESI_STR_LEN];
+ char buf1[INET6_ADDRSTRLEN];
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct attr *attr_new = NULL;
+ struct bgp_node *rn = NULL;
+ struct bgp_info *ri = NULL;
+
+ memset(&attr, 0, sizeof(struct attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = es->originator_ip.ipaddr_v4;
+ attr.mp_nexthop_global_in = es->originator_ip.ipaddr_v4;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ /* Set up extended community. */
+ build_evpn_type4_route_extcomm(es, &attr);
+
+ /* First, create (or fetch) route node within the ESI. */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = update_evpn_type4_route_entry(bgp, es, afi, safi, rn,
+ &attr, 1, &ri,
+ &route_changed);
+ if (ret != 0) {
+ zlog_err("%u ERROR: Failed to updated ES route ESI: %s VTEP %s",
+ bgp->vrf_id,
+ esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
+ ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
+ }
+
+ assert(ri);
+ attr_new = ri->attr;
+
+ /* Perform route selection;
+ * this is just to set the flags correctly
+ * as local route in the ES always wins.
+ */
+ evpn_es_route_select_install(bgp, es, rn);
+ bgp_unlock_node(rn);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_changed) {
+ struct bgp_info *global_ri;
+
+ rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)p, &es->prd);
+ update_evpn_type4_route_entry(bgp, es, afi, safi,
+ rn, attr_new,
+ 1, &global_ri,
+ &route_changed);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, rn, afi, safi);
+ bgp_unlock_node(rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
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,
@@ -1011,8 +1417,9 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_def,
local_ri = tmp_ri;
}
- /* create a new route entry if one doesnt exist.
- Otherwise see if route attr has changed
+ /*
+ * create a new route entry if one doesnt exist.
+ * Otherwise see if route attr has changed
*/
if (!local_ri) {
@@ -1171,8 +1578,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
if (!local_ri) {
/* When learnt locally for the first time but already known from
* remote, we have to initiate appropriate MAC mobility steps.
- * This
- * is applicable when updating the VNI routing table.
+ * This is applicable when updating the VNI routing table.
* We need to skip mobility steps for g/w macs (local mac on g/w
* SVI) advertised in EVPN.
* This will ensure that local routes are preferred for g/w macs
@@ -1354,19 +1760,22 @@ 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)
+/*
+ * Delete EVPN route entry.
+ * The entry can be in ESI/VNI table or the global table.
+ */
+static void delete_evpn_route_entry(struct bgp *bgp,
+ afi_t afi, safi_t safi,
+ struct bgp_node *rn,
+ struct bgp_info **ri)
{
- struct bgp_info *tmp_ri = NULL;
+ struct bgp_info *tmp_ri;
*ri = NULL;
- /* find the matching route entry */
+ /* Now, find matching route. */
for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
- if (tmp_ri->peer == bgp_def->peer_self
+ if (tmp_ri->peer == bgp->peer_self
&& tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_STATIC)
break;
@@ -1378,6 +1787,57 @@ static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
bgp_info_delete(rn, tmp_ri);
}
+
+
+/* Delete EVPN ES (type-4) route */
+static int delete_evpn_type4_route(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_info *ri;
+ struct bgp_node *rn = NULL; /* rn in esi table */
+ struct bgp_node *global_rn = NULL; /* rn in global table */
+
+ /* First, locate the route node within the ESI.
+ * If it doesn't exist, ther is nothing to do.
+ * Note: there is no RD here.
+ */
+ rn = bgp_node_lookup(es->route_table, (struct prefix *)p);
+ if (!rn)
+ return 0;
+
+ /* Next, locate route node in the global EVPN routing table.
+ * Note that this table is a 2-level tree (RD-level + Prefix-level)
+ */
+ global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)p, &es->prd);
+ if (global_rn) {
+
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi,
+ global_rn, &ri);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (ri)
+ bgp_process(bgp, global_rn, afi, safi);
+ bgp_unlock_node(global_rn);
+ }
+
+ /*
+ * Delete route entry in the ESI route table.
+ * This can just be removed.
+ */
+ delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
+ if (ri)
+ bgp_info_reap(rn, ri);
+ bgp_unlock_node(rn);
+ return 0;
+}
+
/* Delete EVPN type5 route */
static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
{
@@ -1397,7 +1857,7 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
if (!rn)
return 0;
- delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri);
+ delete_evpn_route_entry(bgp_def, afi, safi, rn, &ri);
if (ri)
bgp_process(bgp_def, rn, afi, safi);
bgp_unlock_node(rn);
@@ -1405,32 +1865,6 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
}
/*
- * Delete EVPN route entry. This could be in the VNI route table
- * or the global route table.
- */
-static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
- afi_t afi, safi_t safi, struct bgp_node *rn,
- struct bgp_info **ri)
-{
- struct bgp_info *tmp_ri;
-
- *ri = NULL;
-
- /* Now, find matching route. */
- for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
- if (tmp_ri->peer == bgp->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 route (of type based on prefix) for specified VNI and
* schedule for processing.
*/
@@ -1459,7 +1893,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
(struct prefix *)p, &vpn->prd);
if (global_rn) {
/* Delete route entry in the global EVPN table. */
- delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
/* Schedule for processing - withdraws to peers happen from
* this table.
@@ -1471,7 +1905,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
/* Delete route entry in the VNI route table. This can just be removed.
*/
- delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
if (ri)
bgp_info_reap(rn, ri);
bgp_unlock_node(rn);
@@ -1629,7 +2063,7 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
- delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
if (ri)
bgp_process(bgp, rn, afi, safi);
}
@@ -1670,7 +2104,7 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
- delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
/* Route entry in local table gets deleted immediately. */
if (ri)
@@ -1681,6 +2115,27 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
+ * Delete all routes in per ES route-table
+ */
+static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri, *nextri;
+
+ /* Walk this ES's route table and delete all routes. */
+ for (rn = bgp_table_top(es->route_table); rn;
+ rn = bgp_route_next(rn)) {
+ for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1);
+ ri = nextri) {
+ bgp_info_delete(rn, ri);
+ bgp_info_reap(rn, ri);
+ }
+ }
+
+ return 0;
+}
+
+/*
* Delete all routes in the per-VNI route table.
*/
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
@@ -1723,6 +2178,30 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
return update_all_type2_routes(bgp, vpn);
}
+/* Delete (and withdraw) local routes for specified ES from global and ES table.
+ * Also remove all other routes from the per ES table.
+ * Invoked when ES is deleted.
+ */
+static int delete_routes_for_es(struct bgp *bgp, struct evpnes *es)
+{
+ int ret;
+ char buf[ESI_STR_LEN];
+ struct prefix_evpn p;
+
+ /* Delete and withdraw locally learnt ES route */
+ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip.ipaddr_v4);
+ ret = delete_evpn_type4_route(bgp, es, &p);
+ if (ret) {
+ zlog_err(
+ "%u failed to delete type-4 route for ESI %s",
+ bgp->vrf_id,
+ esi_to_str(&es->esi, buf, sizeof(buf)));
+ }
+
+ /* Delete all routes from per ES table */
+ return delete_all_es_routes(bgp, es);
+}
+
/*
* Delete (and withdraw) local routes for specified VNI from the global
* table and per-VNI table. After this, remove all other routes from
@@ -1785,6 +2264,68 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
+/* Install EVPN route entry in ES */
+static int install_evpn_route_entry_in_es(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p,
+ struct bgp_info *parent_ri)
+{
+ int ret = 0;
+ struct bgp_node *rn = NULL;
+ struct bgp_info *ri = NULL;
+ struct attr *attr_new = NULL;
+
+ /* Create (or fetch) route within the VNI.
+ * NOTE: There is no RD here.
+ */
+ rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* 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, BGP_ROUTE_IMPORTED, 0,
+ parent_ri->peer, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(ri);
+ ri->extra->parent = parent_ri;
+ 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. */
+ ret = evpn_es_route_select_install(bgp, es, rn);
+ return ret;
+}
+
/*
* Install route entry into the VRF routing table and invoke route selection.
*/
@@ -1966,6 +2507,47 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
return ret;
}
+/* Uninstall EVPN route entry from ES route table */
+static int uninstall_evpn_route_entry_in_es(struct bgp *bgp,
+ struct evpnes *es,
+ struct prefix_evpn *p,
+ struct bgp_info *parent_ri)
+{
+ int ret;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+
+ if (!es->route_table)
+ return 0;
+
+ /* Locate route within the ESI.
+ * NOTE: There is no RD here.
+ */
+ rn = bgp_node_lookup(es->route_table, (struct prefix *)p);
+ 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. */
+ ret = evpn_es_route_select_install(bgp, es, rn);
+
+ /* Unlock route node. */
+ bgp_unlock_node(rn);
+
+ return ret;
+}
+
/*
* Uninstall route entry from the VRF routing table and send message
* to zebra, if appropriate.
@@ -2071,6 +2653,22 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Given a prefix, see if it belongs to ES.
+ */
+static int is_prefix_matching_for_es(struct prefix_evpn *p,
+ struct evpnes *es)
+{
+ /* if not an ES route return false */
+ if (p->prefix.route_type != BGP_EVPN_ES_ROUTE)
+ return 0;
+
+ if (memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
* Given a route entry and a VRF, see if this route entry should be
* imported into the VRF i.e., RTs match.
*/
@@ -2203,6 +2801,73 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
+static int install_uninstall_routes_for_es(struct bgp *bgp,
+ struct evpnes *es,
+ int install)
+{
+ int ret;
+ afi_t afi;
+ safi_t safi;
+ char buf[PREFIX_STRLEN];
+ char buf1[ESI_STR_LEN];
+ struct bgp_node *rd_rn, *rn;
+ struct bgp_table *table;
+ struct bgp_info *ri;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /*
+ * 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->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;
+
+ for (ri = rn->info; ri; ri = ri->next) {
+ /*
+ * Consider "valid" remote routes applicable for
+ * this ES.
+ */
+ if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID)
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (!is_prefix_matching_for_es(evp, es))
+ continue;
+
+ if (install)
+ ret = install_evpn_route_entry_in_es(
+ bgp, es, evp, ri);
+ else
+ ret = uninstall_evpn_route_entry_in_es(
+ bgp, es, evp, ri);
+
+ if (ret) {
+ zlog_err(
+ "Failed to %s EVPN %s route in ESI %s",
+ install ? "install"
+ : "uninstall",
+ prefix2str(evp, buf,
+ sizeof(buf)),
+ esi_to_str(&es->esi, buf1,
+ sizeof(buf1)));
+ return ret;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
/*
* Install or uninstall mac-ip routes are appropriate for this
* particular VRF.
@@ -2358,6 +3023,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
return 0;
}
+/* Install any existing remote ES routes applicable for this ES into its routing
+ * table. This is invoked when ES comes up.
+ */
+static int install_routes_for_es(struct bgp *bgp, struct evpnes *es)
+{
+ return install_uninstall_routes_for_es(bgp, es, 1);
+}
+
+
/* Install any existing remote routes applicable for this VRF into VRF RIB. This
* is invoked upon l3vni-add or l3vni import rt change
*/
@@ -2416,6 +3090,32 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
0);
}
+/* Install or unistall route in ES */
+static int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es,
+ afi_t afi, safi_t safi,
+ struct prefix_evpn *evp,
+ struct bgp_info *ri,
+ int install)
+{
+ int ret = 0;
+ char buf[ESI_STR_LEN];
+
+ if (install)
+ ret = install_evpn_route_entry_in_es(bgp, es, evp, ri);
+ else
+ ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, ri);
+
+ if (ret) {
+ zlog_err("%u: Failed to %s EVPN %s route in ESI %s",
+ bgp->vrf_id, install ? "install" : "uninstall",
+ "ES",
+ esi_to_str(&evp->prefix.es_addr.esi, buf,
+ sizeof(buf)));
+ return ret;
+ }
+ return 0;
+}
+
/*
* Install or uninstall route in matching VRFs (list).
*/
@@ -2498,7 +3198,7 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
}
/*
- * Install or uninstall route for appropriate VNIs.
+ * Install or uninstall route for appropriate VNIs/ESIs.
*/
static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct prefix *p, struct bgp_info *ri,
@@ -2511,9 +3211,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
assert(attr);
- /* Only type-2 and type-3 and type-5 are supported currently */
+ /* Only type-2, type-3, type-4 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_ES_ROUTE
|| evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
return 0;
@@ -2525,9 +3226,8 @@ 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/VRFs match and import
- * the route into matching VNIs/VRFs.
- */
+ /* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs
+ * attached to the route */
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt;
uint8_t type, sub_type;
@@ -2535,6 +3235,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct ecommunity_val eval_tmp;
struct irt_node *irt; /* import rt for l2vni */
struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
+ struct evpnes *es;
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
@@ -2545,51 +3246,79 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
- /* Import route into matching l2-vnis (type-2/type-3 routes go
- * into l2vni table)
+ /*
+ * macip routes (type-2) are imported into VNI and VRF tables.
+ * IMET route is imported into VNI table.
+ * prefix routes are imported into VRF table.
*/
- irt = lookup_import_rt(bgp, eval);
- if (irt)
- install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
- irt->vnis, import);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IMET_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
- /* 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)
- 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);
+ irt = lookup_import_rt(bgp, eval);
+ if (irt)
+ install_uninstall_route_in_vnis(bgp, afi, safi,
+ evp, ri,
+ irt->vnis,
+ import);
+
+ vrf_irt = lookup_vrf_import_rt(eval);
+ if (vrf_irt)
+ 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)
+ install_uninstall_route_in_vnis(bgp, afi, safi,
+ evp, ri,
+ irt->vnis,
+ import);
+ if (vrf_irt)
+ install_uninstall_route_in_vrfs(bgp, afi, safi,
+ evp, ri,
+ vrf_irt->vrfs,
+ import);
+ }
+
+ /* es route is imported into the es table */
+ if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) {
+
+ /* we will match based on the entire esi to avoid
+ * imoort of an es route for esi2 into esi1
+ */
+ es = bgp_evpn_lookup_es(bgp, &evp->prefix.es_addr.esi);
+ if (es && is_es_local(es))
+ install_uninstall_route_in_es(bgp, es,
+ afi, safi,
+ evp, ri, import);
}
- if (irt)
- install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
- irt->vnis, import);
- if (vrf_irt)
- 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 */
+/*
+ * 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 */
@@ -2601,8 +3330,10 @@ static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
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 */
+/*
+ * 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 */
@@ -2750,7 +3481,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
(struct prefix *)&p, &vpn->prd);
if (global_rn) {
/* Delete route entry in the global EVPN table. */
- delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+ delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
/* Schedule for processing - withdraws to peers happen from
* this table.
@@ -2853,7 +3584,7 @@ static int process_type2_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_2_ROUTE_PREFIXLEN;
+ p.prefixlen = EVPN_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
/* Copy Ethernet Seg Identifier */
@@ -2972,7 +3703,7 @@ static int process_type3_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_3_ROUTE_PREFIXLEN;
+ p.prefixlen = EVPN_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
/* Copy Ethernet Tag */
@@ -3005,6 +3736,66 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
}
/*
+ * Process received EVPN type-4 route (advertise or withdraw).
+ */
+static int process_type4_route(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ int ret;
+ esi_t esi;
+ uint8_t ipaddr_len;
+ struct in_addr vtep_ip;
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+
+ /* Type-4 route should be either 23 or 35 bytes
+ * RD (8), ESI (10), ip-len (1), ip (4 or 16)
+ */
+ if (psize != 23 && psize != 35) {
+ zlog_err("%u:%s - Rx EVPN Type-4 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* get the ESI */
+ memcpy(&esi, pfx, ESI_BYTES);
+ pfx += ESI_BYTES;
+
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len == IPV4_MAX_BITLEN) {
+ memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN);
+ } else {
+ zlog_err(
+ "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ build_evpn_type4_prefix(&p, &esi, vtep_ip);
+ /* Process the route. */
+ if (attr) {
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ } else {
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ }
+ return ret;
+}
+
+
+/*
* Process received EVPN type-5 route (advertise or withdraw).
*/
static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
@@ -3039,7 +3830,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.prefixlen = EVPN_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
/* Additional information outside of prefix - ESI and GW IP */
@@ -3695,6 +4486,7 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
{
char buf1[ETHER_ADDR_STRLEN];
char buf2[PREFIX2STR_BUFFER];
+ char buf3[ESI_STR_LEN];
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
@@ -3736,6 +4528,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
is_evpn_prefix_ipaddr_v4(p)
? inet_ntoa(p->prefix.prefix_addr.ip.ipaddr_v4)
: inet6_ntoa(p->prefix.prefix_addr.ip.ipaddr_v6));
+ } else if (p->prefix.route_type == BGP_EVPN_ES_ROUTE) {
+ snprintf(buf, len, "[%d]:[%s]:[%d]:[%s]",
+ p->prefix.route_type,
+ esi_to_str(&p->prefix.es_addr.esi, buf3, sizeof(buf3)),
+ is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN
+ : IPV6_MAX_BITLEN,
+ inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",
@@ -3801,6 +4600,15 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4);
break;
+ case BGP_EVPN_ES_ROUTE:
+ stream_putc(s, 23); /* TODO: length: assumes ipv4 VTEP */
+ stream_put(s, prd->val, 8); /* RD */
+ stream_put(s, evp->prefix.es_addr.esi.val, 10); /* ESI */
+ stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
+ /* VTEP IP */
+ stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4);
+ break;
+
case BGP_EVPN_IP_PREFIX_ROUTE:
/* TODO: AddPath support. */
evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr);
@@ -3885,6 +4693,17 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
}
break;
+ case BGP_EVPN_ES_ROUTE:
+ if (process_type4_route(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ zlog_err(
+ "%u:%s - Error in processing EVPN type-4 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+ break;
+
case BGP_EVPN_IP_PREFIX_ROUTE:
if (process_type5_route(peer, afi, safi, attr, pnt,
psize, addpath_id, withdraw)) {
@@ -4148,7 +4967,81 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
- * Import route into matching VNI(s).
+ * Lookup local ES.
+ */
+struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi)
+{
+ struct evpnes *es;
+ struct evpnes tmp;
+
+ memset(&tmp, 0, sizeof(struct evpnes));
+ memcpy(&tmp.esi, esi, sizeof(esi_t));
+ es = hash_lookup(bgp->esihash, &tmp);
+ return es;
+}
+
+/*
+ * Create a new local es - invoked upon zebra notification.
+ */
+struct evpnes *bgp_evpn_es_new(struct bgp *bgp,
+ esi_t *esi,
+ struct ipaddr *originator_ip)
+{
+ char buf[100];
+ struct evpnes *es;
+
+ if (!bgp)
+ return NULL;
+
+ es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct evpnes));
+ if (!es)
+ return NULL;
+
+ /* set the ESI and originator_ip */
+ memcpy(&es->esi, esi, sizeof(esi_t));
+ memcpy(&es->originator_ip, originator_ip, sizeof(struct ipaddr));
+
+ /* Initialise the VTEP list */
+ es->vtep_list = list_new();
+ es->vtep_list->cmp = (int (*)(void *, void *))evpn_vtep_ip_cmp;
+
+ /* auto derive RD for this es */
+ bf_assign_index(bm->rd_idspace, es->rd_id);
+ es->prd.family = AF_UNSPEC;
+ es->prd.prefixlen = 64;
+ sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), es->rd_id);
+ (void)str2prefix_rd(buf, &es->prd);
+
+ /* Initialize the ES route table */
+ es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
+
+ /* Add to hash */
+ if (!hash_get(bgp->esihash, es, hash_alloc_intern)) {
+ XFREE(MTYPE_BGP_EVPN_ES, es);
+ return NULL;
+ }
+
+ QOBJ_REG(es, evpnes);
+ return es;
+}
+
+/*
+ * Free a given ES -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es)
+{
+ list_delete_and_null(&es->vtep_list);
+ bgp_table_unlock(es->route_table);
+ bf_release_index(bm->rd_idspace, es->rd_id);
+ hash_release(bgp->esihash, es);
+ QOBJ_UNREG(es);
+ XFREE(MTYPE_BGP_EVPN_ES, es);
+}
+
+/*
+ * Import evpn route from global table to VNI/VRF/ESI.
*/
int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct prefix *p, struct bgp_info *ri)
@@ -4157,7 +5050,7 @@ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
}
/*
- * Unimport route from matching VNI(s).
+ * Unimport evpn route from VNI/VRF/ESI.
*/
int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct prefix *p, struct bgp_info *ri)
@@ -4602,6 +5495,84 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
}
/*
+ * bgp_evpn_local_es_del
+ */
+int bgp_evpn_local_es_del(struct bgp *bgp,
+ esi_t *esi,
+ struct ipaddr *originator_ip)
+{
+ char buf[ESI_STR_LEN];
+ struct evpnes *es = NULL;
+
+ if (!bgp->esihash) {
+ zlog_err("%u: ESI hash not yet created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* Lookup ESI hash - should exist. */
+ es = bgp_evpn_lookup_es(bgp, esi);
+ if (!es) {
+ zlog_warn("%u: ESI hash entry for ESI %s at Local ES DEL",
+ bgp->vrf_id,
+ esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ /* Delete all local EVPN ES routes from ESI table
+ * and schedule for processing (to withdraw from peers))
+ */
+ delete_routes_for_es(bgp, es);
+
+ /* free the hash entry */
+ bgp_evpn_es_free(bgp, es);
+
+ return 0;
+}
+
+/*
+ * bgp_evpn_local_es_add
+ */
+int bgp_evpn_local_es_add(struct bgp *bgp,
+ esi_t *esi,
+ struct ipaddr *originator_ip)
+{
+ char buf[ESI_STR_LEN];
+ struct evpnes *es = NULL;
+ struct prefix_evpn p;
+
+ if (!bgp->esihash) {
+ zlog_err("%u: ESI hash not yet created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* create the new es */
+ es = bgp_evpn_lookup_es(bgp, esi);
+ if (!es) {
+ es = bgp_evpn_es_new(bgp, esi, originator_ip);
+ if (!es) {
+ zlog_err(
+ "%u: Failed to allocate ES entry for ESI %s - at Local ES Add",
+ bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+ }
+ UNSET_FLAG(es->flags, EVPNES_REMOTE);
+ SET_FLAG(es->flags, EVPNES_LOCAL);
+
+ build_evpn_type4_prefix(&p, esi, originator_ip->ipaddr_v4);
+ if (update_evpn_type4_route(bgp, es, &p)) {
+ zlog_err("%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ /* import all remote ES routes in th ES table */
+ install_routes_for_es(bgp, es);
+
+ return 0;
+}
+
+/*
* Cleanup EVPN information on disable - Need to delete and withdraw
* EVPN routes from peers.
*/
@@ -4631,6 +5602,9 @@ void bgp_evpn_cleanup(struct bgp *bgp)
if (bgp->vnihash)
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
+ if (bgp->esihash)
+ hash_free(bgp->esihash);
+ bgp->esihash = NULL;
if (bgp->vrf_import_rtl)
list_delete_and_null(&bgp->vrf_import_rtl);
if (bgp->vrf_export_rtl)
@@ -4649,6 +5623,9 @@ void bgp_evpn_init(struct bgp *bgp)
{
bgp->vnihash =
hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
+ bgp->esihash =
+ hash_create(esi_hash_keymake, esi_cmp,
+ "BGP EVPN Local ESI Hash");
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index 8e954159c0..91d4c9fac4 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -141,6 +141,10 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id);
+extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
+ struct ipaddr *originator_ip);
+extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi,
+ struct ipaddr *originator_ip);
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 39a08e7036..bf6a24dea6 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -31,9 +31,7 @@
#define RT_ADDRSTRLEN 28
/* 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
+#define EVPN_ROUTE_PREFIXLEN 224
/* EVPN route types. */
typedef enum {
@@ -98,6 +96,42 @@ struct bgpevpn {
DECLARE_QOBJ_TYPE(bgpevpn)
+struct evpnes {
+
+ /* Ethernet Segment Identifier */
+ esi_t esi;
+
+ /* es flags */
+ uint16_t flags;
+#define EVPNES_LOCAL 0x01
+#define EVPNES_REMOTE 0x02
+
+ /*
+ * Id for deriving the RD
+ * automatically for this ESI
+ */
+ uint16_t rd_id;
+
+ /* RD for this VNI. */
+ struct prefix_rd prd;
+
+ /* originator ip address */
+ struct ipaddr originator_ip;
+
+ /* list of VTEPs in the same site */
+ struct list *vtep_list;
+
+ /*
+ * Route table for EVPN routes for
+ * this ESI. - type4 routes
+ */
+ struct bgp_table *route_table;
+
+ QOBJ_FIELDS
+};
+
+DECLARE_QOBJ_TYPE(evpnes)
+
/* Mapping of Import RT to VNIs.
* The Import RTs of all VNIs are maintained in a hash table with each
* RT linking to all VNIs that will import routes matching this RT.
@@ -238,6 +272,15 @@ static inline int is_vni_param_configured(struct bgpevpn *vpn)
|| is_export_rt_configured(vpn));
}
+static inline void encode_es_rt_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *mac)
+{
+ memset(eval, 0, sizeof(struct ecommunity_val));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT;
+ memcpy(&eval->val[2], mac, ETH_ALEN);
+}
+
static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
struct ethaddr *rmac)
{
@@ -326,7 +369,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
{
memset(p, 0, sizeof(struct prefix_evpn));
p->family = AF_EVPN;
- p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN);
p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE;
@@ -352,7 +395,7 @@ static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
memset(evp, 0, sizeof(struct prefix_evpn));
evp->family = AF_EVPN;
- evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
+ evp->prefixlen = EVPN_ROUTE_PREFIXLEN;
evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen;
evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type;
@@ -364,12 +407,26 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
{
memset(p, 0, sizeof(struct prefix_evpn));
p->family = AF_EVPN;
- p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
p->prefix.imet_addr.ip.ipa_type = IPADDR_V4;
p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip;
}
+static inline void build_evpn_type4_prefix(struct prefix_evpn *p,
+ esi_t *esi,
+ struct in_addr originator_ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_ES_ROUTE;
+ p->prefix.es_addr.ip_prefix_length = IPV4_MAX_BITLEN;
+ p->prefix.es_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.es_addr.ip.ipaddr_v4 = originator_ip;
+ memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t));
+}
+
static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
safi_t safi)
{
@@ -384,6 +441,21 @@ static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
return 0;
}
+static inline void es_get_system_mac(esi_t *esi,
+ struct ethaddr *mac)
+{
+ /*
+ * for type-1 and type-3 ESIs,
+ * the system mac starts at val[1]
+ */
+ memcpy(mac, &esi->val[1], ETH_ALEN);
+}
+
+static inline int is_es_local(struct evpnes *es)
+{
+ return CHECK_FLAG(es->flags, EVPNES_LOCAL) ? 1 : 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);
@@ -417,4 +489,8 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
+extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi);
+extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi,
+ struct ipaddr *originator_ip);
+extern void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es);
#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 12f18016b6..f87196426c 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -328,6 +328,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
vty_out(vty,
"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
vty_out(vty, "%s", ri_header);
}
@@ -409,6 +410,47 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
json_object_object_add(json, "exportRts", json_export_rtl);
}
+static void display_es(struct vty *vty, struct evpnes *es, json_object *json)
+{
+ struct in_addr *vtep;
+ char buf[ESI_STR_LEN];
+ char buf1[RD_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct listnode *node = NULL;
+ json_object *json_vteps = NULL;
+
+ if (json) {
+ json_vteps = json_object_new_array();
+ json_object_string_add(json, "esi",
+ esi_to_str(&es->esi, buf, sizeof(buf)));
+ json_object_string_add(json, "rd",
+ prefix_rd2str(&es->prd, buf1,
+ sizeof(buf1)));
+ json_object_string_add(
+ json, "originatorIp",
+ ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
+ if (es->vtep_list) {
+ for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
+ json_object_array_add(
+ json_vteps, json_object_new_string(
+ inet_ntoa(*vtep)));
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ } else {
+ vty_out(vty, "ESI: %s\n",
+ esi_to_str(&es->esi, buf, sizeof(buf)));
+ vty_out(vty, " RD: %s\n", prefix_rd2str(&es->prd, buf1,
+ sizeof(buf1)));
+ vty_out(vty, " Originator-IP: %s\n",
+ ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
+ if (es->vtep_list) {
+ vty_out(vty, " VTEP List:\n");
+ for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
+ vty_out(vty, " %s\n", inet_ntoa(*vtep));
+ }
+ }
+}
+
static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
{
char buf1[RD_ADDRSTRLEN];
@@ -487,6 +529,88 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
json_object_object_add(json, "exportRts", json_export_rtl);
}
+static void show_esi_routes(struct bgp *bgp,
+ struct evpnes *es,
+ struct vty *vty,
+ json_object *json)
+{
+ int header = 1;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ uint32_t prefix_cnt, path_cnt;
+ uint64_t tbl_ver;
+
+ prefix_cnt = path_cnt = 0;
+
+ tbl_ver = es->route_table->version;
+ for (rn = bgp_table_top(es->route_table); rn;
+ rn = bgp_route_next(rn)) {
+ int add_prefix_to_json = 0;
+ char prefix_str[BUFSIZ];
+ json_object *json_paths = NULL;
+ json_object *json_prefix = NULL;
+
+ bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
+ sizeof(prefix_str));
+
+ if (json)
+ json_prefix = json_object_new_object();
+
+ if (rn->info) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver, json);
+ header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* 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) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ add_prefix_to_json = 1;
+ }
+
+ if (json && add_prefix_to_json) {
+ json_object_string_add(json_prefix, "prefix",
+ prefix_str);
+ json_object_int_add(json_prefix, "prefixLen",
+ rn->p.prefixlen);
+ json_object_object_add(json_prefix, "paths",
+ json_paths);
+ json_object_object_add(json, prefix_str, json_prefix);
+ }
+ }
+
+ if (json) {
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes exist for this ESI");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)\n",
+ 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)
@@ -575,7 +699,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
vty_out(vty, "No EVPN prefixes %sexist for this VNI",
type ? "(of requested type) " : "");
else
- vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s",
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
prefix_cnt, path_cnt,
type ? " (of requested type)" : "");
}
@@ -707,6 +831,48 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
}
}
+static void show_es_entry(struct hash_backet *backet, void *args[])
+{
+ char buf[ESI_STR_LEN];
+ char buf1[RD_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct in_addr *vtep = NULL;
+ struct vty *vty = args[0];
+ json_object *json = args[1];
+ json_object *json_vteps = NULL;
+ struct listnode *node = NULL;
+ struct evpnes *es = (struct evpnes *)backet->data;
+
+ if (json) {
+ json_vteps = json_object_new_array();
+ json_object_string_add(json, "esi",
+ esi_to_str(&es->esi, buf, sizeof(buf)));
+ json_object_string_add(json, "type",
+ is_es_local(es) ? "Local" : "Remote");
+ json_object_string_add(json, "rd",
+ prefix_rd2str(&es->prd, buf1,
+ sizeof(buf1)));
+ json_object_string_add(
+ json, "originatorIp",
+ ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
+ if (es->vtep_list) {
+ for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
+ json_object_array_add(json_vteps,
+ json_object_new_string(
+ inet_ntoa(*vtep)));
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ } else {
+ vty_out(vty, "%-30s %-6s %-21s %-15s %-6d\n",
+ esi_to_str(&es->esi, buf, sizeof(buf)),
+ is_es_local(es) ? "Local" : "Remote",
+ prefix_rd2str(&es->prd, buf1, sizeof(buf1)),
+ ipaddr2str(&es->originator_ip, buf2,
+ sizeof(buf2)),
+ es->vtep_list ? listcount(es->vtep_list) : 0);
+ }
+}
+
static void show_vni_entry(struct hash_backet *backet, void *args[])
{
struct vty *vty;
@@ -1969,6 +2135,23 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
}
}
+/* Disaplay EVPN routes for a ESI - VTY handler */
+static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp,
+ esi_t *esi, json_object *json)
+{
+ struct evpnes *es = NULL;
+
+ /* locate the ES */
+ es = bgp_evpn_lookup_es(bgp, esi);
+ if (!es) {
+ if (!json)
+ vty_out(vty, "ESI not found\n");
+ return;
+ }
+
+ show_esi_routes(bgp, es, vty, json);
+}
+
/*
* Display EVPN routes for a VNI - vty handler.
* If 'type' is non-zero, only routes matching that type are shown.
@@ -2318,6 +2501,43 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
}
}
+/* Display specific ES */
+static void evpn_show_es(struct vty *vty, struct bgp *bgp, esi_t *esi,
+ json_object *json)
+{
+ struct evpnes *es = NULL;
+
+ es = bgp_evpn_lookup_es(bgp, esi);
+ if (es) {
+ display_es(vty, es, json);
+ } else {
+ if (json) {
+ vty_out(vty, "{}\n");
+ } else {
+ vty_out(vty, "ESI not found\n");
+ return;
+ }
+ }
+}
+
+/* Display all ESs */
+static void evpn_show_all_es(struct vty *vty, struct bgp *bgp,
+ json_object *json)
+{
+ void *args[2];
+
+ if (!json)
+ vty_out(vty, "%-30s %-6s %-21s %-15s %-6s\n",
+ "ESI", "Type", "RD", "Originator-IP", "#VTEPs");
+
+ /* print all ESs */
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(bgp->esihash,
+ (void (*)(struct hash_backet *, void *))show_es_entry,
+ args);
+}
+
/*
* Display specified VNI (vty handler)
*/
@@ -3027,6 +3247,58 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
return CMD_SUCCESS;
}
+/* Disaply ES */
+DEFUN(show_bgp_l2vpn_evpn_es,
+ show_bgp_l2vpn_evpn_es_cmd,
+ "show bgp l2vpn evpn es [ESI] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "ethernet-Segment\n"
+ "Ethernet-Segment Identifier\n")
+{
+ int idx = 0;
+ uint8_t uj = 0;
+ esi_t esi = {0};
+ json_object *json = NULL;
+ struct bgp *bgp = NULL;
+
+ uj = use_json(argc, argv);
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ if ((uj && argc == ((idx + 1) + 2)) ||
+ (!uj && argc == (idx + 1) + 1)) {
+
+ /* show all ESs */
+ evpn_show_all_es(vty, bgp, json);
+ } else {
+
+ /* show a specific ES */
+
+ /* get the ESI - ESI-ID is at argv[5] */
+ if (!str_to_esi(argv[idx + 2]->arg, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ evpn_show_es(vty, bgp, &esi, 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 neighbor summary.
*/
@@ -3056,7 +3328,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary,
*/
DEFUN(show_bgp_l2vpn_evpn_route,
show_bgp_l2vpn_evpn_route_cmd,
- "show bgp l2vpn evpn route [type <macip|multicast|prefix>] [json]",
+ "show bgp l2vpn evpn route [type <macip|multicast|es|prefix>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
@@ -3065,7 +3337,8 @@ DEFUN(show_bgp_l2vpn_evpn_route,
"Specify Route type\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
- "Prefix route\n"
+ "Ethernet Segment (type-4) route \n"
+ "Prefix (type-5 )route\n"
JSON_STR)
{
struct bgp *bgp;
@@ -3090,6 +3363,8 @@ DEFUN(show_bgp_l2vpn_evpn_route,
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
+ type = BGP_EVPN_ES_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
type = BGP_EVPN_IP_PREFIX_ROUTE;
else
@@ -3111,7 +3386,7 @@ DEFUN(show_bgp_l2vpn_evpn_route,
*/
DEFUN(show_bgp_l2vpn_evpn_route_rd,
show_bgp_l2vpn_evpn_route_rd_cmd,
- "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|prefix>] [json]",
+ "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|es|prefix>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
@@ -3122,6 +3397,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
"Specify Route type\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
+ "Ethernet Segment route\n"
"Prefix route\n"
JSON_STR)
{
@@ -3255,6 +3531,50 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
return CMD_SUCCESS;
}
+/* Display per ESI routing table */
+DEFUN(show_bgp_l2vpn_evpn_route_esi,
+ show_bgp_l2vpn_evpn_route_esi_cmd,
+ "show bgp l2vpn evpn route esi ESI [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
+ "Ethernet Segment Identifier\n"
+ "ESI ID\n"
+ JSON_STR)
+{
+ int uj = 0;
+ esi_t esi = {0};
+ struct bgp *bgp = NULL;
+ json_object *json = NULL;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ /* get the ESI - ESI-ID is at argv[6] */
+ if (!str_to_esi(argv[6]->arg, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_show_routes_esi(vty, bgp, &esi, 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 per-VNI EVPN routing table.
*/
@@ -3583,6 +3903,78 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt,
}
#if defined(HAVE_CUMULUS)
+DEFUN(test_adv_evpn_type4_route,
+ test_adv_evpn_type4_route_cmd,
+ "advertise es ESI",
+ "Advertise EVPN ES route\n"
+ "Ethernet-segment\n"
+ "Ethernet-Segment Identifier\n")
+{
+ int ret = 0;
+ esi_t esi;
+ struct bgp *bgp;
+ struct ipaddr vtep_ip;
+
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "%%Default BGP instance not yet created\n");
+ return CMD_WARNING;
+ }
+
+ if (!str_to_esi(argv[2]->arg, &esi)) {
+ vty_out(vty, "%%Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ vtep_ip.ipa_type = IPADDR_V4;
+ vtep_ip.ipaddr_v4 = bgp->router_id;
+
+ ret = bgp_evpn_local_es_add(bgp, &esi, &vtep_ip);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to EVPN advertise type-4 route\n");
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(test_withdraw_evpn_type4_route,
+ test_withdraw_evpn_type4_route_cmd,
+ "withdraw es ESI",
+ "Advertise EVPN ES route\n"
+ "Ethernet-segment\n"
+ "Ethernet-Segment Identifier\n")
+{
+ int ret = 0;
+ esi_t esi;
+ struct bgp *bgp;
+ struct ipaddr vtep_ip;
+
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "%%Default BGP instance not yet created\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp->peer_self) {
+ vty_out(vty, "%%BGP instance doesnt have self peer\n");
+ return CMD_WARNING;
+ }
+
+ if (!str_to_esi(argv[2]->arg, &esi)) {
+ vty_out(vty, "%%Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ vtep_ip.ipa_type = IPADDR_V4;
+ vtep_ip.ipaddr_v4 = bgp->router_id;
+ ret = bgp_evpn_local_es_del(bgp, &esi, &vtep_ip);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to withdraw EVPN type-4 route\n");
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd,
"show bgp evpn vni [(1-16777215)]", SHOW_STR BGP_STR EVPN_HELP_STR
"Show VNI\n"
@@ -4543,12 +4935,18 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd);
+ /* test commands */
+ install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd);
+ install_element(BGP_EVPN_NODE, &test_withdraw_evpn_type4_route_cmd);
+
/* "show bgp l2vpn evpn" commands. */
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_esi_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 4669fad3b7..3e4dfb11ad 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -116,6 +116,8 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string")
DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP Ip")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI 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 6fa3040a19..03715f5621 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -111,6 +111,9 @@ DECLARE_MTYPE(LCOMMUNITY)
DECLARE_MTYPE(LCOMMUNITY_STR)
DECLARE_MTYPE(LCOMMUNITY_VAL)
+DECLARE_MTYPE(BGP_EVPN_ES)
+DECLARE_MTYPE(BGP_EVPN_ES_VTEP)
+
DECLARE_MTYPE(BGP_EVPN)
DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT)
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 37d06c1e53..84a959d0e8 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -723,7 +723,9 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol, bgp->vrf_id, NULL);
+ ainfo->ai_protocol, bgp->vrf_id,
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF ?
+ bgp->name : NULL));
if (bgpd_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (sock < 0) {
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 390bdaeca3..591af0f8cf 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2106,19 +2106,18 @@ int bgp_zebra_has_route_changed(struct bgp_node *rn, struct bgp_info *selected)
{
struct bgp_info *mpinfo;
- /* If this is multipath, check all selected paths for any nexthop change
- * or
- * attribute change. Some attribute changes (e.g., community) aren't of
- * relevance to the RIB, but we'll update zebra to ensure we handle the
- * case of BGP nexthop change. This is the behavior when the best path
- * has
- * an attribute change anyway.
+ /* If this is multipath, check all selected paths for any nexthop
+ * change or attribute change. Some attribute changes (e.g., community)
+ * aren't of relevance to the RIB, but we'll update zebra to ensure
+ * we handle the case of BGP nexthop change. This is the behavior
+ * when the best path has an attribute change anyway.
*/
if (CHECK_FLAG(selected->flags, BGP_INFO_IGP_CHANGED)
|| CHECK_FLAG(selected->flags, BGP_INFO_MULTIPATH_CHG))
return 1;
- /* If this is multipath, check all selected paths for any nexthop change
+ /*
+ * If this is multipath, check all selected paths for any nexthop change
*/
for (mpinfo = bgp_info_mpath_first(selected); mpinfo;
mpinfo = bgp_info_mpath_next(mpinfo)) {
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 257adda3f3..833de64c94 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1991,7 +1991,11 @@ DEFUN (no_bgp_fast_external_failover,
}
/* "bgp enforce-first-as" configuration. */
-DEFUN (bgp_enforce_first_as,
+#if defined(VERSION_TYPE_DEV) && CONFDATE > 20180517
+CPP_NOTICE("bgpd: remove deprecated '[no] bgp enforce-first-as' commands")
+#endif
+
+DEFUN_DEPRECATED (bgp_enforce_first_as,
bgp_enforce_first_as_cmd,
"bgp enforce-first-as",
BGP_STR
@@ -1999,12 +2003,11 @@ DEFUN (bgp_enforce_first_as,
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
- bgp_clear_star_soft_in(vty, bgp->name);
return CMD_SUCCESS;
}
-DEFUN (no_bgp_enforce_first_as,
+DEFUN_DEPRECATED (no_bgp_enforce_first_as,
no_bgp_enforce_first_as_cmd,
"no bgp enforce-first-as",
NO_STR
@@ -2013,7 +2016,6 @@ DEFUN (no_bgp_enforce_first_as,
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
- bgp_clear_star_soft_in(vty, bgp->name);
return CMD_SUCCESS;
}
@@ -3449,7 +3451,7 @@ ALIAS_HIDDEN(no_neighbor_set_peer_group, no_neighbor_set_peer_group_hidden_cmd,
"Peer-group name\n")
static int peer_flag_modify_vty(struct vty *vty, const char *ip_str,
- uint16_t flag, int set)
+ uint32_t flag, int set)
{
int ret;
struct peer *peer;
@@ -3481,13 +3483,13 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str,
return bgp_vty_return(vty, ret);
}
-static int peer_flag_set_vty(struct vty *vty, const char *ip_str, uint16_t flag)
+static int peer_flag_set_vty(struct vty *vty, const char *ip_str, uint32_t flag)
{
return peer_flag_modify_vty(vty, ip_str, flag, 1);
}
static int peer_flag_unset_vty(struct vty *vty, const char *ip_str,
- uint16_t flag)
+ uint32_t flag)
{
return peer_flag_modify_vty(vty, ip_str, flag, 0);
}
@@ -4584,6 +4586,36 @@ DEFUN (no_neighbor_disable_connected_check,
PEER_FLAG_DISABLE_CONNECTED_CHECK);
}
+
+/* enforce-first-as */
+DEFUN (neighbor_enforce_first_as,
+ neighbor_enforce_first_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> enforce-first-as",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enforce the first AS for EBGP routes\n")
+{
+ int idx_peer = 1;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_ENFORCE_FIRST_AS);
+}
+
+DEFUN (no_neighbor_enforce_first_as,
+ no_neighbor_enforce_first_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> enforce-first-as",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enforce the first AS for EBGP routes\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_ENFORCE_FIRST_AS);
+}
+
+
DEFUN (neighbor_description,
neighbor_description_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> description LINE...",
@@ -12984,6 +13016,10 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &neighbor_disable_connected_check_cmd);
install_element(BGP_NODE, &no_neighbor_disable_connected_check_cmd);
+ /* "neighbor enforce-first-as" commands. */
+ install_element(BGP_NODE, &neighbor_enforce_first_as_cmd);
+ install_element(BGP_NODE, &no_neighbor_enforce_first_as_cmd);
+
/* "neighbor description" commands. */
install_element(BGP_NODE, &neighbor_description_cmd);
install_element(BGP_NODE, &no_neighbor_description_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 1cd51cb6f4..2d808a6ffb 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -2249,6 +2249,40 @@ static void bgp_zebra_connected(struct zclient *zclient)
*/
}
+static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ esi_t esi;
+ struct bgp *bgp = NULL;
+ struct stream *s = NULL;
+ char buf[ESI_STR_LEN];
+ char buf1[INET6_ADDRSTRLEN];
+ struct ipaddr originator_ip;
+
+ memset(&esi, 0, sizeof(esi_t));
+ memset(&originator_ip, 0, sizeof(struct ipaddr));
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+ stream_get(&originator_ip, s, sizeof(struct ipaddr));
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx %s ESI %s originator-ip %s",
+ (cmd == ZEBRA_LOCAL_ES_ADD) ? "add" : "del",
+ esi_to_str(&esi, buf, sizeof(buf)),
+ ipaddr2str(&originator_ip, buf1, sizeof(buf1)));
+
+ if (cmd == ZEBRA_LOCAL_ES_ADD)
+ bgp_evpn_local_es_add(bgp, &esi, &originator_ip);
+ else
+ bgp_evpn_local_es_del(bgp, &esi, &originator_ip);
+ return 0;
+}
+
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
@@ -2484,6 +2518,8 @@ void bgp_zebra_init(struct thread_master *master)
zclient->nexthop_update = bgp_read_nexthop_update;
zclient->import_check_update = bgp_read_import_check_update;
zclient->fec_update = bgp_read_fec_update;
+ zclient->local_es_add = bgp_zebra_process_local_es;
+ zclient->local_es_del = bgp_zebra_process_local_es;
zclient->local_vni_add = bgp_zebra_process_local_vni;
zclient->local_vni_del = bgp_zebra_process_local_vni;
zclient->local_macip_add = bgp_zebra_process_local_macip;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 69297cd3e4..cc09d4991d 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -109,12 +109,7 @@ extern struct zclient *zclient;
static int bgp_check_main_socket(bool create, struct bgp *bgp)
{
static int bgp_server_main_created;
- struct listnode *bgpnode, *nbgpnode;
- struct bgp *bgp_temp;
- if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF &&
- vrf_is_mapped_on_netns(bgp->vrf_id))
- return 0;
if (create == true) {
if (bgp_server_main_created)
return 0;
@@ -125,18 +120,6 @@ static int bgp_check_main_socket(bool create, struct bgp *bgp)
}
if (!bgp_server_main_created)
return 0;
- /* only delete socket on some cases */
- for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) {
- /* do not count with current bgp */
- if (bgp_temp == bgp)
- continue;
- /* if other instance non VRF, do not delete socket */
- if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
- return 0;
- /* vrf lite, do not delete socket */
- if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id))
- return 0;
- }
bgp_close();
bgp_server_main_created = 0;
return 0;
@@ -1391,6 +1374,12 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer)
if (!peer->conf_if)
return;
+ /*
+ * Our peer structure is stored in the bgp->peerhash
+ * release it before we modify anything.
+ */
+ hash_release(peer->bgp->peerhash, peer);
+
prev_family = peer->su.sa.sa_family;
if ((ifp = if_lookup_by_name(peer->conf_if, peer->bgp->vrf_id))) {
peer->ifp = ifp;
@@ -1429,8 +1418,9 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer)
memset(&peer->su.sin6.sin6_addr, 0, sizeof(struct in6_addr));
}
- /* Since our su changed we need to del/add peer to the peerhash */
- hash_release(peer->bgp->peerhash, peer);
+ /*
+ * Since our su changed we need to del/add peer to the peerhash
+ */
hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
}
@@ -3079,17 +3069,16 @@ int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
/* Create BGP server socket, if listen mode not disabled */
if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
return 0;
- if (bgp->name && bgp->inst_type == BGP_INSTANCE_TYPE_VRF && vrf) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
/*
* suppress vrf socket
*/
if (create == FALSE) {
- if (vrf_is_mapped_on_netns(vrf->vrf_id))
- bgp_close_vrf_socket(bgp);
- else
- ret = bgp_check_main_socket(create, bgp);
- return ret;
+ bgp_close_vrf_socket(bgp);
+ return 0;
}
+ if (vrf == NULL)
+ return BGP_ERR_INVALID_VALUE;
/* do nothing
* if vrf_id did not change
*/
@@ -3104,21 +3093,12 @@ int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
*/
if (vrf->vrf_id == VRF_UNKNOWN)
return 0;
- /* if BGP VRF instance requested
- * if backend is NETNS, create BGP server socket in the NETNS
- */
- if (vrf_is_mapped_on_netns(bgp->vrf_id)) {
- ret = bgp_socket(bgp, bm->port, bm->address);
- if (ret < 0)
- return BGP_ERR_INVALID_VALUE;
- return 0;
- }
- }
- /* if BGP VRF instance requested or VRF lite backend
- * if BGP non VRF instance, create it
- * if not already done
- */
- return bgp_check_main_socket(create, bgp);
+ ret = bgp_socket(bgp, bm->port, bm->address);
+ if (ret < 0)
+ return BGP_ERR_INVALID_VALUE;
+ return 0;
+ } else
+ return bgp_check_main_socket(create, bgp);
}
/* Called from VTY commands. */
@@ -3495,16 +3475,7 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
struct listnode *bgpnode, *nbgpnode;
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
- /* Skip VRFs Lite only, this function will not be
- * invoked without an instance
- * when examining VRFs.
- */
- if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
- && !vrf_is_mapped_on_netns(bgp->vrf_id))
- continue;
-
peer = hash_lookup(bgp->peerhash, &tmp_peer);
-
if (peer)
break;
}
@@ -3805,6 +3776,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
{PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset},
{PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset},
{PEER_FLAG_CAPABILITY_ENHE, 0, peer_change_reset},
+ {PEER_FLAG_ENFORCE_FIRST_AS, 0, peer_change_reset_in},
{0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = {
@@ -6747,6 +6719,14 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
}
}
+ /* enforce-first-as */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_ENFORCE_FIRST_AS)) {
+ if (!peer_group_active(peer)
+ || !CHECK_FLAG(g_peer->flags, PEER_FLAG_ENFORCE_FIRST_AS)) {
+ vty_out(vty, " neighbor %s enforce-first-as\n", addr);
+ }
+ }
+
/* update-source */
if (peer->update_if) {
if (!peer_group_active(peer) || !g_peer->update_if
@@ -7296,6 +7276,12 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
vty_endframe(vty, " exit-address-family\n");
}
+/* clang-format off */
+#if defined(VERSION_TYPE_DEV) && CONFDATE > 20180517
+CPP_NOTICE("bgpd: remove 'bgp enforce-first-as' config migration from bgp_config_write")
+#endif
+/* clang-format on */
+
int bgp_config_write(struct vty *vty)
{
int write = 0;
@@ -7331,6 +7317,15 @@ int bgp_config_write(struct vty *vty)
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
continue;
+ /* Migrate deprecated 'bgp enforce-first-as'
+ * config to 'neighbor * enforce-first-as' configs
+ */
+ if (bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ peer_flag_set(peer, PEER_FLAG_ENFORCE_FIRST_AS);
+ bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
+ }
+
/* Router bgp ASN */
vty_out(vty, "router bgp %u", bgp->as);
@@ -7429,10 +7424,6 @@ int bgp_config_write(struct vty *vty)
vty_out(vty, "\n");
}
- /* BGP enforce-first-as. */
- if (bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS))
- vty_out(vty, " bgp enforce-first-as\n");
-
/* BGP deterministic-med. */
if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
!= DFLT_BGP_DETERMINISTIC_MED)
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index f663df162f..a685411f6e 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -534,6 +534,9 @@ struct bgp {
struct bgp_pbr_config *bgp_pbr_cfg;
+ /* local esi hash table */
+ struct hash *esihash;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp)
@@ -829,13 +832,14 @@ struct peer {
#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */
#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */
#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1 << 8) /* local-as no-prepend replace-as */
-#define PEER_FLAG_DELETE (1 << 9) /* mark the peer for deleting */
-#define PEER_FLAG_CONFIG_NODE (1 << 10) /* the node to update configs on */
+#define PEER_FLAG_DELETE (1 << 9) /* mark the peer for deleting */
+#define PEER_FLAG_CONFIG_NODE (1 << 10) /* the node to update configs on */
#define PEER_FLAG_LONESOUL (1 << 11)
#define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 12) /* dynamic neighbor */
#define PEER_FLAG_CAPABILITY_ENHE (1 << 13) /* Extended next-hop (rfc 5549)*/
#define PEER_FLAG_IFPEER_V6ONLY (1 << 14) /* if-based peer is v6 only */
-#define PEER_FLAG_IS_RFAPI_HD (1 << 15) /* attached to rfapi HD */
+#define PEER_FLAG_IS_RFAPI_HD (1 << 15) /* attached to rfapi HD */
+#define PEER_FLAG_ENFORCE_FIRST_AS (1 << 16) /* enforce-first-as */
/* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */
char *tx_shutdown_message;
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index eb24970bee..0d630ce1a8 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -214,6 +214,34 @@ FRR implements the following RFCs:
- :rfc:`3137`
:t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin,
D. McPherson. June 2001`
+- :rfc:`4447`
+ :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol
+ (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April
+ 2006.`
+- :rfc:`4762`
+ :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol
+ (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.`
+- :rfc:`5036`
+ :t:`LDP Specification, L. Andersson, I. Minei, and B. Thomas. October 2007.`
+- :rfc:`5561`
+ :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and
+ JL. Le Roux. July 2009.`
+- :rfc:`5918`
+ :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence
+ Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.`
+- :rfc:`5919`
+ :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra,
+ E. Chen, and B. Thomas. August 2010.`
+- :rfc:`6667`
+ :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and
+ Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July
+ 2012.`
+- :rfc:`6720`
+ :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution
+ Protocol (LDP), C. Pignataro and R. Asati. August 2012.`
+- :rfc:`7552`
+ :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral,
+ and R. Papneja. June 2015.`
**When SNMP support is enabled, the following RFCs are also supported:**
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index 9e377330ee..b6060f0737 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -58,6 +58,26 @@ Besides the common invocation options (:ref:`common-invocation-options`), the
.. _interface-commands:
+Configuration Addresses behaviour
+=================================
+
+At startup, *Zebra* will first discover the underlying networking objects
+from the operating system. This includes interfaces, addresses of
+interfaces, static routes, etc. Then, it will read the configuration
+file, including its own interface addresses, static routes, etc. All this
+information comprises the operational context from *Zebra*. But
+configuration context from *Zebra* will remain the same as the one from
+:file:`zebra.conf` config file. As an example, executing the following
+:clicmd:`show running-config` will reflect what was in :file:`zebra.conf`.
+In a similar way, networking objects that are configured outside of the
+*Zebra* like *iproute2* will not impact the configuration context from
+*Zebra*. This behaviour permits you to continue saving your own config
+file, and decide what is really to be pushed on the config file, and what
+is dependent on the underlying system.
+Note that inversely, from *Zebra*, you will not be able to delete networking
+objects that were previously configured outside of *Zebra*.
+
+
Interface Commands
==================
@@ -444,8 +464,9 @@ commands in relationship to VRF. Here is an extract of some of those commands:
This command is available on configuration mode. By default, above command
permits accessing the vrf configuration mode. This mode is available for
- both VRFs. It is to be noted that *Zebra* does not create *Linux VRF*.
- Provisioning this command is used to keep the configuration intact.
+ both VRFs. It is to be noted that *Zebra* does not create Linux VRF.
+ The network administrator can however decide to provision this command in
+ configuration file to provide more clarity about the intended configuration.
.. index:: netns NAMESPACE
.. clicmd:: netns NAMESPACE
@@ -454,8 +475,9 @@ commands in relationship to VRF. Here is an extract of some of those commands:
when *Zebra* is run in :option:`-n` mode. This command reflects which *Linux
network namespace* is to be mapped with *Zebra* VRF. It is to be noted that
*Zebra* creates and detects added/suppressed VRFs from the Linux environment
- (in fact, those managed with iproute2). Provisioning this command is used to
- keep the configuration intact.
+ (in fact, those managed with iproute2). The network administrator can however
+ decide to provision this command in configuration file to provide more clarity
+ about the intended configuration.
.. index:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF
.. clicmd:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF
diff --git a/isisd/isis_te.c b/isisd/isis_te.c
index d7d9ad0d43..6834f52a82 100644
--- a/isisd/isis_te.c
+++ b/isisd/isis_te.c
@@ -919,73 +919,148 @@ void mpls_te_print_detail(struct sbuf *buf, int indent,
struct subtlv_header *tlvh = (struct subtlv_header *)subtlvs;
uint16_t sum = 0;
- for (; sum < subtlv_len; tlvh = SUBTLV_HDR_NEXT(tlvh)) {
+ for (; sum < subtlv_len;
+ tlvh = (struct subtlv_header *)(subtlvs + sum)) {
+ if (subtlv_len - sum < SUBTLV_SIZE(tlvh)) {
+ sbuf_push(buf, indent, "Available data %" PRIu8 " is less than TLV size %u!\n",
+ subtlv_len - sum, SUBTLV_SIZE(tlvh));
+ return;
+ }
+
switch (tlvh->type) {
case TE_SUBTLV_ADMIN_GRP:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Administrative Group!\n");
+ return;
+ }
sum += print_subtlv_admin_grp(buf, indent,
(struct te_subtlv_admin_grp *)tlvh);
break;
case TE_SUBTLV_LLRI:
+ if (tlvh->length != TE_SUBTLV_LLRI_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Link ID!\n");
+ return;
+ }
sum += print_subtlv_llri(buf, indent,
(struct te_subtlv_llri *)tlvh);
break;
case TE_SUBTLV_LOCAL_IPADDR:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Local IP address!\n");
+ return;
+ }
sum += print_subtlv_local_ipaddr(buf, indent,
(struct te_subtlv_local_ipaddr *)tlvh);
break;
case TE_SUBTLV_RMT_IPADDR:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Remote Interface address!\n");
+ return;
+ }
sum += print_subtlv_rmt_ipaddr(buf, indent,
(struct te_subtlv_rmt_ipaddr *)tlvh);
break;
case TE_SUBTLV_MAX_BW:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Maximum Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_max_bw(buf, indent,
(struct te_subtlv_max_bw *)tlvh);
break;
case TE_SUBTLV_MAX_RSV_BW:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Maximum Reservable Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_max_rsv_bw(buf, indent,
(struct te_subtlv_max_rsv_bw *)tlvh);
break;
case TE_SUBTLV_UNRSV_BW:
+ if (tlvh->length != TE_SUBTLV_UNRSV_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Unreserved Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_unrsv_bw(buf, indent,
(struct te_subtlv_unrsv_bw *)tlvh);
break;
case TE_SUBTLV_TE_METRIC:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Traffic Engineering Metric!\n");
+ return;
+ }
sum += print_subtlv_te_metric(buf, indent,
(struct te_subtlv_te_metric *)tlvh);
break;
case TE_SUBTLV_RAS:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Remote AS number!\n");
+ return;
+ }
sum += print_subtlv_ras(buf, indent,
(struct te_subtlv_ras *)tlvh);
break;
case TE_SUBTLV_RIP:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Remote ASBR IP Address!\n");
+ return;
+ }
sum += print_subtlv_rip(buf, indent,
(struct te_subtlv_rip *)tlvh);
break;
case TE_SUBTLV_AV_DELAY:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Average Link Delay!\n");
+ return;
+ }
sum += print_subtlv_av_delay(buf, indent,
(struct te_subtlv_av_delay *)tlvh);
break;
case TE_SUBTLV_MM_DELAY:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Min/Max Link Delay!\n");
+ return;
+ }
sum += print_subtlv_mm_delay(buf, indent,
(struct te_subtlv_mm_delay *)tlvh);
break;
case TE_SUBTLV_DELAY_VAR:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Delay Variation!\n");
+ return;
+ }
sum += print_subtlv_delay_var(buf, indent,
(struct te_subtlv_delay_var *)tlvh);
break;
case TE_SUBTLV_PKT_LOSS:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Link Packet Loss!\n");
+ return;
+ }
sum += print_subtlv_pkt_loss(buf, indent,
(struct te_subtlv_pkt_loss *)tlvh);
break;
case TE_SUBTLV_RES_BW:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_res_bw(buf, indent,
(struct te_subtlv_res_bw *)tlvh);
break;
case TE_SUBTLV_AVA_BW:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Available Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_ava_bw(buf, indent,
(struct te_subtlv_ava_bw *)tlvh);
break;
case TE_SUBTLV_USE_BW:
+ if (tlvh->length != SUBTLV_DEF_SIZE) {
+ sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n");
+ return;
+ }
sum += print_subtlv_use_bw(buf, indent,
(struct te_subtlv_use_bw *)tlvh);
break;
diff --git a/lib/log.c b/lib/log.c
index dbfc95da86..1345ff2fd1 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_ALL_VNI),
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET),
+ DESC_ENTRY(ZEBRA_LOCAL_ES_ADD),
+ DESC_ENTRY(ZEBRA_LOCAL_ES_DEL),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
DESC_ENTRY(ZEBRA_L3VNI_ADD),
diff --git a/lib/prefix.c b/lib/prefix.c
index 05af190e9d..ead5444fd0 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -429,6 +429,15 @@ static const struct in6_addr maskbytes6[] = {
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
+void prefix_hexdump(const struct prefix *p)
+{
+ char buf[PREFIX_STRLEN];
+
+ zlog_debug("prefix: %s",
+ prefix2str(p, buf, sizeof(buf)));
+ zlog_hexdump(p, sizeof(struct prefix));
+}
+
int is_zero_mac(struct ethaddr *mac)
{
int i = 0;
@@ -1262,7 +1271,12 @@ static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str,
static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str,
int size)
{
- snprintf(str, size, "Unsupported EVPN prefix");
+ char buf[ESI_STR_LEN];
+
+ snprintf(str, size, "[%d]:[%s]:[%s]/%d", p->prefix.route_type,
+ esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
+ inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4),
+ p->prefixlen);
return str;
}
@@ -1540,3 +1554,56 @@ unsigned prefix_hash_key(void *pp)
offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen),
0x55aa5a5a);
}
+
+/* converts to internal representation of esi
+ * returns 1 on success, 0 otherwise
+ * format accepted: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa
+ * if esi parameter is null, then check only
+ */
+int str_to_esi(const char *str, esi_t *esi)
+{
+ int i;
+ unsigned int a[ESI_BYTES];
+
+ if (!str)
+ return 0;
+
+ if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
+ a + 0, a + 1, a + 2, a + 3,
+ a + 4, a + 5, a + 6, a + 7,
+ a + 8, a + 9)
+ != ESI_BYTES) {
+ /* error in incoming str length */
+ return 0;
+ }
+
+ /* valid ESI */
+ if (!esi)
+ return 1;
+ for (i = 0; i < ESI_BYTES; ++i)
+ esi->val[i] = a[i] & 0xff;
+ return 1;
+}
+
+char *esi_to_str(const esi_t *esi, char *buf, int size)
+{
+ char *ptr;
+
+ if (!esi)
+ return NULL;
+ if (!buf)
+ ptr = (char *)XMALLOC(MTYPE_TMP,
+ ESI_STR_LEN * sizeof(char));
+ else {
+ assert(size >= ESI_STR_LEN);
+ ptr = buf;
+ }
+
+ snprintf(ptr, ESI_STR_LEN,
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ esi->val[0], esi->val[1], esi->val[2],
+ esi->val[3], esi->val[4], esi->val[5],
+ esi->val[6], esi->val[7], esi->val[8],
+ esi->val[9]);
+ return ptr;
+}
diff --git a/lib/prefix.h b/lib/prefix.h
index ab3c05ae74..f5dbb22f79 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -39,6 +39,9 @@
#define ETH_ALEN 6
#endif
+#define ESI_BYTES 10
+#define ESI_STR_LEN (3 * ESI_BYTES)
+
#define ETHER_ADDR_STRLEN (3*ETH_ALEN)
/*
* there isn't a portable ethernet address type. We define our
@@ -213,6 +216,8 @@ static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp)
return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip);
if (evp->prefix.route_type == 3)
return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 4)
+ return IS_IPADDR_NONE(&(evp)->prefix.es_addr.ip);
if (evp->prefix.route_type == 5)
return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip);
return 0;
@@ -224,6 +229,8 @@ static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp)
return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip);
if (evp->prefix.route_type == 3)
return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 4)
+ return IS_IPADDR_V4(&(evp)->prefix.es_addr.ip);
if (evp->prefix.route_type == 5)
return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip);
return 0;
@@ -235,6 +242,8 @@ static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp)
return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip);
if (evp->prefix.route_type == 3)
return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 4)
+ return IS_IPADDR_V6(&(evp)->prefix.es_addr.ip);
if (evp->prefix.route_type == 5)
return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip);
return 0;
@@ -432,6 +441,11 @@ extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size);
extern unsigned prefix_hash_key(void *pp);
+extern int str_to_esi(const char *str, esi_t *esi);
+extern char *esi_to_str(const esi_t *esi, char *buf, int size);
+extern void prefix_hexdump(const struct prefix *p);
+extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
+
static inline int ipv6_martian(struct in6_addr *addr)
{
struct in6_addr localhost_addr;
@@ -482,5 +496,4 @@ static inline int is_host_route(struct prefix *p)
return (p->prefixlen == IPV6_MAX_BITLEN);
return 0;
}
-
#endif /* _ZEBRA_PREFIX_H */
diff --git a/lib/subdir.am b/lib/subdir.am
index c5719786d6..1c3d31b927 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -190,7 +190,7 @@ if SNMP
lib_LTLIBRARIES += lib/libfrrsnmp.la
endif
-lib_libfrrsnmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+lib_libfrrsnmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0
lib_libfrrsnmp_la_LIBADD = lib/libfrr.la $(SNMP_LIBS)
lib_libfrrsnmp_la_SOURCES = \
diff --git a/lib/vrf.c b/lib/vrf.c
index db539d375d..e1176d1526 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -856,7 +856,7 @@ int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
if (vrf_is_mapped_on_netns(vrf_id))
return fd;
#ifdef SO_BINDTODEVICE
- ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name));
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1);
if (ret < 0)
zlog_debug("bind to interface %s failed, errno=%d", name,
errno);
diff --git a/lib/zclient.c b/lib/zclient.c
index 0f7cf350db..38a9e6c78e 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -2760,6 +2760,16 @@ static int zclient_read(struct thread *thread)
if (zclient->fec_update)
(*zclient->fec_update)(command, zclient, length);
break;
+ case ZEBRA_LOCAL_ES_ADD:
+ if (zclient->local_es_add)
+ (*zclient->local_es_add)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_LOCAL_ES_DEL:
+ if (zclient->local_es_del)
+ (*zclient->local_es_del)(command, zclient, length,
+ vrf_id);
+ break;
case ZEBRA_VNI_ADD:
if (zclient->local_vni_add)
(*zclient->local_vni_add)(command, zclient, length,
diff --git a/lib/zclient.h b/lib/zclient.h
index 2ec03acc44..ad98b8db87 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -124,6 +124,8 @@ typedef enum {
ZEBRA_ADVERTISE_DEFAULT_GW,
ZEBRA_ADVERTISE_SUBNET,
ZEBRA_ADVERTISE_ALL_VNI,
+ ZEBRA_LOCAL_ES_ADD,
+ ZEBRA_LOCAL_ES_DEL,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
ZEBRA_L3VNI_ADD,
@@ -237,6 +239,10 @@ struct zclient {
int (*redistribute_route_del)(int, struct zclient *, uint16_t,
vrf_id_t);
int (*fec_update)(int, struct zclient *, uint16_t);
+ int (*local_es_add)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
+ int (*local_es_del)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
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);
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 76a64cd637..8a6c4a5ccf 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -61,6 +61,6 @@ ospf6d_ospf6d_SOURCES = \
# end
ospf6d_ospf6d_snmp_la_SOURCES = ospf6d/ospf6_snmp.c
-ospf6d_ospf6d_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+ospf6d_ospf6d_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
ospf6d_ospf6d_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/ospfd/subdir.am b/ospfd/subdir.am
index 9f04260366..f2e292e186 100644
--- a/ospfd/subdir.am
+++ b/ospfd/subdir.am
@@ -89,7 +89,7 @@ ospfd_ospfd_LDADD = ospfd/libfrrospf.a lib/libfrr.la @LIBCAP@ @LIBM@
ospfd_ospfd_SOURCES = ospfd/ospf_main.c
ospfd_ospfd_snmp_la_SOURCES = ospfd/ospf_snmp.c
-ospfd_ospfd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+ospfd_ospfd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
ospfd_ospfd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
ospfd_ospfd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/ripd/subdir.am b/ripd/subdir.am
index 7a8f2185ba..1c5167ef4a 100644
--- a/ripd/subdir.am
+++ b/ripd/subdir.am
@@ -35,7 +35,7 @@ ripd_ripd_SOURCES = \
# end
ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
-ripd_ripd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+ripd_ripd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
ripd_ripd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
ripd_ripd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index b1387815ba..be53b74b3f 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -120,7 +120,7 @@ static void zebra_redistribute(struct zserv *client, int type,
if (IS_ZEBRA_DEBUG_EVENT)
zlog_debug(
- "%s: client %s %s(%d) checking: selected=%d, type=%d, distance=%d, metric=%d zebra_check_addr=%d",
+ "%s: client %s %s(%u) checking: selected=%d, type=%d, distance=%d, metric=%d zebra_check_addr=%d",
__func__,
zebra_route_string(client->proto),
prefix2str(dst_p, buf, sizeof(buf)),
@@ -192,7 +192,7 @@ void redistribute_update(struct prefix *p, struct prefix *src_p,
if (send_redistribute) {
if (IS_ZEBRA_DEBUG_EVENT) {
zlog_debug(
- "%s: client %s %s(%d), type=%d, distance=%d, metric=%d",
+ "%s: client %s %s(%u), type=%d, distance=%d, metric=%d",
__func__,
zebra_route_string(client->proto),
prefix2str(p, buf, sizeof(buf)),
@@ -270,7 +270,7 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS)
if (IS_ZEBRA_DEBUG_EVENT)
zlog_debug(
- "%s: client proto %s afi=%d, wants %s, vrf %d, instance=%d",
+ "%s: client proto %s afi=%d, wants %s, vrf %u, instance=%d",
__func__, zebra_route_string(client->proto), afi,
zebra_route_string(type), zvrf_id(zvrf), instance);
@@ -298,7 +298,7 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS)
if (!vrf_bitmap_check(client->redist[afi][type],
zvrf_id(zvrf))) {
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s: setting vrf %d redist bitmap",
+ zlog_debug("%s: setting vrf %u redist bitmap",
__func__, zvrf_id(zvrf));
vrf_bitmap_set(client->redist[afi][type],
zvrf_id(zvrf));
@@ -365,7 +365,8 @@ void zebra_interface_up_update(struct interface *ifp)
struct zserv *client;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_UP %s", ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_UP %s(%u)",
+ ifp->name, ifp->vrf_id);
if (ifp->ptm_status || !ifp->ptm_enable) {
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client))
@@ -384,7 +385,8 @@ void zebra_interface_down_update(struct interface *ifp)
struct zserv *client;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_DOWN %s", ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_DOWN %s(%u)",
+ ifp->name, ifp->vrf_id);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) {
zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp);
@@ -398,7 +400,7 @@ void zebra_interface_add_update(struct interface *ifp)
struct zserv *client;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADD %s[%d]", ifp->name,
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADD %s(%u)", ifp->name,
ifp->vrf_id);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client))
@@ -415,7 +417,8 @@ void zebra_interface_delete_update(struct interface *ifp)
struct zserv *client;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_DELETE %s", ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_DELETE %s(%u)",
+ ifp->name, ifp->vrf_id);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) {
client->ifdel_cnt++;
@@ -435,8 +438,9 @@ void zebra_interface_address_add_update(struct interface *ifp,
char buf[PREFIX_STRLEN];
p = ifc->address;
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %s on %s",
- prefix2str(p, buf, sizeof(buf)), ifc->ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %s on %s(%u)",
+ prefix2str(p, buf, sizeof(buf)), ifp->name,
+ ifp->vrf_id);
}
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
@@ -467,8 +471,9 @@ void zebra_interface_address_delete_update(struct interface *ifp,
char buf[PREFIX_STRLEN];
p = ifc->address;
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %s on %s",
- prefix2str(p, buf, sizeof(buf)), ifc->ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %s on %s(%u)",
+ prefix2str(p, buf, sizeof(buf)),
+ ifp->name, ifp->vrf_id);
}
zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 0);
@@ -768,8 +773,8 @@ void zebra_interface_parameters_update(struct interface *ifp)
struct zserv *client;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s",
- ifp->name);
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s(%u)",
+ ifp->name, ifp->vrf_id);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client))
if (client->ifinfo)
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 45e285a9e7..d3abcef6d5 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -127,7 +127,7 @@ zebra_zebra_irdp_la_SOURCES = \
zebra_zebra_irdp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
zebra_zebra_snmp_la_SOURCES = zebra/zebra_snmp.c
-zebra_zebra_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
+zebra_zebra_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
zebra_zebra_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
zebra_zebra_snmp_la_LIBADD = lib/libfrrsnmp.la