bgpd: Address performance issues in BGP route aggregation.
{
as_t *newas;
+ if (!seg)
+ return seg;
+
newas = XREALLOC(MTYPE_AS_SEG_DATA, seg->as,
ASSEGMENT_DATA_SIZE(seg->length + num, 1));
while (last && last->next)
last = last->next;
- last->next = as2->segments;
+ if (last)
+ last->next = as2->segments;
as2->segments = new;
aspath_str_update(as2, false);
return as2;
* bypass the merged seg2, and attach any chain after it
* to chain descending from as2's head
*/
- as2segtail->next = as2seghead->next;
+ if (as2segtail)
+ as2segtail->next = as2seghead->next;
/* as2->segments is now referenceless and useless */
assegment_free(as2seghead);
void *))aspath_show_all_iterator,
vty);
}
+
+static struct aspath *bgp_aggr_aspath_lookup(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ return hash_lookup(aggregate->aspath_hash, aspath);
+}
+
+static void *bgp_aggr_aspath_hash_alloc(void *p)
+{
+ struct aspath *ref = (struct aspath *)p;
+ struct aspath *aspath = NULL;
+
+ aspath = aspath_dup(ref);
+ return aspath;
+}
+
+static void bgp_aggr_aspath_prepare(struct hash_backet *hb, void *arg)
+{
+ struct aspath *asmerge = NULL;
+ struct aspath *hb_aspath = hb->data;
+ struct aspath **aggr_aspath = arg;
+
+ if (*aggr_aspath) {
+ asmerge = aspath_aggregate(*aggr_aspath, hb_aspath);
+ aspath_free(*aggr_aspath);
+ *aggr_aspath = asmerge;
+ } else
+ *aggr_aspath = aspath_dup(hb_aspath);
+}
+
+void bgp_aggr_aspath_remove(void *arg)
+{
+ struct aspath *aspath = arg;
+
+ aspath_free(aspath);
+}
+
+void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ struct aspath *aggr_aspath = NULL;
+
+ if ((aggregate == NULL) || (aspath == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->aspath_hash == NULL)
+ aggregate->aspath_hash = hash_create(
+ aspath_key_make, aspath_cmp,
+ "BGP Aggregator as-path hash");
+
+ aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath);
+ if (aggr_aspath == NULL) {
+ /* Insert as-path into hash.
+ */
+ aggr_aspath = hash_get(aggregate->aspath_hash, aspath,
+ bgp_aggr_aspath_hash_alloc);
+
+ /* Compute aggregate's as-path.
+ */
+ hash_iterate(aggregate->aspath_hash,
+ bgp_aggr_aspath_prepare,
+ &aggregate->aspath);
+ }
+
+ /* Increment refernce counter.
+ */
+ aggr_aspath->refcnt++;
+}
+
+void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ struct aspath *aggr_aspath = NULL;
+ struct aspath *ret_aspath = NULL;
+
+ if ((aggregate == NULL) || (aspath == NULL))
+ return;
+
+ if (aggregate->aspath_hash == NULL)
+ return;
+
+ /* Look-up the aspath in the hash.
+ */
+ aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath);
+ if (aggr_aspath) {
+ aggr_aspath->refcnt--;
+
+ if (aggr_aspath->refcnt == 0) {
+ ret_aspath = hash_release(aggregate->aspath_hash,
+ aggr_aspath);
+ aspath_free(ret_aspath);
+
+ /* Remove aggregate's old as-path.
+ */
+ aspath_free(aggregate->aspath);
+ aggregate->aspath = NULL;
+
+ /* Compute aggregate's as-path.
+ */
+ hash_iterate(aggregate->aspath_hash,
+ bgp_aggr_aspath_prepare,
+ &aggregate->aspath);
+ }
+ }
+}
#define _QUAGGA_BGP_ASPATH_H
#include "lib/json.h"
+#include "bgpd/bgp_route.h"
/* AS path segment type. */
#define AS_SET 1
/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
extern uint8_t *aspath_snmp_pathseg(struct aspath *, size_t *);
+extern void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+extern void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+extern void bgp_aggr_aspath_remove(void *arg);
+
#endif /* _QUAGGA_BGP_ASPATH_H */
hash_free(comhash);
comhash = NULL;
}
+
+static struct community *bgp_aggr_community_lookup(
+ struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ return hash_lookup(aggregate->community_hash, community);
+}
+
+static void *bgp_aggr_communty_hash_alloc(void *p)
+{
+ struct community *ref = (struct community *)p;
+ struct community *community = NULL;
+
+ community = community_dup(ref);
+ return community;
+}
+
+static void bgp_aggr_community_prepare(struct hash_backet *hb, void *arg)
+{
+ struct community *commerge = NULL;
+ struct community *hb_community = hb->data;
+ struct community **aggr_community = arg;
+
+ if (*aggr_community) {
+ commerge = community_merge(*aggr_community, hb_community);
+ *aggr_community = community_uniq_sort(commerge);
+ community_free(&commerge);
+ } else
+ *aggr_community = community_dup(hb_community);
+}
+
+void bgp_aggr_community_remove(void *arg)
+{
+ struct community *community = arg;
+
+ community_free(&community);
+}
+
+void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ struct community *aggr_community = NULL;
+
+ if ((aggregate == NULL) || (community == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->community_hash == NULL)
+ aggregate->community_hash = hash_create(
+ (unsigned int (*)(void *))community_hash_make,
+ (bool (*)(const void *, const void *))community_cmp,
+ "BGP Aggregator community hash");
+
+ aggr_community = bgp_aggr_community_lookup(aggregate, community);
+ if (aggr_community == NULL) {
+ /* Insert community into hash.
+ */
+ aggr_community = hash_get(aggregate->community_hash, community,
+ bgp_aggr_communty_hash_alloc);
+
+ /* Re-compute aggregate's community.
+ */
+ if (aggregate->community)
+ community_free(&aggregate->community);
+
+ hash_iterate(aggregate->community_hash,
+ bgp_aggr_community_prepare,
+ &aggregate->community);
+ }
+
+ /* Increment refernce counter.
+ */
+ aggr_community->refcnt++;
+}
+
+void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ struct community *aggr_community = NULL;
+ struct community *ret_comm = NULL;
+
+ if ((aggregate == NULL) || (community == NULL))
+ return;
+
+ if (aggregate->community_hash == NULL)
+ return;
+
+ /* Look-up the community in the hash.
+ */
+ aggr_community = bgp_aggr_community_lookup(aggregate, community);
+ if (aggr_community) {
+ aggr_community->refcnt--;
+
+ if (aggr_community->refcnt == 0) {
+ ret_comm = hash_release(aggregate->community_hash,
+ aggr_community);
+ community_free(&ret_comm);
+
+ community_free(&aggregate->community);
+
+ /* Compute aggregate's community.
+ */
+ hash_iterate(aggregate->community_hash,
+ bgp_aggr_community_prepare,
+ &aggregate->community);
+ }
+ }
+}
#define _QUAGGA_BGP_COMMUNITY_H
#include "lib/json.h"
+#include "bgpd/bgp_route.h"
/* Communities attribute. */
struct community {
extern unsigned long community_count(void);
extern struct hash *community_hash(void);
extern uint32_t community_val_get(struct community *com, int i);
+extern void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
+ struct community *community);
+extern void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
+ struct community *community);
+extern void bgp_aggr_community_remove(void *arg);
#endif /* _QUAGGA_BGP_COMMUNITY_H */
return -1;
return 0;
}
+
+static struct ecommunity *bgp_aggr_ecommunity_lookup(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ return hash_lookup(aggregate->ecommunity_hash, ecommunity);
+}
+
+static void *bgp_aggr_ecommunty_hash_alloc(void *p)
+{
+ struct ecommunity *ref = (struct ecommunity *)p;
+ struct ecommunity *ecommunity = NULL;
+
+ ecommunity = ecommunity_dup(ref);
+ return ecommunity;
+}
+
+static void bgp_aggr_ecommunity_prepare(struct hash_backet *hb, void *arg)
+{
+ struct ecommunity *ecommerge = NULL;
+ struct ecommunity *hb_ecommunity = hb->data;
+ struct ecommunity **aggr_ecommunity = arg;
+
+ if (*aggr_ecommunity) {
+ ecommerge = ecommunity_merge(*aggr_ecommunity, hb_ecommunity);
+ *aggr_ecommunity = ecommunity_uniq_sort(ecommerge);
+ ecommunity_free(&ecommerge);
+ } else
+ *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
+}
+
+void bgp_aggr_ecommunity_remove(void *arg)
+{
+ struct ecommunity *ecommunity = arg;
+
+ ecommunity_free(&ecommunity);
+}
+
+void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ struct ecommunity *aggr_ecommunity = NULL;
+
+ if ((aggregate == NULL) || (ecommunity == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->ecommunity_hash == NULL)
+ aggregate->ecommunity_hash = hash_create(
+ ecommunity_hash_make, ecommunity_cmp,
+ "BGP Aggregator ecommunity hash");
+
+ aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
+ if (aggr_ecommunity == NULL) {
+ /* Insert ecommunity into hash.
+ */
+ aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
+ ecommunity,
+ bgp_aggr_ecommunty_hash_alloc);
+
+ /* Re-compute aggregate's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+
+ hash_iterate(aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_prepare,
+ &aggregate->ecommunity);
+ }
+
+ /* Increment refernce counter.
+ */
+ aggr_ecommunity->refcnt++;
+}
+
+void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ struct ecommunity *aggr_ecommunity = NULL;
+ struct ecommunity *ret_ecomm = NULL;
+
+ if ((aggregate == NULL) || (ecommunity == NULL))
+ return;
+
+ if (aggregate->ecommunity_hash == NULL)
+ return;
+
+ /* Look-up the ecommunity in the hash.
+ */
+ aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
+ if (aggr_ecommunity) {
+ aggr_ecommunity->refcnt--;
+
+ if (aggr_ecommunity->refcnt == 0) {
+ ret_ecomm = hash_release(aggregate->ecommunity_hash,
+ aggr_ecommunity);
+ ecommunity_free(&ret_ecomm);
+
+ ecommunity_free(&aggregate->ecommunity);
+
+ /* Compute aggregate's ecommunity.
+ */
+ hash_iterate(aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_prepare,
+ &aggregate->ecommunity);
+ }
+ }
+}
#ifndef _QUAGGA_BGP_ECOMMUNITY_H
#define _QUAGGA_BGP_ECOMMUNITY_H
+#include "bgpd/bgp_route.h"
+
/* High-order octet of the Extended Communities type field. */
#define ECOMMUNITY_ENCODE_AS 0x00
#define ECOMMUNITY_ENCODE_IP 0x01
extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
struct bgp_pbr_entry_action *api);
+extern void bgp_compute_aggregate_ecommunity(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+extern void bgp_remove_ecommunity_from_aggregate(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+extern void bgp_aggr_ecommunity_remove(void *arg);
+
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */
i++;
}
}
+
+static struct lcommunity *bgp_aggr_lcommunity_lookup(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ return hash_lookup(aggregate->lcommunity_hash, lcommunity);
+}
+
+static void *bgp_aggr_lcommunty_hash_alloc(void *p)
+{
+ struct lcommunity *ref = (struct lcommunity *)p;
+ struct lcommunity *lcommunity = NULL;
+
+ lcommunity = lcommunity_dup(ref);
+ return lcommunity;
+}
+
+static void bgp_aggr_lcommunity_prepare(struct hash_backet *hb, void *arg)
+{
+ struct lcommunity *lcommerge = NULL;
+ struct lcommunity *hb_lcommunity = hb->data;
+ struct lcommunity **aggr_lcommunity = arg;
+
+ if (*aggr_lcommunity) {
+ lcommerge = lcommunity_merge(*aggr_lcommunity, hb_lcommunity);
+ *aggr_lcommunity = lcommunity_uniq_sort(lcommerge);
+ lcommunity_free(&lcommerge);
+ } else
+ *aggr_lcommunity = lcommunity_dup(hb_lcommunity);
+}
+
+void bgp_aggr_lcommunity_remove(void *arg)
+{
+ struct lcommunity *lcommunity = arg;
+
+ lcommunity_free(&lcommunity);
+}
+
+void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ struct lcommunity *aggr_lcommunity = NULL;
+
+ if ((aggregate == NULL) || (lcommunity == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->lcommunity_hash == NULL)
+ aggregate->lcommunity_hash = hash_create(
+ lcommunity_hash_make, lcommunity_cmp,
+ "BGP Aggregator lcommunity hash");
+
+ aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
+ if (aggr_lcommunity == NULL) {
+ /* Insert lcommunity into hash.
+ */
+ aggr_lcommunity = hash_get(aggregate->lcommunity_hash,
+ lcommunity,
+ bgp_aggr_lcommunty_hash_alloc);
+
+ /* Re-compute aggregate's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+
+ hash_iterate(aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_prepare,
+ &aggregate->lcommunity);
+ }
+
+ /* Increment refernce counter.
+ */
+ aggr_lcommunity->refcnt++;
+}
+
+void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ struct lcommunity *aggr_lcommunity = NULL;
+ struct lcommunity *ret_lcomm = NULL;
+
+ if ((aggregate == NULL) || (lcommunity == NULL))
+ return;
+
+ if (aggregate->lcommunity_hash == NULL)
+ return;
+
+ /* Look-up the lcommunity in the hash.
+ */
+ aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
+ if (aggr_lcommunity) {
+ aggr_lcommunity->refcnt--;
+
+ if (aggr_lcommunity->refcnt == 0) {
+ ret_lcomm = hash_release(aggregate->lcommunity_hash,
+ aggr_lcommunity);
+ lcommunity_free(&ret_lcomm);
+
+ lcommunity_free(&aggregate->lcommunity);
+
+ /* Compute aggregate's lcommunity.
+ */
+ hash_iterate(aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_prepare,
+ &aggregate->lcommunity);
+ }
+ }
+}
#define _QUAGGA_BGP_LCOMMUNITY_H
#include "lib/json.h"
+#include "bgpd/bgp_route.h"
/* Large Communities value is twelve octets long. */
#define LCOMMUNITY_SIZE 12
extern char *lcommunity_str(struct lcommunity *, bool make_json);
extern int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr);
extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr);
+
+extern void bgp_compute_aggregate_lcommunity(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+extern void bgp_remove_lcommunity_from_aggregate(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+extern void bgp_aggr_lcommunity_remove(void *arg);
+
#endif /* _QUAGGA_BGP_LCOMMUNITY_H */
label_index ? (uint32_t)label_index : BGP_INVALID_LABEL_INDEX);
}
-/* Aggreagete address:
-
- advertise-map Set condition to advertise attribute
- as-set Generate AS set path information
- attribute-map Set attributes of aggregate
- route-map Set parameters of aggregate
- summary-only Filter more specific routes from updates
- suppress-map Conditionally filter more specific routes from updates
- <cr>
- */
-struct bgp_aggregate {
- /* Summary-only flag. */
- uint8_t summary_only;
-
- /* AS set generation. */
- uint8_t as_set;
-
- /* Route-map for aggregated route. */
- struct route_map *map;
-
- /* Suppress-count. */
- unsigned long count;
-
- /* SAFI configuration. */
- safi_t safi;
-};
-
static struct bgp_aggregate *bgp_aggregate_new(void)
{
return XCALLOC(MTYPE_BGP_AGGREGATE, sizeof(struct bgp_aggregate));
/* Update an aggregate as routes are added/removed from the BGP table */
static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p,
- struct bgp_path_info *pinew, afi_t afi,
- safi_t safi, struct bgp_path_info *del,
+ afi_t afi, safi_t safi,
struct bgp_aggregate *aggregate)
{
struct bgp_table *table;
struct bgp_node *rn;
uint8_t origin;
struct aspath *aspath = NULL;
- struct aspath *asmerge = NULL;
struct community *community = NULL;
- struct community *commerge = NULL;
struct ecommunity *ecommunity = NULL;
- struct ecommunity *ecommerge = NULL;
struct lcommunity *lcommunity = NULL;
- struct lcommunity *lcommerge = NULL;
struct bgp_path_info *pi;
unsigned long match = 0;
uint8_t atomic_aggregate = 0;
if (BGP_PATH_HOLDDOWN(pi))
continue;
- if (del && pi == del)
- continue;
-
if (pi->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
atomic_aggregate = 1;
* route MUST have the ORIGIN attribute with the value
* EGP.
*/
- if (origin < pi->attr->origin)
- origin = pi->attr->origin;
+ switch (pi->attr->origin) {
+ case BGP_ORIGIN_INCOMPLETE:
+ aggregate->incomplete_origin_count++;
+ break;
+ case BGP_ORIGIN_EGP:
+ aggregate->egp_origin_count++;
+ break;
+ default:
+ /*Do nothing.
+ */
+ break;
+ }
if (!aggregate->as_set)
continue;
* as-set aggregate route generate origin, as path,
* and community aggregation.
*/
- if (aspath) {
- asmerge = aspath_aggregate(aspath,
- pi->attr->aspath);
- aspath_free(aspath);
- aspath = asmerge;
- } else
- aspath = aspath_dup(pi->attr->aspath);
-
- if (pi->attr->community) {
- if (community) {
- commerge = community_merge(
- community, pi->attr->community);
- community =
- community_uniq_sort(commerge);
- community_free(&commerge);
- } else
- community = community_dup(
- pi->attr->community);
- }
-
- if (pi->attr->ecommunity) {
- if (ecommunity) {
- ecommerge = ecommunity_merge(
- ecommunity,
- pi->attr->ecommunity);
- ecommunity =
- ecommunity_uniq_sort(ecommerge);
- ecommunity_free(&ecommerge);
- } else
- ecommunity = ecommunity_dup(
- pi->attr->ecommunity);
- }
-
- if (pi->attr->lcommunity) {
- if (lcommunity) {
- lcommerge = lcommunity_merge(
- lcommunity,
- pi->attr->lcommunity);
- lcommunity =
- lcommunity_uniq_sort(lcommerge);
- lcommunity_free(&lcommerge);
- } else
- lcommunity = lcommunity_dup(
- pi->attr->lcommunity);
- }
+ /* Compute aggregate route's as-path.
+ */
+ bgp_compute_aggregate_aspath(aggregate,
+ pi->attr->aspath);
+
+ /* Compute aggregate route's community.
+ */
+ if (pi->attr->community)
+ bgp_compute_aggregate_community(
+ aggregate,
+ pi->attr->community);
+
+ /* Compute aggregate route's extended community.
+ */
+ if (pi->attr->ecommunity)
+ bgp_compute_aggregate_ecommunity(
+ aggregate,
+ pi->attr->ecommunity);
+
+ /* Compute aggregate route's large community.
+ */
+ if (pi->attr->lcommunity)
+ bgp_compute_aggregate_lcommunity(
+ aggregate,
+ pi->attr->lcommunity);
}
if (match)
bgp_process(bgp, rn, afi, safi);
}
bgp_unlock_node(top);
- if (pinew) {
- aggregate->count++;
- if (aggregate->summary_only)
- (bgp_path_info_extra_get(pinew))->suppress++;
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
- if (origin < pinew->attr->origin)
- origin = pinew->attr->origin;
+ if (aggregate->as_set) {
+ if (aggregate->aspath)
+ /* Retrieve aggregate route's as-path.
+ */
+ aspath = aspath_dup(aggregate->aspath);
- if (aggregate->as_set) {
- if (aspath) {
- asmerge = aspath_aggregate(aspath,
- pinew->attr->aspath);
- aspath_free(aspath);
- aspath = asmerge;
- } else
- aspath = aspath_dup(pinew->attr->aspath);
+ if (aggregate->community)
+ /* Retrieve aggregate route's community.
+ */
+ community = community_dup(aggregate->community);
- if (pinew->attr->community) {
- if (community) {
- commerge = community_merge(
- community,
- pinew->attr->community);
- community =
- community_uniq_sort(commerge);
- community_free(&commerge);
- } else
- community = community_dup(
- pinew->attr->community);
- }
+ if (aggregate->ecommunity)
+ /* Retrieve aggregate route's ecommunity.
+ */
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
- if (pinew->attr->ecommunity) {
- if (ecommunity) {
- ecommerge = ecommunity_merge(
- ecommunity,
- pinew->attr->ecommunity);
- ecommunity =
- ecommunity_uniq_sort(ecommerge);
- ecommunity_free(&ecommerge);
- } else
- ecommunity = ecommunity_dup(
- pinew->attr->ecommunity);
- }
-
- if (pinew->attr->lcommunity) {
- if (lcommunity) {
- lcommerge = lcommunity_merge(
- lcommunity,
- pinew->attr->lcommunity);
- lcommunity =
- lcommunity_uniq_sort(lcommerge);
- lcommunity_free(&lcommerge);
- } else
- lcommunity = lcommunity_dup(
- pinew->attr->lcommunity);
- }
- }
+ if (aggregate->lcommunity)
+ /* Retrieve aggregate route's lcommunity.
+ */
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
}
bgp_aggregate_install(bgp, afi, safi, p, origin, aspath, community,
ecommunity, lcommunity, atomic_aggregate,
aggregate);
-
- if (aggregate->count == 0) {
- if (aspath)
- aspath_free(aspath);
- if (community)
- community_free(&community);
- if (ecommunity)
- ecommunity_free(&ecommunity);
- if (lcommunity)
- lcommunity_free(&lcommunity);
- }
}
static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi,
}
}
aggregate->count--;
+
+ if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
+ aggregate->incomplete_origin_count--;
+ else if (pi->attr->origin == BGP_ORIGIN_EGP)
+ aggregate->egp_origin_count--;
+
+ if (aggregate->as_set) {
+ /* Remove as-path from aggregate.
+ */
+ bgp_remove_aspath_from_aggregate(
+ aggregate,
+ pi->attr->aspath);
+
+ if (pi->attr->community)
+ /* Remove community from aggregate.
+ */
+ bgp_remove_community_from_aggregate(
+ aggregate,
+ pi->attr->community);
+
+ if (pi->attr->ecommunity)
+ /* Remove ecommunity from aggregate.
+ */
+ bgp_remove_ecommunity_from_aggregate(
+ aggregate,
+ pi->attr->ecommunity);
+
+ if (pi->attr->lcommunity)
+ /* Remove lcommunity from aggregate.
+ */
+ bgp_remove_lcommunity_from_aggregate(
+ aggregate,
+ pi->attr->lcommunity);
+ }
+
}
/* If this node was suppressed, process the change. */
bgp_unlock_node(top);
}
+static void bgp_add_route_to_aggregate(struct bgp *bgp, struct prefix *aggr_p,
+ struct bgp_path_info *pinew, afi_t afi,
+ safi_t safi,
+ struct bgp_aggregate *aggregate)
+{
+ uint8_t origin;
+ struct aspath *aspath = NULL;
+ uint8_t atomic_aggregate = 0;
+ struct community *community = NULL;
+ struct ecommunity *ecommunity = NULL;
+ struct lcommunity *lcommunity = NULL;
+
+ /* ORIGIN attribute: If at least one route among routes that are
+ * aggregated has ORIGIN with the value INCOMPLETE, then the
+ * aggregated route must have the ORIGIN attribute with the value
+ * INCOMPLETE. Otherwise, if at least one route among routes that
+ * are aggregated has ORIGIN with the value EGP, then the aggregated
+ * route must have the origin attribute with the value EGP. In all
+ * other case the value of the ORIGIN attribute of the aggregated
+ * route is INTERNAL.
+ */
+ origin = BGP_ORIGIN_IGP;
+
+ aggregate->count++;
+
+ if (aggregate->summary_only)
+ (bgp_path_info_extra_get(pinew))->suppress++;
+
+ switch (pinew->attr->origin) {
+ case BGP_ORIGIN_INCOMPLETE:
+ aggregate->incomplete_origin_count++;
+ break;
+ case BGP_ORIGIN_EGP:
+ aggregate->egp_origin_count++;
+ break;
+ default:
+ /* Do nothing.
+ */
+ break;
+ }
+
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
+
+ if (aggregate->as_set) {
+ /* Compute aggregate route's as-path.
+ */
+ bgp_compute_aggregate_aspath(aggregate,
+ pinew->attr->aspath);
+
+ /* Compute aggregate route's community.
+ */
+ if (pinew->attr->community)
+ bgp_compute_aggregate_community(
+ aggregate,
+ pinew->attr->community);
+
+ /* Compute aggregate route's extended community.
+ */
+ if (pinew->attr->ecommunity)
+ bgp_compute_aggregate_ecommunity(
+ aggregate,
+ pinew->attr->ecommunity);
+
+ /* Compute aggregate route's large community.
+ */
+ if (pinew->attr->lcommunity)
+ bgp_compute_aggregate_lcommunity(
+ aggregate,
+ pinew->attr->lcommunity);
+
+ /* Retrieve aggregate route's as-path.
+ */
+ if (aggregate->aspath)
+ aspath = aspath_dup(aggregate->aspath);
+
+ /* Retrieve aggregate route's community.
+ */
+ if (aggregate->community)
+ community = community_dup(aggregate->community);
+
+ /* Retrieve aggregate route's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
+
+ /* Retrieve aggregate route's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
+ }
+
+ bgp_aggregate_install(bgp, afi, safi, aggr_p, origin,
+ aspath, community, ecommunity,
+ lcommunity, atomic_aggregate, aggregate);
+}
+
+static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi,
+ safi_t safi,
+ struct bgp_path_info *pi,
+ struct bgp_aggregate *aggregate,
+ struct prefix *aggr_p)
+{
+ uint8_t origin;
+ struct aspath *aspath = NULL;
+ uint8_t atomic_aggregate = 0;
+ struct community *community = NULL;
+ struct ecommunity *ecommunity = NULL;
+ struct lcommunity *lcommunity = NULL;
+ unsigned long match = 0;
+
+ if (BGP_PATH_HOLDDOWN(pi))
+ return;
+
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ return;
+
+ if (aggregate->summary_only
+ && pi->extra
+ && pi->extra->suppress > 0) {
+ pi->extra->suppress--;
+
+ if (pi->extra->suppress == 0) {
+ bgp_path_info_set_flag(pi->net, pi,
+ BGP_PATH_ATTR_CHANGED);
+ match++;
+ }
+ }
+
+ if (aggregate->count > 0)
+ aggregate->count--;
+
+ if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
+ aggregate->incomplete_origin_count--;
+ else if (pi->attr->origin == BGP_ORIGIN_EGP)
+ aggregate->egp_origin_count--;
+
+ if (aggregate->as_set) {
+ /* Remove as-path from aggregate.
+ */
+ bgp_remove_aspath_from_aggregate(aggregate,
+ pi->attr->aspath);
+
+ if (pi->attr->community)
+ /* Remove community from aggregate.
+ */
+ bgp_remove_community_from_aggregate(
+ aggregate,
+ pi->attr->community);
+
+ if (pi->attr->ecommunity)
+ /* Remove ecommunity from aggregate.
+ */
+ bgp_remove_ecommunity_from_aggregate(
+ aggregate,
+ pi->attr->ecommunity);
+
+ if (pi->attr->lcommunity)
+ /* Remove lcommunity from aggregate.
+ */
+ bgp_remove_lcommunity_from_aggregate(
+ aggregate,
+ pi->attr->lcommunity);
+ }
+
+ /* If this node was suppressed, process the change. */
+ if (match)
+ bgp_process(bgp, pi->net, afi, safi);
+
+ origin = BGP_ORIGIN_IGP;
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
+
+ if (aggregate->as_set) {
+ /* Retrieve aggregate route's as-path.
+ */
+ if (aggregate->aspath)
+ aspath = aspath_dup(aggregate->aspath);
+
+ /* Retrieve aggregate route's community.
+ */
+ if (aggregate->community)
+ community = community_dup(aggregate->community);
+
+ /* Retrieve aggregate route's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
+
+ /* Retrieve aggregate route's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
+ }
+
+ bgp_aggregate_install(bgp, afi, safi, aggr_p, origin,
+ aspath, community, ecommunity,
+ lcommunity, atomic_aggregate, aggregate);
+}
+
void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p,
struct bgp_path_info *pi, afi_t afi, safi_t safi)
{
for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) {
aggregate = bgp_node_get_bgp_aggregate_info(rn);
if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) {
- bgp_aggregate_delete(bgp, &rn->p, afi, safi, aggregate);
- bgp_aggregate_route(bgp, &rn->p, pi, afi, safi, NULL,
- aggregate);
+ bgp_add_route_to_aggregate(bgp, &rn->p, pi, afi,
+ safi, aggregate);
}
}
bgp_unlock_node(child);
for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) {
aggregate = bgp_node_get_bgp_aggregate_info(rn);
if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) {
- bgp_aggregate_delete(bgp, &rn->p, afi, safi, aggregate);
- bgp_aggregate_route(bgp, &rn->p, NULL, afi, safi, del,
- aggregate);
+ bgp_remove_route_from_aggregate(bgp, afi, safi,
+ del, aggregate, &rn->p);
}
}
bgp_unlock_node(child);
/* Unlock aggregate address configuration. */
bgp_node_set_bgp_aggregate_info(rn, NULL);
+
+ if (aggregate->community)
+ community_free(&aggregate->community);
+
+ if (aggregate->community_hash) {
+ /* Delete all communities in the hash.
+ */
+ hash_clean(aggregate->community_hash,
+ bgp_aggr_community_remove);
+ /* Free up the community_hash.
+ */
+ hash_free(aggregate->community_hash);
+ }
+
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+
+ if (aggregate->ecommunity_hash) {
+ /* Delete all ecommunities in the hash.
+ */
+ hash_clean(aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_remove);
+ /* Free up the ecommunity_hash.
+ */
+ hash_free(aggregate->ecommunity_hash);
+ }
+
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+
+ if (aggregate->lcommunity_hash) {
+ /* Delete all lcommunities in the hash.
+ */
+ hash_clean(aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_remove);
+ /* Free up the lcommunity_hash.
+ */
+ hash_free(aggregate->lcommunity_hash);
+ }
+
+ if (aggregate->aspath)
+ aspath_free(aggregate->aspath);
+
+ if (aggregate->aspath_hash) {
+ /* Delete all as-paths in the hash.
+ */
+ hash_clean(aggregate->aspath_hash,
+ bgp_aggr_aspath_remove);
+ /* Free up the aspath_hash.
+ */
+ hash_free(aggregate->aspath_hash);
+ }
+
bgp_aggregate_free(aggregate);
bgp_unlock_node(rn);
bgp_unlock_node(rn);
bgp_node_set_bgp_aggregate_info(rn, aggregate);
/* Aggregate address insert into BGP routing table. */
- bgp_aggregate_route(bgp, &p, NULL, afi, safi, NULL, aggregate);
+ bgp_aggregate_route(bgp, &p, afi, safi, aggregate);
return CMD_SUCCESS;
}
struct prefix gatewayIp;
};
+/* Aggreagete address:
+ *
+ * advertise-map Set condition to advertise attribute
+ * as-set Generate AS set path information
+ * attribute-map Set attributes of aggregate
+ * route-map Set parameters of aggregate
+ * summary-only Filter more specific routes from updates
+ * suppress-map Conditionally filter more specific routes from updates
+ * <cr>
+ */
+struct bgp_aggregate {
+ /* Summary-only flag. */
+ uint8_t summary_only;
+
+ /* AS set generation. */
+ uint8_t as_set;
+
+ /* Route-map for aggregated route. */
+ struct route_map *map;
+
+ /* Suppress-count. */
+ unsigned long count;
+
+ /* Count of routes of origin type incomplete under this aggregate. */
+ unsigned long incomplete_origin_count;
+
+ /* Count of routes of origin type egp under this aggregate. */
+ unsigned long egp_origin_count;
+
+ /* Hash containing the communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *community_hash;
+
+ /* Hash containing the extended communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *ecommunity_hash;
+
+ /* Hash containing the large communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *lcommunity_hash;
+
+ /* Hash containing the AS-Path of all the
+ * routes under this aggregate.
+ */
+ struct hash *aspath_hash;
+
+ /* Aggregate route's community. */
+ struct community *community;
+
+ /* Aggregate route's extended community. */
+ struct ecommunity *ecommunity;
+
+ /* Aggregate route's large community. */
+ struct lcommunity *lcommunity;
+
+ /* Aggregate route's as-path. */
+ struct aspath *aspath;
+
+ /* SAFI configuration. */
+ safi_t safi;
+};
+
#define BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen) \
((nhlen) < IPV4_MAX_BYTELEN \
? 0 \