Commit c0adf7e9 authored by Ondřej Zajíček's avatar Ondřej Zajíček

Better support for multitable protocols.

The nest-protocol interaction is changed to better handle multitable
protocols. Multitable protocols now declare that by 'multitable' field,
which tells nest that a protocol handles things related to proto-rtable
interaction (table locking, announce hook adding, reconfiguration of
filters) itself.

Filters and stats are moved to announce hooks, a protocol could have
different filters and stats to different tables.

The patch is based on one from Alexander V. Chernikov, thanks.
parent 46c1a583
...@@ -90,7 +90,7 @@ void dump_attrs(rte *e) ...@@ -90,7 +90,7 @@ void dump_attrs(rte *e)
* @p: protocol instance * @p: protocol instance
* *
* The start() hook is called by the core when it wishes to start * The start() hook is called by the core when it wishes to start
* the instance. * the instance. Multitable protocols should lock their tables here.
* *
* Result: new protocol state * Result: new protocol state
*/ */
...@@ -109,6 +109,17 @@ int start(struct proto *p) ...@@ -109,6 +109,17 @@ int start(struct proto *p)
int shutdown(struct proto *p) int shutdown(struct proto *p)
{ DUMMY; } { DUMMY; }
/**
* cleanup - request instance cleanup
* @p: protocol instance
*
* The cleanup() hook is called by the core when the protocol became
* hungry/down, i.e. all protocol ahooks and routes are flushed.
* Multitable protocols should unlock their tables here.
*/
void cleanup(struct proto *p)
{ DUMMY; }
/** /**
* get_status - get instance status * get_status - get instance status
* @p: protocol instance * @p: protocol instance
......
This diff is collapsed.
...@@ -39,6 +39,7 @@ struct protocol { ...@@ -39,6 +39,7 @@ struct protocol {
char *template; /* Template for automatic generation of names */ char *template; /* Template for automatic generation of names */
int name_counter; /* Counter for automatic name generation */ int name_counter; /* Counter for automatic name generation */
int attr_class; /* Attribute class known to this protocol */ int attr_class; /* Attribute class known to this protocol */
int multitable; /* Protocol handles all announce hooks itself */
unsigned preference; /* Default protocol preference */ unsigned preference; /* Default protocol preference */
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */ void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
...@@ -193,8 +194,7 @@ struct proto { ...@@ -193,8 +194,7 @@ struct proto {
void (*rte_remove)(struct network *, struct rte *); void (*rte_remove)(struct network *, struct rte *);
struct rtable *table; /* Our primary routing table */ struct rtable *table; /* Our primary routing table */
struct filter *in_filter; /* Input filter */ struct announce_hook *main_ahook; /* Primary announcement hook */
struct filter *out_filter; /* Output filter */
struct announce_hook *ahooks; /* Announcement hooks for this protocol */ struct announce_hook *ahooks; /* Announcement hooks for this protocol */
struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */ struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */
...@@ -218,6 +218,9 @@ static inline void ...@@ -218,6 +218,9 @@ static inline void
proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size) proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); } { memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
void proto_show_basic_info(struct proto *p);
void proto_cmd_show(struct proto *, unsigned int, int); void proto_cmd_show(struct proto *, unsigned int, int);
void proto_cmd_disable(struct proto *, unsigned int, int); void proto_cmd_disable(struct proto *, unsigned int, int);
void proto_cmd_enable(struct proto *, unsigned int, int); void proto_cmd_enable(struct proto *, unsigned int, int);
...@@ -352,18 +355,13 @@ struct announce_hook { ...@@ -352,18 +355,13 @@ struct announce_hook {
node n; node n;
struct rtable *table; struct rtable *table;
struct proto *proto; 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 *next; /* Next hook for the same protocol */
}; };
struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *); struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *, struct filter *, struct filter *, struct proto_stats *);
struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
/*
* Some pipe-specific nest hacks
*/
#ifdef CONFIG_PIPE
#include "proto/pipe/pipe.h"
#endif
#endif #endif
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/timer.h" #include "lib/timer.h"
#include "nest/protocol.h"
struct protocol; struct protocol;
struct proto; struct proto;
...@@ -179,7 +180,7 @@ struct hostentry { ...@@ -179,7 +180,7 @@ struct hostentry {
typedef struct rte { typedef struct rte {
struct rte *next; struct rte *next;
net *net; /* Network this RTE belongs to */ net *net; /* Network this RTE belongs to */
struct proto *sender; /* Protocol instance that sent the route to the routing table */ struct announce_hook *sender; /* Announce hook used to send the route to the routing table */
struct rta *attrs; /* Attributes of this route */ struct rta *attrs; /* Attributes of this route */
byte flags; /* Flags (REF_...) */ byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */ byte pflags; /* Protocol-specific flags */
...@@ -234,7 +235,8 @@ static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (n ...@@ -234,7 +235,8 @@ static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (n
static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); } static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); }
rte *rte_find(net *net, struct proto *p); rte *rte_find(net *net, struct proto *p);
rte *rte_get_temp(struct rta *); rte *rte_get_temp(struct rta *);
void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new); void rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src);
static inline void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new) { rte_update2(p->main_ahook, net, new, src); }
void rte_discard(rtable *tab, rte *old); void rte_discard(rtable *tab, rte *old);
void rte_dump(rte *); void rte_dump(rte *);
void rte_free(rte *); void rte_free(rte *);
......
...@@ -184,24 +184,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) ...@@ -184,24 +184,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
} }
static inline void static inline void
do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
{ {
struct proto *p = a->proto; struct proto *p = ah->proto;
struct filter *filter = p->out_filter; struct filter *filter = ah->out_filter;
struct proto_stats *stats = &p->stats; struct proto_stats *stats = ah->stats;
rte *new0 = new; rte *new0 = new;
rte *old0 = old; rte *old0 = old;
int ok; 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) if (new)
{ {
stats->exp_updates_received++; stats->exp_updates_received++;
...@@ -294,18 +286,18 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt ...@@ -294,18 +286,18 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt
rte_trace_out(D_ROUTES, p, old, "removed"); rte_trace_out(D_ROUTES, p, old, "removed");
} }
if (!new) if (!new)
p->rt_notify(p, a->table, net, NULL, old, NULL); p->rt_notify(p, ah->table, net, NULL, old, NULL);
else if (tmpa) else if (tmpa)
{ {
ea_list *t = tmpa; ea_list *t = tmpa;
while (t->next) while (t->next)
t = t->next; t = t->next;
t->next = new->attrs->eattrs; t->next = new->attrs->eattrs;
p->rt_notify(p, a->table, net, new, old, tmpa); p->rt_notify(p, ah->table, net, new, old, tmpa);
t->next = NULL; t->next = NULL;
} }
else else
p->rt_notify(p, a->table, net, new, old, new->attrs->eattrs); p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
if (new && new != new0) /* Discard temporary rte's */ if (new && new != new0) /* Discard temporary rte's */
rte_free(new); rte_free(new);
if (old && old != old0) if (old && old != old0)
...@@ -375,7 +367,7 @@ rte_validate(rte *e) ...@@ -375,7 +367,7 @@ rte_validate(rte *e)
if ((n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen)) if ((n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen))
{ {
log(L_WARN "Ignoring bogus prefix %I/%d received via %s", log(L_WARN "Ignoring bogus prefix %I/%d received via %s",
n->n.prefix, n->n.pxlen, e->sender->name); n->n.prefix, n->n.pxlen, e->sender->proto->name);
return 0; return 0;
} }
...@@ -383,7 +375,7 @@ rte_validate(rte *e) ...@@ -383,7 +375,7 @@ rte_validate(rte *e)
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
{ {
log(L_WARN "Ignoring bogus route %I/%d received via %s", log(L_WARN "Ignoring bogus route %I/%d received via %s",
n->n.prefix, n->n.pxlen, e->sender->name); n->n.prefix, n->n.pxlen, e->sender->proto->name);
return 0; return 0;
} }
...@@ -423,18 +415,15 @@ rte_same(rte *x, rte *y) ...@@ -423,18 +415,15 @@ rte_same(rte *x, rte *y)
} }
static void static void
rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa) rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src)
{ {
struct proto_stats *stats = &p->stats; struct proto *p = ah->proto;
struct rtable *table = ah->table;
struct proto_stats *stats = ah->stats;
rte *old_best = net->routes; rte *old_best = net->routes;
rte *old = NULL; rte *old = NULL;
rte **k, *r, *s; rte **k, *r, *s;
#ifdef CONFIG_PIPE
if (proto_is_pipe(p) && (p->table == table))
stats = pipe_get_peer_stats(p);
#endif
k = &net->routes; /* Find and remove original route from the same protocol */ k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k) while (old = *k)
{ {
...@@ -449,7 +438,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte ...@@ -449,7 +438,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte
* ignore it completely (there might be 'spurious withdraws', * ignore it completely (there might be 'spurious withdraws',
* see FIXME in do_rte_announce()) * see FIXME in do_rte_announce())
*/ */
if (old->sender != p) if (old->sender->proto != p)
{ {
if (new) if (new)
{ {
...@@ -613,6 +602,7 @@ rte_update_unlock(void) ...@@ -613,6 +602,7 @@ rte_update_unlock(void)
/** /**
* rte_update - enter a new update to a routing table * rte_update - enter a new update to a routing table
* @table: table to be updated * @table: table to be updated
* @ah: pointer to table announce hook
* @net: network node * @net: network node
* @p: protocol submitting the update * @p: protocol submitting the update
* @src: protocol originating the update * @src: protocol originating the update
...@@ -652,28 +642,17 @@ rte_update_unlock(void) ...@@ -652,28 +642,17 @@ rte_update_unlock(void)
*/ */
void void
rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new) rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src)
{ {
struct proto *p = ah->proto;
struct proto_stats *stats = ah->stats;
struct filter *filter = ah->in_filter;
ea_list *tmpa = NULL; ea_list *tmpa = NULL;
struct proto_stats *stats = &p->stats;
#ifdef CONFIG_PIPE
if (proto_is_pipe(p) && (p->table == table))
stats = pipe_get_peer_stats(p);
#endif
rte_update_lock(); rte_update_lock();
if (new) if (new)
{ {
new->sender = p; new->sender = ah;
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++; stats->imp_updates_received++;
if (!rte_validate(new)) if (!rte_validate(new))
...@@ -710,13 +689,13 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new ...@@ -710,13 +689,13 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new
else else
stats->imp_withdraws_received++; stats->imp_withdraws_received++;
rte_recalculate(table, net, p, src, new, tmpa); rte_recalculate(ah, net, new, tmpa, src);
rte_update_unlock(); rte_update_unlock();
return; return;
drop: drop:
rte_free(new); rte_free(new);
rte_recalculate(table, net, p, src, NULL, NULL); rte_recalculate(ah, net, NULL, NULL, src);
rte_update_unlock(); rte_update_unlock();
} }
...@@ -739,7 +718,7 @@ void ...@@ -739,7 +718,7 @@ void
rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */
{ {
rte_update_lock(); rte_update_lock();
rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL); rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->proto);
rte_update_unlock(); rte_update_unlock();
} }
...@@ -912,8 +891,8 @@ again: ...@@ -912,8 +891,8 @@ again:
ncnt++; ncnt++;
rescan: rescan:
for (e=n->routes; e; e=e->next, rcnt++) for (e=n->routes; e; e=e->next, rcnt++)
if (e->sender->core_state != FS_HAPPY && if (e->sender->proto->core_state != FS_HAPPY &&
e->sender->core_state != FS_FEEDING) e->sender->proto->core_state != FS_FEEDING)
{ {
rte_discard(tab, e); rte_discard(tab, e);
rdel++; rdel++;
...@@ -1026,7 +1005,7 @@ rt_next_hop_update_net(rtable *tab, net *n) ...@@ -1026,7 +1005,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
*k = new; *k = new;
rte_announce_i(tab, RA_ANY, n, new, e); rte_announce_i(tab, RA_ANY, n, new, e);
rte_trace_in(D_ROUTES, new->sender, new, "updated"); rte_trace_in(D_ROUTES, new->sender->proto, new, "updated");
/* Call a pre-comparison hook */ /* Call a pre-comparison hook */
/* Not really an efficient way to compute this */ /* Not really an efficient way to compute this */
...@@ -1066,7 +1045,7 @@ rt_next_hop_update_net(rtable *tab, net *n) ...@@ -1066,7 +1045,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
if (new != old_best) if (new != old_best)
{ {
rte_announce_i(tab, RA_OPTIMAL, n, new, old_best); rte_announce_i(tab, RA_OPTIMAL, n, new, old_best);
rte_trace_in(D_ROUTES, new->sender, new, "updated [best]"); rte_trace_in(D_ROUTES, new->sender->proto, new, "updated [best]");
} }
if (free_old_best) if (free_old_best)
...@@ -1693,6 +1672,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ...@@ -1693,6 +1672,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{ {
rte *e, *ee; rte *e, *ee;
byte ia[STD_ADDRESS_P_LENGTH+8]; byte ia[STD_ADDRESS_P_LENGTH+8];
struct announce_hook *a;
int ok; int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen); bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
...@@ -1722,8 +1702,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ...@@ -1722,8 +1702,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
'configure soft' command may change the export filter 'configure soft' command may change the export filter
and do not update routes */ and do not update routes */
if ((p1->out_filter == FILTER_REJECT) || if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) ||
(p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)) (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
ok = 0; ok = 0;
} }
} }
......
...@@ -1125,6 +1125,8 @@ bgp_show_proto_info(struct proto *P) ...@@ -1125,6 +1125,8 @@ bgp_show_proto_info(struct proto *P)
struct bgp_proto *p = (struct bgp_proto *) P; struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_conn *c = p->conn; struct bgp_conn *c = p->conn;
proto_show_basic_info(P);
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p)); cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface); cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
cli_msg(-1006, " Neighbor AS: %u", p->remote_as); cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
......
...@@ -16,6 +16,17 @@ ...@@ -16,6 +16,17 @@
* *
* To avoid pipe loops, Pipe keeps a `being updated' flag in each routing * To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
* table. * table.
*
* A pipe has two announce hooks, the first connected to the main
* table, the second connected to the peer table. When a new route is
* announced on the main table, it gets checked by an export filter in
* ahook 1, and, after that, it is announced to the peer table via
* rte_update(), an import filter in ahook 2 is called. When a new
* route is announced in the peer table, an export filter in ahook2
* and an import filter in ahook 1 are used. Oviously, there is no
* need in filtering the same route twice, so both import filters
* are set to accept, while user configured 'import' and 'export'
* filters are used as export filters in ahooks 2 and 1.
*/ */
#undef LOCAL_DEBUG #undef LOCAL_DEBUG
...@@ -24,6 +35,7 @@ ...@@ -24,6 +35,7 @@
#include "nest/iface.h" #include "nest/iface.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/cli.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "filter/filter.h" #include "filter/filter.h"
#include "lib/string.h" #include "lib/string.h"
...@@ -34,7 +46,8 @@ static void ...@@ -34,7 +46,8 @@ static void
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs) 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; struct pipe_proto *p = (struct pipe_proto *) P;
rtable *dest = (src_table == P->table) ? p->peer : P->table; /* The other side of the pipe */ struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
rtable *dst_table = ah->table;
struct proto *src; struct proto *src;
net *nn; net *nn;
...@@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e ...@@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
if (!new && !old) if (!new && !old)
return; return;
if (dest->pipe_busy) if (dst_table->pipe_busy)
{ {
log(L_ERR "Pipe loop detected when sending %I/%d to table %s", log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
n->n.prefix, n->n.pxlen, dest->name); n->n.prefix, n->n.pxlen, dst_table->name);
return; return;
} }
nn = net_get(dest, n->n.prefix, n->n.pxlen);
nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
if (new) if (new)
{ {
memcpy(&a, new->attrs, sizeof(rta)); memcpy(&a, new->attrs, sizeof(rta));
...@@ -85,14 +99,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e ...@@ -85,14 +99,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
} }
src_table->pipe_busy = 1; src_table->pipe_busy = 1;
rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e); rte_update2(ah, nn, e, (p->mode == PIPE_OPAQUE) ? &p->p : src);
src_table->pipe_busy = 0; src_table->pipe_busy = 0;
} }
static int static int
pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED) pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
{ {
struct proto *pp = (*ee)->sender; struct proto *pp = (*ee)->sender->proto;
if (pp == P) if (pp == P)
return -1; /* Avoid local loops automatically */ return -1; /* Avoid local loops automatically */
...@@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P) ...@@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P)
return 1; return 1;
} }
static struct proto *
pipe_init(struct proto_config *C)
{
struct pipe_config *c = (struct pipe_config *) C;
struct proto *P = proto_new(C, sizeof(struct pipe_proto));
struct pipe_proto *p = (struct pipe_proto *) P;
p->mode = c->mode;
p->peer_table = c->peer->table;
P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
P->rt_notify = pipe_rt_notify;
P->import_control = pipe_import_control;
P->reload_routes = pipe_reload_routes;
return P;
}
static int static int
pipe_start(struct proto *P) pipe_start(struct proto *P)
{ {
struct pipe_proto *p = (struct pipe_proto *) P; struct pipe_proto *p = (struct pipe_proto *) P;
struct announce_hook *a;
/* Clean up the secondary stats */ /* Lock both tables, unlock is handled in pipe_cleanup() */
bzero(&p->peer_stats, sizeof(struct proto_stats)); rt_lock_table(P->table);
rt_lock_table(p->peer_table);
/* Lock the peer table, unlock is handled in pipe_cleanup() */ /* Going directly to PS_UP - prepare for feeding,
rt_lock_table(p->peer); connect the protocol to both routing tables */
/* Connect the protocol also to the peer routing table. */ P->main_ahook = proto_add_announce_hook(P, P->table,
a = proto_add_announce_hook(P, p->peer); FILTER_ACCEPT, P->cf->out_filter, &P->stats);
p->peer_ahook = proto_add_announce_hook(P, p->peer_table,
FILTER_ACCEPT, P->cf->in_filter, &p->peer_stats);
return PS_UP; return PS_UP;
} }
...@@ -134,24 +167,15 @@ static void ...@@ -134,24 +167,15 @@ static void
pipe_cleanup(struct proto *P) pipe_cleanup(struct proto *P)
{ {
struct pipe_proto *p = (struct pipe_proto *) P; struct pipe_proto *p = (struct pipe_proto *) P;
rt_unlock_table(p->peer);
}
static struct proto * bzero(&P->stats, sizeof(struct proto_stats));
pipe_init(struct proto_config *C) bzero(&p->peer_stats, sizeof(struct proto_stats));
{
struct pipe_config *c = (struct pipe_config *) C;
struct proto *P = proto_new(C, sizeof(struct pipe_proto));
struct pipe_proto *p = (struct pipe_proto *) P;
p->peer = c->peer->table; P->main_ahook = NULL;
p->mode = c->mode; p->peer_ahook = NULL;
P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
P->rt_notify = pipe_rt_notify;
P->import_control = pipe_import_control;
P->reload_routes = pipe_reload_routes;
return P; rt_unlock_table(P->table);
rt_unlock_table(p->peer_table);
} }
static void static void
...@@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C) ...@@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C)
cf_error("Primary table and peer table must be different"); cf_error("Primary table and peer table must be different");
} }
extern int proto_reconfig_type;
static int static int
pipe_reconfigure(struct proto *P, struct proto_config *new) pipe_reconfigure(struct proto *P, struct proto_config *new)
{ {
// struct pipe_proto *p = (struct pipe_proto *) P; struct pipe_proto *p = (struct pipe_proto *)P;
struct pipe_config *o = (struct pipe_config *) P->cf; struct proto_config *old = P->cf;
struct pipe_config *n = (struct pipe_config *) new; struct pipe_config *oc = (struct pipe_config *) old;
struct pipe_config *nc = (struct pipe_config *) new;
if ((o->peer->table != n->peer->table) || (o->mode != n->mode)) if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
return 0; return 0;
/* Update output filters in ahooks */
if (P->main_ahook)
P->main_ahook->out_filter = new->out_filter;
if (p->peer_ahook)
p->peer_ahook->out_filter = new->in_filter;
if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
return 1;
if ((new->preference != old->preference)
|| ! filter_same(new->in_filter, old->in_filter)
|| ! filter_same(new->out_filter, old->out_filter))
proto_request_feeding(P);
return 1; return 1;
} }
...@@ -190,19 +232,80 @@ pipe_get_status(struct proto *P, byte *buf) ...@@ -190,19 +232,80 @@ pipe_get_status(struct proto *P, byte *buf)
{ {
struct pipe_proto *p = (struct pipe_proto *) P; struct pipe_proto *p = (struct pipe_proto *) P;
bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name); bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
}
static void
pipe_show_stats(struct pipe_proto *p)
{
struct proto_stats *s1 = &p->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 ahook to the primary routing and
* s2 for the ahook to the secondary routing table. The user point
* of view is that routes going from the primary routing table to
* the secondary routing table are 'exported', while routes going in
* the other direction are 'imported'.
*
* 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