diff options
-rw-r--r-- | bgpd/bgp_attr.c | 5 | ||||
-rw-r--r-- | bgpd/bgp_attr.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_attr_evpn.c | 38 | ||||
-rw-r--r-- | bgpd/bgp_attr_evpn.h | 1 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.c | 21 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_evpn_mh.c | 225 | ||||
-rw-r--r-- | bgpd/bgp_evpn_mh.h | 15 | ||||
-rw-r--r-- | bgpd/bgp_evpn_private.h | 11 | ||||
-rw-r--r-- | bgpd/bgp_evpn_vty.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 11 | ||||
-rw-r--r-- | lib/prefix.c | 24 | ||||
-rw-r--r-- | lib/prefix.h | 2 | ||||
-rw-r--r-- | lib/stream.h | 10 | ||||
-rw-r--r-- | lib/vxlan.h | 10 | ||||
-rw-r--r-- | lib/zclient.h | 6 |
16 files changed, 329 insertions, 62 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 429a68d19..b94e24e87 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -725,6 +725,8 @@ bool attrhash_cmp(const void *p1, const void *p2) && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) && attr1->es_flags == attr2->es_flags && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum + && attr1->df_pref == attr2->df_pref + && attr1->df_alg == attr2->df_alg && attr1->nh_ifindex == attr2->nh_ifindex && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex && attr1->distance == attr2->distance @@ -2247,6 +2249,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); + /* Extract DF election preference and mobility sequence number */ + attr->df_pref = bgp_attr_df_pref_from_ec(attr, &attr->df_alg); + /* Extract MAC mobility sequence number, if any. */ attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky); attr->sticky = sticky; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index e6e953364..ef0e74344 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -294,6 +294,10 @@ struct attr { /* SR-TE Color */ uint32_t srte_color; + + /* EVPN DF preference and algorithm for DF election on local ESs */ + uint16_t df_pref; + uint8_t df_alg; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index aa0c59f3a..7cc9ecd79 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -26,6 +26,7 @@ #include "log.h" #include "memory.h" #include "stream.h" +#include "vxlan.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -146,6 +147,43 @@ uint8_t bgp_attr_default_gw(struct attr *attr) } /* + * Fetch and return the DF preference and algorithm from + * DF election extended community, if present, else 0. + */ +uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg) +{ + struct ecommunity *ecom; + int i; + uint16_t df_pref = 0; + + *alg = EVPN_MH_DF_ALG_SERVICE_CARVING; + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return 0; + + for (i = 0; i < ecom->size; i++) { + uint8_t *pnt; + uint8_t type, sub_type; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + if (!(type == ECOMMUNITY_ENCODE_EVPN + && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION)) + continue; + + *alg = (*pnt++) & ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS; + + pnt += 3; + pnt = ptr_get_be16(pnt, &df_pref); + (void)pnt; /* consume value */ + break; + } + + return df_pref; +} + +/* * Fetch and return the sequence number from MAC Mobility extended * community, if present, else 0. */ diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 19c028a82..6fdf73fd1 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -48,6 +48,7 @@ extern uint8_t bgp_attr_default_gw(struct attr *attr); extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag, bool *proxy); +extern uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg); extern bool is_zero_gw_ip(const union gw_addr *gw_ip, afi_t afi); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 353b003c3..de3757aeb 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1038,6 +1038,27 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) (flags & ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ? "SA":"AA"); + } else if (*pnt + == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) { + uint8_t alg; + uint16_t pref; + uint16_t bmap; + + alg = *(pnt + 1); + memcpy(&bmap, pnt + 2, 2); + bmap = ntohs(bmap); + memcpy(&pref, pnt + 5, 2); + pref = ntohs(pref); + + if (bmap) + snprintf( + encbuf, sizeof(encbuf), + "DF: (alg: %u, bmap: 0x%x pref: %u)", + alg, bmap, pref); + else + snprintf(encbuf, sizeof(encbuf), + "DF: (alg: %u, pref: %u)", alg, + pref); } else unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index fe90efe1f..e9c52287f 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -73,11 +73,15 @@ #define ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL 0x01 #define ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT 0x02 #define ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC 0x03 +#define ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION 0x06 #define ECOMMUNITY_EVPN_SUBTYPE_DEF_GW 0x0d #define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08 #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01 +/* DF alg bits - only lower 5 bits are applicable */ +#define ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS 0x1f + #define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01 #define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02 #define ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG 0x04 diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 8f81278de..2e49e85a7 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -54,7 +54,10 @@ static void bgp_evpn_local_es_down(struct bgp *bgp, static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp, struct bgp_evpn_es *es); static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, - struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); + struct bgp_evpn_es *es, + struct in_addr vtep_ip, + bool esr, uint8_t df_alg, + uint16_t df_pref); static void bgp_evpn_es_vtep_del(struct bgp *bgp, struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es); @@ -111,9 +114,10 @@ static int bgp_evpn_es_route_select_install(struct bgp *bgp, && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { if (bgp_zebra_has_route_changed(old_select)) { - bgp_evpn_es_vtep_add(bgp, es, - old_select->attr->nexthop, - true /*esr*/); + bgp_evpn_es_vtep_add(bgp, es, old_select->attr->nexthop, + true /*esr*/, + old_select->attr->df_alg, + old_select->attr->df_pref); } UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); bgp_zebra_clear_route_change_flags(dest); @@ -140,8 +144,9 @@ static int bgp_evpn_es_route_select_install(struct bgp *bgp, if (new_select && new_select->type == ZEBRA_ROUTE_BGP && new_select->sub_type == BGP_ROUTE_IMPORTED) { - bgp_evpn_es_vtep_add(bgp, es, - new_select->attr->nexthop, true /*esr */); + bgp_evpn_es_vtep_add(bgp, es, new_select->attr->nexthop, + true /*esr */, new_select->attr->df_alg, + new_select->attr->df_pref); } else { if (old_select && old_select->type == ZEBRA_ROUTE_BGP && old_select->sub_type == BGP_ROUTE_IMPORTED) @@ -508,8 +513,10 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, /***************************************************************************** * Ethernet Segment (Type-4) Routes - * ESRs are used for BUM handling. XXX - BUM support is planned for phase-2 i.e. - * this code is just a place holder for now + * ESRs are used for DF election. Currently service-carving described in + * RFC 7432 is NOT supported. Instead preference based DF election is + * used by default. + * Reference: draft-ietf-bess-evpn-pref-df */ /* Build extended community for EVPN ES (type-4) route */ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, @@ -517,8 +524,10 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, { struct ecommunity ecom_encap; struct ecommunity ecom_es_rt; + struct ecommunity ecom_df; struct ecommunity_val eval; struct ecommunity_val eval_es_rt; + struct ecommunity_val eval_df; bgp_encap_types tnl_type; struct ethaddr mac; @@ -542,6 +551,13 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_es_rt); + /* DF election extended community */ + memset(&ecom_df, 0, sizeof(ecom_df)); + encode_df_elect_extcomm(&eval_df, es->df_pref); + ecom_df.size = 1; + ecom_df.val = (uint8_t *)eval_df.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_df); + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); } @@ -1142,6 +1158,7 @@ static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp, { struct bgp_evpn_es *es = es_vtep->es; struct stream *s; + uint32_t flags = 0; /* Check socket. */ if (!zclient || zclient->sock < 0) @@ -1155,6 +1172,9 @@ static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp, return 0; } + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) + flags |= ZAPI_ES_VTEP_FLAG_ESR_RXED; + s = zclient->obuf; stream_reset(s); @@ -1163,6 +1183,11 @@ static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp, bgp->vrf_id); stream_put(s, &es->esi, sizeof(esi_t)); stream_put_ipv4(s, es_vtep->vtep_ip.s_addr); + if (add) { + stream_putl(s, flags); + stream_putc(s, es_vtep->df_alg); + stream_putw(s, es_vtep->df_pref); + } stream_putw_at(s, 0, stream_get_endp(s)); @@ -1174,7 +1199,8 @@ static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp, } static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, - struct bgp_evpn_es_vtep *es_vtep) + struct bgp_evpn_es_vtep *es_vtep, + bool param_change) { bool old_active; bool new_active; @@ -1190,25 +1216,30 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, new_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE); - if (old_active == new_active) - return; + if ((old_active != new_active) || (new_active && param_change)) { - if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("es %s vtep %pI4 %s", es_vtep->es->esi_str, - &es_vtep->vtep_ip, - new_active ? "active" : "inactive"); + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vtep %pI4 %s df %u/%u", + es_vtep->es->esi_str, &es_vtep->vtep_ip, + new_active ? "active" : "inactive", + es_vtep->df_alg, es_vtep->df_pref); - /* send remote ES to zebra */ - bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active); + /* send remote ES to zebra */ + bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active); - /* queue up the es for background consistency checks */ - bgp_evpn_es_cons_checks_pend_add(es_vtep->es); + /* queue up the es for background consistency checks */ + bgp_evpn_es_cons_checks_pend_add(es_vtep->es); + } } static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, - struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr) + struct bgp_evpn_es *es, + struct in_addr vtep_ip, + bool esr, uint8_t df_alg, + uint16_t df_pref) { struct bgp_evpn_es_vtep *es_vtep; + bool param_change = false; es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip); @@ -1216,15 +1247,23 @@ static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, es_vtep = bgp_evpn_es_vtep_new(es, vtep_ip); if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("es %s vtep %pI4 add %s", es_vtep->es->esi_str, - &es_vtep->vtep_ip, esr ? "esr" : "ead"); + zlog_debug("es %s vtep %pI4 add %s df %u/%u", + es_vtep->es->esi_str, &es_vtep->vtep_ip, + esr ? "esr" : "ead", df_alg, df_pref); - if (esr) + if (esr) { SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR); - else + if ((es_vtep->df_pref != df_pref) + || (es_vtep->df_alg != df_alg)) { + param_change = true; + es_vtep->df_pref = df_pref; + es_vtep->df_alg = df_alg; + } + } else { ++es_vtep->evi_cnt; + } - bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep); + bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change); return es_vtep; } @@ -1232,17 +1271,24 @@ static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, static void bgp_evpn_es_vtep_do_del(struct bgp *bgp, struct bgp_evpn_es_vtep *es_vtep, bool esr) { + bool param_change = false; + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) zlog_debug("es %s vtep %pI4 del %s", es_vtep->es->esi_str, &es_vtep->vtep_ip, esr ? "esr" : "ead"); if (esr) { UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR); + if (es_vtep->df_pref || es_vtep->df_alg) { + param_change = true; + es_vtep->df_pref = 0; + es_vtep->df_alg = 0; + } } else { if (es_vtep->evi_cnt) --es_vtep->evi_cnt; } - bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep); + bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change); bgp_evpn_es_vtep_free(es_vtep); } @@ -1424,32 +1470,43 @@ static void bgp_evpn_local_es_down(struct bgp *bgp, } /* Process ES link oper-up by generating ES-EAD and ESR */ -static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es) +static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es, + bool regen_esr) { struct prefix_evpn p; + bool regen_ead = false; - if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) - return; - - SET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s up", es->esi_str); - if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("local es %s up", es->esi_str); + SET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + regen_esr = true; + regen_ead = true; + } - /* generate ESR */ - build_evpn_type4_prefix(&p, &es->esi, es->originator_ip); - if (bgp_evpn_type4_route_update(bgp, es, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: Type4 route creation failure for ESI %s", - bgp->vrf_id, es->esi_str); + if (regen_esr) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s generate ESR", es->esi_str); + /* generate ESR */ + build_evpn_type4_prefix(&p, &es->esi, es->originator_ip); + if (bgp_evpn_type4_route_update(bgp, es, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: Type4 route creation failure for ESI %s", + bgp->vrf_id, es->esi_str); + } - /* generate EAD-EVI */ - bgp_evpn_local_type1_evi_route_add(bgp, es); + if (regen_ead) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s generate EAD", es->esi_str); + /* generate EAD-EVI */ + bgp_evpn_local_type1_evi_route_add(bgp, es); - /* generate EAD-ES */ - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, - &es->esi, es->originator_ip); - bgp_evpn_type1_route_update(bgp, es, NULL, &p); + /* generate EAD-ES */ + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, + es->originator_ip); + bgp_evpn_type1_route_update(bgp, es, NULL, &p); + } } static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) @@ -1507,11 +1564,13 @@ int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) * ES. */ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, - struct in_addr originator_ip, bool oper_up) + struct in_addr originator_ip, bool oper_up, + uint16_t df_pref) { char buf[ESI_STR_LEN]; struct bgp_evpn_es *es; bool new_es = true; + bool regen_esr = false; /* create the new es */ es = bgp_evpn_es_find(esi); @@ -1529,10 +1588,14 @@ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, } if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("add local es %s orig-ip %pI4", es->esi_str, - &originator_ip); + zlog_debug("add local es %s orig-ip %pI4 df_pref %u", es->esi_str, + &originator_ip, df_pref); es->originator_ip = originator_ip; + if (df_pref != es->df_pref) { + es->df_pref = df_pref; + regen_esr = true; + } bgp_evpn_es_local_info_set(bgp, es); /* import all remote Type-4 routes in the ES table */ @@ -1551,7 +1614,7 @@ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, * can be generated even if the link is inactive. */ if (oper_up) - bgp_evpn_local_es_up(bgp, es); + bgp_evpn_local_es_up(bgp, es, regen_esr); else bgp_evpn_local_es_down(bgp, es); @@ -1621,12 +1684,49 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE) json_array_string_add(json_flags, "active"); json_object_object_add(json_vtep_entry, "flags", json_flags); + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) { + json_object_int_add(json_vtep_entry, "dfPreference", + es_vtep->df_pref); + json_object_int_add(json_vtep_entry, "dfAlgorithm", + es_vtep->df_pref); + } } json_object_array_add(json_vteps, json_vtep_entry); } +static void bgp_evpn_es_vteps_show_detail(struct vty *vty, + struct bgp_evpn_es *es) +{ + char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ]; + struct listnode *node; + struct bgp_evpn_es_vtep *es_vtep; + char alg_buf[EVPN_DF_ALG_STR_LEN]; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + vtep_flag_str[0] = '\0'; + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) + strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str)); + if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE) + strlcat(vtep_flag_str, "A", sizeof(vtep_flag_str)); + + if (!strlen(vtep_flag_str)) + strlcat(vtep_flag_str, "-", sizeof(vtep_flag_str)); + + vty_out(vty, " %s flags: %s", inet_ntoa(es_vtep->vtep_ip), + vtep_flag_str); + + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) + vty_out(vty, " df_alg: %s df_pref: %u\n", + evpn_es_df_alg2str(es_vtep->df_alg, alg_buf, + sizeof(alg_buf)), + es_vtep->df_pref); + else + vty_out(vty, "\n"); + } +} + static void bgp_evpn_es_show_entry(struct vty *vty, struct bgp_evpn_es *es, json_object *json) { @@ -1695,6 +1795,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, if (json) { json_object *json_flags; json_object *json_incons; + json_object *json_vteps; + struct listnode *node; + struct bgp_evpn_es_vtep *es_vtep; /* Add the "brief" info first */ bgp_evpn_es_show_entry(vty, es, json); @@ -1715,6 +1818,14 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, es->remote_es_evi_cnt); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); + if (listcount(es->es_vtep_list)) { + json_vteps = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, + es_vtep)) { + bgp_evpn_es_json_vtep_fill(json_vteps, es_vtep); + } + json_object_object_add(json, "vteps", json_vteps); + } if (es->inconsistencies) { json_incons = json_object_new_array(); if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST) @@ -1726,7 +1837,6 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, } else { char incons_str[BGP_EVPNES_INCONS_STR_SZ]; char type_str[4]; - char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; char buf1[RD_ADDRSTRLEN]; type_str[0] = '\0'; @@ -1735,10 +1845,6 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, if (es->flags & BGP_EVPNES_REMOTE) strlcat(type_str, "R", sizeof(type_str)); - bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); - if (!strlen(vtep_str)) - strlcpy(buf1, "-", sizeof(buf1)); - if (es->flags & BGP_EVPNES_LOCAL) prefix_rd2str(&es->prd, buf1, sizeof(buf1)); else @@ -1748,6 +1854,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, " Type: %s\n", type_str); vty_out(vty, " RD: %s\n", buf1); vty_out(vty, " Originator-IP: %pI4\n", &es->originator_ip); + if (es->flags & BGP_EVPNES_LOCAL) + vty_out(vty, " Local ES DF preference: %u\n", + es->df_pref); vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); vty_out(vty, " Remote VNI Count: %d\n", es->remote_es_evi_cnt); @@ -1763,7 +1872,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, } vty_out(vty, " Inconsistencies: %s\n", incons_str); - vty_out(vty, " VTEPs: %s\n", vtep_str); + if (listcount(es->es_vtep_list)) { + vty_out(vty, " VTEPs:\n"); + bgp_evpn_es_vteps_show_detail(vty, es); + } vty_out(vty, "\n"); } } @@ -1936,7 +2048,8 @@ static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp, struct bgp_evpn_es_vtep *es_vtep; es_vtep = bgp_evpn_es_vtep_add(bgp, evi_vtep->es_evi->es, - evi_vtep->vtep_ip, false /*esr*/); + evi_vtep->vtep_ip, false /*esr*/, + 0, 0); evi_vtep->es_vtep = es_vtep; } else { if (evi_vtep->es_vtep) { diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 93355d495..d719524bd 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -110,6 +110,9 @@ struct bgp_evpn_es { */ uint32_t incons_evi_vtep_cnt; + /* preference config for BUM-DF election. advertised via the ESR. */ + uint16_t df_pref; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp_evpn_es) @@ -131,6 +134,10 @@ struct bgp_evpn_es_vtep { uint32_t evi_cnt; /* es_evis referencing this vtep as an active path */ + /* Algorithm and preference for DF election. Rxed via the ESR */ + uint8_t df_alg; + uint16_t df_pref; + /* memory used for adding the entry to es->es_vtep_list */ struct listnode es_listnode; }; @@ -264,6 +271,11 @@ static inline bool bgp_evpn_attr_is_local_es(struct attr *attr) return attr ? !!(attr->es_flags & ATTR_ES_IS_LOCAL) : false; } +static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr) +{ + return (attr) ? attr->df_pref : 0; +} + /****************************************************************************/ extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, afi_t afi, safi_t safi, @@ -276,7 +288,8 @@ int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, struct attr *attr, uint8_t *pfx, int psize, uint32_t addpath_id); extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, - struct in_addr originator_ip, bool oper_up); + struct in_addr originator_ip, bool oper_up, + uint16_t df_pref); extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi); extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni); extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 611566201..c47576c00 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -308,6 +308,17 @@ static inline void encode_es_rt_extcomm(struct ecommunity_val *eval, memcpy(&eval->val[2], mac, ETH_ALEN); } +static inline void encode_df_elect_extcomm(struct ecommunity_val *eval, + uint16_t pref) +{ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_EVPN; + eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION; + eval->val[2] = EVPN_MH_DF_ALG_PREF; + eval->val[6] = (pref >> 8) & 0xff; + eval->val[7] = pref & 0xff; +} + static inline void encode_esi_label_extcomm(struct ecommunity_val *eval, bool single_active) { diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index c22bfefb6..b2491e118 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -23,6 +23,7 @@ #include "prefix.h" #include "lib/json.h" #include "lib/printfrr.h" +#include "lib/vxlan.h" #include "stream.h" #include "bgpd/bgpd.h" @@ -4629,7 +4630,8 @@ DEFPY_HIDDEN(test_es_add, oper_up = false; vtep_ip = bgp->router_id; - ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up); + ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up, + EVPN_MH_DF_PREF_MIN); if (ret == -1) { vty_out(vty, "%%Failed to add ES\n"); return CMD_WARNING; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index d4a69af4f..00213b423 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2545,6 +2545,7 @@ static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS) char buf[ESI_STR_LEN]; struct in_addr originator_ip; uint8_t active; + uint16_t df_pref; bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) @@ -2554,13 +2555,15 @@ static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS) stream_get(&esi, s, sizeof(esi_t)); originator_ip.s_addr = stream_get_ipv4(s); active = stream_getc(s); + df_pref = stream_getw(s); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx add ESI %s originator-ip %pI4 active %u", - esi_to_str(&esi, buf, sizeof(buf)), &originator_ip, - active); + zlog_debug( + "Rx add ESI %s originator-ip %pI4 active %u df_pref %u", + esi_to_str(&esi, buf, sizeof(buf)), + &originator_ip, active, df_pref); - bgp_evpn_local_es_add(bgp, &esi, originator_ip, active); + bgp_evpn_local_es_add(bgp, &esi, originator_ip, active, df_pref); return 0; } diff --git a/lib/prefix.c b/lib/prefix.c index 24def1bac..663a87afd 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -30,6 +30,7 @@ #include "jhash.h" #include "lib_errors.h" #include "printfrr.h" +#include "vxlan.h" DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix") DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") @@ -1336,6 +1337,29 @@ char *esi_to_str(const esi_t *esi, char *buf, int size) return ptr; } +char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len) +{ + switch (df_alg) { + case EVPN_MH_DF_ALG_SERVICE_CARVING: + snprintf(buf, buf_len, "service-carving"); + break; + + case EVPN_MH_DF_ALG_HRW: + snprintf(buf, buf_len, "HRW"); + break; + + case EVPN_MH_DF_ALG_PREF: + snprintf(buf, buf_len, "preference"); + break; + + default: + snprintf(buf, buf_len, "unknown %u", df_alg); + break; + } + + return buf; +} + printfrr_ext_autoreg_p("EA", printfrr_ea) static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt, int prec, const void *ptr) diff --git a/lib/prefix.h b/lib/prefix.h index 471978ed2..d2cabf310 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -62,6 +62,7 @@ typedef enum { #define EVPN_ETH_TAG_BYTES 4 #define ESI_BYTES 10 #define ESI_STR_LEN (3 * ESI_BYTES) +#define EVPN_DF_ALG_STR_LEN 24 /* Maximum number of VTEPs per-ES - * XXX - temporary limit for allocating strings etc. @@ -515,6 +516,7 @@ extern unsigned prefix_hash_key(const 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 char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); static inline int ipv6_martian(struct in6_addr *addr) diff --git a/lib/stream.h b/lib/stream.h index 23f85d809..4f75f121c 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -386,6 +386,16 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out) return ptr + 4; } +static inline uint8_t *ptr_get_be16(uint8_t *ptr, uint16_t *out) +{ + uint16_t tmp; + + memcpy(&tmp, ptr, sizeof(tmp)); + *out = ntohs(tmp); + + return ptr + 2; +} + /* * so Normal stream_getX functions assert. Which is anathema * to keeping a daemon up and running when something goes south diff --git a/lib/vxlan.h b/lib/vxlan.h index 69d393959..62963a609 100644 --- a/lib/vxlan.h +++ b/lib/vxlan.h @@ -26,6 +26,16 @@ extern "C" { #endif +/* EVPN MH DF election alogorithm */ +#define EVPN_MH_DF_ALG_SERVICE_CARVING 0 +#define EVPN_MH_DF_ALG_HRW 1 +#define EVPN_MH_DF_ALG_PREF 2 + +/* preference range for DF election */ +#define EVPN_MH_DF_PREF_MIN 0 +#define EVPN_MH_DF_PREF_DEFAULT 32767 +#define EVPN_MH_DF_PREF_MAX 65535 + /* VxLAN Network Identifier - 24-bit (RFC 7348) */ typedef uint32_t vni_t; #define VNI_MAX 16777215 /* (2^24 - 1) */ diff --git a/lib/zclient.h b/lib/zclient.h index 959a10139..80dca3fc5 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -690,6 +690,12 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) * to allocate past 0x80 */ +/* Zebra ES VTEP flags (ZEBRA_REMOTE_ES_VTEP_ADD) */ +/* ESR has been rxed from the VTEP. Only VTEPs that have advertised the + * Type-4 route can participate in DF election. + */ +#define ZAPI_ES_VTEP_FLAG_ESR_RXED (1 << 0) + enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; struct zclient_options { |