summaryrefslogtreecommitdiff
path: root/nest/proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/proto.c')
-rw-r--r--nest/proto.c1368
1 files changed, 963 insertions, 405 deletions
diff --git a/nest/proto.c b/nest/proto.c
index 31ee1fa1..2546e812 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -15,6 +15,7 @@
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h"
+#include "lib/coro.h"
#include "conf/conf.h"
#include "nest/route.h"
#include "nest/iface.h"
@@ -31,7 +32,6 @@ struct protocol *class_to_protocol[PROTOCOL__MAX];
#define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
#define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); })
-static timer *proto_shutdown_timer;
static timer *gr_wait_timer;
#define GRS_NONE 0
@@ -43,24 +43,34 @@ static int graceful_restart_state;
static u32 graceful_restart_locks;
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
-static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
-static char *e_states[] = { "DOWN", "FEEDING", "READY" };
+static char *c_states[] = { "DOWN", "START", "UP", "STOP", "RESTART" };
extern struct protocol proto_unix_iface;
-static void channel_request_reload(struct channel *c);
-static void proto_shutdown_loop(timer *);
+static void channel_aux_request_refeed(struct channel_aux_table *cat);
static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p);
-static void channel_verify_limits(struct channel *c);
-static inline void channel_reset_limit(struct channel_limit *l);
-
+static void channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
+static void channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
+static void channel_reset_limit(struct channel *c, struct limit *l, int dir);
+static void channel_feed_end(struct channel *c);
+static void channel_export_stopped(struct rt_export_request *req);
static inline int proto_is_done(struct proto *p)
-{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
+{ return (p->proto_state == PS_DOWN) && proto_is_inactive(p); }
+
+static inline event_list *proto_event_list(struct proto *p)
+{ return p->loop == &main_birdloop ? &global_event_list : birdloop_event_list(p->loop); }
+
+static inline event_list *proto_work_list(struct proto *p)
+{ return p->loop == &main_birdloop ? &global_work_list : birdloop_event_list(p->loop); }
+
+static inline void proto_send_event(struct proto *p)
+{ ev_send(proto_event_list(p), p->event); }
+
static inline int channel_is_active(struct channel *c)
-{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
+{ return (c->channel_state != CS_DOWN); }
static inline int channel_reloadable(struct channel *c)
{ return c->proto->reload_routes && c->reloadable; }
@@ -68,10 +78,48 @@ static inline int channel_reloadable(struct channel *c)
static inline void
channel_log_state_change(struct channel *c)
{
- if (c->export_state)
- CD(c, "State changed to %s/%s", c_states[c->channel_state], e_states[c->export_state]);
- else
- CD(c, "State changed to %s", c_states[c->channel_state]);
+ CD(c, "State changed to %s", c_states[c->channel_state]);
+}
+
+void
+channel_import_log_state_change(struct rt_import_request *req, u8 state)
+{
+ struct channel *c = SKIP_BACK(struct channel, in_req, req);
+ CD(c, "Channel import state changed to %s", rt_import_state_name(state));
+}
+
+void
+channel_export_log_state_change(struct rt_export_request *req, u8 state)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ CD(c, "Channel export state changed to %s", rt_export_state_name(state));
+
+ switch (state)
+ {
+ case TES_FEEDING:
+ if (c->out_table)
+ rt_refresh_begin(&c->out_table->push);
+ else if (c->proto->feed_begin)
+ c->proto->feed_begin(c, !c->refeeding);
+ break;
+ case TES_READY:
+ channel_feed_end(c);
+ break;
+ }
+}
+
+static void
+channel_dump_import_req(struct rt_import_request *req)
+{
+ struct channel *c = SKIP_BACK(struct channel, in_req, req);
+ debug(" Channel %s.%s import request %p\n", c->proto->name, c->name, req);
+}
+
+static void
+channel_dump_export_req(struct rt_export_request *req)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ debug(" Channel %s.%s export request %p\n", c->proto->name, c->name, req);
}
static void
@@ -111,7 +159,7 @@ proto_cf_find_channel(struct proto_config *pc, uint net_type)
* Returns pointer to channel or NULL
*/
struct channel *
-proto_find_channel_by_table(struct proto *p, struct rtable *t)
+proto_find_channel_by_table(struct proto *p, rtable *t)
{
struct channel *c;
@@ -141,6 +189,16 @@ proto_find_channel_by_name(struct proto *p, const char *n)
return NULL;
}
+rte * channel_preimport(struct rt_import_request *req, rte *new, rte *old);
+rte * channel_in_preimport(struct rt_import_request *req, rte *new, rte *old);
+
+void rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
+void rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
+void rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+void rt_notify_accepted(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+void rt_notify_merged(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+
+
/**
* proto_add_channel - connect protocol to a routing table
* @p: protocol instance
@@ -166,11 +224,17 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->proto = p;
c->table = cf->table->table;
+ RT_LOCKED(c->table, t)
+ rt_lock_table(t);
+
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
- c->rx_limit = cf->rx_limit;
- c->in_limit = cf->in_limit;
- c->out_limit = cf->out_limit;
+
+ channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
+ channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
+ channel_init_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
+
+ c->rte_update_pool = lp_new_default(proto_pool);
c->net_type = cf->net_type;
c->ra_mode = cf->ra_mode;
@@ -181,7 +245,6 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->rpki_reload = cf->rpki_reload;
c->channel_state = CS_DOWN;
- c->export_state = ES_DOWN;
c->last_state_change = current_time();
c->reloadable = 1;
@@ -203,6 +266,9 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c)
CD(c, "Removed", c->name);
+ RT_LOCKED(c->table, t)
+ rt_unlock_table(t);
+
rem_node(&c->n);
mb_free(c);
}
@@ -223,7 +289,7 @@ proto_pause_channels(struct proto *p)
struct channel *c;
WALK_LIST(c, p->channels)
if (!c->disabled && channel_is_active(c))
- channel_set_state(c, CS_START);
+ channel_set_state(c, CS_PAUSE);
}
static void
@@ -232,7 +298,7 @@ proto_stop_channels(struct proto *p)
struct channel *c;
WALK_LIST(c, p->channels)
if (!c->disabled && channel_is_active(c))
- channel_set_state(c, CS_FLUSHING);
+ channel_set_state(c, CS_STOP);
}
static void
@@ -244,96 +310,25 @@ proto_remove_channels(struct proto *p)
}
static void
-channel_schedule_feed(struct channel *c, int initial)
-{
- // DBG("%s: Scheduling meal\n", p->name);
- ASSERT(c->channel_state == CS_UP);
-
- c->export_state = ES_FEEDING;
- c->refeeding = !initial;
-
- ev_schedule_work(c->feed_event);
-}
-
-static void
-channel_feed_loop(void *ptr)
-{
- struct channel *c = ptr;
-
- if (c->export_state != ES_FEEDING)
- return;
-
- /* Start feeding */
- if (!c->feed_active)
- {
- if (c->proto->feed_begin)
- c->proto->feed_begin(c, !c->refeeding);
-
- c->refeed_pending = 0;
- }
-
- // DBG("Feeding protocol %s continued\n", p->name);
- if (!rt_feed_channel(c))
- {
- ev_schedule_work(c->feed_event);
- return;
- }
-
- /* Reset export limit if the feed ended with acceptable number of exported routes */
- struct channel_limit *l = &c->out_limit;
- if (c->refeeding &&
- (l->state == PLS_BLOCKED) &&
- (c->refeed_count <= l->limit) &&
- (c->stats.exp_routes <= l->limit))
- {
- log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
- channel_reset_limit(&c->out_limit);
-
- /* Continue in feed - it will process routing table again from beginning */
- c->refeed_count = 0;
- ev_schedule_work(c->feed_event);
- return;
- }
-
- // DBG("Feeding protocol %s finished\n", p->name);
- c->export_state = ES_READY;
- channel_log_state_change(c);
-
- if (c->proto->feed_end)
- c->proto->feed_end(c);
-
- /* Restart feeding */
- if (c->refeed_pending)
- channel_request_feeding(c);
-}
-
-
-static void
-channel_roa_in_changed(struct rt_subscription *s)
+channel_roa_in_changed(void *_data)
{
- struct channel *c = s->data;
- int active = c->reload_event && ev_active(c->reload_event);
+ struct channel *c = _data;
- CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : "");
+ CD(c, "Reload triggered by RPKI change");
- if (!active)
- channel_request_reload(c);
- else
- c->reload_pending = 1;
+ channel_request_reload(c);
}
static void
-channel_roa_out_changed(struct rt_subscription *s)
+channel_roa_out_changed(void *_data)
{
- struct channel *c = s->data;
- int active = (c->export_state == ES_FEEDING);
+ struct channel *c = _data;
+ CD(c, "Feeding triggered by RPKI change");
- CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : "");
+ c->refeed_pending = 1;
- if (!active)
- channel_request_feeding(c);
- else
- c->refeed_pending = 1;
+ if (c->out_req.hook)
+ rt_stop_export(&c->out_req, channel_export_stopped);
}
/* Temporary code, subscriptions should be changed to resources */
@@ -345,14 +340,14 @@ struct roa_subscription {
static int
channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
{
- void (*hook)(struct rt_subscription *) =
+ void (*hook)(void *) =
dir ? channel_roa_in_changed : channel_roa_out_changed;
struct roa_subscription *s;
node *n;
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
- if ((s->s.tab == tab) && (s->s.hook == hook))
+ if ((s->s.tab == tab) && (s->s.event->hook == hook))
return 1;
return 0;
@@ -366,9 +361,9 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
return;
struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription));
+ s->s.event = ev_new_init(c->proto->pool, dir ? channel_roa_in_changed : channel_roa_out_changed, c);
+ s->s.event->list = proto_work_list(c->proto);
- s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed;
- s->s.data = c;
rt_subscribe(tab, &s->s);
add_tail(&c->roa_subscriptions, &s->roa_node);
@@ -379,6 +374,7 @@ channel_roa_unsubscribe(struct roa_subscription *s)
{
rt_unsubscribe(&s->s);
rem_node(&s->roa_node);
+ rfree(s->s.event);
mb_free(s);
}
@@ -386,7 +382,7 @@ static void
channel_roa_subscribe_filter(struct channel *c, int dir)
{
const struct filter *f = dir ? c->in_filter : c->out_filter;
- struct rtable *tab;
+ rtable *tab;
int valid = 1, found = 0;
if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT))
@@ -445,119 +441,554 @@ channel_roa_unsubscribe_all(struct channel *c)
}
static void
-channel_start_export(struct channel *c)
+channel_start_import(struct channel *c)
{
+ if (c->in_req.hook)
+ {
+ log(L_WARN "%s.%s: Attempted to start channel's already started import", c->proto->name, c->name);
+ return;
+ }
+
+ int nlen = strlen(c->name) + strlen(c->proto->name) + 2;
+ char *rn = mb_allocz(c->proto->pool, nlen);
+ bsprintf(rn, "%s.%s", c->proto->name, c->name);
+
+ c->in_req = (struct rt_import_request) {
+ .name = rn,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_dump_import_req,
+ .log_state_change = channel_import_log_state_change,
+ .preimport = channel_preimport,
+ };
+
ASSERT(c->channel_state == CS_UP);
- ASSERT(c->export_state == ES_DOWN);
- channel_schedule_feed(c, 1); /* Sets ES_FEEDING */
+ channel_reset_limit(c, &c->rx_limit, PLD_RX);
+ channel_reset_limit(c, &c->in_limit, PLD_IN);
+
+ memset(&c->import_stats, 0, sizeof(struct channel_import_stats));
+
+ DBG("%s.%s: Channel start import req=%p\n", c->proto->name, c->name, &c->in_req);
+ rt_request_import(c->table, &c->in_req);
}
static void
-channel_stop_export(struct channel *c)
+channel_start_export(struct channel *c)
{
- /* Need to abort feeding */
- if (c->export_state == ES_FEEDING)
- rt_feed_channel_abort(c);
+ if (c->out_req.hook)
+ {
+ c->restart_export = 1;
+ log(L_WARN "%s.%s: Fast channel export restart", c->proto->name, c->name);
+ return;
+ }
+
+ ASSERT(c->channel_state == CS_UP);
+ int nlen = strlen(c->name) + strlen(c->proto->name) + 2;
+ char *rn = mb_allocz(c->proto->pool, nlen);
+ bsprintf(rn, "%s.%s", c->proto->name, c->name);
+
+ c->out_req = (struct rt_export_request) {
+ .name = rn,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_dump_export_req,
+ .log_state_change = channel_export_log_state_change,
+ };
+
+ bmap_init(&c->export_map, c->proto->pool, 1024);
+ bmap_init(&c->export_reject_map, c->proto->pool, 1024);
+
+ channel_reset_limit(c, &c->out_limit, PLD_OUT);
- c->export_state = ES_DOWN;
- c->stats.exp_routes = 0;
- bmap_reset(&c->export_map, 1024);
+ memset(&c->export_stats, 0, sizeof(struct channel_export_stats));
+
+ switch (c->ra_mode) {
+ case RA_OPTIMAL:
+ c->out_req.export_one = rt_notify_optimal;
+ break;
+ case RA_ANY:
+ c->out_req.export_one = rt_notify_any;
+ c->out_req.export_bulk = rt_feed_any;
+ break;
+ case RA_ACCEPTED:
+ c->out_req.export_bulk = rt_notify_accepted;
+ break;
+ case RA_MERGED:
+ c->out_req.export_bulk = rt_notify_merged;
+ break;
+ default:
+ bug("Unknown route announcement mode");
+ }
+
+ DBG("%s.%s: Channel start export req=%p\n", c->proto->name, c->name, &c->out_req);
+ rt_request_export(c->table, &c->out_req);
}
+static void
+channel_check_stopped(struct channel *c)
+{
+ switch (c->channel_state)
+ {
+ case CS_STOP:
+ if (c->out_req.hook || c->in_req.hook || c->out_table || c->in_table)
+ return;
+
+ channel_set_state(c, CS_DOWN);
+ proto_send_event(c->proto);
+
+ break;
+ case CS_PAUSE:
+ if (c->out_req.hook)
+ return;
+
+ channel_set_state(c, CS_START);
+ break;
+ default:
+ bug("Stopped channel in a bad state: %d", c->channel_state);
+ }
+
+ DBG("%s.%s: Channel requests/hooks stopped (in state %s)\n", c->proto->name, c->name, c_states[c->channel_state]);
+}
-/* Called by protocol for reload from in_table */
void
-channel_schedule_reload(struct channel *c)
+channel_import_stopped(void *_c)
{
- ASSERT(c->channel_state == CS_UP);
+ struct channel *c = _c;
+
+ c->in_req.hook = NULL;
- rt_reload_channel_abort(c);
- ev_schedule_work(c->reload_event);
+ mb_free(c->in_req.name);
+ c->in_req.name = NULL;
+
+ channel_check_stopped(c);
}
static void
-channel_reload_loop(void *ptr)
+channel_export_stopped(struct rt_export_request *req)
{
- struct channel *c = ptr;
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
- /* Start reload */
- if (!c->reload_active)
- c->reload_pending = 0;
+ /* The hook has already stopped */
+ req->hook = NULL;
- if (!rt_reload_channel(c))
+ if (c->refeed_pending)
{
- ev_schedule_work(c->reload_event);
+ c->refeeding = 1;
+ c->refeed_pending = 0;
+
+ bmap_reset(&c->export_map, 1024);
+ bmap_reset(&c->export_reject_map, 1024);
+
+ rt_request_export(c->table, req);
return;
}
- /* Restart reload */
- if (c->reload_pending)
- channel_request_reload(c);
+ mb_free(c->out_req.name);
+ c->out_req.name = NULL;
+
+ bmap_free(&c->export_map);
+ bmap_free(&c->export_reject_map);
+
+ if (c->restart_export)
+ {
+ c->restart_export = 0;
+ channel_start_export(c);
+ }
+ else
+ channel_check_stopped(c);
+}
+
+static void
+channel_feed_end(struct channel *c)
+{
+ struct rt_export_request *req = &c->out_req;
+
+ /* Reset export limit if the feed ended with acceptable number of exported routes */
+ struct limit *l = &c->out_limit;
+ if (c->refeeding &&
+ (c->limit_active & (1 << PLD_OUT)) &&
+ (c->refeed_count <= l->max) &&
+ (l->count <= l->max))
+ {
+ log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->max);
+ channel_reset_limit(c, &c->out_limit, PLD_OUT);
+
+ c->refeed_pending = 1;
+ rt_stop_export(req, channel_export_stopped);
+ return;
+ }
+
+ if (c->out_table)
+ rt_refresh_end(&c->out_table->push);
+ else if (c->proto->feed_end)
+ c->proto->feed_end(c);
+
+ if (c->refeed_pending)
+ rt_stop_export(req, channel_export_stopped);
+}
+
+#define CHANNEL_AUX_TABLE_DUMP_REQ(inout, imex, pgimex, pushget) static void \
+ channel_##inout##_##pushget##_dump_req(struct rt_##pgimex##_request *req) { \
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, pushget, req); \
+ debug(" Channel %s.%s " #imex " table " #pushget " request %p\n", cat->c->proto->name, cat->c->name, req); }
+
+CHANNEL_AUX_TABLE_DUMP_REQ(in, import, import, push)
+CHANNEL_AUX_TABLE_DUMP_REQ(in, import, export, get)
+CHANNEL_AUX_TABLE_DUMP_REQ(out, export, import, push)
+CHANNEL_AUX_TABLE_DUMP_REQ(out, export, export, get)
+
+#undef CHANNEL_AUX_TABLE_DUMP_REQ
+
+static uint channel_aux_imex(struct channel_aux_table *cat)
+{
+ if (cat->c->in_table == cat)
+ return 0;
+ else if (cat->c->out_table == cat)
+ return 1;
+ else
+ bug("Channel aux table must be in_table or out_table");
}
static void
-channel_reset_import(struct channel *c)
+channel_aux_stopped(void *data)
{
- /* Need to abort feeding */
- ev_postpone(c->reload_event);
- rt_reload_channel_abort(c);
+ struct channel_aux_table *cat;
+
+ RT_LOCKED((rtable *) data, t)
+ cat = t->config->owner;
- rt_prune_sync(c->in_table, 1);
+ ASSERT_DIE(cat->push.hook == NULL);
+ ASSERT_DIE(cat->get.hook == NULL);
+ ASSERT_DIE(cat->stop_pending);
+
+ struct channel *c = cat->c;
+
+ if (channel_aux_imex(cat))
+ c->out_table = NULL;
+ else
+ c->in_table = NULL;
+
+ mb_free(cat);
+ channel_check_stopped(c);
}
static void
-channel_reset_export(struct channel *c)
+channel_aux_import_stopped(void *_cat)
{
- /* Just free the routes */
- rt_prune_sync(c->out_table, 1);
+ struct channel_aux_table *cat = _cat;
+
+ cat->push.hook = NULL;
+
+ if (!cat->get.hook)
+ RT_LOCKED(cat->tab, t)
+ {
+ t->delete = channel_aux_stopped;
+ rt_unlock_table(t);
+ }
}
-/* Called by protocol to activate in_table */
-void
-channel_setup_in_table(struct channel *c)
+static void
+channel_aux_export_stopped(struct rt_export_request *req)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ req->hook = NULL;
+
+ if (cat->refeed_pending && !cat->stop_pending)
+ {
+ cat->refeed_pending = 0;
+ rt_request_export(cat->tab, req);
+
+ return;
+ }
+
+ if (!cat->push.hook)
+ RT_LOCKED(cat->tab, t)
+ {
+ t->delete = channel_aux_stopped;
+ rt_unlock_table(t);
+ }
+}
+
+static void
+channel_aux_stop(struct channel_aux_table *cat)
+{
+ ASSERT_DIE(!cat->stop_pending);
+
+ cat->stop_pending = 1;
+
+ RT_LOCKED(cat->tab, t)
+ rt_lock_table(t);
+
+ cat->push_stopped = (event) {
+ .hook = channel_aux_import_stopped,
+ .data = cat,
+ .list = proto_event_list(cat->c->proto),
+ };
+
+ rt_stop_import(&cat->push, &cat->push_stopped);
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+}
+
+static void
+channel_push_log_state_change(struct rt_import_request *req, u8 state)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req);
+ const char *imex = channel_aux_imex(cat) ? "export" : "import";
+ CD(cat->c, "Channel %s table import state changed to %s", imex, rt_import_state_name(state));
+}
+
+static void
+channel_get_log_state_change(struct rt_export_request *req, u8 state)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ const char *imex = channel_aux_imex(cat) ? "export" : "import";
+ CD(cat->c, "Channel %s table export state changed to %s", imex, rt_export_state_name(state));
+
+ switch (state)
+ {
+ case TES_FEEDING:
+ if (imex && cat->c->proto->feed_begin)
+ cat->c->proto->feed_begin(cat->c, !cat->c->refeeding);
+ else if (!imex)
+ rt_refresh_begin(&cat->c->in_req);
+ break;
+
+ case TES_READY:
+ if (imex && cat->c->proto->feed_end)
+ cat->c->proto->feed_end(cat->c);
+ else if (!imex)
+ rt_refresh_end(&cat->c->in_req);
+
+ if (cat->refeed_pending)
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+
+ break;
+ }
+}
+
+void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
+
+static int
+channel_aux_export_one_any(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old)
+{
+ struct rte_src *src = rpe->new ? rpe->new->rte.src : rpe->old->rte.src;
+ *old = RTES_OR_NULL(rpe->old);
+ struct rte_storage *new_stored;
+
+ while (rpe)
+ {
+ new_stored = rpe->new;
+ rpe_mark_seen(req->hook, rpe);
+ rpe = rpe_next(rpe, src);
+ }
+
+ *new = RTES_CLONE(new_stored, *new);
+
+ return (*new || *old) && (&new_stored->rte != *old);
+}
+
+static int
+channel_aux_export_one_best(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old)
+{
+ *old = RTES_OR_NULL(rpe->old_best);
+ struct rte_storage *new_stored;
+
+ while (rpe)
+ {
+ new_stored = rpe->new_best;
+ rpe_mark_seen(req->hook, rpe);
+ rpe = rpe_next(rpe, NULL);
+ }
+
+ *new = RTES_CLONE(new_stored, *new);
+
+ return (*new || *old) && (&new_stored->rte != *old);
+}
+
+static void
+channel_in_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_any(req, rpe, &new, &old))
+ rte_update_direct(cat->c, net, new, old ? old->src : new->src);
+}
+
+static void
+channel_in_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_best(req, rpe, &new, &old))
+ rte_update_direct(cat->c, net, new, old ? old->src : new->src);
+}
+
+static void
+channel_in_export_bulk_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
{
- struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ for (uint i=0; i<count; i++)
+ {
+ rte n0 = *feed[i];
+ rte_update_direct(cat->c, net, &n0, n0.src);
+ }
+}
- cf->name = "import";
- cf->addr_type = c->net_type;
- cf->internal = 1;
+static void
+channel_in_export_bulk_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ if (!count)
+ return;
+
+ rte n0 = *feed[0];
+ rte_update_direct(cat->c, net, &n0, n0.src);
+}
+
+void do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old);
+
+static void
+channel_out_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_any(req, rpe, &new, &old))
+ do_rt_notify_direct(cat->c, net, new, old);
+}
+
+static void
+channel_out_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_best(req, rpe, &new, &old))
+ do_rt_notify_direct(cat->c, net, new, old);
+}
+
+static void
+channel_out_export_bulk(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ if (cat->c->ra_mode != RA_ANY)
+ ASSERT_DIE(count <= 1);
+
+ for (uint i=0; i<count; i++)
+ {
+ rte n0 = *feed[i];
+ do_rt_notify_direct(cat->c, net, &n0, NULL);
+ }
+}
+
+/* Called by protocol to activate in_table */
+void
+channel_setup_in_table(struct channel *c, int best)
+{
+ int nlen = sizeof("import") + strlen(c->name) + strlen(c->proto->name) + 3;
+
+ struct {
+ struct channel_aux_table cat;
+ struct rtable_config tab_cf;
+ char name[0];
+ } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
+
+ bsprintf(cat->name, "%s.%s.import", c->proto->name, c->name);
+
+ cat->tab_cf.owner = cat;
+ cat->tab_cf.name = cat->name;
+ cat->tab_cf.addr_type = c->net_type;
+ cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export);
+
+ c->in_table = &cat->cat;
+ c->in_table->push = (struct rt_import_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_in_push_dump_req,
+ .log_state_change = channel_push_log_state_change,
+ .preimport = channel_in_preimport,
+ };
+ c->in_table->get = (struct rt_export_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_in_get_dump_req,
+ .log_state_change = channel_get_log_state_change,
+ .export_one = best ? channel_in_export_one_best : channel_in_export_one_any,
+ .export_bulk = best ? channel_in_export_bulk_best : channel_in_export_bulk_any,
+ };
- c->in_table = rt_setup(c->proto->pool, cf);
+ c->in_table->c = c;
+ c->in_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
- c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
+ rt_request_import(c->in_table->tab, &c->in_table->push);
+ rt_request_export(c->in_table->tab, &c->in_table->get);
}
/* Called by protocol to activate out_table */
void
channel_setup_out_table(struct channel *c)
{
- struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
- cf->name = "export";
- cf->addr_type = c->net_type;
- cf->internal = 1;
+ int nlen = sizeof("export") + strlen(c->name) + strlen(c->proto->name) + 3;
+
+ struct {
+ struct channel_aux_table cat;
+ struct rtable_config tab_cf;
+ char name[0];
+ } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
- c->out_table = rt_setup(c->proto->pool, cf);
+ bsprintf(cat->name, "%s.%s.export", c->proto->name, c->name);
+
+ cat->tab_cf.owner = cat;
+ cat->tab_cf.name = cat->name;
+ cat->tab_cf.addr_type = c->net_type;
+ cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export);
+
+ c->out_table = &cat->cat;
+ c->out_table->push = (struct rt_import_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_out_push_dump_req,
+ .log_state_change = channel_push_log_state_change,
+ };
+ c->out_table->get = (struct rt_export_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_out_get_dump_req,
+ .log_state_change = channel_get_log_state_change,
+ .export_one = (c->ra_mode == RA_ANY) ? channel_out_export_one_any : channel_out_export_one_best,
+ .export_bulk = channel_out_export_bulk,
+ };
+
+ c->out_table->c = c;
+ c->out_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
+
+ rt_request_import(c->out_table->tab, &c->out_table->push);
+ rt_request_export(c->out_table->tab, &c->out_table->get);
}
+static void
+channel_aux_request_refeed(struct channel_aux_table *cat)
+{
+ if (cat->stop_pending)
+ return;
+
+ cat->refeed_pending = 1;
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+}
static void
channel_do_start(struct channel *c)
{
- rt_lock_table(c->table);
- add_tail(&c->table->channels, &c->table_node);
c->proto->active_channels++;
- c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c);
-
- bmap_init(&c->export_map, c->proto->pool, 1024);
- memset(&c->stats, 0, sizeof(struct proto_stats));
-
- channel_reset_limit(&c->rx_limit);
- channel_reset_limit(&c->in_limit);
- channel_reset_limit(&c->out_limit);
-
CALL(c->channel->start, c);
+
+ channel_start_import(c);
}
static void
@@ -572,9 +1003,38 @@ channel_do_up(struct channel *c)
}
static void
-channel_do_flush(struct channel *c)
+channel_do_pause(struct channel *c)
+{
+ /* Stop export */
+ if (c->out_req.hook)
+ {
+ rt_stop_export(&c->out_req, channel_export_stopped);
+ c->refeeding = 0;
+ }
+
+ channel_roa_unsubscribe_all(c);
+}
+
+static void
+channel_do_stop(struct channel *c)
{
- rt_schedule_prune(c->table);
+ /* Drop auxiliary tables */
+ if (c->in_table)
+ channel_aux_stop(c->in_table);
+
+ if (c->out_table)
+ channel_aux_stop(c->out_table);
+
+ /* Stop import */
+ if (c->in_req.hook)
+ {
+ c->in_stopped = (event) {
+ .hook = channel_import_stopped,
+ .data = c,
+ .list = proto_event_list(c->proto),
+ };
+ rt_stop_import(&c->in_req, &c->in_stopped);
+ }
c->gr_wait = 0;
if (c->gr_lock)
@@ -582,48 +1042,30 @@ channel_do_flush(struct channel *c)
CALL(c->channel->shutdown, c);
- /* This have to be done in here, as channel pool is freed before channel_do_down() */
- bmap_free(&c->export_map);
- c->in_table = NULL;
- c->reload_event = NULL;
- c->out_table = NULL;
-
channel_roa_unsubscribe_all(c);
}
static void
channel_do_down(struct channel *c)
{
- ASSERT(!c->feed_active && !c->reload_active);
+ ASSERT(!c->out_req.hook && !c->in_req.hook && !c->out_table && !c->in_table);
- rem_node(&c->table_node);
- rt_unlock_table(c->table);
c->proto->active_channels--;
- if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
- log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
-
- // bmap_free(&c->export_map);
- memset(&c->stats, 0, sizeof(struct proto_stats));
-
- c->in_table = NULL;
- c->reload_event = NULL;
- c->out_table = NULL;
-
- /* The in_table and out_table are going to be freed by freeing their resource pools. */
+ memset(&c->import_stats, 0, sizeof(struct channel_import_stats));
+ memset(&c->export_stats, 0, sizeof(struct channel_export_stats));
CALL(c->channel->cleanup, c);
/* Schedule protocol shutddown */
if (proto_is_done(c->proto))
- ev_schedule(c->proto->event);
+ proto_send_event(c->proto);
}
void
channel_set_state(struct channel *c, uint state)
{
uint cs = c->channel_state;
- uint es = c->export_state;
DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, c_states[cs], c_states[state]);
if (state == cs)
@@ -635,24 +1077,15 @@ channel_set_state(struct channel *c, uint state)
switch (state)
{
case CS_START:
- ASSERT(cs == CS_DOWN || cs == CS_UP);
+ ASSERT(cs == CS_DOWN || cs == CS_PAUSE);
if (cs == CS_DOWN)
channel_do_start(c);
- if (es != ES_DOWN)
- channel_stop_export(c);
-
- if (c->in_table && (cs == CS_UP))
- channel_reset_import(c);
-
- if (c->out_table && (cs == CS_UP))
- channel_reset_export(c);
-
break;
case CS_UP:
- ASSERT(cs == CS_DOWN || cs == CS_START);
+ ASSERT(cs == CS_DOWN || cs == CS_START || cs == CS_PAUSE);
if (cs == CS_DOWN)
channel_do_start(c);
@@ -663,23 +1096,24 @@ channel_set_state(struct channel *c, uint state)
channel_do_up(c);
break;
- case CS_FLUSHING:
- ASSERT(cs == CS_START || cs == CS_UP);
+ case CS_PAUSE:
+ ASSERT(cs == CS_UP);
- if (es != ES_DOWN)
- channel_stop_export(c);
+ if (cs == CS_UP)
+ channel_do_pause(c);
+ break;
- if (c->in_table && (cs == CS_UP))
- channel_reset_import(c);
+ case CS_STOP:
+ ASSERT(cs == CS_UP || cs == CS_START || cs == CS_PAUSE);
- if (c->out_table && (cs == CS_UP))
- channel_reset_export(c);
+ if (cs == CS_UP)
+ channel_do_pause(c);
- channel_do_flush(c);
+ channel_do_stop(c);
break;
case CS_DOWN:
- ASSERT(cs == CS_FLUSHING);
+ ASSERT(cs == CS_STOP);
channel_do_down(c);
break;
@@ -701,50 +1135,62 @@ channel_set_state(struct channel *c, uint state)
* completed, it will switch back to ES_READY. This function can be called
* even when feeding is already running, in that case it is restarted.
*/
-void
-channel_request_feeding(struct channel *c)
+static void
+channel_request_table_feeding(struct channel *c)
{
- ASSERT(c->channel_state == CS_UP);
+ ASSERT(c->out_req.hook);
- CD(c, "Feeding requested");
+ c->refeed_pending = 1;
+ rt_stop_export(&c->out_req, channel_export_stopped);
+}
- /* Do nothing if we are still waiting for feeding */
- if (c->export_state == ES_DOWN)
+void
+channel_request_feeding(struct channel *c)
+{
+ if (c->gr_wait || !c->proto->rt_notify)
return;
- /* If we are already feeding, we want to restart it */
- if (c->export_state == ES_FEEDING)
- {
- /* Unless feeding is in initial state */
- if (!c->feed_active)
- return;
-
- rt_feed_channel_abort(c);
- }
+ CD(c, "Refeed requested");
- /* Track number of exported routes during refeed */
- c->refeed_count = 0;
+ ASSERT_DIE(c->out_req.hook);
- channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
- channel_log_state_change(c);
+ if (c->out_table)
+ channel_aux_request_refeed(c->out_table);
+ else
+ channel_request_table_feeding(c);
}
-static void
+void
channel_request_reload(struct channel *c)
{
- ASSERT(c->channel_state == CS_UP);
+ ASSERT(c->in_req.hook);
ASSERT(channel_reloadable(c));
CD(c, "Reload requested");
- c->proto->reload_routes(c);
+ if (c->in_table)
+ channel_aux_request_refeed(c->in_table);
+ else
+ c->proto->reload_routes(c);
+}
- /*
- * Should this be done before reload_routes() hook?
- * Perhaps, but routes are updated asynchronously.
- */
- channel_reset_limit(&c->rx_limit);
- channel_reset_limit(&c->in_limit);
+void
+channel_refresh_begin(struct channel *c)
+{
+ CD(c, "Channel route refresh begin");
+ if (c->in_table)
+ rt_refresh_begin(&c->in_table->push);
+ else
+ rt_refresh_begin(&c->in_req);
+}
+
+void
+channel_refresh_end(struct channel *c)
+{
+ if (c->in_table)
+ rt_refresh_end(&c->in_table->push);
+ else
+ rt_refresh_end(&c->in_req);
}
const struct channel_class channel_basic = {
@@ -847,19 +1293,19 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* Reconfigure channel fields */
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
- c->rx_limit = cf->rx_limit;
- c->in_limit = cf->in_limit;
- c->out_limit = cf->out_limit;
+
+ channel_update_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
+ channel_update_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
+ channel_update_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
// c->ra_mode = cf->ra_mode;
c->merge_limit = cf->merge_limit;
c->preference = cf->preference;
c->debug = cf->debug;
+ c->in_req.trace_routes = c->out_req.trace_routes = c->debug | c->proto->debug;
c->in_keep_filtered = cf->in_keep_filtered;
c->rpki_reload = cf->rpki_reload;
- channel_verify_limits(c);
-
/* Execute channel-specific reconfigure hook */
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
return 0;
@@ -902,7 +1348,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
channel_request_reload(c);
if (export_changed)
- channel_request_feeding(c);
+ channel_request_table_feeding(c);
done:
CD(c, "Reconfigured");
@@ -950,34 +1396,50 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con
return 1;
}
+static void
+proto_cleanup(struct proto *p)
+{
+ p->active = 0;
+ proto_log_state_change(p);
+ proto_rethink_goal(p);
+}
static void
-proto_event(void *ptr)
+proto_loop_stopped(void *ptr)
{
struct proto *p = ptr;
- if (p->do_start)
- {
- if_feed_baby(p);
- p->do_start = 0;
- }
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+
+ p->loop = &main_birdloop;
+ p->pool = NULL;
+ p->event->list = NULL;
+
+ proto_cleanup(p);
+}
+
+static void
+proto_event(void *ptr)
+{
+ struct proto *p = ptr;
if (p->do_stop)
{
if (p->proto == &proto_unix_iface)
if_flush_ifaces(p);
+
p->do_stop = 0;
}
if (proto_is_done(p))
- {
- if (p->proto->cleanup)
- p->proto->cleanup(p);
-
- p->active = 0;
- proto_log_state_change(p);
- proto_rethink_goal(p);
- }
+ if (p->loop != &main_birdloop)
+ birdloop_stop_self(p->loop, proto_loop_stopped, p);
+ else
+ {
+ rp_free(p->pool, proto_pool);
+ p->pool = NULL;
+ proto_cleanup(p);
+ }
}
@@ -1018,10 +1480,10 @@ proto_init(struct proto_config *c, node *n)
struct protocol *pr = c->protocol;
struct proto *p = pr->init(c);
+ p->loop = &main_birdloop;
p->proto_state = PS_DOWN;
p->last_state_change = current_time();
p->vrf = c->vrf;
- p->vrf_set = c->vrf_set;
insert_node(&p->n, n);
p->event = ev_new_init(proto_pool, proto_event, p);
@@ -1034,11 +1496,30 @@ proto_init(struct proto_config *c, node *n)
static void
proto_start(struct proto *p)
{
- /* Here we cannot use p->cf->name since it won't survive reconfiguration */
- p->pool = rp_new(proto_pool, p->proto->name);
+ DBG("Kicking %s up\n", p->name);
+ PD(p, "Starting");
+
+ int ns = strlen("Protocol ") + strlen(p->cf->name) + 1;
+ void *nb = mb_alloc(proto_pool, ns);
+ ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Protocol %s", p->cf->name));
if (graceful_restart_state == GRS_INIT)
p->gr_recovery = 1;
+
+ if (p->cf->loop_order == DOMAIN_ORDER(the_bird))
+ p->pool = rp_new(proto_pool, &main_birdloop, nb);
+ else
+ {
+ p->loop = birdloop_new(proto_pool, p->cf->loop_order, nb);
+ p->pool = birdloop_pool(p->loop);
+ }
+
+ p->event->list = proto_event_list(p);
+
+ mb_move(nb, p->pool);
+
+ PROTO_LOCKED_FROM_MAIN(p)
+ proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP));
}
@@ -1074,6 +1555,7 @@ proto_config_new(struct protocol *pr, int class)
cf->class = class;
cf->debug = new_config->proto_default_debug;
cf->mrtdump = new_config->proto_default_mrtdump;
+ cf->loop_order = DOMAIN_ORDER(the_bird);
init_list(&cf->channels);
@@ -1189,8 +1671,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
if ((nc->protocol != oc->protocol) ||
(nc->net_type != oc->net_type) ||
(nc->disabled != p->disabled) ||
- (nc->vrf != oc->vrf) ||
- (nc->vrf_set != oc->vrf_set))
+ (nc->vrf != oc->vrf))
return 0;
p->name = nc->name;
@@ -1279,8 +1760,14 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
nc->proto = p;
/* We will try to reconfigure protocol p */
- if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
- continue;
+ if (!force_reconfig)
+ {
+ int ok;
+ PROTO_LOCKED_FROM_MAIN(p)
+ ok = proto_reconfigure(p, oc, nc, type);
+ if (ok)
+ continue;
+ }
if (nc->parent)
{
@@ -1363,11 +1850,20 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
}
static void
-proto_rethink_goal(struct proto *p)
+proto_shutdown(struct proto *p)
{
- struct protocol *q;
- byte goal;
+ if (p->proto_state == PS_START || p->proto_state == PS_UP)
+ {
+ /* Going down */
+ DBG("Kicking %s down\n", p->name);
+ PD(p, "Shutting down");
+ proto_notify_state(p, (p->proto->shutdown ? p->proto->shutdown(p) : PS_DOWN));
+ }
+}
+static void
+proto_rethink_goal(struct proto *p)
+{
if (p->reconfiguring && !p->active)
{
struct proto_config *nc = p->cf_new;
@@ -1387,32 +1883,12 @@ proto_rethink_goal(struct proto *p)
/* Determine what state we want to reach */
if (p->disabled || p->reconfiguring)
- goal = PS_DOWN;
- else
- goal = PS_UP;
-
- q = p->proto;
- if (goal == PS_UP)
- {
- if (!p->active)
- {
- /* Going up */
- DBG("Kicking %s up\n", p->name);
- PD(p, "Starting");
- proto_start(p);
- proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
- }
- }
- else
{
- if (p->proto_state == PS_START || p->proto_state == PS_UP)
- {
- /* Going down */
- DBG("Kicking %s down\n", p->name);
- PD(p, "Shutting down");
- proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
- }
+ PROTO_LOCKED_FROM_MAIN(p)
+ proto_shutdown(p);
}
+ else if (!p->active)
+ proto_start(p);
}
struct proto *
@@ -1524,7 +2000,7 @@ graceful_restart_done(timer *t UNUSED)
WALK_LIST(c, p->channels)
{
/* Resume postponed export of routes */
- if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify)
+ if ((c->channel_state == CS_UP) && c->gr_wait && p->rt_notify)
channel_start_export(c);
/* Cleanup */
@@ -1614,7 +2090,11 @@ protos_dump_all(void)
struct proto *p;
WALK_LIST(p, proto_list)
{
- debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]);
+#define DPF(x) (p->x ? " " #x : "")
+ debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s\n",
+ p->name, p, p_states[p->proto_state], p->active_channels,
+ DPF(disabled), DPF(active), DPF(do_stop), DPF(reconfiguring));
+#undef DPF
struct channel *c;
WALK_LIST(c, p->channels)
@@ -1624,6 +2104,23 @@ protos_dump_all(void)
debug("\tInput filter: %s\n", filter_name(c->in_filter));
if (c->out_filter)
debug("\tOutput filter: %s\n", filter_name(c->out_filter));
+ debug("\tChannel state: %s/%s/%s\n", c_states[c->channel_state],
+ c->in_req.hook ? rt_import_state_name(rt_import_get_state(c->in_req.hook)) : "-",
+ c->out_req.hook ? rt_export_state_name(rt_export_get_state(c->out_req.hook)) : "-");
+ if (c->in_table)
+ {
+ debug("\tInput aux table:\n");
+ rt_dump_hooks(c->in_table->tab);
+ rt_dump(c->in_table->tab);
+ debug("\tEnd of input aux table.\n");
+ }
+ if (c->out_table)
+ {
+ debug("\tOutput aux table:\n");
+ rt_dump_hooks(c->in_table->tab);
+ rt_dump(c->in_table->tab);
+ debug("\tEnd of output aux table.\n");
+ }
}
if (p->proto->dump && (p->proto_state != PS_DOWN))
@@ -1702,9 +2199,7 @@ protos_build(void)
proto_build(&proto_perf);
#endif
- proto_pool = rp_new(&root_pool, "Protocols");
- proto_shutdown_timer = tm_new(proto_pool);
- proto_shutdown_timer->hook = proto_shutdown_loop;
+ proto_pool = rp_new(&root_pool, &main_birdloop, "Protocols");
}
@@ -1712,7 +2207,7 @@ protos_build(void)
int proto_restart;
static void
-proto_shutdown_loop(timer *t UNUSED)
+proto_shutdown_loop(void *data UNUSED)
{
struct proto *p, *p_next;
@@ -1731,6 +2226,11 @@ proto_shutdown_loop(timer *t UNUSED)
}
}
+static event proto_schedule_down_event = {
+ .hook = proto_shutdown_loop,
+ .list = &global_event_list,
+};
+
static inline void
proto_schedule_down(struct proto *p, byte restart, byte code)
{
@@ -1743,7 +2243,8 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
p->down_code = code;
- tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0);
+
+ ev_send_self(&proto_schedule_down_event);
}
/**
@@ -1780,108 +2281,136 @@ proto_set_message(struct proto *p, char *msg, int len)
}
-static const char *
-channel_limit_name(struct channel_limit *l)
-{
- const char *actions[] = {
- [PLA_WARN] = "warn",
- [PLA_BLOCK] = "block",
- [PLA_RESTART] = "restart",
- [PLA_DISABLE] = "disable",
- };
+static const char * channel_limit_name[] = {
+ [PLA_WARN] = "warn",
+ [PLA_BLOCK] = "block",
+ [PLA_RESTART] = "restart",
+ [PLA_DISABLE] = "disable",
+};
- return actions[l->action];
-}
-/**
- * channel_notify_limit: notify about limit hit and take appropriate action
- * @c: channel
- * @l: limit being hit
- * @dir: limit direction (PLD_*)
- * @rt_count: the number of routes
- *
- * The function is called by the route processing core when limit @l
- * is breached. It activates the limit and tooks appropriate action
- * according to @l->action.
- */
-void
-channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count)
+static void
+channel_log_limit(struct channel *c, struct limit *l, int dir)
{
const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
- const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
- struct proto *p = c->proto;
+ log(L_WARN "Channel %s.%s hits route %s limit (%d), action: %s",
+ c->proto->name, c->name, dir_name[dir], l->max, channel_limit_name[c->limit_actions[dir]]);
+}
- if (l->state == PLS_BLOCKED)
+static void
+channel_activate_limit(struct channel *c, struct limit *l, int dir)
+{
+ if (c->limit_active & (1 << dir))
return;
- /* For warning action, we want the log message every time we hit the limit */
- if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
- log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
- p->name, dir_name[dir], l->limit, channel_limit_name(l));
+ c->limit_active |= (1 << dir);
+ channel_log_limit(c, l, dir);
+}
+
+static int
+channel_limit_warn(struct limit *l, void *data)
+{
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ int dir = cld->dir;
- switch (l->action)
- {
- case PLA_WARN:
- l->state = PLS_ACTIVE;
- break;
+ channel_log_limit(c, l, dir);
- case PLA_BLOCK:
- l->state = PLS_BLOCKED;
- break;
+ return 0;
+}
- case PLA_RESTART:
- case PLA_DISABLE:
- l->state = PLS_BLOCKED;
- if (p->proto_state == PS_UP)
- proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
- break;
- }
+static int
+channel_limit_block(struct limit *l, void *data)
+{
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ int dir = cld->dir;
+
+ channel_activate_limit(c, l, dir);
+
+ return 1;
}
-static void
-channel_verify_limits(struct channel *c)
+static const byte chl_dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
+
+static int
+channel_limit_down(struct limit *l, void *data)
{
- struct channel_limit *l;
- u32 all_routes = c->stats.imp_routes + c->stats.filt_routes;
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ struct proto *p = c->proto;
+ int dir = cld->dir;
- l = &c->rx_limit;
- if (l->action && (all_routes > l->limit))
- channel_notify_limit(c, l, PLD_RX, all_routes);
+ channel_activate_limit(c, l, dir);
- l = &c->in_limit;
- if (l->action && (c->stats.imp_routes > l->limit))
- channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes);
+ if (p->proto_state == PS_UP)
+ proto_schedule_down(p, c->limit_actions[dir] == PLA_RESTART, chl_dir_down[dir]);
- l = &c->out_limit;
- if (l->action && (c->stats.exp_routes > l->limit))
- channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes);
+ return 1;
}
-static inline void
-channel_reset_limit(struct channel_limit *l)
+static int (*channel_limit_action[])(struct limit *, void *) = {
+ [PLA_NONE] = NULL,
+ [PLA_WARN] = channel_limit_warn,
+ [PLA_BLOCK] = channel_limit_block,
+ [PLA_RESTART] = channel_limit_down,
+ [PLA_DISABLE] = channel_limit_down,
+};
+
+static void
+channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
+{
+ l->action = channel_limit_action[cf->action];
+ c->limit_actions[dir] = cf->action;
+
+ struct channel_limit_data cld = { .c = c, .dir = dir };
+ limit_update(l, &cld, cf->action ? cf->limit : ~((u32) 0));
+}
+
+static void
+channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
{
- if (l->action)
- l->state = PLS_INITIAL;
+ channel_reset_limit(c, l, dir);
+ channel_update_limit(c, l, dir, cf);
}
+static void
+channel_reset_limit(struct channel *c, struct limit *l, int dir)
+{
+ limit_reset(l);
+ c->limit_active &= ~(1 << dir);
+}
+
+static struct rte_owner_class default_rte_owner_class;
+
static inline void
proto_do_start(struct proto *p)
{
+ ASSERT_DIE(birdloop_inside(p->loop));
+
p->active = 1;
- p->do_start = 1;
- ev_schedule(p->event);
+
+ rt_init_sources(&p->sources, p->name, proto_work_list(p));
+ if (!p->sources.class)
+ p->sources.class = &default_rte_owner_class;
+
+ if (!p->cf->late_if_feed)
+ if_feed_baby(p);
}
static void
proto_do_up(struct proto *p)
{
+ ASSERT_DIE(birdloop_inside(p->loop));
+
if (!p->main_source)
- {
p->main_source = rt_get_source(p, 0);
- rt_lock_source(p->main_source);
- }
+ // Locked automaticaly
proto_start_channels(p);
+
+ if (p->cf->late_if_feed)
+ if_feed_baby(p);
}
static inline void
@@ -1896,9 +2425,6 @@ proto_do_stop(struct proto *p)
p->down_sched = 0;
p->gr_recovery = 0;
- p->do_stop = 1;
- ev_schedule(p->event);
-
if (p->main_source)
{
rt_unlock_source(p->main_source);
@@ -1906,19 +2432,21 @@ proto_do_stop(struct proto *p)
}
proto_stop_channels(p);
+ rt_destroy_sources(&p->sources, p->event);
+
+ p->do_stop = 1;
+ proto_send_event(p);
}
static void
proto_do_down(struct proto *p)
{
p->down_code = 0;
- neigh_prune();
- rfree(p->pool);
- p->pool = NULL;
+ neigh_prune(p);
/* Shutdown is finished in the protocol event */
if (proto_is_done(p))
- ev_schedule(p->event);
+ proto_send_event(p);
}
@@ -2009,38 +2537,58 @@ proto_state_name(struct proto *p)
static void
channel_show_stats(struct channel *c)
{
- struct proto_stats *s = &c->stats;
+ struct channel_import_stats *ch_is = &c->import_stats;
+ struct channel_export_stats *ch_es = &c->export_stats;
+ struct rt_import_stats *rt_is = c->in_req.hook ? &c->in_req.hook->stats : NULL;
+ struct rt_export_stats *rt_es = c->out_req.hook ? &c->out_req.hook->stats : NULL;
+
+#define SON(ie, item) ((ie) ? (ie)->item : 0)
+#define SCI(item) SON(ch_is, item)
+#define SCE(item) SON(ch_es, item)
+#define SRI(item) SON(rt_is, item)
+#define SRE(item) SON(rt_es, item)
+
+ u32 rx_routes = c->rx_limit.count;
+ u32 in_routes = c->in_limit.count;
+ u32 out_routes = c->out_limit.count;
if (c->in_keep_filtered)
cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
- s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
+ in_routes, (rx_routes - in_routes), out_routes, SRI(pref));
else
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");
- cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- s->imp_updates_received, s->imp_updates_invalid,
- s->imp_updates_filtered, s->imp_updates_ignored,
- s->imp_updates_accepted);
- cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- s->imp_withdraws_received, s->imp_withdraws_invalid,
- s->imp_withdraws_ignored, s->imp_withdraws_accepted);
- cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
- s->exp_updates_received, s->exp_updates_rejected,
- s->exp_updates_filtered, s->exp_updates_accepted);
- cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
- s->exp_withdraws_received, s->exp_withdraws_accepted);
+ in_routes, out_routes, SRI(pref));
+
+ cli_msg(-1006, " Route change stats: received rejected filtered ignored limited accepted");
+ cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u %10u",
+ SCI(updates_received), SCI(updates_invalid),
+ SCI(updates_filtered), SRI(updates_ignored),
+ SCI(updates_limited_rx) + SCI(updates_limited_in),
+ SRI(updates_accepted));
+ cli_msg(-1006, " Import withdraws: %10u %10u --- %10u --- %10u",
+ SCI(withdraws_received), SCI(withdraws_invalid),
+ SRI(withdraws_ignored), SRI(withdraws_accepted));
+ cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u %10u",
+ SRE(updates_received), SCE(updates_rejected),
+ SCE(updates_filtered), SCE(updates_limited), SCE(updates_accepted));
+ cli_msg(-1006, " Export withdraws: %10u --- --- --- ---%10u",
+ SRE(withdraws_received), SCE(withdraws_accepted));
+
+#undef SRI
+#undef SRE
+#undef SCI
+#undef SCE
+#undef SON
}
void
-channel_show_limit(struct channel_limit *l, const char *dsc)
+channel_show_limit(struct limit *l, const char *dsc, int active, int action)
{
if (!l->action)
return;
- cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
- cli_msg(-1006, " Action: %s", channel_limit_name(l));
+ cli_msg(-1006, " %-16s%d%s", dsc, l->max, active ? " [HIT]" : "");
+ cli_msg(-1006, " Action: %s", channel_limit_name[action]);
}
void
@@ -2048,6 +2596,8 @@ channel_show_info(struct channel *c)
{
cli_msg(-1006, " Channel %s", c->name);
cli_msg(-1006, " State: %s", c_states[c->channel_state]);
+ cli_msg(-1006, " Import state: %s", rt_import_state_name(rt_import_get_state(c->in_req.hook)));
+ cli_msg(-1006, " Export state: %s", rt_export_state_name(rt_export_get_state(c->out_req.hook)));
cli_msg(-1006, " Table: %s", c->table->name);
cli_msg(-1006, " Preference: %d", c->preference);
cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter));
@@ -2058,9 +2608,9 @@ channel_show_info(struct channel *c)
c->gr_lock ? " pending" : "",
c->gr_wait ? " waiting" : "");
- channel_show_limit(&c->rx_limit, "Receive limit:");
- channel_show_limit(&c->in_limit, "Import limit:");
- channel_show_limit(&c->out_limit, "Export limit:");
+ channel_show_limit(&c->rx_limit, "Receive limit:", c->limit_active & (1 << PLD_RX), c->limit_actions[PLD_RX]);
+ channel_show_limit(&c->in_limit, "Import limit:", c->limit_active & (1 << PLD_IN), c->limit_actions[PLD_IN]);
+ channel_show_limit(&c->out_limit, "Export limit:", c->limit_active & (1 << PLD_OUT), c->limit_actions[PLD_OUT]);
if (c->channel_state != CS_DOWN)
channel_show_stats(c);
@@ -2106,8 +2656,8 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
cli_msg(-1006, " Message: %s", p->message);
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
- if (p->vrf_set)
- cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default");
+ if (p->vrf)
+ cli_msg(-1006, " VRF: %s", p->vrf->name);
if (p->proto->show_proto_info)
p->proto->show_proto_info(p);
@@ -2135,7 +2685,7 @@ proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
p->disabled = 1;
p->down_code = PDC_CMD_DISABLE;
proto_set_message(p, (char *) arg, -1);
- proto_rethink_goal(p);
+ proto_shutdown(p);
cli_msg(-9, "%s: disabled", p->name);
}
@@ -2168,9 +2718,9 @@ proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
p->disabled = 1;
p->down_code = PDC_CMD_RESTART;
proto_set_message(p, (char *) arg, -1);
- proto_rethink_goal(p);
+ proto_shutdown(p);
p->disabled = 0;
- proto_rethink_goal(p);
+ /* After the protocol shuts down, proto_rethink_goal() is run from proto_event. */
cli_msg(-12, "%s: restarted", p->name);
}
@@ -2243,8 +2793,15 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
return;
}
- cmd(s->proto->proto, arg, 0);
- cli_msg(0, "");
+ if (s->proto->proto)
+ {
+ struct proto *p = s->proto->proto;
+ PROTO_LOCKED_FROM_MAIN(p)
+ cmd(p, arg, 0);
+ cli_msg(0, "");
+ }
+ else
+ cli_msg(9002, "%s does not exist", s->name);
}
static void
@@ -2255,7 +2812,8 @@ proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, i
WALK_LIST(p, proto_list)
if (!patt || patmatch(patt, p->name))
- cmd(p, arg, cnt++);
+ PROTO_LOCKED_FROM_MAIN(p)
+ cmd(p, arg, cnt++);
if (!cnt)
cli_msg(8003, "No protocols match");