[PATCH 1/1] * Implement general aggregation protocol

Alexander V. Chernikov melifaro at ipfw.ru
Tue Feb 28 02:01:08 CET 2012


---
 configure.in         |    4 +-
 filter/config.Y      |    2 +-
 filter/filter.h      |    7 +-
 filter/trie.c        |  111 +++++++-
 nest/proto-hooks.c   |   11 +
 nest/proto.c         |    3 +
 nest/protocol.h      |    9 +-
 nest/rt-table.c      |   19 +-
 proto/agg/Doc        |    1 +
 proto/agg/Makefile   |    6 +
 proto/agg/agg.c      |  685 +++++++++++++++++++++++++++++++++++++++++++++
 proto/agg/agg.h      |   81 ++++++
 proto/agg/config.Y   |   99 +++++++
 proto/bgp/attrs.c    |  754 ++++++++++++++++++++++++++++++++++++++++++++++++++
 proto/bgp/bgp.c      |    7 +-
 proto/bgp/bgp.h      |    2 +
 sysdep/autoconf.h.in |    1 +
 17 files changed, 1778 insertions(+), 24 deletions(-)
 create mode 100644 proto/agg/Doc
 create mode 100644 proto/agg/Makefile
 create mode 100644 proto/agg/agg.c
 create mode 100644 proto/agg/agg.h
 create mode 100644 proto/agg/config.Y

diff --git a/configure.in b/configure.in
index 46a6ecd..aff445a 100644
--- a/configure.in
+++ b/configure.in
@@ -43,11 +43,11 @@ AC_SUBST(srcdir_rel_mf)
 if test "$enable_ipv6" = yes ; then
 	ip=ipv6
 	SUFFIX6=6
-	all_protocols=bgp,ospf,pipe,radv,rip,static
+	all_protocols=bgp,ospf,pipe,radv,rip,static,agg
 else
 	ip=ipv4
 	SUFFIX6=""
-	all_protocols=bgp,ospf,pipe,rip,static
+	all_protocols=bgp,ospf,pipe,rip,static,agg
 fi
 
 if test "$with_protocols" = all ; then
diff --git a/filter/config.Y b/filter/config.Y
index 0166d27..4c6dc48 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -557,7 +557,7 @@ fprefix:
  ;
 
 fprefix_set:
-   fprefix { $$ = f_new_trie(cfg_mem); trie_add_fprefix($$, &($1.val.px)); }
+   fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_fprefix($$, &($1.val.px)); }
  | fprefix_set ',' fprefix { $$ = $1; trie_add_fprefix($$, &($3.val.px)); }
  ;
 
diff --git a/filter/filter.h b/filter/filter.h
index 2cf4652..4c60abe 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -71,11 +71,13 @@ struct f_tree *build_tree(struct f_tree *);
 struct f_tree *find_tree(struct f_tree *t, struct f_val val);
 int same_tree(struct f_tree *t1, struct f_tree *t2);
 
-struct f_trie *f_new_trie(linpool *lp);
-void trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h);
+struct f_trie *f_new_trie(linpool *lp, size_t node_size);
+void *trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h);
 int trie_match_prefix(struct f_trie *t, ip_addr px, int plen);
+void *trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen);
 int trie_same(struct f_trie *t1, struct f_trie *t2);
 void trie_print(struct f_trie *t);
+void trie_walk(struct f_trie *t, void *func, void *data);
 
 void fprefix_get_bounds(struct f_prefix *px, int *l, int *h);
 
@@ -177,6 +179,7 @@ struct f_trie
 {
   linpool *lp;
   int zero;
+  size_t node_size;
   struct f_trie_node root;
 };
 
diff --git a/filter/trie.c b/filter/trie.c
index 581332c..12d7755 100644
--- a/filter/trie.c
+++ b/filter/trie.c
@@ -75,23 +75,24 @@
 #include "filter/filter.h"
 
 /**
- * f_new_trie
- *
- * Allocates and returns a new empty trie.
+ * f_new_trie - Allocates and returns a new empty trie.
+ * @lp: linear pool to allocate items from
+ * @node_size: element size to allocate
  */
 struct f_trie *
-f_new_trie(linpool *lp)
+f_new_trie(linpool *lp, size_t node_size)
 {
   struct f_trie * ret;
-  ret = lp_allocz(lp, sizeof(struct f_trie));
+  ret = lp_allocz(lp, sizeof(struct f_trie) + node_size - sizeof(struct f_trie_node));
   ret->lp = lp;
+  ret->node_size = node_size;
   return ret;
 }
 
 static inline struct f_trie_node *
 new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
 {
-  struct f_trie_node *n = lp_allocz(t->lp, sizeof(struct f_trie_node));
+  struct f_trie_node *n = lp_allocz(t->lp, t->node_size);
   n->plen = plen;
   n->addr = paddr;
   n->mask = pmask;
@@ -116,9 +117,13 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child)
  * Adds prefix (prefix pattern) @px/@plen to trie @t.  @l and @h are lower
  * and upper bounds on accepted prefix lengths, both inclusive.
  * 0 <= l, h <= 32 (128 for IPv6).
+ *
+ * Returns pointer to allocated node. Function can return pointer to
+ * existing node if @px and @plen are the same. If px/plen == 0/0 (or ::/0)
+ * pointer to root node is returned
  */
 
-void
+void *
 trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 {
   if (l == 0)
@@ -156,7 +161,7 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 	  attach_node(o, b);
 	  attach_node(b, n);
 	  attach_node(b, a);
-	  return;
+	  return a;
 	}
 
       if (plen < n->plen)
@@ -166,14 +171,14 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 	  struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
 	  attach_node(o, a);
 	  attach_node(a, n);
-	  return;
+	  return a;
 	}
 	
       if (plen == n->plen)
 	{
 	  /* We already found added node in trie. Just update accept mask */
 	  n->accept = ipa_or(n->accept, amask);
-	  return;
+	  return n;
 	}
 
       /* Update accept mask part M2 and go deeper */
@@ -187,6 +192,8 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
   /* We add new tail node 'a' after node 'o' */
   struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
   attach_node(o, a);
+
+  return a;
 }
 
 /**
@@ -234,6 +241,90 @@ trie_match_prefix(struct f_trie *t, ip_addr px, int plen)
   return 0;
 }
 
+#define NODE_IS_BRANCHING(x)	(*((u32 *)(((struct f_trie_node *)(x)) + 1)) == 0)
+/**
+ * trie_match_longest_prefix - find longest prefix match
+ * @t: trie
+ * @px: prefix address
+ * @plen: prefix length
+ *
+ * Tries to find a matching prefix pattern in the trie such that
+ * prefix @px/@plen matches that prefix pattern. Returns 1 if there
+ * is such prefix pattern in the trie.
+ */
+void *
+trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen)
+{
+  ip_addr pmask = ipa_mkmask(plen);
+  ip_addr paddr = ipa_and(px, pmask);
+  ip_addr cmask;
+  struct f_trie_node *n = &t->root, *parent = NULL;
+
+  /* Skip root node since it is cath-all node */
+  n = n->c[(ipa_getbit(paddr, 0)) ? 1 : 0];
+
+  while (n)
+    {
+      cmask = ipa_and(n->mask, pmask);
+
+      /* We are out of path */
+      if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
+	break;
+
+      /* Mask is too specific */
+      if (n->plen > plen)
+	break;
+
+      /* Do not save pointer to branching nodes */
+      if (!NODE_IS_BRANCHING(n))
+	parent = n;
+
+      /* Choose children */
+      n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0];
+    }
+
+  /*
+   * parent is either
+   * 1) NULL (if the first non-null node does not exist oris out of path)
+   * or 
+   * 2) points to the last entry that match
+   *
+   * In former case we check if catch-all prefix really exists and return
+   * either pointer to root node or NULL. In latter case we simply return parent.
+   */
+
+  return parent ? parent : (t->zero ? &t->root : NULL);
+}
+
+static void
+trie_walk_call(struct f_trie_node *n, void *func, void *data)
+{
+  void (*f)(struct f_trie_node *, void *) = func;
+
+  if (n)
+    f(n, data);
+
+  if (n->c[0])
+    trie_walk_call(n->c[0], func, data);
+ 
+  if (n->c[1])
+    trie_walk_call(n->c[1], func, data);
+}
+
+void
+trie_walk(struct f_trie *t, void *func, void *data)
+{
+  void (*f)(struct f_trie_node *, void *) = func;
+
+  if (t->zero)
+    f(&t->root, data);
+
+  if (t->root.c[0])
+    trie_walk_call(t->root.c[0], func, data);
+  if (t->root.c[1])
+  trie_walk_call(t->root.c[1], func, data);
+}
+
 static int
 trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2)
 {
diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c
index 2582c48..1b59fbb 100644
--- a/nest/proto-hooks.c
+++ b/nest/proto-hooks.c
@@ -150,6 +150,17 @@ int get_attr(eattr *a, byte *buf, int buflen)
 { DUMMY; }
 
 /**
+ * get_route_ainfo - get additional route information
+ * @c: pointer to cli
+ * @cli_val: cli format value
+ * @e: a route entry
+ *
+ * This hook is called after printing extended route attributes
+ */
+void get_route_ainfo(struct cli *c, int cli_val, rte *e)
+{ DUMMY; }
+
+/**
  * if_notify - notify instance about interface changes
  * @p: protocol instance
  * @flags: interface change flags
diff --git a/nest/proto.c b/nest/proto.c
index 0fc72ce..a48656c 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -633,6 +633,9 @@ protos_build(void)
 #ifdef CONFIG_BGP
   proto_build(&proto_bgp);
 #endif
+#ifdef CONFIG_AGG
+  proto_build(&proto_agg);
+#endif
   proto_pool = rp_new(&root_pool, "Protocols");
   proto_flush_event = ev_new(proto_pool);
   proto_flush_event->hook = proto_flush_all;
diff --git a/nest/protocol.h b/nest/protocol.h
index a83c4ff..e61b8d3 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -28,6 +28,10 @@ struct event;
 struct ea_list;
 struct eattr;
 struct symbol;
+struct agg_sumroute;
+struct agg_route;
+struct agg_proto;
+struct cli;
 
 /*
  *	Routing Protocol
@@ -53,8 +57,11 @@ struct protocol {
   void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
   void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
   int (*get_attr)(struct eattr *, byte *buf, int buflen);	/* ASCIIfy dynamic attribute (returns GA_*) */
+  void (*create_sumroute)(struct agg_proto *, struct agg_sumroute *);	/* Create summary route */
+  void (*update_sumroute)(struct agg_proto *, struct agg_sumroute *, struct agg_route *, struct rta *, struct rta *);	/* Update summary route */
   void (*show_proto_info)(struct proto *);	/* Show protocol info (for `show protocols all' command) */
   void (*copy_config)(struct proto_config *, struct proto_config *);	/* Copy config from given protocol instance */
+  void (*get_route_ainfo)(struct cli *, int, struct rte *); /* Print additional information (for `show route' command) */
 };
 
 void protos_build(void);
