From 1207a5bc9bd81a7e7fffe1bfb1904d3ff18d3028 Mon Sep 17 00:00:00 2001 From: vivek Date: Tue, 24 Mar 2020 13:50:20 -0700 Subject: [PATCH] bgpd: Ability to add/update unique extended communities Certain extended communities cannot be repeated. An example is the BGP link bandwidth extended community. Enhance the extended community add function to ensure uniqueness, if requested. Note: This commit does not change the lack of uniqueness for any of the already-supported extended communities. Many of them such as the BGP route target can obviously be present multiple times. Others like the Router's MAC should most probably be present only once. The portions of the code which add these may already be structured such that duplicates do not arise. Signed-off-by: Vivek Venkatraman --- bgpd/bgp_attr_evpn.c | 2 +- bgpd/bgp_ecommunity.c | 54 +++++++++++++++++++++++++++---------- bgpd/bgp_ecommunity.h | 4 +-- bgpd/bgp_evpn.c | 4 +-- bgpd/bgp_pbr.c | 3 ++- bgpd/rfapi/rfapi.c | 8 +++--- bgpd/rfapi/rfapi_import.c | 2 +- bgpd/rfapi/vnc_export_bgp.c | 4 +-- bgpd/rfapi/vnc_import_bgp.c | 2 +- 9 files changed, 56 insertions(+), 27 deletions(-) diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 7239ddef93..65072088ae 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -45,7 +45,7 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac) memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN); if (!attr->ecommunity) attr->ecommunity = ecommunity_new(); - ecommunity_add_val(attr->ecommunity, &routermac_ecom); + ecommunity_add_val(attr->ecommunity, &routermac_ecom, false, false); ecommunity_str(attr->ecommunity); } diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index dd97a3d213..0fbbc885b9 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -74,10 +74,16 @@ static void ecommunity_hash_free(struct ecommunity *ecom) Attribute structure. When the value is already exists in the structure, we don't add the value. Newly added value is sorted by numerical order. When the value is added to the structure return 1 - else return 0. */ -bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval) + else return 0. + The additional parameters 'unique' and 'overwrite' ensure a particular + extended community (based on type and sub-type) is present only + once and whether the new value should replace what is existing or + not. +*/ +bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, + bool unique, bool overwrite) { - int c; + int c, ins_idx; /* When this is fist value, just add it. */ if (ecom->val == NULL) { @@ -88,25 +94,45 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval) } /* If the value already exists in the structure return 0. */ + /* check also if the extended community itself exists. */ c = 0; + ins_idx = -1; for (uint8_t *p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + if (unique) { + if (p[0] == eval->val[0] && + p[1] == eval->val[1]) { + if (overwrite) { + memcpy(p, eval->val, ECOMMUNITY_SIZE); + return 1; + } + return 0; + } + } int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE); if (ret == 0) - return false; - else if (ret > 0) - break; + return 0; + if (ret > 0) { + if (!unique) + break; + if (ins_idx == -1) + ins_idx = c; + } } + if (ins_idx == -1) + ins_idx = c; + /* Add the value to the structure with numerical sorting. */ ecom->size++; ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, ecom->size * ECOMMUNITY_SIZE); - memmove(ecom->val + ((c + 1) * ECOMMUNITY_SIZE), - ecom->val + (c * ECOMMUNITY_SIZE), - (ecom->size - 1 - c) * ECOMMUNITY_SIZE); - memcpy(ecom->val + (c * ECOMMUNITY_SIZE), eval->val, ECOMMUNITY_SIZE); + memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE), + ecom->val + (ins_idx * ECOMMUNITY_SIZE), + (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE); + memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE), + eval->val, ECOMMUNITY_SIZE); return true; } @@ -128,7 +154,7 @@ struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) for (i = 0; i < ecom->size; i++) { eval = (struct ecommunity_val *)(ecom->val + (i * ECOMMUNITY_SIZE)); - ecommunity_add_val(new, eval); + ecommunity_add_val(new, eval, false, false); } return new; } @@ -543,7 +569,7 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, if (ecom == NULL) ecom = ecommunity_new(); eval.val[1] = type; - ecommunity_add_val(ecom, &eval); + ecommunity_add_val(ecom, &eval, false, false); break; case ecommunity_token_unknown: default: @@ -927,8 +953,8 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, /* remove ext. community matching type and subtype * return 1 on success ( removed ), 0 otherwise (not present) */ -extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, - uint8_t subtype) +bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, + uint8_t subtype) { uint8_t *p, *q, *new; int c, found = 0; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 3aeafb75ea..094c677ff9 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -206,11 +206,11 @@ extern char *ecommunity_str(struct ecommunity *); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); extern bool ecommunity_add_val(struct ecommunity *ecom, - struct ecommunity_val *eval); + struct ecommunity_val *eval, + bool unique, bool overwrite); /* for vpn */ extern struct ecommunity *ecommunity_new(void); -extern bool ecommunity_add_val(struct ecommunity *, struct ecommunity_val *); extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, uint8_t subtype); extern struct ecommunity *ecommunity_new(void); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index a77a1e912e..ed2b3c9235 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -548,7 +548,7 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecomadd = ecommunity_new(); - ecommunity_add_val(ecomadd, &eval); + ecommunity_add_val(ecomadd, &eval, false, false); for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) if (ecommunity_cmp(ecomadd, ecom)) ecom_found = true; @@ -4633,7 +4633,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecom_auto = ecommunity_new(); - ecommunity_add_val(ecom_auto, &eval); + ecommunity_add_val(ecom_auto, &eval, false, false); node_to_del = NULL; for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index fd3fad63f5..ab134b15c4 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -738,7 +738,8 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, ecom_copy.val[0] &= ~ECOMMUNITY_ENCODE_TRANS_EXP; ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; - ecommunity_add_val(eckey, &ecom_copy); + ecommunity_add_val(eckey, &ecom_copy, + false, false); api_action->action = ACTION_REDIRECT; api_action->u.redirect_vrf = diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 435b61edf0..a0d8995a0f 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -838,7 +838,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP; beec.val[6] = ((TunnelType) >> 8) & 0xff; beec.val[7] = (TunnelType)&0xff; - ecommunity_add_val(attr.ecommunity, &beec); + ecommunity_add_val(attr.ecommunity, &beec, false, false); } /* @@ -2650,7 +2650,8 @@ int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, ecom_value.val[7] = (l2o->logical_net_id >> 0) & 0xff; rtlist = ecommunity_new(); - ecommunity_add_val(rtlist, &ecom_value); + ecommunity_add_val(rtlist, &ecom_value, + false, false); } if (l2o->tag_id) { as_t as = bgp->as; @@ -2675,7 +2676,8 @@ int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, ecom_value.val[7] = val & 0xff; if (rtlist == NULL) rtlist = ecommunity_new(); - ecommunity_add_val(rtlist, &ecom_value); + ecommunity_add_val(rtlist, &ecom_value, + false, false); } } diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index d058fe3b28..d261a3ee88 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -573,7 +573,7 @@ struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, uint32_t lni) eval.val[7] = (lni >> 0) & 0xff; enew = ecommunity_new(); - ecommunity_add_val(enew, &eval); + ecommunity_add_val(enew, &eval, false, false); it->rt_import_list = enew; for (afi = AFI_IP; afi < AFI_MAX; ++afi) { diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index a7aa4c66fa..bd3395b49f 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -532,7 +532,7 @@ static struct ecommunity *vnc_route_origin_ecom(struct agg_node *rn) &bpi->attr->mp_nexthop_global_in.s_addr, 4); roec.val[6] = 0; roec.val[7] = 0; - ecommunity_add_val(new, &roec); + ecommunity_add_val(new, &roec, false, false); break; case AF_INET6: /* No support for IPv6 addresses in extended communities @@ -563,7 +563,7 @@ static struct ecommunity *vnc_route_origin_ecom_single(struct in_addr *origin) new = ecommunity_new(); assert(new); - ecommunity_add_val(new, &roec); + ecommunity_add_val(new, &roec, false, false); if (!new->size) { ecommunity_free(&new); diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 915dfaabf2..ac5beed0e3 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -415,7 +415,7 @@ static int process_unicast_route(struct bgp *bgp, /* in */ localadmin = htons(hc->resolve_nve_roo_local_admin); memcpy(vnc_gateway_magic.val + 6, (char *)&localadmin, 2); - ecommunity_add_val(*ecom, &vnc_gateway_magic); + ecommunity_add_val(*ecom, &vnc_gateway_magic, false, false); } return 0; -- 2.39.5