]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #3743 from NaveenThanikachalam/2990_New
authorDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 1 Mar 2019 14:54:10 +0000 (09:54 -0500)
committerGitHub <noreply@github.com>
Fri, 1 Mar 2019 14:54:10 +0000 (09:54 -0500)
bgpd: Address performance issues in BGP route aggregation.

bgpd/bgp_aspath.c
bgpd/bgp_aspath.h
bgpd/bgp_community.c
bgpd/bgp_community.h
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_lcommunity.c
bgpd/bgp_lcommunity.h
bgpd/bgp_route.c
bgpd/bgp_route.h

index 51833394d997f779ae07175fe4db411d39969c2a..3bd3de031ccf4f6b62e270c30c4566abca4b5f2c 100644 (file)
@@ -212,6 +212,9 @@ static struct assegment *assegment_append_asns(struct assegment *seg,
 {
        as_t *newas;
 
+       if (!seg)
+               return seg;
+
        newas = XREALLOC(MTYPE_AS_SEG_DATA, seg->as,
                         ASSEGMENT_DATA_SIZE(seg->length + num, 1));
 
@@ -1372,7 +1375,8 @@ static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2)
        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;
@@ -1447,7 +1451,8 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *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);
@@ -2096,3 +2101,110 @@ void aspath_print_all_vty(struct vty *vty)
                                       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);
+               }
+       }
+}
index 9c9c687a6b7ae9d801dff98f2f2556d3446b733e..be5725c1aea739a5aa088b0eb786aa5036f2c1a9 100644 (file)
@@ -22,6 +22,7 @@
 #define _QUAGGA_BGP_ASPATH_H
 
 #include "lib/json.h"
+#include "bgpd/bgp_route.h"
 
 /* AS path segment type.  */
 #define AS_SET                       1
@@ -130,4 +131,10 @@ extern unsigned int aspath_has_as4(struct aspath *);
 /* 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 */
index 614e24ca4f377c870f94ff74cf6f402f37f94c3f..2e28c30950b91ac96c36e3d882a42f4260aafee7 100644 (file)
@@ -910,3 +910,112 @@ void community_finish(void)
        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);
+               }
+       }
+}
index e1545249d73dee637e33bcaa7d69d9abb0d09a74..4ff4d214a5606e0de610c909cad11ccf5f492122 100644 (file)
@@ -22,6 +22,7 @@
 #define _QUAGGA_BGP_COMMUNITY_H
 
 #include "lib/json.h"
+#include "bgpd/bgp_route.h"
 
 /* Communities attribute.  */
 struct community {
@@ -89,5 +90,10 @@ extern void community_del_val(struct community *, uint32_t *);
 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 */
index ed0900a7218c93b14df79672bc6f52541631d6e7..fcfaa388d91d031c7b3bd47fdb721f75dd93f4db 100644 (file)
@@ -1026,3 +1026,112 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
                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);
+               }
+       }
+}
index 519991da5a3ad55991c69400ed9ba140796647f8..62b213775391f64f599c94567a97423cacde894c 100644 (file)
@@ -21,6 +21,8 @@
 #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
@@ -184,4 +186,12 @@ struct bgp_pbr_entry_action;
 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 */
index cfc9af7777e81a0827bb606c1e12c571849733b7..1e458971926080e27238ffcc6dfd97bc3694ca40 100644 (file)
@@ -537,3 +537,112 @@ void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
                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);
+               }
+       }
+}
index 23c777d9fd38da5720f6b025ff18def08a1d3164..aa4e8c69fe4744e9a8eb3c24b6898ae26224ba90 100644 (file)
@@ -22,6 +22,7 @@
 #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
@@ -70,4 +71,13 @@ extern int lcommunity_match(const struct lcommunity *,
 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 */
index 82fbf136da90eb55bbfdfd91c3b2140cdb72ca1d..666254a62010bed4fef15aa48930844843332caa 100644 (file)
@@ -5584,33 +5584,6 @@ DEFPY(ipv6_bgp_network,
                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));
@@ -5736,8 +5709,7 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi,
 
 /* 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;
@@ -5745,13 +5717,9 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p,
        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;
@@ -5780,9 +5748,6 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p,
                        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;
@@ -5813,8 +5778,18 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p,
                         * 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;
@@ -5823,130 +5798,68 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p,
                         * 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,
@@ -5985,6 +5898,41 @@ 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. */
@@ -5994,6 +5942,210 @@ static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi,
        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)
 {
@@ -6020,9 +6172,8 @@ void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p,
        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);
@@ -6051,9 +6202,8 @@ void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p,
        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);
@@ -6095,6 +6245,59 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str,
 
        /* 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);
@@ -6148,7 +6351,7 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi,
        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;
 }
index 8efd3aea955431cffba9ed58279bb565f57b7db0..04a3c85f2c3ca6f7d5d787f879c4170090d17e9c 100644 (file)
@@ -272,6 +272,71 @@ struct bgp_static {
        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                                                           \