@@ -74,7 +81,7 @@ void protos_dump_all(void);
 
 extern struct protocol
   proto_device, proto_radv, proto_rip, proto_static,
-  proto_ospf, proto_pipe, proto_bgp;
+  proto_ospf, proto_pipe, proto_bgp, proto_agg;
 
 /*
  *	Routing Protocol Instance
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 377687d..4709544 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -1440,7 +1440,7 @@ rt_init_hostcache(rtable *tab)
   hc->slab = sl_new(rt_table_pool, sizeof(struct hostentry));
 
   hc->lp = lp_new(rt_table_pool, 1008);
-  hc->trie = f_new_trie(hc->lp);
+  hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
 
   tab->hostcache = hc;
 }
@@ -1587,7 +1587,7 @@ rt_update_hostcache(rtable *tab)
 
   /* Reset the trie */
   lp_flush(hc->lp);
-  hc->trie = f_new_trie(hc->lp);
+  hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
 
   WALK_LIST_DELSAFE(n, x, hc->hostentries)
     {
@@ -1634,7 +1634,7 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_add
  *  CLI commands
  */
 
-static void
+void
 rt_format_via(rte *e, byte *via)
 {
   rta *a = e->attrs;
@@ -1660,6 +1660,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
   int primary = (e->net->routes == e);
   int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
   struct mpnh *nh;
+  struct protocol *P = a->proto->proto;
 
   rt_format_via(e, via);
   tm_format_datetime(tm, &config->tf_route, e->lastmod);
@@ -1667,7 +1668,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
     bsprintf(from, " from %I", a->from);
   else
     from[0] = 0;
-  if (a->proto->proto->get_route_info || d->verbose)
+  if (P->get_route_info || d->verbose)
     {
       /* Need to normalize the extended attributes */
       ea_list *t = tmpa;
@@ -1676,8 +1677,8 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
       ea_merge(t, tmpa);
       ea_sort(tmpa);
     }
-  if (a->proto->proto->get_route_info)
-    a->proto->proto->get_route_info(e, info, tmpa);
+  if (P->get_route_info)
+    P->get_route_info(e, info, tmpa);
   else
     bsprintf(info, " (%d)", e->pref);
   cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, via, a->proto->name,
@@ -1685,7 +1686,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
   for (nh = a->nexthops; nh; nh = nh->next)
     cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
   if (d->verbose)
-    rta_show(c, a, tmpa);
+    {
+      rta_show(c, a, tmpa);
+      if (P->get_route_ainfo)
+	P->get_route_ainfo(c, -1007, e);
+    }
 }
 
 static void
diff --git a/proto/agg/Doc b/proto/agg/Doc
new file mode 100644
index 0000000..486cd10
--- /dev/null
+++ b/proto/agg/Doc
@@ -0,0 +1 @@
+S agg.c
diff --git a/proto/agg/Makefile b/proto/agg/Makefile
new file mode 100644
index 0000000..3039207
--- /dev/null
+++ b/proto/agg/Makefile
@@ -0,0 +1,6 @@
+source=agg.c
+root-rel=../../
+dir-name=proto/agg
+
+include ../../Rules
+
diff --git a/proto/agg/agg.c b/proto/agg/agg.c
new file mode 100644
index 0000000..d7faba6
--- /dev/null
+++ b/proto/agg/agg.c
@@ -0,0 +1,685 @@
+/*
+ *	BIRD -- BGP route aggregation
+ *
+ *	(c) 2012 Yandex LLC
+ *	(c) 2012 Alexander V. Chernikov <melifaro at yandex-team.ru>
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Route aggregation
+ *
+ * Firewall protocol is very simple. It adds or removes exported routes to given firewall
+ * table with zero (or filter-specified) value. Table can be flushed on startup to
+ * avoid error messages on bird restart.
+ */
+
+
+#undef LOCAL_DEBUG
+
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
+#include "conf/conf.h"
+#include "nest/cli.h"
+#include "filter/filter.h"
+#include "lib/string.h"
+
+#include "proto/agg/agg.h"
+
+#define ADBG(msg, ...)	DBG("%s:%d " msg "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+static void agg_init_sumroute(struct agg_proto *p, struct agg_sumroute *asr);
+static void agg_mark_sumroute(struct f_trie_node *n, void *data UNUSED);
+static void agg_update_sumroute(struct agg_proto *p, struct agg_sumroute *asr, struct agg_route *ar, rta *old, rta *new);
+static void agg_announce_sumroute(struct agg_proto *p, struct agg_sumroute *asr);
+
+static int
+agg_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
+{
+  struct proto *pp = (*ee)->sender;
+
+  if (pp == P)
+    return -1;	/* Avoid local loops automatically */
+  return 0;
+}
+
+static int
+agg_reload_routes(struct proto *P)
+{
+  return 1;
+}
+
+static void
+agg_initroute(struct fib_node *fn)
+{
+  struct agg_route *ar = (struct agg_route *)fn;
+
+  memset((byte *)ar + sizeof(struct fib_node), 0, sizeof(struct agg_route) - sizeof(struct fib_node));
+  ar->flags = AGG_FLAG_NEW;
+}
+
+static int
+agg_can_announce(struct agg_sumroute *asr)
+{
+  return (asr->mandatory_current == asr->mandatory_total);
+}
+
+/*
+ * agg_make_route - create new route
+ * @p: protocol instance
+ * @addr: pointer to network address
+ * @plen: prefix length
+ *
+ * Adds mandatory route to fib and links it to 
+ */
+static struct agg_route *
+agg_make_route(struct agg_proto *p, ip_addr *addr, int plen)
+{
+  struct agg_route *ar = fib_get(&p->route_fib, addr, plen);
+
+  if (ar->flags & AGG_FLAG_NEW)
+    {
+      /* New route. Do init */
+      init_list(&ar->sum_membership);
+      ar->flags &= ~AGG_FLAG_NEW;
+    }
+
+  return ar;
+}
+
+static void
+agg_link_mroute(struct agg_proto *p, struct agg_sumroute *asr, struct agg_route *ar)
+{
+  struct agg_membership *ms;
+  
+  ms = mb_alloc(p->p.pool, sizeof(struct agg_membership));
+  ms->ar = ar;
+  ms->asr = asr;
+
+  ADBG("Linking mandatory route %I/%d to summary %I/%d", ar->fn.prefix, ar->fn.pxlen, asr->tn.addr, asr->tn.plen);
+
+  add_tail(&ar->sum_membership, &ms->n_mandatory);
+  add_tail(&asr->mandatory_list, &ms->n_sumroute);
+}
+
+static void
+agg_unlink_mroute(struct agg_proto *p, struct agg_membership *ms)
+{
+  struct agg_route *ar = ms->ar;
+
+  ADBG("Unlinking mandatory route %I/%d from summary %I/%d", ar->fn.prefix, ar->fn.pxlen, ms->asr->tn.addr, ms->asr->tn.plen);
+
+  rem_node(&ms->n_mandatory);
+  rem_node(&ms->n_sumroute);
+  mb_free(ms);
+
+  /* Check if we need to free route iself */
+  if (!EMPTY_LIST(ar->sum_membership))
+    return;
+
+  if (ar->attrs)
+    return;
+
+  /* No other mandatory routes, no route entry. We can safely free node */
+  fib_delete(&p->route_fib, ar);
+}
+
+static void
+agg_walk_sumroutes_initial(struct f_trie_node *n, void *data)
+{
+  struct agg_sumroute *asr = (struct agg_sumroute *)n;
+  struct agg_proto *p = (struct agg_proto *)data;
+
+  if (!(asr->flags & AGG_FLAG_PREPARED))
+    return;
+
+  agg_init_sumroute(p, asr);
+}
+
+static int
+agg_start(struct proto *P)
+{
+  struct agg_proto *p = (struct agg_proto *)P;
+  struct agg_config *cf = (struct agg_config *)P->cf;
+
+  fib_init(&p->route_fib, P->pool, sizeof(struct agg_route), 0, agg_initroute);
+  p->summary_trie = cf->summary_trie;
+
+  /* Import mandatory routes if any */
+  trie_walk(p->summary_trie, agg_walk_sumroutes_initial, p);
+
+  /* Allocate by 16k blocks (while BGP requests 1k block) */
+  p->lp = lp_new(P->pool, 16384 - 16);
+
+  return PS_UP;
+}
+
+/*
+ * Mark given summary route as deleted
+ */
+static void
+agg_mark_sumroute(struct f_trie_node *n, void *data UNUSED)
+{
+  struct agg_sumroute *asr = (struct agg_sumroute *)n;
+
+  if (!(asr->flags & AGG_FLAG_PREPARED))
+    return;
+
+  asr->flags |= AGG_FLAG_DELETED;
+}
+
+/*
+ * Initialize newly-allocated summary route. Add all mandatory routes
+ * to protocol FIB
+ */
+static void
+agg_init_sumroute(struct agg_proto *p, struct agg_sumroute *asr)
+{
+  struct cf_route *cr;
+  struct agg_route *ar;
+  node *n, *n_next;
+
+  ADBG("New summary route %I/%d", asr->tn.addr, asr->tn.plen);
+
+  /* New summary route. Let's add mandatory routes to our fib */
+  WALK_LIST_DELSAFE(n, n_next, asr->cf_routes)
+    {
+      cr = (struct cf_route *)n;
+
+      /* In any case, we need to increase count of mandatory routes */
+      asr->mandatory_total++;
+
+      /* Get or create new route entry */
+      ar = agg_make_route(p, &cr->px.addr, cr->px.len);
+
+      /* Increate current counter IFF we have real best rte associated with entry */
+      if (ar->attrs)
+	{
+	  asr->mandatory_current++;
+	  /* Set installed flag */
+	  ar->flags |= AGG_FLAG_INSTALLED;
+	}
+
+      /* Add link */
+      agg_link_mroute(p, asr, ar);
+  }
+}
+
+static void
+agg_announce_sumroute(struct agg_proto *p, struct agg_sumroute *asr)
+{
+  //net *n;
+
+  if (!agg_can_announce(asr))
+    return;
+
+  if (EMPTY_LIST(asr->routes))
+    return;
+
+  /* Generate summary route */
+  switch (asr->route_src)
+    {
+#ifdef CONFIG_BGP
+    case RTS_BGP:
+      proto_bgp.create_sumroute(p, asr);
+      break;
+#endif
+    }
+}
+
+static void
+agg_withdraw_sumroute(struct agg_proto *p, struct agg_sumroute *asr)
+{
+  net *n;
+
+  /* Withdraw route if any */
+  if (asr->attrs)
+    {
+      ADBG("Withdraw summary %I/%d", asr->tn.addr, asr->tn.plen);
+      if (n = fib_find(&p->p.table->fib, &asr->tn.addr, asr->tn.plen))
+	rte_update(p->p.table, n, &p->p, &p->p, NULL);
+
+      /* Free rta */
+      rta_free(asr->attrs);
+      asr->attrs = NULL;
+    }
+}
+
+static void
+agg_update_sumroute(struct agg_proto *p, struct agg_sumroute *asr, struct agg_route *ar, rta *old, rta *new)
+{
+  switch (asr->route_src)
+    {
+#ifdef CONFIG_BGP
+    case RTS_BGP:
+      proto_bgp.update_sumroute(p, asr, ar, old, new);
+      break;
+#endif
+    }
+}
+
+/*
+ * Remove non-config data associated with summary route
+ */
+static void
+agg_clear_sumroute(struct f_trie_node *tn, void *P)
+{
+  struct agg_proto *p = (struct agg_proto *)P;
+  struct agg_sumroute *asr = (struct agg_sumroute *)tn;
+  struct agg_membership *ms;
+  struct agg_route *ar;
+  node *n, *n_next;
+
+  if (!(asr->flags & AGG_FLAG_PREPARED))
+    return;
+
+  if (!(asr->flags & AGG_FLAG_DELETED))
+    return;
+
+  ADBG("Removing summary %I/%d", asr->tn.addr, asr->tn.plen);
+  /* Remove mandatory routes  (allocated from protocol pool) */
+  WALK_LIST_DELSAFE(n, n_next, asr->mandatory_list)
+    {
+      ms = SKIP_BACK(struct agg_membership, n_sumroute, n);
+      agg_unlink_mroute(p, ms);
+    }
+
+  WALK_LIST_DELSAFE(n, n_next, asr->routes)
+    {
+      ar = SKIP_BACK(struct agg_route, n_sumroute, n);
+
+      rem_node(&ar->n_sumroute);
+
+      if (ar->attrs)
+	rta_free(ar->attrs);
+      ar->attrs = NULL;
+      ar->asr = NULL;
+
+      /* Check if we can delete route */
+      if (!EMPTY_LIST(ar->sum_membership))
+	continue;
+
+      /* Node can be safely deleted */
+      fib_delete(&p->route_fib, ar);
+    }
+
+  agg_withdraw_sumroute(p, asr);
+}
+
+static void
+agg_reconfig_sumroute(struct f_trie_node *tn, void *P)
+{
+  struct agg_proto *p = (struct agg_proto *)P;
+  struct agg_sumroute *asr_o, *asr = (struct agg_sumroute *)tn;
+  struct agg_route *ar;
+  struct agg_membership *ms;
+  struct cf_route *cr;
+  node *n, *n_next;
+  node *nn, *nn_next;
+  int found;
+
+  if (!(asr->flags & AGG_FLAG_PREPARED))
+    return;
+
+  /* Find old corresponding route */
+  asr_o = trie_match_longest_prefix(p->summary_trie, asr->tn.addr, asr->tn.plen);
+
+  if ((!asr_o) || (!ipa_equal(asr_o->tn.addr, asr->tn.addr)) || (asr_o->tn.plen != asr->tn.plen))
+    {
+      /* New summary route */
+      agg_init_sumroute(p, asr);
+      return;      
+    }
+
+  ADBG("Reconfiguring summary route %I/%d", asr->tn.addr, asr->tn.plen);
+
+  /*
+   * Old summary route exists. We need to:
+   * 1) remove DELETED flag
+   * 2) move every route to new list
+   * 3) compare mandatory routes
+   */
+
+  asr_o->flags &= ~AGG_FLAG_DELETED;
+
+  /* 
+   * Move usual routes to new list.
+   * Update ther pointer to summary route
+   */
+
+  WALK_LIST_DELSAFE(n, n_next, asr_o->routes)
+    {
+      ar = SKIP_BACK(struct agg_route, n_sumroute, n);
+
+      ar->asr = asr;
+      add_tail(&asr->routes, &ar->n_sumroute);
+    }
+
+  /* Mark old mandatory routes (instead of membership structurs) as deleted */
+  WALK_LIST_DELSAFE(n, n_next, asr_o->mandatory_list)
+    {
+      ms = SKIP_BACK(struct agg_membership, n_sumroute, n);
+      ar = ms->ar;
+      ar->flags |= AGG_FLAG_DELETED;
+    }
+
+  /* Walk all new mandatory routes */
+  WALK_LIST_DELSAFE(n, n_next, asr->cf_routes)
+    {
+      cr = (struct cf_route *)n;
+
+      /* In any case, we need to increase count of mandatory routes */
+      asr->mandatory_total++;
+
+      /* Check if prefix exists */
+      ar = fib_find(&p->route_fib, &cr->px.addr, cr->px.len);
+
+      if (!ar)
+	  ar = agg_make_route(p, &cr->px.addr, cr->px.len);
+
+      /* Increate current counter IFF we have real best rte associated with entry */
+      if (ar->attrs)
+	{
+	  asr->mandatory_current++;
+	  ar->flags |= AGG_FLAG_INSTALLED;
+	}
+
+      /*
+       * Check if we have summary membership with current asr (e.g.
+       * if we already are mandatory route for this asr). In this case
+       * we have to update asr pointer.
+       *
+       * No need to update summary route:
+       * no new routes are announced, mandatory route limit is not hit
+       */
+
+      found = 0;
+      WALK_LIST_DELSAFE(nn, nn_next, ar->sum_membership)
+	{
+	  ms = SKIP_BACK(struct agg_membership, n_mandatory, nn);
+	  if (ms->asr != asr_o)
+	    continue;
+
+	  ADBG("Mandatory route %I/%d remains as is, removing deleted flag", ar->fn.prefix, ar->fn.pxlen);
+	  /* Update pointers and relink */
+	  ms->asr = asr;
+	  add_tail(&asr->mandatory_list, &ms->n_sumroute);
+	  ar->flags &= ~AGG_FLAG_DELETED;
+	  found = 1;
+	  break;
+	}
+
+      if (found)
+	continue;
+
+      /* Add link to mandatory list of summary route */
+      agg_link_mroute(p, asr, ar);
+    }
+
+  /* Delete old mandatory routes */
+  WALK_LIST_DELSAFE(n, n_next, asr_o->mandatory_list)
+    {
+      ms = SKIP_BACK(struct agg_membership, n_sumroute, n);
+      ar = ms->ar;
+      if (!(ar->flags & AGG_FLAG_DELETED))
+	continue;
+
+      /* 
+       * This route is not mandatory for new asr.
+       * No need to update old configuration so
+       * we need to unlink node from ar and free it
+       */
+
+      agg_unlink_mroute(p, ms);
+    }
+
+  /* XXX: we can possibly check new mandatory routes */
+}
+
+static int
+agg_reconfigure(struct proto *P, struct proto_config *new)
+{
+  struct agg_config *o = (struct agg_config *)P->cf;
+  struct agg_config *n = (struct agg_config *)new;
+  struct agg_proto *p = (struct agg_proto *)P;
+  //struct agg_sumroute *;
+
+  ADBG("Reconfiguting..");
+
+  /* Mark all old summary routes as deleted */
+  trie_walk(o->summary_trie, agg_mark_sumroute, NULL);
+
+  /* Walk new trie */
+  trie_walk(n->summary_trie, agg_reconfig_sumroute, p);
+
+  /* Cleanup all old summary routes */
+  trie_walk(o->summary_trie, agg_clear_sumroute, p);
+
+  /* Update trie pointer */
+  p->summary_trie = n->summary_trie;
+
+  return 1;
+}
+
+
+static void
+agg_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
+{
+  struct agg_proto *p = (struct agg_proto *) P;
+  struct agg_sumroute *asr;
+  struct agg_route *ar;
+  struct agg_membership *ms;
+  node *nn, *nn_next;
+  rta *old_rta = NULL;
+
+  /* Ignore unreachable routes */
+  if ((new) && (new->attrs->dest == RTD_UNREACHABLE))
+    new = NULL;
+
+  if ((old) && (old->attrs->dest == RTD_UNREACHABLE))
+    old = NULL;
+
+  if (!new && !old)
+    return;
+
+
+  //ADBG("RT event about %I/%d", n->n.prefix, n->n.pxlen);
+  /* 
+   * Search trie to determine summary route.
+   * We use 1 bit less specific prefix to deal with the following 2 cases:
+   * 1) if announced X/Y prefix is the same as summary route this is clearly not the case for summarization
+   * 2) if nested summary routes are congigured and 1) is in action we got wrong asr pointer.
+   *
+   * We skip 0/0 and :: due to it can'be summarized
+   */
+  if ((n->n.pxlen) && ((asr = trie_match_longest_prefix(p->summary_trie, n->n.prefix, n->n.pxlen - 1))))
+    {
+	/*
+	 * TODO: Find longest-match asr for found ar in new trie.
+	 * If asr changes this means hieharchical summary is in action
+	 */
+      
+	ADBG("Found matched summary route %I/%d", asr->tn.addr, asr->tn.plen);
+      
+	/* Summary route found. Let's find/create route node */
+	ar = agg_make_route(p, &n->n.prefix, n->n.pxlen);
+      
+	/* (new route, route update) */
+	if (new)
+	  {
+	    old_rta = ar->attrs;
+	    /*
+	     * rte_update() guarantees rta to be cached.
+	     * However, if import filter exists, attribute rta MAY be
+	     * uncached or rta itself can be allocated from update linear pool.
+	     * See f_run() and f_rta_cow() comments for more details
+	     */
+
+	    if (new->attrs->aflags & RTAF_CACHED)
+	      ar->attrs = rta_clone(new->attrs);
+	    else
+	      {
+		/* Attributes are modified by filter, let's create stable storage. */
+		ar->attrs = rta_clone(rta_lookup(new->attrs));
+	      }
+      
+	    /* Add link to summary route if route is new */
+	    if (!ar->asr)
+	      {
+		ar->asr = asr;
+		add_tail(&asr->routes, &ar->n_sumroute);
+	      }
+      
+	    /* Call route update */
+	    if (agg_can_announce(asr))
+	      agg_update_sumroute(p, asr, ar, old_rta, ar->attrs);
+      
+	    /* Remove old rte */
+	    if (old_rta)
+	      rta_free(old_rta);
+	  }
+	else
+	  {
+	    /* route withdrawal */
+	    rem_node(&ar->n_sumroute);
+
+	    /* Take into account that create_sumroute() callback can be called from here */
+	    if (agg_can_announce(asr))
+	      agg_update_sumroute(p, asr, ar, ar->attrs, NULL);
+      
+	    if (ar->attrs)
+	      rta_free(ar->attrs);
+	    ar->attrs = NULL;
+	    ar->asr = NULL;
+
+	    /* INSTALLED flag is removed later */
+
+	    if (EMPTY_LIST(ar->sum_membership))
+	      fib_delete(&p->route_fib, ar);
+	  }
+    }
+
+  /* Check if network is from our mandatory list */
+  if ((ar = fib_find(&p->route_fib, &n->n.prefix, n->n.pxlen)))
+    {
+      ADBG("FIB record found for route %I/%d", n->n.prefix, n->n.pxlen);
+      /* Check if we need to change summary routes */
+      if ((new && (!(ar->flags & AGG_FLAG_INSTALLED))) || (!new && (ar->flags & AGG_FLAG_INSTALLED)))
+	{
+	  if (new)
+	    ar->flags |= AGG_FLAG_INSTALLED;
+	  else
+	    ar->flags &= ~AGG_FLAG_INSTALLED;
+
+	  WALK_LIST_DELSAFE(nn, nn_next, ar->sum_membership)
+	    {
+	      ms = SKIP_BACK(struct agg_membership, n_mandatory, nn);
+	      asr = ms->asr;
+
+	      ADBG("Found membership with summary route %I/%d", asr->tn.addr, asr->tn.plen);
+
+	      if (new)
+	        {
+		  asr->mandatory_current++;
+		  /* Possible route announce */
+		  agg_announce_sumroute(p, asr);
+		}
+	      else
+		{
+		  /* Possible route withdrawal */
+		  if (agg_can_announce(asr))
+		    agg_withdraw_sumroute(p, asr);
+		  asr->mandatory_current--;
+		}
+	    }
+	}
+    }
+}
+
+
+static struct proto *
+agg_init(struct proto_config *C)
+{
+  struct proto *P = proto_new(C, sizeof(struct agg_proto));
+
+  P->accept_ra_types = RA_OPTIMAL;
+  P->reload_routes = agg_reload_routes;
+  P->import_control = agg_import_control;
+  P->rt_notify = agg_rt_notify;
+
+  return P;
+}
+
+static int
+agg_shutdown(struct proto *P)
+{
+  struct agg_proto *p = (struct agg_proto *)P;
+
+  /* Mark all summary routes as deleted */
+  trie_walk(p->summary_trie, agg_mark_sumroute, NULL);
+
+  /* Cleanup all (now marked) summary routes */
+  trie_walk(p->summary_trie, agg_clear_sumroute, p);
+
+  return PS_DOWN;
+}
+
+static void
+agg_format_dest(struct rta *a, byte *via)
+{
+  switch (a->dest)
+    {
+    case RTD_ROUTER:	bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
+    case RTD_DEVICE:	bsprintf(via, "dev %s", a->iface->name); break;
+    case RTD_BLACKHOLE:	bsprintf(via, "blackhole"); break;
+    case RTD_UNREACHABLE:	bsprintf(via, "unreachable"); break;
+    case RTD_PROHIBIT:	bsprintf(via, "prohibited"); break;
+    case RTD_MULTIPATH:	bsprintf(via, "multipath"); break;
+    default:		bsprintf(via, "???");
+    }
+}
+
+static void
+agg_get_route_ainfo(struct cli *c, int cli_val, struct rte *e)
+{
+  struct agg_proto *p = (struct agg_proto *)e->attrs->proto;
+  node *n, *n_next;
+  struct rta *a;
+  struct agg_sumroute *asr;
+  struct agg_route *ar;
+  byte via[STD_ADDRESS_P_LENGTH+32], from[STD_ADDRESS_P_LENGTH+8];
+  byte ia[STD_ADDRESS_P_LENGTH+8];
+
+
+  if (!(asr = trie_match_longest_prefix(p->summary_trie, e->net->n.prefix, e->net->n.pxlen)))
+    return;
+
+  WALK_LIST_DELSAFE(n, n_next, asr->routes)
+    {
+      ar = SKIP_BACK(struct agg_route, n_sumroute, n);
+      a = ar->attrs;
+
+      bsprintf(ia, "%I/%d", ar->fn.prefix, ar->fn.pxlen);
+      agg_format_dest(a, via);
+      if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw))
+	bsprintf(from, " from %I", a->from);
+      else
+	from[0] = 0;
+
+      cli_printf(c, cli_val, " + %-18s %s [%s%s]", ia, via, a->proto->name, from);
+    }
+}
+
+struct protocol proto_agg = {
+  name:		"ABGP",
+  template:	"abgp%d",
+  preference:	0,
+  init:		agg_init,
+  start:	agg_start,
+  reconfigure:	agg_reconfigure,
+  shutdown:	agg_shutdown,
+  get_route_ainfo:	agg_get_route_ainfo,
+};
diff --git a/proto/agg/agg.h b/proto/agg/agg.h
new file mode 100644
index 0000000..b20be86
--- /dev/null
+++ b/proto/agg/agg.h
@@ -0,0 +1,81 @@
+/*
+ *	BIRD -- BGP route aggregation
+ *
+ *	(c) 2012 Yandex LLC
+ *	(c) 2012 Alexander V. Chernikov <melifaro at yandex-team.ru>
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_RT_AGG_H_
+#define _BIRD_RT_AGG_H_
+
+struct agg_proto {
+  struct proto p;
+  struct f_trie *summary_trie;		/* Trie with summary routes */
+  struct fib route_fib;			/* Fib with original/mandatory routes */
+  struct linpool *lp;			/* Linear pool used by aggregation functions */
+};
+
+struct agg_config {
+  struct proto_config c;
+  struct f_trie *summary_trie;		/* Trie for holding summary/mandatory route */
+  list temp_list[BITS_PER_IP_ADDRESS];	/* Pre-sort lists */
+};
+
+extern struct protocol proto_agg;
+
+/* route flags */
+#define AGG_FLAG_DELETED	0x0010	/* Summary/mandatory route is candidate for deletion */
+#define AGG_FLAG_MANDATORY	0x0020	/* Existance of this route is mandatory to advertise summary */
+#define AGG_FLAG_INSTALLED	0x0040	/* Route is installed */
+#define AGG_FLAG_NEW		0x0080	/* Newly allocated route */
+
+/* Summary route flags */
+#define AGG_FLAG_PREPARED	0x0100	/* Entry is set up (ised in trie checking) */
+#define AGG_FLAG_SUMONLY	0x0200	/* Advertise summary route only */
+#define AGG_FLAG_MAXINFO	0x0400	/* Save as much info as possible */
+
+/* Masks */
+#define AGG_FLAG_RMASK		0x00F0	/* Mask for route flags */
+#define AGG_FLAG_SUMMASK	0xFF00	/* Flags for summary rouutes */
+
+/* Aggregated route information */
+struct agg_sumroute {
+  struct f_trie_node tn;		/* Information about network */
+  u16 route_src;			/* Route source type (RTS_). XXX: Note field MUST not be zero */
+  u16 flags;				/* Aggregation flags */
+  u16 mandatory_total;			/* Number of mandatory routes */
+  u16 mandatory_current;		/* Number of currently advertised mandatory routes */
+  struct rta *attrs;			/* Aggregated route attributes */
+  list routes;				/* Networks summarized */
+  list mandatory_list;			/* List of mandatory2summary structures */
+  list cf_routes;			/* List of mandatory routes (used in config parsing) */
+};
+
+
+/* Route entry. Used by mandatory and "casual" routes */
+struct agg_route {
+  struct fib_node fn;			/* Network node (both) */
+  u16 flags;				/* Route flafs (both) */
+  struct agg_sumroute *asr;		/* Pointer to summary route (casual) */
+  struct rta *attrs;			/* Attributes of best current rte (casual) */
+  list sum_membership;			/* List with mandatory route membership info (mandatory) */
+  node n_sumroute;			/* Per-sumroute list node (casual) */
+};
+
+/* Mandatory route */
+struct cf_route {
+  node n;				/* Node from cf_entries */
+  struct prefix px;			/* Prefix */
+};
+
+/* Mandatory-2-Summary membership */
+struct agg_membership {
+  struct agg_sumroute *asr;		/* Pointer to summary route */
+  struct agg_route *ar;			/* Pointer to mandatory route */
+  node n_mandatory;			/* agg_mandatory node */
+  node n_sumroute;			/* agg_summary node */
+};
+
+#endif
diff --git a/proto/agg/config.Y b/proto/agg/config.Y
new file mode 100644
index 0000000..1d7fdb0
--- /dev/null
+++ b/proto/agg/config.Y
@@ -0,0 +1,99 @@
+/*
+ *	BIRD -- BGP route aggregation
+ *
+ *	(c) 2012 Yandex LLC
+ *	(c) 2012 Alexander V. Chernikov <melifaro at yandex-team.ru>
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/agg/agg.h"
+
+CF_DEFINES
+
+#define LOCAL_DEBUG
+#define AGG_CFG ((struct agg_config *) this_proto)
+int current_rtype = 0;
+struct agg_sumroute *asr;
+
+CF_DECLS
+
+CF_KEYWORDS(AGGREGATOR, AGGREGATE, ADDRESS, SUMMARY, ONLY, SAVE, ATTRIBUTES, MANDATORY, LIST, BGP, OSPF, E1, E2)
+
+%type <i> agg_route_type
+CF_GRAMMAR
+
+CF_ADDTO(proto, agg_proto '}')
+
+agg_proto_start: proto_start AGGREGATOR {
+     this_proto = proto_config_new(&proto_agg, sizeof(struct agg_config), $1);
+     AGG_CFG->summary_trie = f_new_trie(cfg_mem, sizeof(struct agg_sumroute));
+  }
+ ;
+
+agg_proto:
+   agg_proto_start proto_name '{'
+ | agg_proto agg_proto_item ';'
+ ;
+
+agg_proto_item:
+   proto_item
+ | agg_sum_routes
+ ;
+
+agg_sum_routes:
+   agg_route_type '{' agg_routes_entries '}'
+ ;
+
+agg_routes_entries:
+   agg_route_entry ';'
+ | agg_routes_entries agg_route_entry ';'
+ ;
+
+agg_route_entry:
+  AGGREGATE ADDRESS prefix {
+    asr = (struct agg_sumroute *)trie_add_prefix(AGG_CFG->summary_trie, $3.addr, $3.len, $3.len + 1, MAX_PREFIX_LENGTH);
+    if (asr->flags & AGG_FLAG_PREPARED)
+      cf_error("Prefix %I/%d already exists", $3.addr, $3.len);
+
+    asr->route_src = current_rtype;
+    init_list(&asr->routes);
+    init_list(&asr->mandatory_list);
+    init_list(&asr->cf_routes);
+    asr->flags = AGG_FLAG_PREPARED;
+  } agg_options
+  ;
+
+agg_options:
+    SUMMARY ONLY { asr->flags |= AGG_FLAG_SUMONLY; }
+  | SAVE ATTRIBUTES { asr->flags |= AGG_FLAG_MAXINFO; }
+  | MANDATORY LIST '{' agg_option_mlist '}'
+  |
+  ;
+
+agg_option_mlist:
+    agg_option_mlist_entry
+  | agg_option_mlist ',' agg_option_mlist_entry
+  ;
+
+agg_option_mlist_entry:
+    prefix {
+      /* Simply add to cf_routes */
+      struct cf_route *mr = cfg_allocz(sizeof(struct cf_route));
+      mr->px = $1;
+      add_tail(&asr->cf_routes, &mr->n);
+    }
+  ;
+
+agg_route_type:
+   BGP { current_rtype = RTS_BGP; }
+ | OSPF E1 { current_rtype = RTS_OSPF_EXT1; }
+ | OSPF E2 { current_rtype = RTS_OSPF_EXT2; }
+ ; 
+ 
+ 
+CF_CODE
+
+CF_END
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 4495c03..180ccbf 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -19,9 +19,14 @@
 #include "lib/resource.h"
 #include "lib/string.h"
 #include "lib/unaligned.h"
