Ondrej Zajicek wrote:
> On Mon, Jan 23, 2012 at 10:30:48PM +0400, Alexander V. Chernikov wrote:
>> On 23.01.2012 22:00, Ondrej Zajicek wrote:
>>> On Mon, Jan 23, 2012 at 08:14:28PM +0400, Alexander V. Chernikov wrote:
>>>> On 23.01.2012 20:01, Maciej Wierzbicki wrote:
>>>>> Hello.
>>>>>
>>>>> Case study:
>>>>> * importing full BGP table from various uplinks
>>>>> * some routes received by BGP are being exported via OSPF to core1,
>>>>> using filters:
>>>>> (source = RTS_BGP&& bgp_path ~ [= * ASNXYZ * =])
>>>>>
>>>>> Question: how to aggregate routes (whenever possible) before exporting
>>>>> them via OSPF to core?
>>>> It is not possible currently.
>>>>
>>>> I'm working on BGP route aggregation and I plan to get more or less
>>>> working code at the end of this week.
>>> Do you plan to integrate it to the BGP protocol? I don't think it is
>> This is separate protocol, of course.
>>> a good idea. It would be easy to make generic route aggregation -
>> My first idea was to implement generic aggregation protocol.
>> However, do we really need it generic?
>> Currently we have bunch of link-state protocols (ISIS / OSPF) which are
>> pure singletons, and, even if not we probably don't want to make summary
>> routes between instances. RIP[ng] is RIP(c). There are also some
>> multicast protocols but is is far-far away. Not sure if we should permit
>> route aggregation from different protocol types.
>
> My idea is to to make this independent of a source protocol of
> aggregated routes. So the question would be: Is there any advantage to
> make it specific? Generally, the aggregator would accept any routes and
> generates a new one, without any protocol specific attributes. There may
> be an option for processing BGP attributes and generating proper BGP
> attributes, but i guess this is not essential.
>
> I guess most users would want either aggregate received BGP routes to
> generate the default (or some more specific) routes for IGP (for this
> usage pattern it would be useful to have option for mandatory minimal
> number of routes to originate aggregate), or aggregate their IGP routes
> for origination to BGP.
>
>> Yes. I personally see this as following:
>>
>> protocol abgp agg1 {
>> aggregate address 1.2.3.0/24;
>> aggregate address 1.2.4.0/24 save attributes (or other keywords); #
>> Aggregate as much attributes as possible ( see RFC 4271 9.2.2.2. )
>> #
>> http://www.cisco.com/en/US/tech/tk365/technologies_tech_note09186a0080094826.shtml
>> aggregate address 2.3.4.5.0/24 summary only;
>> aggregate address 3.4.5.0/24 mandatory list { 3.4.5.1/32, 3.4.5.8/29};
>> import filter somefilter; # Change summary route attributes
>> }
>>
>> protocol bgp bgp1 {
>> ..
>> aggregator agg1;
>> }
>
>
>> Rte's matching 'summary only' instances have to be modified (no-export
>> community have to be added to community attribute) by aggregator before
>> passing them to rte_update
>
> What about just having an aggregator protocol connected directly to a
> table? Routes would be received from a table and aggregated ones are put
> to the same table. This would work well for BGP->IGP and IGP->BGP
Yes, if we assume protocol instance can aggregate any routes we can use
singe instance per rtable and add pointer to it inside struct rtable.
First disadvantage is that protocol lost last bits of knowledge about
aggregation. I mean, in this approach we will do tree checks for every
best rte regardless of protocol type. We still have to add
no-export community to matching rtes for supporting summary-only routes
(nearly the same task as in discussion about LDP.label attribute).
The second one is summarized attributes:
1) We have to announce summary rte with some source (RTS_). If we're
aggregating BGP routes it is BGP. And if we get 3 BGP and 1 OSPF ?
We can't detertmine it reliably.
2) If we try to save attributes as much as possible (AS-PATH / AS-SET,
even communities) we can end with route having both OSPF metric, for
example and BGP attributes.
So in this approach we have to change syntax to
proto aggregator agg1 {
aggregate-address bgp 1.2.3.0/24;
}
or even
proto aggregator agg1 {
bgp {
aggregate-address 1.2.3.0/24;
};
ospf {
..
}
}
> aggregation, where we do not care for non-aggregated routes. Not sure
> about BGP->BGP transit, but i guess it could also work, or aggegator
> could work like a pipe. Not sure if my explanation is clear enough, i
> could add some examples.
>
>> Aggregator stores its summary and mandatory routes in modified f_trie.
>> (
>> I think, there is no need to import/implement another tree if we can
>> modify current implementation:
>> e.g. use regular pools (flag passed to f_new_trie, along with node_size)
>> and add trie_remove_prefix
>
> OK
>
>> Btw, I've got small patch from my previous approach, it moves default
>> protocol preference to struct protocol and assigns it in
>> proto_config_new instead of assigning it in every protocol manually.
>> Maybe it is a good candidate for the next commit? :)
>
> Merged.
Thanks!
Maybe another simple patch? :)
It shows "UNNAMED" as filter name instead of <NULL> where protocol
filter is configured as input filter { ... };
(It is patch #2 from general protocol limiting patch tale)
Btw #2,
And. there is another patch that removes [nearly] all pipe hacks from
core, can you take a look at it? (I've sent both patches on 29 october
with last limiting patch version) (And I probably know now how can I fix
last FIXME)
>From 05c95bd23e2da5a3032fead63cf3884409d29f79 Mon Sep 17 00:00:00 2001
From: Alexander V. Chernikov <[email protected]>
Date: Sun, 27 Nov 2011 07:09:25 +0000
Subject: [PATCH 2/3] * Fix BGP filters naming
---
filter/f-util.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/filter/f-util.c b/filter/f-util.c
index 9f2eb6b..eef09da 100644
--- a/filter/f-util.c
+++ b/filter/f-util.c
@@ -61,6 +61,6 @@ filter_name(struct filter *filter)
return "ACCEPT";
else if (filter == FILTER_REJECT)
return "REJECT";
- else
- return filter->name;
+
+ return (filter->name ? filter->name : "UNNAMED");
}
--
1.7.3.2
>From f1d306f2b5071d46b85d2db597a94a8f5e9376c5 Mon Sep 17 00:00:00 2001
From: Alexander V. Chernikov <[email protected]>
Date: Sun, 27 Nov 2011 07:08:12 +0000
Subject: [PATCH 1/3] * Implement per-table protocol hooks in core
---
nest/proto.c | 147 +++++++++++++++++++++++++++++++----------------------
nest/protocol.h | 16 ++----
nest/route.h | 3 +
nest/rt-table.c | 64 +++++++++--------------
proto/pipe/pipe.c | 119 +++++++++++++++++++++++++++++++++++++++++-
proto/pipe/pipe.h | 7 +--
6 files changed, 238 insertions(+), 118 deletions(-)
diff --git a/nest/proto.c b/nest/proto.c
index d55c348..451db2c 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -19,6 +19,9 @@
#include "nest/iface.h"
#include "nest/cli.h"
#include "filter/filter.h"
+#ifdef CONFIG_PIPE
+#include "proto/pipe/pipe.h"
+#endif
pool *proto_pool;
@@ -112,8 +115,6 @@ proto_new(struct proto_config *c, unsigned size)
p->disabled = c->disabled;
p->proto = pr;
p->table = c->table->table;
- p->in_filter = c->in_filter;
- p->out_filter = c->out_filter;
p->hash_key = random_u32();
c->proto = p;
return p;
@@ -142,6 +143,8 @@ proto_init_instance(struct proto *p)
* protocol does), you needn't to worry about this function since the
* connection to the protocol's primary routing table is initialized
* automatically by the core code.
+ *
+ * Returns pointer to announce hook or NULL
*/
struct announce_hook *
proto_add_announce_hook(struct proto *p, struct rtable *t)
@@ -152,7 +155,7 @@ proto_add_announce_hook(struct proto *p, struct rtable *t)
return NULL;
DBG("Connecting protocol %s to table %s\n", p->name, t->name);
PD(p, "Connected to table %s", t->name);
- h = mb_alloc(p->pool, sizeof(struct announce_hook));
+ h = mb_allocz(p->pool, sizeof(struct announce_hook));
h->table = t;
h->proto = p;
h->next = p->ahooks;
@@ -161,6 +164,25 @@ proto_add_announce_hook(struct proto *p, struct rtable *t)
return h;
}
+/**
+ * proto_find_announce_hook - find announce hooks
+ * @p: protocol instance
+ * @t: routing table
+ *
+ * Returns pointer to announce hook or NULL
+ */
+struct announce_hook *
+proto_find_announce_hook(struct proto *p, struct rtable *t)
+{
+ struct announce_hook *a;
+
+ for (a = p->ahooks; a; a = a->next)
+ if (a->table == t)
+ return a;
+
+ return NULL;
+}
+
static void
proto_flush_hooks(struct proto *p)
{
@@ -324,6 +346,8 @@ proto_init(struct proto_config *c)
static int
proto_reconfigure(struct proto *p, struct proto_config *oc, struct
proto_config *nc, int type)
{
+ struct announce_hook *a;
+
/* If the protocol is DOWN, we just restart it */
if (p->proto_state == PS_DOWN)
return 0;
@@ -353,6 +377,19 @@ proto_reconfigure(struct proto *p, struct proto_config
*oc, struct proto_config
p->debug = nc->debug;
p->mrtdump = nc->mrtdump;
+ /*
+ * Set import/export filters if protocol is connected to the route table.
+ * We need to to this before protocol-specific reconfigure hook due to
+ * possible filter manipulations in pipe-like protocols.
+ * If no connection to master table is available, skip this step.
+ * In this case filters are assigned in proto_feed_initial
+ */
+ if (a = p->thook)
+ {
+ a->in_filter = nc->in_filter;
+ a->out_filter = nc->out_filter;
+ }
+
/* Execute protocol specific reconfigure hook */
if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
return 0;
@@ -361,8 +398,6 @@ proto_reconfigure(struct proto *p, struct proto_config *oc,
struct proto_config
PD(p, "Reconfigured");
p->cf = nc;
p->name = nc->name;
- p->in_filter = nc->in_filter;
- p->out_filter = nc->out_filter;
p->preference = nc->preference;
if (import_changed || export_changed)
@@ -552,6 +587,7 @@ void
protos_dump_all(void)
{
struct proto *p;
+ struct announce_hook *a;
debug("Protocols:\n");
@@ -559,10 +595,14 @@ protos_dump_all(void)
{
debug(" protocol %s state %s/%s\n", p->name,
p_states[p->proto_state], c_states[p->core_state]);
- if (p->in_filter)
- debug("\tInput filter: %s\n", filter_name(p->in_filter));
- if (p->out_filter != FILTER_REJECT)
- debug("\tOutput filter: %s\n", filter_name(p->out_filter));
+ for (a = p->ahooks; a; a = a->next)
+ {
+ debug("\tTABLE %s\n", a->table->name);
+ if (a->in_filter)
+ debug("\tInput filter: %s\n", filter_name(a->in_filter));
+ if (a->out_filter != FILTER_REJECT)
+ debug("\tOutput filter: %s\n", filter_name(a->out_filter));
+ }
if (p->disabled)
debug("\tDISABLED\n");
else if (p->proto->dump)
@@ -680,12 +720,30 @@ static void
proto_feed_initial(void *P)
{
struct proto *p = P;
+ struct announce_hook *a;
if (p->core_state != FS_FEEDING)
return;
DBG("Feeding protocol %s\n", p->name);
- proto_add_announce_hook(p, p->table);
+ if (a = proto_add_announce_hook(p, p->table))
+ {
+ /* Set master table */
+ p->thook = a;
+ /* Set filters */
+ a->in_filter = p->cf->in_filter;
+ a->out_filter = p->cf->out_filter;
+ /* Set pointer to stats */
+ a->stats = &p->stats;
+ }
+
+ /*
+ * Call reconfigure hook to permit possible filter changes.
+ * Old config is the same as new so no problems should arise
+ */
+ if (p->proto->reconfigure)
+ p->proto->reconfigure(p, p->cf);
+
if_feed_baby(p);
proto_feed_more(P);
}
@@ -854,9 +912,8 @@ proto_state_name(struct proto *p)
}
static void
-proto_do_show_stats(struct proto *p)
+proto_do_show_stats(struct proto_stats *s)
{
- struct proto_stats *s = &p->stats;
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
s->imp_routes, s->exp_routes, s->pref_routes);
cli_msg(-1006, " Route change stats: received rejected filtered
ignored accepted");
@@ -874,52 +931,11 @@ proto_do_show_stats(struct proto *p)
s->exp_withdraws_received, s->exp_withdraws_accepted);
}
-#ifdef CONFIG_PIPE
-static void
-proto_do_show_pipe_stats(struct proto *p)
-{
- struct proto_stats *s1 = &p->stats;
- struct proto_stats *s2 = pipe_get_peer_stats(p);
-
- /*
- * Pipe stats (as anything related to pipes) are a bit tricky. There
- * are two sets of stats - s1 for routes going from the primary
- * routing table to the secondary routing table ('exported' from the
- * user point of view) and s2 for routes going in the other
- * direction ('imported' from the user point of view).
- *
- * Each route going through a pipe is, technically, first exported
- * to the pipe and then imported from that pipe and such operations
- * are counted in one set of stats according to the direction of the
- * route propagation. Filtering is done just in the first part
- * (export). Therefore, we compose stats for one directon for one
- * user direction from both import and export stats, skipping
- * immediate and irrelevant steps (exp_updates_accepted,
- * imp_updates_received, imp_updates_filtered, ...)
- */
-
- cli_msg(-1006, " Routes: %u imported, %u exported",
- s2->imp_routes, s1->imp_routes);
- cli_msg(-1006, " Route change stats: received rejected filtered
ignored accepted");
- cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- s2->exp_updates_received, s2->exp_updates_rejected +
s2->imp_updates_invalid,
- s2->exp_updates_filtered, s2->imp_updates_ignored,
s2->imp_updates_accepted);
- cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- s2->exp_withdraws_received, s2->imp_withdraws_invalid,
- s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
- cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
- s1->exp_updates_received, s1->exp_updates_rejected +
s1->imp_updates_invalid,
- s1->exp_updates_filtered, s1->imp_updates_ignored,
s1->imp_updates_accepted);
- cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
- s1->exp_withdraws_received, s1->imp_withdraws_invalid,
- s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
-}
-#endif
-
void
proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
{
byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
+ struct announce_hook *a;
/* First protocol - show header */
if (!cnt)
@@ -943,17 +959,26 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int
cnt)
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
cli_msg(-1006, " Preference: %d", p->preference);
- cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter));
- cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter));
- if (p->proto_state != PS_DOWN)
+ for (a = p->ahooks; a; a = a->next)
{
+ cli_msg(-1006, " Table: %s", a->table->name);
+ cli_msg(-1006, " Input filter: %s", filter_name(a->in_filter));
+ cli_msg(-1006, " Output filter: %s", filter_name(a->out_filter));
+
+ if (p->proto_state != PS_DOWN)
+ {
#ifdef CONFIG_PIPE
- if (proto_is_pipe(p))
- proto_do_show_pipe_stats(p);
- else
+ /* XXX: This block should be removed */
+ if (proto_is_pipe(p))
+ {
+ if (a == p->thook)
+ pipe_show_stats(p);
+ }
+ else
#endif
- proto_do_show_stats(p);
+ proto_do_show_stats(a->stats);
+ }
}
if (p->proto->show_proto_info)
diff --git a/nest/protocol.h b/nest/protocol.h
index a7518c2..09ccd9c 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -190,8 +190,7 @@ struct proto {
void (*rte_remove)(struct network *, struct rte *);
struct rtable *table; /* Our primary routing table */
- struct filter *in_filter; /* Input filter */
- struct filter *out_filter; /* Output filter */
+ struct announce_hook *thook; /* Announcement hook for the master
table */
struct announce_hook *ahooks; /* Announcement hooks for this
protocol */
struct fib_iterator *feed_iterator; /* Routing table iterator used during
protocol feeding */
@@ -349,18 +348,13 @@ struct announce_hook {
node n;
struct rtable *table;
struct proto *proto;
+ struct filter *in_filter; /* Input filter */
+ struct filter *out_filter; /* Output filter */
+ struct proto_stats *stats; /* Per-table protocol statistics */
struct announce_hook *next; /* Next hook for the same protocol */
};
struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *);
-
-/*
- * Some pipe-specific nest hacks
- */
-
-#ifdef CONFIG_PIPE
-#include "proto/pipe/pipe.h"
-#endif
-
+struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable
*t);
#endif
diff --git a/nest/route.h b/nest/route.h
index a4c0154..03703c6 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -218,6 +218,7 @@ typedef struct rte {
#define RA_ANY 2 /* Announcement of any route change */
struct config;
+struct announce_hook;
void rt_init(void);
void rt_preconfig(struct config *);
@@ -230,6 +231,8 @@ static inline net *net_get(rtable *tab, ip_addr addr,
unsigned len) { return (ne
rte *rte_find(net *net, struct proto *p);
rte *rte_get_temp(struct rta *);
void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte
*new);
+#define rte_update(tab, net, p, src, new) do_rte_update(tab, (p)->thook,
net, p, src, new)
+void do_rte_update(rtable *tab, struct announce_hook *a, net *net, struct
proto *p, struct proto *src, rte *new);
void rte_discard(rtable *tab, rte *old);
void rte_dump(rte *);
void rte_free(rte *);
diff --git a/nest/rt-table.c b/nest/rt-table.c
index e20d2f6..0c0a042 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -187,21 +187,12 @@ static inline void
do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new,
rte *old, ea_list *tmpa, int refeed)
{
struct proto *p = a->proto;
- struct filter *filter = p->out_filter;
- struct proto_stats *stats = &p->stats;
+ struct filter *filter = a->out_filter;
+ struct proto_stats *stats = a->stats;
rte *new0 = new;
rte *old0 = old;
int ok;
-#ifdef CONFIG_PIPE
- /* The secondary direction of the pipe */
- if (proto_is_pipe(p) && (p->table != a->table))
- {
- filter = p->in_filter;
- stats = pipe_get_peer_stats(p);
- }
-#endif
-
if (new)
{
stats->exp_updates_received++;
@@ -423,18 +414,14 @@ rte_same(rte *x, rte *y)
}
static void
-rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src,
rte *new, ea_list *tmpa)
+rte_recalculate(rtable *table, struct announce_hook *a, net *net, struct proto
*p, struct proto *src, rte *new, ea_list *tmpa)
{
- struct proto_stats *stats = &p->stats;
+ struct proto_stats *stats;
rte *old_best = net->routes;
rte *old = NULL;
rte **k, *r, *s;
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p) && (p->table == table))
- stats = pipe_get_peer_stats(p);
-#endif
-
+ stats = a ? a->stats : &p->stats;
k = &net->routes; /* Find and remove original route from
the same protocol */
while (old = *k)
{
@@ -607,8 +594,9 @@ rte_update_unlock(void)
}
/**
- * rte_update - enter a new update to a routing table
+ * do_rte_update - enter a new update to a routing table
* @table: table to be updated
+ * @a: pointer to table announce hook
* @net: network node
* @p: protocol submitting the update
* @src: protocol originating the update
@@ -648,28 +636,27 @@ rte_update_unlock(void)
*/
void
-rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte
*new)
+do_rte_update(rtable *table, struct announce_hook *a, net *net, struct proto
*p, struct proto *src, rte *new)
{
ea_list *tmpa = NULL;
- struct proto_stats *stats = &p->stats;
+ struct proto_stats *stats;
+ struct filter *filter;
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p) && (p->table == table))
- stats = pipe_get_peer_stats(p);
-#endif
+ if (a)
+ {
+ stats = a->stats;
+ filter = a->in_filter;
+ }
+ else
+ {
+ stats = &p->stats;
+ filter = p->cf->in_filter;
+ }
rte_update_lock();
if (new)
{
new->sender = p;
- struct filter *filter = p->in_filter;
-
- /* Do not filter routes going through the pipe,
- they are filtered in the export filter only. */
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p))
- filter = FILTER_ACCEPT;
-#endif
stats->imp_updates_received++;
if (!rte_validate(new))
@@ -706,13 +693,13 @@ rte_update(rtable *table, net *net, struct proto *p,
struct proto *src, rte *new
else
stats->imp_withdraws_received++;
- rte_recalculate(table, net, p, src, new, tmpa);
+ rte_recalculate(table, a, net, p, src, new, tmpa);
rte_update_unlock();
return;
drop:
rte_free(new);
- rte_recalculate(table, net, p, src, NULL, NULL);
+ rte_recalculate(table, a, net, p, src, NULL, NULL);
rte_update_unlock();
}
@@ -735,7 +722,7 @@ void
rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used
during garbage collection */
{
rte_update_lock();
- rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL);
+ rte_recalculate(t, old->sender->thook, old->net, old->sender,
old->attrs->proto, NULL, NULL);
rte_update_unlock();
}
@@ -1680,6 +1667,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
rte *e, *ee;
byte ia[STD_ADDRESS_P_LENGTH+8];
+ struct announce_hook *a;
int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
@@ -1709,8 +1697,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
'configure soft' command may change the export filter
and do not update routes */
- if ((p1->out_filter == FILTER_REJECT) ||
- (p1->out_filter && f_run(p1->out_filter, &e, &tmpa,
rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
+ if ((a = proto_find_announce_hook(p1, d->table)) &&
((a->out_filter == FILTER_REJECT) ||
+ (a->out_filter && f_run(a->out_filter, &e, &tmpa,
rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
ok = 0;
}
}
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 420c5a9..2f1b519 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -24,6 +24,7 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
+#include "nest/cli.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
@@ -34,8 +35,10 @@ static void
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old,
ea_list *attrs)
{
struct pipe_proto *p = (struct pipe_proto *) P;
- rtable *dest = (src_table == P->table) ? p->peer : P->table; /* The other
side of the pipe */
+ rtable *dest;
struct proto *src;
+ struct announce_hook *h;
+ struct proto_stats *stats, *old_stats;
net *nn;
rte *e;
@@ -44,6 +47,19 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n,
rte *new, rte *old, e
if (!new && !old)
return;
+ if (src_table == P->table)
+ {
+ dest = p->peer;
+ h = p->a;
+ stats = &P->stats;
+ }
+ else
+ {
+ dest = P->table;
+ h = P->thook;
+ stats = &p->peer_stats;
+ }
+
if (dest->pipe_busy)
{
log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
@@ -85,7 +101,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n,
rte *new, rte *old, e
}
src_table->pipe_busy = 1;
- rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
+ /*
+ * FIXME: Trick with stats pointer is needed to produce
+ * valid numbers on protocol shutdown.
+ */
+ old_stats = h->stats;
+ h->stats = stats;
+ do_rte_update(dest, h, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
+ h->stats = old_stats;
src_table->pipe_busy = 0;
}
@@ -126,6 +149,10 @@ pipe_start(struct proto *P)
/* Connect the protocol also to the peer routing table. */
a = proto_add_announce_hook(P, p->peer);
+ p->a = a;
+
+ /* Set up filters and stats */
+ a->stats = &p->peer_stats;
return PS_UP;
}
@@ -168,16 +195,101 @@ pipe_postconfig(struct proto_config *C)
static int
pipe_reconfigure(struct proto *P, struct proto_config *new)
{
- // struct pipe_proto *p = (struct pipe_proto *) P;
+ struct announce_hook *a, *pa;
+ struct pipe_proto *p = (struct pipe_proto *)P;
struct pipe_config *o = (struct pipe_config *) P->cf;
struct pipe_config *n = (struct pipe_config *) new;
if ((o->peer->table != n->peer->table) || (o->mode != n->mode))
return 0;
+ /*
+ * reconfigure can be called in any protocol status
+ */
+ if (!((a = P->thook) && (pa = p->a)))
+ return 1;
+
+ /* Update filters */
+ /*
+ * Export filter remains as is and import filter moves to
+ * peer table
+ *
+ * /------\ /------\
+ * | | --- (OUT)(1) (OUT)(3) --- | |
+ * | MAIN | | PIPE | | PEER |
+ * | | ---- (IN)(2) (IN)(4) --- | |
+ * \------/ \------/
+ *
+ * When new route is announced on MAIN table it gets checked by
+ * export filter (1), and, after that, it is announced to peer table
+ * via rte_update. import filter (4) is called. When new route is
+ * annouced in PEER table (3) and (2) are used. Oviously, there is
+ * no need in filtering the same route twice, so (4) and (2) filters
+ * should be set to pass all routes.
+ *
+ * User can configure (1) and (2) filters so we move filter (2) to
+ * (3) and set (2) and (4) to FILTER_ACCEPT
+ *
+ * This is the right place to do it since
+ * 1) configure hook is called exactly before initial feeding
+ * 2) it is (now) called exactly after setting new filters in
proto_reconfigure
+ */
+
+ /*
+ * Check for changed filters:
+ * Import and export filters are checked in proto_reconfigure by
+ * comparing old/new configurations so no additional checks are
+ * required here
+ */
+
+ pa->out_filter = a->in_filter;
+ a->in_filter = FILTER_ACCEPT;
+ pa->in_filter = FILTER_ACCEPT;
+
return 1;
}
+void
+pipe_show_stats(struct proto *P)
+{
+ struct pipe_proto *p = (struct pipe_proto *) P;
+ struct proto_stats *s1 = &P->stats;
+ struct proto_stats *s2 = &p->peer_stats;
+
+ /*
+ * Pipe stats (as anything related to pipes) are a bit tricky. There
+ * are two sets of stats - s1 for routes going from the primary
+ * routing table to the secondary routing table ('exported' from the
+ * user point of view) and s2 for routes going in the other
+ * direction ('imported' from the user point of view).
+ *
+ * Each route going through a pipe is, technically, first exported
+ * to the pipe and then imported from that pipe and such operations
+ * are counted in one set of stats according to the direction of the
+ * route propagation. Filtering is done just in the first part
+ * (export). Therefore, we compose stats for one directon for one
+ * user direction from both import and export stats, skipping
+ * immediate and irrelevant steps (exp_updates_accepted,
+ * imp_updates_received, imp_updates_filtered, ...)
+ */
+
+ cli_msg(-1006, " Routes: %u imported, %u exported",
+ s2->imp_routes, s1->imp_routes);
+ cli_msg(-1006, " Route change stats: received rejected filtered
ignored accepted");
+ cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
+ s2->exp_updates_received, s2->exp_updates_rejected +
s2->imp_updates_invalid,
+ s2->exp_updates_filtered, s2->imp_updates_ignored,
s2->imp_updates_accepted);
+ cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
+ s2->exp_withdraws_received, s2->imp_withdraws_invalid,
+ s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
+ cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
+ s1->exp_updates_received, s1->exp_updates_rejected +
s1->imp_updates_invalid,
+ s1->exp_updates_filtered, s1->imp_updates_ignored,
s1->imp_updates_accepted);
+ cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
+ s1->exp_withdraws_received, s1->imp_withdraws_invalid,
+ s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+}
+
static void
pipe_copy_config(struct proto_config *dest, struct proto_config *src)
{
@@ -194,6 +306,7 @@ pipe_get_status(struct proto *P, byte *buf)
}
+
struct protocol proto_pipe = {
name: "Pipe",
template: "pipe%d",
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index fbd2129..6eb5b03 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -22,6 +22,7 @@ struct pipe_proto {
struct proto p;
struct rtable *peer;
struct proto_stats peer_stats; /* Statistics for the direction
peer->primary */
+ struct announce_hook *a; /* Announce hook for the peer table */
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
};
@@ -31,10 +32,6 @@ extern struct protocol proto_pipe;
static inline int proto_is_pipe(struct proto *p)
{ return p->proto == &proto_pipe; }
-static inline struct rtable * pipe_get_peer_table(struct proto *P)
-{ return ((struct pipe_proto *) P)->peer; }
-
-static inline struct proto_stats * pipe_get_peer_stats(struct proto *P)
-{ return &((struct pipe_proto *) P)->peer_stats; }
+void pipe_show_stats(struct proto *P);
#endif
--
1.7.3.2