diff options
| -rw-r--r-- | bgpd/Makefile.am | 2 | ||||
| -rw-r--r-- | bgpd/bgp_attr.c | 3 | ||||
| -rw-r--r-- | bgpd/bgp_ecommunity.c | 15 | ||||
| -rw-r--r-- | bgpd/bgp_evpn.c | 1169 | ||||
| -rw-r--r-- | bgpd/bgp_evpn.h | 4 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_private.h | 88 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_vty.c | 406 | ||||
| -rw-r--r-- | bgpd/bgp_memory.c | 2 | ||||
| -rw-r--r-- | bgpd/bgp_memory.h | 3 | ||||
| -rw-r--r-- | bgpd/bgp_network.c | 4 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 15 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 50 | ||||
| -rw-r--r-- | bgpd/bgp_zebra.c | 36 | ||||
| -rw-r--r-- | bgpd/bgpd.c | 97 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 10 | ||||
| -rw-r--r-- | doc/user/overview.rst | 28 | ||||
| -rw-r--r-- | doc/user/zebra.rst | 30 | ||||
| -rw-r--r-- | isisd/isis_te.c | 77 | ||||
| -rw-r--r-- | lib/log.c | 2 | ||||
| -rw-r--r-- | lib/prefix.c | 69 | ||||
| -rw-r--r-- | lib/prefix.h | 15 | ||||
| -rw-r--r-- | lib/subdir.am | 2 | ||||
| -rw-r--r-- | lib/vrf.c | 2 | ||||
| -rw-r--r-- | lib/zclient.c | 10 | ||||
| -rw-r--r-- | lib/zclient.h | 6 | ||||
| -rw-r--r-- | ospf6d/subdir.am | 2 | ||||
| -rw-r--r-- | ospfd/subdir.am | 2 | ||||
| -rw-r--r-- | ripd/subdir.am | 2 | ||||
| -rw-r--r-- | zebra/redistribute.c | 33 | ||||
| -rw-r--r-- | zebra/subdir.am | 2 |
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; @@ -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 = \ @@ -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 |