+#ifdef CONFIG_AGG
+#include "filter/filter.h"
+#include "proto/agg/agg.h"
+#endif
 
 #include "bgp.h"
 
+#define BDBG(msg, ...)	log("%s:%d " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__)
 /*
  *   UPDATE message error handling
  *
@@ -1516,6 +1521,755 @@ bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
     }
 }
 
+#define BGP_AS_MAX_NUMBER	256
+#define BGP_AS_MAX_LEN		1024	/* 256 4-byte ASNs (maximum tuple size) */
+#define BGP_AS_MAX_PTRS		64	/* 64 tuples max */
+/*
+ * bgp_append_as_tuple - add item to sorted array of fixed size
+ * @number: item
+ * @pbuf: pointer to start of array
+ * @count: pointer to current iterms count
+ *
+ * Returns: 1 if item is added (@count is incremented)
+ * 0 if item already exists
+ * -1 if array size is exceeded
+ */
+static int
+bgp_sorted_add_as4(u32 number, u32 *pbuf, byte *count)
+{
+  int min, max, mid, shift;
+
+  if (*count == 0)
+    {
+      *count = (*count) + 1;
+      *pbuf = number;
+      return 1;
+    }
+
+  /* Binary search */
+  min = 0;
+  max = *count - 1;
+  mid = 0;
+  while (min <= max)
+    {
+      mid = (min + max) / 2;
+      if (pbuf[mid] == number)
+	  return 0;
+
+      if (pbuf[mid] > number)
+	  max = mid - 1;
+      else
+	  min = mid + 1;
+    }
+
+  /* Not found. */
+  if (*count == BGP_AS_MAX_NUMBER - 1)
+    return -1;
+
+  if (pbuf[mid] < number)
+    shift = mid + 1;
+  else
+    shift = mid;
+
+  if (*count > shift)
+    memmove(pbuf + shift + 1, pbuf + shift, (*count - shift) * sizeof(u32));
+  pbuf[shift] = number;
+  *count = *count + 1;
+
+  return 1;
+}
+
+/*
+ * bgp_append_as_tuple - append ASNs from one or more AS_SEQ/AS_SET tuples to an array
+ * @src_buf: buffer with chain of AS_SEQUNCE or AS_SET tuples
+ * @src_len: buffer length
+ * @as_set_ptrs: pointer to array of pointers to sorted u32 arrays of ASNs
+ * @as_set_len: pointer to array of length of given arrays
+ * @as_set_index: current array index
+ * @lp: linear pool to allocate data from
+ */
+static void
+bgp_append_as_tuple(byte *src_buf, int src_len, byte **as_set_ptrs, byte *as_set_length, int *as_set_index, struct linpool *lp)
+{
+  u32 asn;
+  int asn_count, i = *as_set_index;
+  u32 *set_ptr;
+  byte *cnt_ptr;
+
+  set_ptr = (u32 *)as_set_ptrs[i];
+  cnt_ptr = &as_set_length[i];
+
+  while (src_len)
+    {
+      asn_count = src_buf[1];
+      src_len -= 2 + 4 * asn_count;
+      src_buf += 2;
+      while (asn_count)
+	{
+	  asn = get_u32(src_buf);
+
+	  /* Append number to array */
+	  if (bgp_sorted_add_as4(asn, set_ptr, cnt_ptr) == -1)
+	    {
+	      /* This tuple is full, let's advance to the next */
+
+	      /* We have to leave room for other BGP data */
+	      if (i == BGP_AS_MAX_PTRS - 2)
+		return;
+
+	      *as_set_index = ++i;
+	      as_set_ptrs[i] = lp_alloc(lp, BGP_AS_MAX_LEN);
+	      set_ptr = (u32 *)as_set_ptrs[i];
+	      cnt_ptr = &as_set_length[i];
+
+	      BDBG("Index increased to %d", i);
+
+	      /* Add to empty array */
+	      bgp_sorted_add_as4(asn, set_ptr, cnt_ptr);
+	    }
+  
+	  //BDBG("Index: %d asn_count: %d cnt: %d curr_asn=%u", i, asn_count, *cnt_ptr, asn);
+
+	  src_buf += 4;
+	  asn_count--;
+	}
+    }
+}
+
+/*
+ * bgp_compile_sum_aspath - make adata attribute for AS_PATH
+ * @as_data_ptr: pointer to common data for all routes
+ * @as_len: common data length
+ * @as_set_ptrs: pointer to array of pointers to sorted u32 arrays of ASNs
+ * @as_set_len: pointer to array of length of given arrays
+ * @as_set_index: current array index
+ * @lp: linear pool to allocate data from
+ *
+ * Function gets 'common' data (possibly consisting of one or more AS_SEQUNCE / AS_SET tuples) and
+ * several arrays with sorted list of ASNs. Each array is converted to AS_SET tuple, All these AS_SET
+ * tuples are added to the end of 'common' data.
+ *
+ */
+static struct adata *
+bgp_compile_sum_aspath(byte *as_data_ptr, int as_len, byte **as_set_ptrs, byte *as_set_len, int *as_set_index, struct linpool *lp)
+{
+  int i, j, len = 0;
+  u32 *asn;
+  byte *q;
+  struct adata *a;
+
+  for (i = 0; i <= *as_set_index; i++)
+    {
+      if (as_set_len[i])
+	len += 2 + 4 * as_set_len[i];
+    }
+
+  //BDBG("bgp_compile_sum_aspath(): Len=%d as_len=%d", len, as_len);
+
+  /* Merge both paths to contiguous storage */
+  a = bgp_alloc_adata(lp, len + as_len);
+  q = a->data;
+  /* Copy 'common' part */
+  memcpy(q, as_data_ptr, as_len);
+
+  if (!len)
+    return a;
+
+  q += as_len;
+  /* For each array, write AS_SET header and data */
+  for (i = 0; i <= *as_set_index; i++)
+    {
+      *q++ = AS_PATH_SET;
+      *q++ = as_set_len[i];
+      asn = (u32 *)as_set_ptrs[i];
+      for (j = 0; j < as_set_len[i]; j++, q += sizeof(u32))
+	put_u32(q, *asn++);
+    }
+
+  return a;
+}
+
+/*
+ * bgp_sum_origin - update summary ORIGIN attribute
+ * @attrs: pointer to new route attributes
+ * @origin: pointer to current ORIGIN value
+ */
+inline void
+bgp_sum_origin(rta *attrs, int *origin)
+{
+  struct eattr *ea;
+  int new_origin;
+
+  if (ea = ea_find(attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)))
+    new_origin = ea->u.data;
+  else
+    {
+      switch (attrs->source)
+	{
+	case RTS_OSPF:
+	case RTS_OSPF_IA:
+	case RTS_OSPF_EXT1:
+	case RTS_OSPF_EXT2:
+	  new_origin = ORIGIN_IGP;
+	  break;
+
+	default:
+	  new_origin = ORIGIN_INCOMPLETE;
+	}
+    }
+
+  if (new_origin == ORIGIN_INCOMPLETE)
+    *origin = ORIGIN_INCOMPLETE;
+  else if ((new_origin == ORIGIN_EGP) && (*origin == ORIGIN_IGP))
+    *origin = ORIGIN_EGP;
+}
+
+/*
+ * bgp_sum_aspath - update summary AS_PATH attribute
+ * @ea: new AS_PATH attribuye
+ * @as_differs: are we already in 'differ' mode
+ * @as_data_ptr: pointer to common data for all routes
+ * @as_len: common data length
+ * @as_set_ptrs: pointer to array of pointers to sorted u32 arrays of ASNs
+ * @as_set_len: pointer to array of length of given arrays
+ * @as_set_index: current array index
+ * @lp: linear pool to allocate data from
+ */
+void
+bgp_sum_aspath(eattr *ea, int *as_differs, byte *as_data_ptr, int *as_len, byte **as_set_ptrs, byte *as_set_len, int *as_set_index, struct linpool *lp)
+{
+  int new_len, mlen, slen;
+  byte *sum_off, *new_off, *new_ptr;
+
+  new_len = ea->u.ptr->length;
+  new_ptr = ea->u.ptr->data;
+
+  /* Check if new AS_PATH is the same */
+  if ((*as_differs == 0) && (*as_len == new_len) && (memcmp(as_data_ptr, new_ptr, new_len) == 0))
+    return;
+
+  /* 
+   * New AS_PATH differs. We use easy and naive implementation
+   * from RFC4271 9.2.2.2:
+   * 1) Find as much as possible AS_SEQ / AS_SET segments at the 
+   * beginning (usually zero)
+   * 2) put the rest into huge sorted AS_SET (or several AS_SETs)
+   */
+  *as_differs = 1;
+
+  /* 
+   * Compare AS_SET / AS_SEQ tuples one by one.
+   * We assume both SETs to be validated
+   */
+
+  mlen = MIN(*as_len, new_len);
+  sum_off = as_data_ptr;
+  new_off = new_ptr;
+
+  while (mlen > 0)
+    {
+      /* Check if segment type and length is the same */
+      if (memcmp(sum_off, new_off, 2))
+	break;
+
+      slen = 2 + 4 * new_off[1];
+      if (memcmp(sum_off, new_off, slen))
+	break;
+
+      /* Segment is the same, moving to the next  */
+      sum_off += slen;
+      new_off += slen;
+      mlen -= slen;
+    }
+
+  //BDBG("MIN=%d mlen=%d", MIN(*as_len, new_len), mlen);
+
+  /*
+   * 1) If xlen is > 0 we need to put to AS_SET buffer ALL different tuples from sum_off and new_off.
+   * 2) If xlen is zero but new_len is larger, we need to put to AS_SET buffer tuples from new_off
+   * 3) If xlen is zero but sum_len is larger, we need to put to AS_SET buffer tuples from sum_off
+   */
+  if (sum_off != as_data_ptr + *as_len)
+    {
+      BDBG("Move ASNs from summary to AS-SET, length=%d", as_data_ptr + *as_len - sum_off);
+      bgp_append_as_tuple(sum_off, as_data_ptr + *as_len - sum_off, as_set_ptrs, as_set_len, as_set_index, lp);
+      *as_len = sum_off - as_data_ptr;
+    }
+
+  if (new_off != new_ptr + new_len)
+    {
+      BDBG("Move ASNs from new to AS-SET, length=%d", new_ptr + new_len - new_off);
+      bgp_append_as_tuple(new_off, new_ptr + new_len - new_off, as_set_ptrs, as_set_len, as_set_index, lp);
+    }
+}
+
+/*
+ * bgp_update_sum_rte - create and announce updated summary rte
+ * @p: pointer to protocol instance
+ * @asr: pointer to summary route
+ * @origin: value of ORIGIN attribute
+ * @as_part: pointer to AS_PATH attribute data
+ * @atomic_agg: value of ATOMIC_AGGREGATE attribute
+ * @aggregator: pointer to AGGREGATOR attribute value
+ *
+ * Function creates stable rta (via rta_clone) and announces it
+ */
+static void
+bgp_update_sum_rte(struct agg_proto *p, struct agg_sumroute *asr, int origin, struct adata *as_path, int atomic_agg, struct adata *aggregator)
+{
+  int i, slen;
+  struct ea_list *eal;
+  rta a, *attrs;
+  rte *route;
+  struct adata *atomic_ad;
+
+  slen = atomic_agg ? 4 : 3;
+  eal = lp_allocz(p->lp, sizeof(struct ea_list) + sizeof(eattr) * slen);
+  eal->flags = EALF_SORTED;
+  eal->count = slen;
+
+  i = 0;
+
+  /* ORIGIN */
+  bgp_set_attr(&eal->attrs[i++], BA_ORIGIN, origin);
+
+  /* AS_PATH */
+  bgp_set_attr(&eal->attrs[i++], BA_AS_PATH, (uintptr_t)as_path);
+
+  /* ATOMIC_AGGREGATE */
+  if (atomic_agg)
+    {
+      atomic_ad = bgp_alloc_adata(p->lp, 0);
+      bgp_set_attr(&eal->attrs[i++], BA_ATOMIC_AGGR, (uintptr_t)atomic_ad);
+    }
+
+  /* AGGREGATOR */
+  bgp_set_attr(&eal->attrs[i++], BA_AGGREGATOR, (uintptr_t)aggregator);
+
+  /* Fill in temporary rta */
+  bzero(&a, sizeof(a));
+  a.proto = &p->p;
+  a.source = RTS_BGP;
+  a.scope = SCOPE_UNIVERSE;
+  a.cast = RTC_UNICAST;
+  a.dest = RTD_BLACKHOLE;
+/*
+  a.gw = r->via;
+  a.iface = NULL;
+*/
+  a.eattrs = eal;
+
+  attrs = rta_lookup(&a);
+
+  route = rte_get_temp(attrs);
+
+  /* Save copy of attributes */
+  attrs = rta_clone(attrs);
+
+  route->net = net_get(p->p.table, asr->tn.addr, asr->tn.plen);
+  route->pflags = 0;
+
+  /* Update summary route */
+  rte_update(p->p.table, route->net, &p->p, &p->p, route);
+
+  /* Free old attrs if any */
+  if (asr->attrs)
+    rta_free(asr->attrs);
+  /* Save copy of attributes */
+  asr->attrs = attrs;
+}
+
+
+void
+bgp_create_sumroute(struct agg_proto *p, struct agg_sumroute *asr)
+{
+  int as_set = 0, as_len = 0, new_len;
+  struct agg_route *ar;
+  struct eattr *ea;
+  struct rta *attrs;
+  node *n, *n_next;
+  int origin = ORIGIN_IGP, atomic_agg = 0;
+  u32 agg_as = 0, agg_id = 0, asn_set = 0;
+  byte *new_ptr;
+  int as_differs = 0;
+  int as_set_index = 0;
+  int agg_skip = 1;
+  struct bgp_proto *bgp_p;
+  byte *as_data_ptr = NULL;
+  struct adata *ad, *as_path;
+  byte *as_set_ptrs[BGP_AS_MAX_PTRS], as_set_len[BGP_AS_MAX_PTRS];
+
+  BDBG("bgp_create_sumroute() called for %I/%d", asr->tn.addr, asr->tn.plen);
+
+  /*
+   * Do route aggregation per RFC4271 9.2.2.2 rules
+   *
+   * [0] NEXT_HOP (4 or 16 or 2x16)
+   * [1] ORIGIN (internal, u32)
+   * [2] AS_PATH (variable)
+   * [3] AGGREGATOR (8 bytes)
+   * [4] ATOMIC_AGGREGATE (opt, 6 bytes)
+   *
+   */
+  /* Zero set length */
+  memset(&as_set_len, 0, sizeof(as_set_len));
+  as_set_ptrs[0] = lp_alloc(p->lp, BGP_AS_MAX_LEN);
+
+  WALK_LIST_DELSAFE(n, n_next, asr->routes)
+    {
+      ar = SKIP_BACK(struct agg_route, n_sumroute, n);
+
+      attrs = ar->attrs;
+      BDBG("Working on route %I/%d source=%d", ar->fn.prefix, ar->fn.pxlen, attrs->source);
+
+      /*
+       * FIXME: Routes with different MED should not be aggregated.
+       * However this is another non-deterministic place
+       */
+      /* Save ASN & BGP router id from first BGP route */
+      if (attrs->source == RTS_BGP)
+	{
+	  bgp_p = (struct bgp_proto *)attrs->proto;
+	  if (!asn_set)
+	    {
+	      /* Save data */
+	      agg_as = bgp_p->local_as;
+	      agg_id = bgp_p->local_id;
+	      BDBG("Summary route ASN/ID set to %d/%R", agg_as, agg_id);
+	      asn_set = 1;
+	      agg_skip = 0;
+	    }
+	  else
+	    {
+	      if ((agg_as != bgp_p->local_as) || (agg_id != bgp_p->local_id))
+		{
+		  log(L_ERR "%s: Cannot aggregate route %I/%d into %I/%d, skipping",
+			asr->tn.addr, asr->tn.plen, ar->fn.prefix, ar->fn.pxlen);
+		  agg_skip = 1;
+		}
+	    }
+	}
+
+      /*
+       * Check AS_PATH. AS_PATH is normalized to 4b ASNs in bgp_decode_attr().
+       * We assume all AS_PATH attributes BGP routes are encoded in 4b format
+       */
+      if (ea = ea_find(attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)))
+	{
+	  /* BGP route */
+	  new_len = ea->u.ptr->length;
+	  new_ptr = ea->u.ptr->data;
+	}
+      else
+	{
+	  /* Non-BGP route, let's set empty attribute */
+	  new_len = 0;
+	  new_ptr = NULL;
+	}
+
+      if (asr->flags & AGG_FLAG_MAXINFO)
+	{
+	  if (!as_set)
+	    {
+	      as_len = new_len;
+	      as_data_ptr = lp_alloc(p->lp, as_len ? as_len : 4);
+	      memcpy(as_data_ptr, new_ptr, as_len);
+	      as_set = 1;
+	    }
+	  else if (new_ptr)
+	    bgp_sum_aspath(ea, &as_differs, as_data_ptr, &as_len, as_set_ptrs, as_set_len, &as_set_index, p->lp);
+	}
+
+      /* Check ORIGIN () */
+      bgp_sum_origin(attrs, &origin);
+
+      /* Check ATOMIC_AGGREGATE */
+      if (ea = ea_find(attrs->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
+	atomic_agg = 1;
+    }
+
+  /* Skip route? */
+  if (agg_skip)
+    {
+      log(L_ERR "%s: Route %I/%d cannot be sumarized due to nonexisting/conflicting Router Id/ASN", asr->tn.addr, asr->tn.plen);
+      return;
+    }
+
+  /*
+   * Make out list sorted by default
+   *
+   * [0] ORIGIN (V=1) (internal, u32)
+   * [1] AS_PATH (V=2) (variable)
+   * [2] ATOMIC_AGGREGATE (V=6) (opt, zero)
+   * [3] AGGREGATOR (V=7) (8 bytes)
+   *
+   */
+
+  /* Prepare AS_PATH */
+  as_path = bgp_compile_sum_aspath(as_data_ptr, as_len, as_set_ptrs, as_set_len, &as_set_index, p->lp);
+
+  /* Prepare AGGREGATOR */
+  ad = bgp_alloc_adata(p->lp, 8);
+  new_ptr = ad->data;
+  put_u32(new_ptr, agg_as);
+  put_u32(new_ptr + 4, agg_id);
+
+  /* Create stable attributes with rte */
+  bgp_update_sum_rte(p, asr, origin, as_path, atomic_agg, ad);
+
+  lp_flush(p->lp);
+}
+
+
+#define DBG_UPD(x)  BDBG("Summary route update requires reannounce due to changed " x " attribute")
+void
+bgp_update_sumroute(struct agg_proto *p, struct agg_sumroute *asr, struct agg_route *ar, struct rta *old, struct rta *new)
+{
+  struct eattr *ea, *ea_new;
+  rta *a;
+  int origin = ORIGIN_IGP, atomic_agg = 0, rebuild = 0;
+  struct adata *as_path, *aggregator;
+  node *n, *n_next;
+
+  BDBG("bgp_update_sumroute: route %I/%d , summary %I/%d", ar->fn.prefix, ar->fn.pxlen, asr->tn.addr, asr->tn.plen);
+
+  if (!(a = asr->attrs))
+    {
+      if (!new)
+	return;
+
+      bgp_create_sumroute(p, asr);
+      return;
+    }
+
+  /*
+   * [0] ORIGIN (V=1) (internal, u32)
+   * [1] AS_PATH (V=2) (variable)
+   * [3] ATOMIC_AGGREGATE (V=6) (opt, zero)
+   * [4] AGGREGATOR (V=7) (8 bytes)
+   *
+   */
+  if (!new)
+    {
+
+      /* route witdrawal */
+      /* Check if we can skip rebuilding */
+      BDBG("Widrawing route %I/%d from summary %I/%d", ar->fn.prefix, ar->fn.pxlen, asr->tn.addr, asr->tn.plen);
+
+      /*
+       * AS_PATH
+       * If MAXINFO flag is not set we don't care (AS_PATH is empty)
+       * if MAXINFO is set but attribute length is zero we don't care, too
+       * Overwise, full rebuild is requires
+       */
+      if ((asr->flags & AGG_FLAG_MAXINFO) && (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH))))
+	{
+	  if (ea->u.ptr->length > 0)
+	    {
+	      /* We have to save every AS in AS_PATH and it is not empty. */
+	      DBG_UPD("AS_PATH");
+	      bgp_create_sumroute(p, asr);
+	      return;
+	    }
+	}
+
+      /* Summary AS_PATH is not changed */
+
+      /*
+       * ORIGIN
+       * In most cases we got INCOMPLETE in both summary route and witdrawn attribute,
+       * so we simply cycle thru all more specific routes to determine new origin attribute
+       *
+       * ATOMIC_AGGREGATE
+       * Check for its new value, too
+       */
+
+      WALK_LIST_DELSAFE(n, n_next, asr->routes)
+	{
+	  ar = SKIP_BACK(struct agg_route, n_sumroute, n);
+
+	  BDBG("Working on route %I/%d", ar->fn.prefix, ar->fn.pxlen);
+	  bgp_sum_origin(ar->attrs, &origin);
+
+	  if (ea = ea_find(ar->attrs->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
+	    atomic_agg = 1;
+	}
+
+      if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)))
+	{
+	  if ((ea->u.data != origin))
+	    {
+	      DBG_UPD("ORIGIN");
+	      rebuild = 1;
+	    }
+	}
+
+      if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
+	{
+	  if ((ea->u.data != atomic_agg))
+	    {
+	      DBG_UPD("ATOMIC_AGG");
+	      rebuild = 1;
+	    }
+	}
+
+      if (!rebuild)
+	{
+	  BDBG("Withdrawal of route %I/%d does not require summary route to be updated", ar->fn.prefix, ar->fn.pxlen);
+	  return;
+	}
+
+      BDBG("Withdrawal of route %I/%d require summary route to be updated", ar->fn.prefix, ar->fn.pxlen);
+
+      /*
+       * We don't need full update here since we already know all summarized attributes data:
+       * AS_PATH is empty
+       * ORIGIN / ATOMIC_AGGREGATE values are known
+       * AGGREGATOR value cannot change (so we import it from current summary route)
+       */
+
+      /* Create empty AS_PATH */
+      as_path = bgp_alloc_adata(p->lp, 0);
+
+      /* Create AGGREGATOR attribute */
+      aggregator = bgp_alloc_adata(p->lp, 8);
+
+      if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR)))
+	memcpy(aggregator + 1, ea->u.ptr->data, 8);
+
+      bgp_update_sum_rte(p, asr, origin, as_path, atomic_agg, aggregator);
+      lp_flush(p->lp);
+      return;
+    }
+
+   /************************************************
+   *         New route or route update.            *
+   ************************************************/
+  /* Check ORIGIN */
+  if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)))
+    {
+      origin = ea->u.data;
+      bgp_sum_origin(new, &origin);
+      if (origin != ea->u.data)
+	{
+	  DBG_UPD("ORIGIN");
+	  rebuild = 1;
+	}
+    }
+
+  /* Check AS_PATH */
+  ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+
+  byte *as_set_ptrs[BGP_AS_MAX_PTRS], as_set_len[BGP_AS_MAX_PTRS];
+  int as_differs, as_len, as_set_index;
+  byte *as_data_ptr;
+
+  /*
+   * Check if new route:
+   * 1) is BGP route (contains AS_PATH)
+   * 2) New AS_PATH is not empty
+   * 3) New AS_PATH is different
+   */
+  as_path = NULL;
+
+  if ((asr->flags & AGG_FLAG_MAXINFO) && (ea_new = ea_find(new->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH))) && (ea_new->u.ptr->length))
+    {
+      if ((ea->u.ptr->length != ea_new->u.ptr->length) || (memcmp(ea->u.ptr->data, ea_new->u.ptr->data, ea_new->u.ptr->length)))
+	{
+	  /* AS_PATH differs */
+	  as_len = ea->u.ptr->length;
+	  as_data_ptr = lp_alloc(p->lp, as_len ? as_len : 4);
+	  memcpy(as_data_ptr, ea->u.ptr->data, as_len);
+
+	  as_differs = 1;
+	  memset(&as_set_len, 0, sizeof(as_set_len));
+	  as_set_ptrs[0] = lp_alloc(p->lp, BGP_AS_MAX_LEN);
+	  as_set_index = 0;
+
+	  bgp_sum_aspath(ea_new, &as_differs, as_data_ptr, &as_len, as_set_ptrs, as_set_len, &as_set_index, p->lp);
+	  as_path = bgp_compile_sum_aspath(as_data_ptr, as_len, as_set_ptrs, as_set_len, &as_set_index, p->lp);
+
+	  DBG_UPD("AS_PATH");
+	  rebuild = 1;
+	}
+    }
+
+  /* Check ATOMIC_AGGREGATE */
+  if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
+    atomic_agg = 1;
+
+  if ((ea_find(new->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR))) && (!atomic_agg))
+    {
+      DBG_UPD("ATOMIC_AGGREGATE");
+      atomic_agg = 1;
+      rebuild = 1;
+    }
+
+  /* Check ORIGIN */
+  if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)))
+    {
+      origin = ea->u.data;
+      bgp_sum_origin(new, &origin);
+      if (origin != ea->u.data)
+	{
+	  DBG_UPD("ORIGIN");
+	  rebuild = 1;
+	}
+    }
+
+  /* Check AGGREGATOR */
+  struct bgp_proto *bgp_p = NULL;
+  byte agg[8];
+  if (new->source == RTS_BGP)
+    {
+      bgp_p = (struct bgp_proto *)new->proto;
+      put_u32(agg, bgp_p->local_as);
+      put_u32(agg + 4, bgp_p->local_id);
+      ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR));
+      if (memcmp(agg, ea->u.ptr->data, 8))
+	{
+	  BDBG("New route %I/%d %d/%R ASN/BGP ID differs from summary route (%d/%R). Ignoring",
+	    ar->fn.prefix, ar->fn.pxlen, bgp_p->local_as, bgp_p->local_id,
+	    get_u32(ea->u.ptr->data), get_u32(ea->u.ptr->data + 4));
+	  if (rebuild)
+	    lp_flush(p->lp);
+	  return;
+	}
+    }
+
+  if (!rebuild)
+    {
+      BDBG("New route %I/%d does not require summary route to be updated", ar->fn.prefix, ar->fn.pxlen);
+      return;
+    }
+
+  DBG("New route %I/%d require summary route to be updated", ar->fn.prefix, ar->fn.pxlen);
+
+  /* Copy current AS_PATH if not set */
+  if (!as_path)
+    {
+      ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+      as_len = ea->u.ptr->length;
+      as_path = bgp_alloc_adata(p->lp, as_len);
+      memcpy(as_path->data, ea->u.ptr->data, as_len);
+    }
+
+  /* Copy AGGREGATOR attribute */
+  aggregator = bgp_alloc_adata(p->lp, 8);
+
+  if (bgp_p)
+    memcpy(aggregator + 1, agg, 8);
+  else
+    {
+      if (ea = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR)))
+	memcpy(aggregator + 1, ea->u.ptr->data, 8);
+    }
+
+  bgp_update_sum_rte(p, asr, origin, as_path, atomic_agg, aggregator);
+  lp_flush(p->lp);
+}
+#undef BGP_UPD
+
 /**
  * bgp_decode_attrs - check and decode BGP attributes
  * @conn: connection
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 4d3c32f..b23e21a 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1174,6 +1174,7 @@ bgp_show_proto_info(struct proto *P)
     }
 }
 
+
 struct protocol proto_bgp = {
   name:			"BGP",
   template:		"bgp%d",
@@ -1188,5 +1189,9 @@ struct protocol proto_bgp = {
   get_status:		bgp_get_status,
   get_attr:		bgp_get_attr,
   get_route_info:	bgp_get_route_info,
-  show_proto_info:	bgp_show_proto_info
+  show_proto_info:	bgp_show_proto_info,
+#ifdef CONFIG_AGG
+  create_sumroute:	bgp_create_sumroute,
+  update_sumroute:	bgp_update_sumroute
+#endif
 };
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index a8c5818..21ace7b 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -184,6 +184,8 @@ static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = ad
 
 void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val);
 byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len);
+void bgp_create_sumroute(struct agg_proto *p, struct agg_sumroute *asr);
+void bgp_update_sumroute(struct agg_proto *p, struct agg_sumroute *asr, struct agg_route *ar, struct rta *old, struct rta *new);
 struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory);
 int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
 int bgp_rte_better(struct rte *, struct rte *);
diff --git a/sysdep/autoconf.h.in b/sysdep/autoconf.h.in
index d029e2a..d10b409 100644
--- a/sysdep/autoconf.h.in
+++ b/sysdep/autoconf.h.in
@@ -42,6 +42,7 @@
 #undef CONFIG_BGP
 #undef CONFIG_OSPF
 #undef CONFIG_PIPE
+#undef CONFIG_AGG
 
 /* We have <syslog.h> and syslog() */
 #undef HAVE_SYSLOG
-- 
1.7.3.2


--------------000303020204090509040505--



More information about the Bird-users mailing list