aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorSergey Matveychuk <sem@FreeBSD.org>2012-03-22 16:52:51 +0000
committerSergey Matveychuk <sem@FreeBSD.org>2012-03-22 16:52:51 +0000
commit0ccd6cfd6c619af054a0ee92aecc2241714b2335 (patch)
tree662b16e890e0c0a051332f46593d5832dc084760 /net
parent8390db3ec3cd06744b923d917114982cf4916f9d (diff)
downloadports-0ccd6cfd6c619af054a0ee92aecc2241714b2335.tar.gz
ports-0ccd6cfd6c619af054a0ee92aecc2241714b2335.zip
- Missed a patch with the last commit
Feature safe: Yes
Notes
Notes: svn path=/head/; revision=293676
Diffstat (limited to 'net')
-rw-r--r--net/bird/files/agg_support.patch4404
1 files changed, 4404 insertions, 0 deletions
diff --git a/net/bird/files/agg_support.patch b/net/bird/files/agg_support.patch
new file mode 100644
index 000000000000..83f756e0a110
--- /dev/null
+++ b/net/bird/files/agg_support.patch
@@ -0,0 +1,4404 @@
+From 6178c758c99bf6b1d9402489e8974ee3598675cf Mon Sep 17 00:00:00 2001
+From: Alexander V. Chernikov <melifaro@ipfw.ru>
+Date: Thu, 22 Mar 2012 15:28:02 +0000
+Subject: [PATCH 1/1] * Implement general aggregation protocol, v5
+
+---
+ configure.in | 4 +-
+ doc/bird.conf.example | 9 +
+ 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 | 720 +++++++++++++++++++++++++++++++++++++++++++++++
+ proto/agg/agg.h | 87 ++++++
+ proto/agg/config.Y | 108 +++++++
+ proto/bgp/attrs.c | 748 +++++++++++++++++++++++++++++++++++++++++++++++++
+ proto/bgp/bgp.c | 7 +-
+ proto/bgp/bgp.h | 2 +
+ sysdep/autoconf.h.in | 1 +
+ 18 files changed, 1831 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
+--- configure.in
++++ 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/doc/bird.conf.example b/doc/bird.conf.example
+index 5e07ab5..2cab8be 100644
+--- doc/bird.conf.example
++++ doc/bird.conf.example
+@@ -163,6 +163,15 @@ protocol static {
+ # };
+ #}
+
++#protocol agg {
++# bgp id 198.51.100.1 as 65000 {
++# aggregate address 198.51.100.64/26;
++# aggregate address 198.51.100.0/26 save attributes; # Aggregate AS_PATH
++# aggregate address 198.51.100.128/16 mandatory list {
++# 198.51.100.12/32;
++# }; # Announce summary IFF all prefixes from mandatory list exists
++# }
++#}
+
+ #protocol bgp {
+ # disabled;
+diff --git a/filter/config.Y b/filter/config.Y
+index 2e8b522..a13f33c 100644
+--- filter/config.Y
++++ filter/config.Y
+@@ -558,7 +558,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 2386fc9..f2a5d06 100644
+--- filter/filter.h
++++ filter/filter.h
+@@ -79,11 +79,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);
+
+@@ -186,6 +188,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
+--- filter/trie.c
++++ 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
+--- nest/proto-hooks.c
++++ 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
+--- nest/proto.c
++++ 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
+--- nest/protocol.h
++++ 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
+--- nest/rt-table.c
++++ 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
++++ 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
++++ 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..5b9cae1
+--- /dev/null
++++ proto/agg/agg.c
+@@ -0,0 +1,720 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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 "lib/alloca.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) ||
++ (asr_o->route_src != asr->route_src))
++ {
++ /* New summary route */
++ agg_init_sumroute(p, asr);
++ return;
++ }
++
++ /* Should we move this to protocol-specific hook? */
++ switch (asr->route_src)
++ {
++ case RTS_BGP:
++ if ((asr_o->u.bgp.local_id != asr->u.bgp.local_id) || (asr_o->u.bgp.local_as != asr->u.bgp.local_as))
++ {
++ agg_init_sumroute(p, asr);
++ return;
++ }
++ break;
++ }
++
++ 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);
++
++ /*
++ * XXX: we possibly have to determine if summary routes configuration
++ * is changed and we hate to request refeeding
++ */
++
++ /* 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, *new_rta;
++
++ /* 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;
++ /*
++ * We want to get stable attribute copy.
++ *
++ * Base attributes (direct next hop) can be changed in rta directly,
++ * imposing COW in some cases.)
++ * Extended attributes can be added or updated in:
++ * * make_tmp_attrs() import hook
++ * * export filter
++ * * import/export pipe filter.
++ *
++ * So, if either
++ * * new is not cached OR
++ * * tmpa != new->attrs->eattrs (see end of do_rte_announce)
++ *
++ * we have to create and lookup new rta.
++ */
++ if ((new->attrs->aflags & RTAF_CACHED) && (attrs == new->attrs->eattrs))
++ ar->attrs = rta_clone(new->attrs);
++ else
++ {
++ /*
++ * Attributes or extended attributes are modified by filter,
++ * we need to create stable storage
++ */
++ new_rta = alloca(sizeof(rta));
++ memcpy(new_rta, new->attrs, sizeof(rta));
++ new_rta->eattrs = attrs;
++ new_rta->aflags = 0;
++ ar->attrs = rta_clone(rta_lookup(new_rta));
++ }
++
++ /* 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: "AGG",
++ template: "agg%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..d3e6f65
+--- /dev/null
++++ proto/agg/agg.h
+@@ -0,0 +1,87 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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 */
++ union {
++ struct {
++ u32 local_id; /* BGP router id */
++ u32 local_as; /* BGP local ASn */
++ } bgp;
++ } u;
++ 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..652b461
+--- /dev/null
++++ proto/agg/config.Y
+@@ -0,0 +1,108 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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;
++u32 bgp_id = 0, bgp_as = 0;
++struct agg_sumroute *asr;
++
++CF_DECLS
++
++CF_KEYWORDS(AGGREGATOR, AGGREGATE, ADDRESS, SUMMARY, ONLY, SAVE, ATTRIBUTES, MANDATORY, LIST, BGP, OSPF, E1, E2)
++CF_KEYWORDS(ID, AS)
++
++%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;
++ switch (current_rtype)
++ {
++ case RTS_BGP:
++ asr->u.bgp.local_id = bgp_id;
++ asr->u.bgp.local_as = bgp_as;
++ break;
++ }
++ 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 ID idval AS expr { current_rtype = RTS_BGP; bgp_id = $3; bgp_as = $5; }
++ | 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..9b068a8 100644
+--- proto/bgp/attrs.c
++++ 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,749 @@ 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, agg_id;
++ byte *new_ptr;
++ int as_differs = 0;
++ int as_set_index = 0;
++ int agg_count = 0;
++ 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);
++
++ agg_as = asr->u.bgp.local_as;
++ agg_id = asr->u.bgp.local_id;
++ BDBG("Summary route ASN/ID set to %d/%R", agg_as, agg_id);
++
++ 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 ((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",
++ p->p.name, asr->tn.addr, asr->tn.plen, ar->fn.prefix, ar->fn.pxlen);
++ continue;
++ }
++ }
++
++ /*
++ * 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;
++
++ agg_count++;
++ }
++
++ /* Skip route? */
++ if (!agg_count)
++ {
++ log(L_ERR "%s: Route %I/%d cannot be summarized due to conflicting Router Id/ASN", p->p.name, 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(new->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
++ atomic_agg = 1;
++
++ if ((ea_find(a->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
+--- proto/bgp/bgp.c
++++ 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
+--- proto/bgp/bgp.h
++++ 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
+--- sysdep/autoconf.h.in
++++ 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
+
+From 6178c758c99bf6b1d9402489e8974ee3598675cf Mon Sep 17 00:00:00 2001
+From: Alexander V. Chernikov <melifaro@ipfw.ru>
+Date: Thu, 22 Mar 2012 15:28:02 +0000
+Subject: [PATCH 1/1] * Implement general aggregation protocol, v5
+
+---
+ configure.in | 4 +-
+ doc/bird.conf.example | 9 +
+ 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 | 720 +++++++++++++++++++++++++++++++++++++++++++++++
+ proto/agg/agg.h | 87 ++++++
+ proto/agg/config.Y | 108 +++++++
+ proto/bgp/attrs.c | 748 +++++++++++++++++++++++++++++++++++++++++++++++++
+ proto/bgp/bgp.c | 7 +-
+ proto/bgp/bgp.h | 2 +
+ sysdep/autoconf.h.in | 1 +
+ 18 files changed, 1831 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
+--- configure.in
++++ 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/doc/bird.conf.example b/doc/bird.conf.example
+index 5e07ab5..2cab8be 100644
+--- doc/bird.conf.example
++++ doc/bird.conf.example
+@@ -163,6 +163,15 @@ protocol static {
+ # };
+ #}
+
++#protocol agg {
++# bgp id 198.51.100.1 as 65000 {
++# aggregate address 198.51.100.64/26;
++# aggregate address 198.51.100.0/26 save attributes; # Aggregate AS_PATH
++# aggregate address 198.51.100.128/16 mandatory list {
++# 198.51.100.12/32;
++# }; # Announce summary IFF all prefixes from mandatory list exists
++# }
++#}
+
+ #protocol bgp {
+ # disabled;
+diff --git a/filter/config.Y b/filter/config.Y
+index 2e8b522..a13f33c 100644
+--- filter/config.Y
++++ filter/config.Y
+@@ -558,7 +558,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 2386fc9..f2a5d06 100644
+--- filter/filter.h
++++ filter/filter.h
+@@ -79,11 +79,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);
+
+@@ -186,6 +188,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
+--- filter/trie.c
++++ 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
+--- nest/proto-hooks.c
++++ 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
+--- nest/proto.c
++++ 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
+--- nest/protocol.h
++++ 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
+--- nest/rt-table.c
++++ 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
++++ 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
++++ 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..5b9cae1
+--- /dev/null
++++ proto/agg/agg.c
+@@ -0,0 +1,720 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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 "lib/alloca.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) ||
++ (asr_o->route_src != asr->route_src))
++ {
++ /* New summary route */
++ agg_init_sumroute(p, asr);
++ return;
++ }
++
++ /* Should we move this to protocol-specific hook? */
++ switch (asr->route_src)
++ {
++ case RTS_BGP:
++ if ((asr_o->u.bgp.local_id != asr->u.bgp.local_id) || (asr_o->u.bgp.local_as != asr->u.bgp.local_as))
++ {
++ agg_init_sumroute(p, asr);
++ return;
++ }
++ break;
++ }
++
++ 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);
++
++ /*
++ * XXX: we possibly have to determine if summary routes configuration
++ * is changed and we hate to request refeeding
++ */
++
++ /* 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, *new_rta;
++
++ /* 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;
++ /*
++ * We want to get stable attribute copy.
++ *
++ * Base attributes (direct next hop) can be changed in rta directly,
++ * imposing COW in some cases.)
++ * Extended attributes can be added or updated in:
++ * * make_tmp_attrs() import hook
++ * * export filter
++ * * import/export pipe filter.
++ *
++ * So, if either
++ * * new is not cached OR
++ * * tmpa != new->attrs->eattrs (see end of do_rte_announce)
++ *
++ * we have to create and lookup new rta.
++ */
++ if ((new->attrs->aflags & RTAF_CACHED) && (attrs == new->attrs->eattrs))
++ ar->attrs = rta_clone(new->attrs);
++ else
++ {
++ /*
++ * Attributes or extended attributes are modified by filter,
++ * we need to create stable storage
++ */
++ new_rta = alloca(sizeof(rta));
++ memcpy(new_rta, new->attrs, sizeof(rta));
++ new_rta->eattrs = attrs;
++ new_rta->aflags = 0;
++ ar->attrs = rta_clone(rta_lookup(new_rta));
++ }
++
++ /* 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: "AGG",
++ template: "agg%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..d3e6f65
+--- /dev/null
++++ proto/agg/agg.h
+@@ -0,0 +1,87 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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 */
++ union {
++ struct {
++ u32 local_id; /* BGP router id */
++ u32 local_as; /* BGP local ASn */
++ } bgp;
++ } u;
++ 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..652b461
+--- /dev/null
++++ proto/agg/config.Y
+@@ -0,0 +1,108 @@
++/*
++ * BIRD -- BGP route aggregation
++ *
++ * (c) 2012 Yandex LLC
++ * (c) 2012 Alexander V. Chernikov <melifaro@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;
++u32 bgp_id = 0, bgp_as = 0;
++struct agg_sumroute *asr;
++
++CF_DECLS
++
++CF_KEYWORDS(AGGREGATOR, AGGREGATE, ADDRESS, SUMMARY, ONLY, SAVE, ATTRIBUTES, MANDATORY, LIST, BGP, OSPF, E1, E2)
++CF_KEYWORDS(ID, AS)
++
++%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;
++ switch (current_rtype)
++ {
++ case RTS_BGP:
++ asr->u.bgp.local_id = bgp_id;
++ asr->u.bgp.local_as = bgp_as;
++ break;
++ }
++ 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 ID idval AS expr { current_rtype = RTS_BGP; bgp_id = $3; bgp_as = $5; }
++ | 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..9b068a8 100644
+--- proto/bgp/attrs.c
++++ 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,749 @@ 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, agg_id;
++ byte *new_ptr;
++ int as_differs = 0;
++ int as_set_index = 0;
++ int agg_count = 0;
++ 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);
++
++ agg_as = asr->u.bgp.local_as;
++ agg_id = asr->u.bgp.local_id;
++ BDBG("Summary route ASN/ID set to %d/%R", agg_as, agg_id);
++
++ 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 ((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",
++ p->p.name, asr->tn.addr, asr->tn.plen, ar->fn.prefix, ar->fn.pxlen);
++ continue;
++ }
++ }
++
++ /*
++ * 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;
++
++ agg_count++;
++ }
++
++ /* Skip route? */
++ if (!agg_count)
++ {
++ log(L_ERR "%s: Route %I/%d cannot be summarized due to conflicting Router Id/ASN", p->p.name, 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(new->eattrs, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)))
++ atomic_agg = 1;
++
++ if ((ea_find(a->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
+--- proto/bgp/bgp.c
++++ 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
+--- proto/bgp/bgp.h
++++ 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
+--- sysdep/autoconf.h.in
++++ 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
+