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.
This commit is contained in:
parent
46c1a583a5
commit
c0adf7e9fc
8 changed files with 329 additions and 204 deletions
|
@ -90,7 +90,7 @@ void dump_attrs(rte *e)
|
|||
* @p: protocol instance
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
@ -109,6 +109,17 @@ int start(struct proto *p)
|
|||
int shutdown(struct proto *p)
|
||||
{ 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
|
||||
* @p: protocol instance
|
||||
|
|
200
nest/proto.c
200
nest/proto.c
|
@ -112,8 +112,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;
|
||||
|
@ -126,49 +124,102 @@ proto_init_instance(struct proto *p)
|
|||
p->pool = rp_new(proto_pool, p->proto->name);
|
||||
p->attn = ev_new(p->pool);
|
||||
p->attn->data = p;
|
||||
|
||||
if (! p->proto->multitable)
|
||||
rt_lock_table(p->table);
|
||||
}
|
||||
|
||||
extern pool *rt_table_pool;
|
||||
/**
|
||||
* proto_add_announce_hook - connect protocol to a routing table
|
||||
* @p: protocol instance
|
||||
* @t: routing table to connect to
|
||||
* @in: input filter
|
||||
* @out: output filter
|
||||
* @stats: per-table protocol statistics
|
||||
*
|
||||
* This function creates a connection between the protocol instance @p
|
||||
* and the routing table @t, making the protocol hear all changes in
|
||||
* the table.
|
||||
*
|
||||
* The announce hook is linked in the protocol ahook list and, if the
|
||||
* protocol accepts routes, also in the table ahook list. Announce
|
||||
* hooks are allocated from the routing table resource pool, they are
|
||||
* unlinked from the table ahook list after the protocol went down,
|
||||
* (in proto_schedule_flush()) and they are automatically freed after the
|
||||
* protocol is flushed (in proto_fell_down()).
|
||||
*
|
||||
* Unless you want to listen to multiple routing tables (as the Pipe
|
||||
* 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.
|
||||
*/
|
||||
struct announce_hook *
|
||||
proto_add_announce_hook(struct proto *p, struct rtable *t)
|
||||
proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
|
||||
struct filter *out, struct proto_stats *stats)
|
||||
{
|
||||
struct announce_hook *h;
|
||||
|
||||
if (!p->rt_notify)
|
||||
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(rt_table_pool, sizeof(struct announce_hook));
|
||||
h->table = t;
|
||||
h->proto = p;
|
||||
h->in_filter = in;
|
||||
h->out_filter = out;
|
||||
h->stats = stats;
|
||||
|
||||
h->next = p->ahooks;
|
||||
p->ahooks = h;
|
||||
|
||||
if (p->rt_notify)
|
||||
add_tail(&t->hooks, &h->n);
|
||||
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)
|
||||
proto_unlink_ahooks(struct proto *p)
|
||||
{
|
||||
struct announce_hook *h;
|
||||
|
||||
if (p->rt_notify)
|
||||
for(h=p->ahooks; h; h=h->next)
|
||||
rem_node(&h->n);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_free_ahooks(struct proto *p)
|
||||
{
|
||||
struct announce_hook *h, *hn;
|
||||
|
||||
for(h = p->ahooks; h; h = hn)
|
||||
{
|
||||
hn = h->next;
|
||||
mb_free(h);
|
||||
}
|
||||
|
||||
p->ahooks = NULL;
|
||||
p->main_ahook = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -322,6 +373,8 @@ proto_init(struct proto_config *c)
|
|||
return q;
|
||||
}
|
||||
|
||||
int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hook */
|
||||
|
||||
static int
|
||||
proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
|
||||
{
|
||||
|
@ -336,23 +389,10 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
|
|||
(proto_get_router_id(nc) != proto_get_router_id(oc)))
|
||||
return 0;
|
||||
|
||||
int import_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->in_filter, oc->in_filter);
|
||||
int export_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->out_filter, oc->out_filter);
|
||||
|
||||
/* We treat a change in preferences by reimporting routes */
|
||||
if (nc->preference != oc->preference)
|
||||
import_changed = 1;
|
||||
|
||||
/* If the protocol in not UP, it has no routes and we can ignore such changes */
|
||||
if (p->proto_state != PS_UP)
|
||||
import_changed = export_changed = 0;
|
||||
|
||||
/* Without this hook we cannot reload routes and have to restart the protocol */
|
||||
if (import_changed && ! p->reload_routes)
|
||||
return 0;
|
||||
|
||||
p->debug = nc->debug;
|
||||
p->mrtdump = nc->mrtdump;
|
||||
proto_reconfig_type = type;
|
||||
|
||||
/* Execute protocol specific reconfigure hook */
|
||||
if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
|
||||
|
@ -362,14 +402,37 @@ 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;
|
||||
|
||||
|
||||
/* Multitable protocols handle rest in their reconfigure hooks */
|
||||
if (p->proto->multitable)
|
||||
return 1;
|
||||
|
||||
/* Update filters in the main announce hook */
|
||||
if (p->main_ahook)
|
||||
{
|
||||
p->main_ahook->in_filter = nc->in_filter;
|
||||
p->main_ahook->out_filter = nc->out_filter;
|
||||
}
|
||||
|
||||
/* Update routes when filters changed. If the protocol in not UP,
|
||||
it has no routes and we can ignore such changes */
|
||||
if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT))
|
||||
return 1;
|
||||
|
||||
int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
|
||||
int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
|
||||
|
||||
/* We treat a change in preferences by reimporting routes */
|
||||
if (nc->preference != oc->preference)
|
||||
import_changed = 1;
|
||||
|
||||
if (import_changed || export_changed)
|
||||
log(L_INFO "Reloading protocol %s", p->name);
|
||||
|
||||
if (import_changed && ! p->reload_routes(p))
|
||||
/* If import filter changed, call reload hook */
|
||||
if (import_changed && ! (p->reload_routes && p->reload_routes(p)))
|
||||
{
|
||||
/* Now, the protocol is reconfigured. But route reload failed
|
||||
and we have to do regular protocol restart. */
|
||||
|
@ -553,6 +616,7 @@ void
|
|||
protos_dump_all(void)
|
||||
{
|
||||
struct proto *p;
|
||||
struct announce_hook *a;
|
||||
|
||||
debug("Protocols:\n");
|
||||
|
||||
|
@ -560,10 +624,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)
|
||||
|
@ -647,6 +715,9 @@ proto_fell_down(struct proto *p)
|
|||
log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes);
|
||||
|
||||
bzero(&p->stats, sizeof(struct proto_stats));
|
||||
proto_free_ahooks(p);
|
||||
|
||||
if (! p->proto->multitable)
|
||||
rt_unlock_table(p->table);
|
||||
|
||||
if (p->proto->cleanup)
|
||||
|
@ -686,7 +757,7 @@ proto_feed_initial(void *P)
|
|||
return;
|
||||
|
||||
DBG("Feeding protocol %s\n", p->name);
|
||||
proto_add_announce_hook(p, p->table);
|
||||
|
||||
if_feed_baby(p);
|
||||
proto_feed_more(P);
|
||||
}
|
||||
|
@ -701,7 +772,7 @@ proto_schedule_flush(struct proto *p)
|
|||
DBG("%s: Scheduling flush\n", p->name);
|
||||
p->core_state = FS_FLUSHING;
|
||||
proto_relink(p);
|
||||
proto_flush_hooks(p);
|
||||
proto_unlink_ahooks(p);
|
||||
ev_schedule(proto_flush_event);
|
||||
}
|
||||
|
||||
|
@ -716,6 +787,11 @@ proto_schedule_feed(struct proto *p, int initial)
|
|||
if (!initial)
|
||||
p->stats.exp_routes = 0;
|
||||
|
||||
/* Connect protocol to routing table */
|
||||
if (initial && !p->proto->multitable)
|
||||
p->main_ahook = proto_add_announce_hook(p, p->table,
|
||||
p->cf->in_filter, p->cf->out_filter, &p->stats);
|
||||
|
||||
proto_relink(p);
|
||||
p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
|
||||
ev_schedule(p->attn);
|
||||
|
@ -855,9 +931,8 @@ proto_state_name(struct proto *p)
|
|||
}
|
||||
|
||||
static void
|
||||
proto_do_show_stats(struct proto *p)
|
||||
proto_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");
|
||||
|
@ -875,47 +950,17 @@ 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)
|
||||
void
|
||||
proto_show_basic_info(struct proto *p)
|
||||
{
|
||||
struct proto_stats *s1 = &p->stats;
|
||||
struct proto_stats *s2 = pipe_get_peer_stats(p);
|
||||
// cli_msg(-1006, " Table: %s", p->table->name);
|
||||
cli_msg(-1006, " Preference: %d", p->preference);
|
||||
cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter));
|
||||
cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter));
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (p->proto_state != PS_DOWN)
|
||||
proto_show_stats(&p->stats);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
|
||||
|
@ -943,22 +988,11 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
|
|||
cli_msg(-1006, " Description: %s", p->cf->dsc);
|
||||
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)
|
||||
{
|
||||
#ifdef CONFIG_PIPE
|
||||
if (proto_is_pipe(p))
|
||||
proto_do_show_pipe_stats(p);
|
||||
else
|
||||
#endif
|
||||
proto_do_show_stats(p);
|
||||
}
|
||||
|
||||
if (p->proto->show_proto_info)
|
||||
p->proto->show_proto_info(p);
|
||||
else
|
||||
proto_show_basic_info(p);
|
||||
|
||||
cli_msg(-1006, "");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ struct protocol {
|
|||
char *template; /* Template for automatic generation of names */
|
||||
int name_counter; /* Counter for automatic name generation */
|
||||
int attr_class; /* Attribute class known to this protocol */
|
||||
int multitable; /* Protocol handles all announce hooks itself */
|
||||
unsigned preference; /* Default protocol preference */
|
||||
|
||||
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
|
||||
|
@ -193,8 +194,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 *main_ahook; /* Primary announcement hook */
|
||||
struct announce_hook *ahooks; /* Announcement hooks for this protocol */
|
||||
|
||||
struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */
|
||||
|
@ -218,6 +218,9 @@ static inline void
|
|||
proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
|
||||
{ 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_disable(struct proto *, unsigned int, int);
|
||||
void proto_cmd_enable(struct proto *, unsigned int, int);
|
||||
|
@ -352,18 +355,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_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);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/timer.h"
|
||||
#include "nest/protocol.h"
|
||||
|
||||
struct protocol;
|
||||
struct proto;
|
||||
|
@ -179,7 +180,7 @@ struct hostentry {
|
|||
typedef struct rte {
|
||||
struct rte *next;
|
||||
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 */
|
||||
byte flags; /* Flags (REF_...) */
|
||||
byte pflags; /* Protocol-specific flags */
|
||||
|
@ -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); }
|
||||
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);
|
||||
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_dump(rte *);
|
||||
void rte_free(rte *);
|
||||
|
|
|
@ -184,24 +184,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
|
|||
}
|
||||
|
||||
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 filter *filter = p->out_filter;
|
||||
struct proto_stats *stats = &p->stats;
|
||||
struct proto *p = ah->proto;
|
||||
struct filter *filter = ah->out_filter;
|
||||
struct proto_stats *stats = ah->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++;
|
||||
|
@ -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");
|
||||
}
|
||||
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)
|
||||
{
|
||||
ea_list *t = tmpa;
|
||||
while (t->next)
|
||||
t = t->next;
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
rte_free(new);
|
||||
if (old && old != old0)
|
||||
|
@ -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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -383,7 +375,7 @@ rte_validate(rte *e)
|
|||
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -423,18 +415,15 @@ 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(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 = NULL;
|
||||
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 */
|
||||
while (old = *k)
|
||||
{
|
||||
|
@ -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',
|
||||
* see FIXME in do_rte_announce())
|
||||
*/
|
||||
if (old->sender != p)
|
||||
if (old->sender->proto != p)
|
||||
{
|
||||
if (new)
|
||||
{
|
||||
|
@ -613,6 +602,7 @@ rte_update_unlock(void)
|
|||
/**
|
||||
* rte_update - enter a new update to a routing table
|
||||
* @table: table to be updated
|
||||
* @ah: pointer to table announce hook
|
||||
* @net: network node
|
||||
* @p: protocol submitting the update
|
||||
* @src: protocol originating the update
|
||||
|
@ -652,28 +642,17 @@ rte_update_unlock(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;
|
||||
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();
|
||||
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
|
||||
new->sender = ah;
|
||||
|
||||
stats->imp_updates_received++;
|
||||
if (!rte_validate(new))
|
||||
|
@ -710,13 +689,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(ah, net, new, tmpa, src);
|
||||
rte_update_unlock();
|
||||
return;
|
||||
|
||||
drop:
|
||||
rte_free(new);
|
||||
rte_recalculate(table, net, p, src, NULL, NULL);
|
||||
rte_recalculate(ah, net, NULL, NULL, src);
|
||||
rte_update_unlock();
|
||||
}
|
||||
|
||||
|
@ -739,7 +718,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(old->sender, old->net, NULL, NULL, old->attrs->proto);
|
||||
rte_update_unlock();
|
||||
}
|
||||
|
||||
|
@ -912,8 +891,8 @@ again:
|
|||
ncnt++;
|
||||
rescan:
|
||||
for (e=n->routes; e; e=e->next, rcnt++)
|
||||
if (e->sender->core_state != FS_HAPPY &&
|
||||
e->sender->core_state != FS_FEEDING)
|
||||
if (e->sender->proto->core_state != FS_HAPPY &&
|
||||
e->sender->proto->core_state != FS_FEEDING)
|
||||
{
|
||||
rte_discard(tab, e);
|
||||
rdel++;
|
||||
|
@ -1026,7 +1005,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
|
|||
*k = new;
|
||||
|
||||
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 */
|
||||
/* Not really an efficient way to compute this */
|
||||
|
@ -1066,7 +1045,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
|
|||
if (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)
|
||||
|
@ -1693,6 +1672,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);
|
||||
|
@ -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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1125,6 +1125,8 @@ bgp_show_proto_info(struct proto *P)
|
|||
struct bgp_proto *p = (struct bgp_proto *) P;
|
||||
struct bgp_conn *c = p->conn;
|
||||
|
||||
proto_show_basic_info(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 AS: %u", p->remote_as);
|
||||
|
|
|
@ -16,6 +16,17 @@
|
|||
*
|
||||
* To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
|
||||
* 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
|
||||
|
@ -24,6 +35,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,7 +46,8 @@ 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 */
|
||||
struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
|
||||
rtable *dst_table = ah->table;
|
||||
struct proto *src;
|
||||
|
||||
net *nn;
|
||||
|
@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
|
|||
if (!new && !old)
|
||||
return;
|
||||
|
||||
if (dest->pipe_busy)
|
||||
if (dst_table->pipe_busy)
|
||||
{
|
||||
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;
|
||||
}
|
||||
nn = net_get(dest, n->n.prefix, n->n.pxlen);
|
||||
|
||||
nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
|
||||
if (new)
|
||||
{
|
||||
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
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
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)
|
||||
return -1; /* Avoid local loops automatically */
|
||||
|
@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P)
|
|||
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
|
||||
pipe_start(struct proto *P)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct announce_hook *a;
|
||||
|
||||
/* Clean up the secondary stats */
|
||||
bzero(&p->peer_stats, sizeof(struct proto_stats));
|
||||
/* Lock both tables, unlock is handled in pipe_cleanup() */
|
||||
rt_lock_table(P->table);
|
||||
rt_lock_table(p->peer_table);
|
||||
|
||||
/* Lock the peer table, unlock is handled in pipe_cleanup() */
|
||||
rt_lock_table(p->peer);
|
||||
/* Going directly to PS_UP - prepare for feeding,
|
||||
connect the protocol to both routing tables */
|
||||
|
||||
/* Connect the protocol also to the peer routing table. */
|
||||
a = proto_add_announce_hook(P, p->peer);
|
||||
P->main_ahook = proto_add_announce_hook(P, P->table,
|
||||
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;
|
||||
}
|
||||
|
@ -134,24 +167,15 @@ static void
|
|||
pipe_cleanup(struct proto *P)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
rt_unlock_table(p->peer);
|
||||
}
|
||||
|
||||
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;
|
||||
bzero(&P->stats, sizeof(struct proto_stats));
|
||||
bzero(&p->peer_stats, sizeof(struct proto_stats));
|
||||
|
||||
p->peer = c->peer->table;
|
||||
p->mode = c->mode;
|
||||
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;
|
||||
P->main_ahook = NULL;
|
||||
p->peer_ahook = NULL;
|
||||
|
||||
return P;
|
||||
rt_unlock_table(P->table);
|
||||
rt_unlock_table(p->peer_table);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C)
|
|||
cf_error("Primary table and peer table must be different");
|
||||
}
|
||||
|
||||
extern int proto_reconfig_type;
|
||||
|
||||
static int
|
||||
pipe_reconfigure(struct proto *P, struct proto_config *new)
|
||||
{
|
||||
// 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;
|
||||
struct pipe_proto *p = (struct pipe_proto *)P;
|
||||
struct proto_config *old = P->cf;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -190,13 +232,73 @@ pipe_get_status(struct proto *P, byte *buf)
|
|||
{
|
||||
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
|
||||
* (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, ...).
|
||||
*
|
||||
* Rule of thumb is that stats s1 have the correct 'polarity'
|
||||
* (imp/exp), while stats s2 have switched 'polarity'.
|
||||
*/
|
||||
|
||||
cli_msg(-1006, " Routes: %u imported, %u exported",
|
||||
s1->imp_routes, s2->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 + s1->imp_updates_invalid,
|
||||
s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
|
||||
cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
|
||||
s2->exp_withdraws_received, s1->imp_withdraws_invalid,
|
||||
s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
|
||||
cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
|
||||
s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
|
||||
s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
|
||||
cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
|
||||
s1->exp_withdraws_received, s2->imp_withdraws_invalid,
|
||||
s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_show_proto_info(struct proto *P)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
|
||||
// cli_msg(-1006, " Table: %s", P->table->name);
|
||||
// cli_msg(-1006, " Peer table: %s", p->peer_table->name);
|
||||
cli_msg(-1006, " Preference: %d", P->preference);
|
||||
cli_msg(-1006, " Input filter: %s", filter_name(P->cf->in_filter));
|
||||
cli_msg(-1006, " Output filter: %s", filter_name(P->cf->out_filter));
|
||||
|
||||
if (P->proto_state != PS_DOWN)
|
||||
pipe_show_stats(p);
|
||||
}
|
||||
|
||||
|
||||
struct protocol proto_pipe = {
|
||||
name: "Pipe",
|
||||
template: "pipe%d",
|
||||
multitable: 1,
|
||||
preference: DEF_PREF_PIPE,
|
||||
postconfig: pipe_postconfig,
|
||||
init: pipe_init,
|
||||
|
@ -205,4 +307,5 @@ struct protocol proto_pipe = {
|
|||
reconfigure: pipe_reconfigure,
|
||||
copy_config: pipe_copy_config,
|
||||
get_status: pipe_get_status,
|
||||
show_proto_info: pipe_show_proto_info
|
||||
};
|
||||
|
|
|
@ -20,7 +20,8 @@ struct pipe_config {
|
|||
|
||||
struct pipe_proto {
|
||||
struct proto p;
|
||||
struct rtable *peer;
|
||||
struct rtable *peer_table;
|
||||
struct announce_hook *peer_ahook; /* Announce hook for direction peer->primary */
|
||||
struct proto_stats peer_stats; /* Statistics for the direction peer->primary */
|
||||
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
|
||||
};
|
||||
|
@ -31,10 +32,4 @@ 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; }
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue