0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-03-22 14:27:03 +00:00

Merge commit 'f81702b7' into haugesund

This commit is contained in:
Maria Matejka 2022-03-09 15:03:48 +01:00
commit 41572e0c1b
15 changed files with 1371 additions and 825 deletions

View File

@ -425,6 +425,23 @@ filter_commit(struct config *new, struct config *old)
}
}
void channel_filter_dump(const struct filter *f)
{
if (f == FILTER_ACCEPT)
debug(" ALL");
else if (f == FILTER_REJECT)
debug(" NONE");
else if (f == FILTER_UNDEF)
debug(" UNDEF");
else if (f->sym) {
ASSERT(f->sym->filter == f);
debug(" named filter %s", f->sym->name);
} else {
debug("\n");
f_dump_line(f->root, 2);
}
}
void filters_dump_all(void)
{
struct symbol *sym;
@ -444,19 +461,10 @@ void filters_dump_all(void)
struct channel *c;
WALK_LIST(c, sym->proto->proto->channels) {
debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
if (c->in_filter == FILTER_ACCEPT)
debug(" ALL\n");
else if (c->in_filter == FILTER_REJECT)
debug(" NONE\n");
else if (c->in_filter == FILTER_UNDEF)
debug(" UNDEF\n");
else if (c->in_filter->sym) {
ASSERT(c->in_filter->sym->filter == c->in_filter);
debug(" named filter %s\n", c->in_filter->sym->name);
} else {
channel_filter_dump(c->in_filter);
debug(" EXPORT", c->name, net_label[c->net_type]);
channel_filter_dump(c->out_filter);
debug("\n");
f_dump_line(c->in_filter->root, 2);
}
}
}
}

View File

@ -851,8 +851,10 @@ CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
{ neigh_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
{ rta_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
CF_CLI(DUMP ROUTES,,, [[Dump routes]])
{ rt_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP TABLES,,, [[Dump table connections]])
{ rt_dump_hooks_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])

49
nest/limit.h Normal file
View File

@ -0,0 +1,49 @@
/*
* BIRD Internet Routing Daemon -- Limits
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_LIMIT_H_
#define _BIRD_LIMIT_H_
struct limit {
u32 max;
u32 count;
int (*action)(struct limit *, void *data);
};
static inline int limit_do_action(struct limit *l, void *data)
{
return l->action ? l->action(l, data) : 1;
}
static inline int limit_push(struct limit *l, void *data)
{
if ((l->count >= l->max) && limit_do_action(l, data))
return 1;
l->count++;
return 0;
}
static inline void limit_pop(struct limit *l)
{
--l->count;
}
static inline void limit_reset(struct limit *l)
{
l->count = 0;
}
static inline void limit_update(struct limit *l, void *data, u32 max)
{
if (l->count > (l->max = max))
limit_do_action(l, data);
}
#endif

View File

@ -43,8 +43,7 @@ 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;
@ -52,15 +51,17 @@ static void channel_request_reload(struct channel *c);
static void proto_shutdown_loop(timer *);
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); }
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,12 +69,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]);
}
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->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
proto_log_state_change(struct proto *p)
{
@ -141,6 +178,15 @@ 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);
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
@ -165,12 +211,14 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->channel = cf->channel;
c->proto = p;
c->table = cf->table->table;
rt_lock_table(c->table);
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->net_type = cf->net_type;
c->ra_mode = cf->ra_mode;
@ -181,7 +229,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 +250,7 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c)
CD(c, "Removed", c->name);
rt_unlock_table(c->table);
rem_node(&c->n);
mb_free(c);
}
@ -223,7 +271,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 +280,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
@ -243,71 +291,6 @@ proto_remove_channels(struct proto *p)
proto_remove_channel(p, c);
}
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->export_stats.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)
{
@ -326,14 +309,12 @@ static void
channel_roa_out_changed(struct rt_subscription *s)
{
struct channel *c = s->data;
int active = (c->export_state == ES_FEEDING);
CD(c, "Feeding triggered by RPKI change");
CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : "");
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 */
@ -445,33 +426,189 @@ channel_roa_unsubscribe_all(struct channel *c)
}
static void
channel_start_export(struct channel *c)
channel_start_import(struct channel *c)
{
ASSERT(c->channel_state == CS_UP);
ASSERT(c->export_state == ES_DOWN);
if (c->in_req.hook)
{
log(L_WARN "%s.%s: Attempted to start channel's already started import", c->proto->name, c->name);
return;
}
channel_schedule_feed(c, 1); /* Sets ES_FEEDING */
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,
.trace_routes = c->debug | c->proto->debug,
.dump_req = channel_dump_import_req,
.log_state_change = channel_import_log_state_change,
.preimport = channel_preimport,
.rte_modify = c->proto->rte_modify,
};
ASSERT(c->channel_state == CS_UP);
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);
c->export_state = ES_DOWN;
c->export_stats.routes = 0;
bmap_reset(&c->export_map, 1024);
bmap_reset(&c->export_reject_map, 1024);
if (c->out_req.hook)
{
log(L_WARN "%s.%s: Attempted to start channel's already started export", 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,
.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);
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)
return;
channel_set_state(c, CS_DOWN);
ev_schedule(c->proto->event);
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]);
}
void
channel_import_stopped(struct rt_import_request *req)
{
struct channel *c = SKIP_BACK(struct channel, in_req, req);
req->hook = NULL;
if (c->in_table)
rt_prune_sync(c->in_table, 1);
mb_free(c->in_req.name);
c->in_req.name = NULL;
channel_check_stopped(c);
}
static void
channel_export_stopped(struct rt_export_request *req)
{
struct channel *c = SKIP_BACK(struct channel, out_req, req);
/* The hook has already stopped */
req->hook = NULL;
if (c->refeed_pending)
{
c->refeeding = 1;
c->refeed_pending = 0;
rt_request_export(c->table, req);
return;
}
/* Free the routes from out_table */
if (c->out_table)
rt_prune_sync(c->out_table, 1);
mb_free(c->out_req.name);
c->out_req.name = NULL;
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);
c->refeed_pending = 1;
rt_stop_export(req, channel_export_stopped);
return;
}
if (c->proto->feed_end)
c->proto->feed_end(c);
if (c->refeed_pending)
rt_stop_export(req, channel_export_stopped);
else
c->refeeding = 0;
}
/* Called by protocol for reload from in_table */
void
channel_schedule_reload(struct channel *c)
{
ASSERT(c->channel_state == CS_UP);
ASSERT(c->in_req.hook);
rt_reload_channel_abort(c);
ev_schedule_work(c->reload_event);
@ -497,23 +634,6 @@ channel_reload_loop(void *ptr)
channel_request_reload(c);
}
static void
channel_reset_import(struct channel *c)
{
/* Need to abort feeding */
ev_postpone(c->reload_event);
rt_reload_channel_abort(c);
rt_prune_sync(c->in_table, 1);
}
static void
channel_reset_export(struct channel *c)
{
/* Just free the routes */
rt_prune_sync(c->out_table, 1);
}
/* Called by protocol to activate in_table */
void
channel_setup_in_table(struct channel *c)
@ -545,22 +665,11 @@ channel_setup_out_table(struct channel *c)
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);
bmap_init(&c->export_reject_map, c->proto->pool, 1024);
memset(&c->export_stats, 0, sizeof(struct export_stats));
memset(&c->import_stats, 0, sizeof(struct import_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
@ -575,9 +684,31 @@ channel_do_up(struct channel *c)
}
static void
channel_do_flush(struct channel *c)
channel_do_pause(struct channel *c)
{
rt_schedule_prune(c->table);
/* Need to abort feeding */
if (c->reload_event)
{
ev_postpone(c->reload_event);
rt_reload_channel_abort(c);
}
/* Stop export */
if (c->out_req.hook)
rt_stop_export(&c->out_req, channel_export_stopped);
channel_roa_unsubscribe_all(c);
bmap_free(&c->export_map);
bmap_free(&c->export_reject_map);
}
static void
channel_do_stop(struct channel *c)
{
/* Stop import */
if (c->in_req.hook)
rt_stop_import(&c->in_req, channel_import_stopped);
c->gr_wait = 0;
if (c->gr_lock)
@ -586,30 +717,21 @@ 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);
bmap_free(&c->export_reject_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->reload_active);
rem_node(&c->table_node);
rt_unlock_table(c->table);
c->proto->active_channels--;
if ((c->import_stats.routes + c->import_stats.filtered) != 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->import_stats, 0, sizeof(struct import_stats));
memset(&c->export_stats, 0, sizeof(struct export_stats));
memset(&c->import_stats, 0, sizeof(struct channel_import_stats));
memset(&c->export_stats, 0, sizeof(struct channel_export_stats));
c->in_table = NULL;
c->reload_event = NULL;
@ -628,7 +750,6 @@ 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)
@ -640,20 +761,11 @@ 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:
@ -668,23 +780,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;
@ -709,35 +822,16 @@ channel_set_state(struct channel *c, uint state)
void
channel_request_feeding(struct channel *c)
{
ASSERT(c->channel_state == CS_UP);
ASSERT(c->out_req.hook);
CD(c, "Feeding requested");
/* Do nothing if we are still waiting for feeding */
if (c->export_state == ES_DOWN)
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);
}
/* Track number of exported routes during refeed */
c->refeed_count = 0;
channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
channel_log_state_change(c);
c->refeed_pending = 1;
rt_stop_export(&c->out_req, channel_export_stopped);
}
static 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");
@ -748,8 +842,8 @@ channel_request_reload(struct channel *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);
channel_reset_limit(c, &c->rx_limit, PLD_RX);
channel_reset_limit(c, &c->in_limit, PLD_IN);
}
const struct channel_class channel_basic = {
@ -852,19 +946,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;
@ -976,8 +1070,8 @@ proto_event(void *ptr)
if (proto_is_done(p))
{
if (p->proto->cleanup)
p->proto->cleanup(p);
rfree(p->pool);
p->pool = NULL;
p->active = 0;
proto_log_state_change(p);
@ -1529,7 +1623,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 */
@ -1619,7 +1713,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%s\n",
p->name, p, p_states[p->proto_state], p->active_channels,
DPF(disabled), DPF(active), DPF(do_start), DPF(do_stop), DPF(reconfiguring));
#undef DPF
struct channel *c;
WALK_LIST(c, p->channels)
@ -1629,6 +1727,9 @@ 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 (p->proto->dump && (p->proto_state != PS_DOWN))
@ -1785,88 +1886,104 @@ proto_set_message(struct proto *p, char *msg, int len)
}
static const char *
channel_limit_name(struct channel_limit *l)
{
const char *actions[] = {
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;
if (l->state == PLS_BLOCKED)
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));
switch (l->action)
{
case PLA_WARN:
l->state = PLS_ACTIVE;
break;
case PLA_BLOCK:
l->state = PLS_BLOCKED;
break;
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;
}
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]]);
}
static void
channel_verify_limits(struct channel *c)
channel_activate_limit(struct channel *c, struct limit *l, int dir)
{
struct channel_limit *l;
u32 all_routes = c->import_stats.routes + c->import_stats.filtered;
if (c->limit_active & (1 << dir))
return;
l = &c->rx_limit;
if (l->action && (all_routes > l->limit))
channel_notify_limit(c, l, PLD_RX, all_routes);
l = &c->in_limit;
if (l->action && (c->import_stats.routes > l->limit))
channel_notify_limit(c, l, PLD_IN, c->import_stats.routes);
l = &c->out_limit;
if (l->action && (c->export_stats.routes > l->limit))
channel_notify_limit(c, l, PLD_OUT, c->export_stats.routes);
c->limit_active |= (1 << dir);
channel_log_limit(c, l, dir);
}
static inline void
channel_reset_limit(struct channel_limit *l)
static int
channel_limit_warn(struct limit *l, void *data)
{
if (l->action)
l->state = PLS_INITIAL;
struct channel_limit_data *cld = data;
struct channel *c = cld->c;
int dir = cld->dir;
channel_log_limit(c, l, dir);
return 0;
}
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 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_data *cld = data;
struct channel *c = cld->c;
struct proto *p = c->proto;
int dir = cld->dir;
channel_activate_limit(c, l, dir);
if (p->proto_state == PS_UP)
proto_schedule_down(p, c->limit_actions[dir] == PLA_RESTART, chl_dir_down[dir]);
return 1;
}
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)
{
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 inline void
@ -1918,8 +2035,6 @@ proto_do_down(struct proto *p)
{
p->down_code = 0;
neigh_prune();
rfree(p->pool);
p->pool = NULL;
/* Shutdown is finished in the protocol event */
if (proto_is_done(p))
@ -2014,39 +2129,58 @@ proto_state_name(struct proto *p)
static void
channel_show_stats(struct channel *c)
{
struct import_stats *is = &c->import_stats;
struct export_stats *es = &c->export_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",
is->routes, is->filtered, es->routes, is->pref);
in_routes, (rx_routes - in_routes), out_routes, SRI(pref));
else
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
is->routes, es->routes, is->pref);
in_routes, out_routes, SRI(pref));
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
is->updates_received, is->updates_invalid,
is->updates_filtered, is->updates_ignored,
is->updates_accepted);
cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
is->withdraws_received, is->withdraws_invalid,
is->withdraws_ignored, is->withdraws_accepted);
cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
es->updates_received, es->updates_rejected,
es->updates_filtered, es->updates_accepted);
cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
es->withdraws_received, es->withdraws_accepted);
cli_msg(-1006, " Route change stats: received rejected filtered ignored RX limit IN limit accepted");
cli_msg(-1006, " Import updates: %10u %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
@ -2054,6 +2188,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));
@ -2064,9 +2200,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);

View File

@ -13,6 +13,7 @@
#include "lib/resource.h"
#include "lib/event.h"
#include "nest/route.h"
#include "nest/limit.h"
#include "conf/conf.h"
struct iface;
@ -76,7 +77,6 @@ struct protocol {
void (*dump)(struct proto *); /* Debugging dump */
int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
@ -132,33 +132,6 @@ struct proto_config {
};
/* Protocol statistics */
struct import_stats {
/* Import - from protocol to core */
u32 routes; /* Number of routes successfully imported to the (adjacent) routing table */
u32 filtered; /* Number of routes rejected in import filter but kept in the routing table */
u32 pref; /* Number of routes selected as best in the (adjacent) routing table */
u32 updates_received; /* Number of route updates received */
u32 updates_invalid; /* Number of route updates rejected as invalid */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_ignored; /* Number of route updates rejected as already in route table */
u32 updates_accepted; /* Number of route updates accepted and imported */
u32 withdraws_received; /* Number of route withdraws received */
u32 withdraws_invalid; /* Number of route withdraws rejected as invalid */
u32 withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
};
struct export_stats {
/* Export - from core to protocol */
u32 routes; /* Number of routes successfully exported to the protocol */
u32 updates_received; /* Number of route updates received */
u32 updates_rejected; /* Number of route updates rejected by protocol */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_accepted; /* Number of route updates accepted and exported */
u32 withdraws_received; /* Number of route withdraws received */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
};
struct proto {
node n; /* Node in global proto_list */
struct protocol *proto; /* Protocol */
@ -277,7 +250,7 @@ void channel_graceful_restart_unlock(struct channel *c);
#define DEFAULT_GR_WAIT 240
void channel_show_limit(struct channel_limit *l, const char *dsc);
void channel_show_limit(struct limit *l, const char *dsc, int active, int action);
void channel_show_info(struct channel *c);
void channel_cmd_debug(struct channel *c, uint mask);
@ -432,18 +405,29 @@ extern struct proto_config *cf_dev_proto;
#define PLA_RESTART 4 /* Force protocol restart */
#define PLA_DISABLE 5 /* Shutdown and disable protocol */
#define PLS_INITIAL 0 /* Initial limit state after protocol start */
#define PLS_ACTIVE 1 /* Limit was hit */
#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */
struct channel_limit {
u32 limit; /* Maximum number of prefixes */
u8 action; /* Action to take (PLA_*) */
u8 state; /* State of limit (PLS_*) */
};
void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
struct channel_limit_data {
struct channel *c;
int dir;
};
#define CLP__RX(_c) (&(_c)->rx_limit)
#define CLP__IN(_c) (&(_c)->in_limit)
#define CLP__OUT(_c) (&(_c)->out_limit)
#if 0
#define CHANNEL_LIMIT_LOG(_c, _dir, _op) log(L_TRACE "%s.%s: %s limit %s %u", (_c)->proto->name, (_c)->name, #_dir, _op, (CLP__##_dir(_c))->count)
#else
#define CHANNEL_LIMIT_LOG(_c, _dir, _op)
#endif
#define CHANNEL_LIMIT_PUSH(_c, _dir) ({ CHANNEL_LIMIT_LOG(_c, _dir, "push from"); struct channel_limit_data cld = { .c = (_c), .dir = PLD_##_dir }; limit_push(CLP__##_dir(_c), &cld); })
#define CHANNEL_LIMIT_POP(_c, _dir) ({ limit_pop(CLP__##_dir(_c)); CHANNEL_LIMIT_LOG(_c, _dir, "pop to"); })
/*
* Channels
@ -486,6 +470,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
const struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@ -502,7 +487,6 @@ struct channel_config {
struct channel {
node n; /* Node in proto->channels */
node table_node; /* Node in table->channels */
const char *name; /* Channel name (may be NULL) */
const struct channel_class *channel;
@ -513,14 +497,36 @@ struct channel {
const struct filter *out_filter; /* Output filter */
struct bmap export_map; /* Keeps track which routes were really exported */
struct bmap export_reject_map; /* Keeps track which routes were rejected by export filter */
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct channel_limit in_limit; /* Input limit */
struct channel_limit out_limit; /* Output limit */
struct event *feed_event; /* Event responsible for feeding */
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
struct import_stats import_stats; /* Import statistics */
struct export_stats export_stats; /* Export statistics */
struct limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct limit in_limit; /* Input limit */
struct limit out_limit; /* Output limit */
u8 limit_actions[PLD_MAX]; /* Limit actions enum */
u8 limit_active; /* Flags for active limits */
struct channel_import_stats {
/* Import - from protocol to core */
u32 updates_received; /* Number of route updates received */
u32 updates_invalid; /* Number of route updates rejected as invalid */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_limited_rx; /* Number of route updates exceeding the rx_limit */
u32 updates_limited_in; /* Number of route updates exceeding the in_limit */
u32 withdraws_received; /* Number of route withdraws received */
u32 withdraws_invalid; /* Number of route withdraws rejected as invalid */
} import_stats;
struct channel_export_stats {
/* Export - from core to protocol */
u32 updates_rejected; /* Number of route updates rejected by protocol */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_accepted; /* Number of route updates accepted and exported */
u32 updates_limited; /* Number of route updates exceeding the out_limit */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
} export_stats;
struct rt_import_request in_req; /* Table import connection */
struct rt_export_request out_req; /* Table export connection */
u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */
@ -534,10 +540,7 @@ struct channel {
u8 stale; /* Used in reconfiguration */
u8 channel_state;
u8 export_state; /* Route export state (ES_*, see below) */
u8 feed_active;
u8 flush_active;
u8 refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */
u8 refeeding; /* Refeeding the channel. */
u8 reloadable; /* Hook reload_routes() is allowed on the channel */
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
@ -585,34 +588,34 @@ struct channel {
* restricted by that and is on volition of the protocol. Generally, channels
* are opened in protocols' start() hooks when going to PS_UP.
*
* CS_FLUSHING - The transitional state between initialized channel and closed
* CS_STOP - The transitional state between initialized channel and closed
* channel. The channel is still initialized, but no route exchange is allowed.
* Instead, the associated table is running flush loop to remove routes imported
* through the channel. After that, the channel changes state to CS_DOWN and
* is detached from the table (the table is unlocked and the channel is unlinked
* from it). Unlike other states, the CS_FLUSHING state is not explicitly
* from it). Unlike other states, the CS_STOP state is not explicitly
* entered or left by the protocol. A protocol may request to close a channel
* (by calling channel_close()), which causes the channel to change state to
* CS_FLUSHING and later to CS_DOWN. Also note that channels are closed
* CS_STOP and later to CS_DOWN. Also note that channels are closed
* automatically by the core when the protocol is going down.
*
* CS_PAUSE - Almost the same as CS_STOP, just the table import is kept and
* the table export is stopped before transitioning to CS_START.
*
* Allowed transitions:
*
* CS_DOWN -> CS_START / CS_UP
* CS_START -> CS_UP / CS_FLUSHING
* CS_UP -> CS_START / CS_FLUSHING
* CS_FLUSHING -> CS_DOWN (automatic)
* CS_START -> CS_UP / CS_STOP
* CS_UP -> CS_PAUSE / CS_STOP
* CS_PAUSE -> CS_START (automatic)
* CS_STOP -> CS_DOWN (automatic)
*/
#define CS_DOWN 0
#define CS_START 1
#define CS_UP 2
#define CS_FLUSHING 3
#define ES_DOWN 0
#define ES_FEEDING 1
#define ES_READY 2
#define CS_STOP 3
#define CS_PAUSE 4
struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type);
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
@ -630,7 +633,7 @@ void channel_schedule_reload(struct channel *c);
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_STOP); }
void channel_request_feeding(struct channel *c);
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);

View File

@ -2,6 +2,7 @@
* BIRD Internet Routing Daemon -- Routing Table
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2019--2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -17,6 +18,7 @@
struct ea_list;
struct protocol;
struct proto;
struct channel;
struct rte_src;
struct symbol;
struct timer;
@ -165,12 +167,12 @@ typedef struct rtable {
struct fib fib;
struct f_trie *trie; /* Trie of prefixes defined in fib */
char *name; /* Name of this table */
list channels; /* List of attached channels (struct channel) */
uint addr_type; /* Type of address data stored in table (NET_*) */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
byte internal; /* Internal table of a protocol */
list imports; /* Registered route importers */
list exports; /* Registered route exporters */
struct hmap id_map;
struct hostcache *hostcache;
@ -188,6 +190,7 @@ typedef struct rtable {
byte prune_trie; /* Prune prefix trie during next table prune */
byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */
byte internal; /* This table is internal for some other object */
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
struct f_trie *trie_new; /* New prefix trie defined during pruning */
@ -257,7 +260,7 @@ typedef struct rte {
struct rta *attrs; /* Attributes of this route */
const net_addr *net; /* Network this RTE belongs to */
struct rte_src *src; /* Route source that created the route */
struct channel *sender; /* Channel used to send the route to the routing table */
struct rt_import_hook *sender; /* Import hook used to send the route to the routing table */
btime lastmod; /* Last modified (set by table) */
u32 id; /* Table specific route id */
byte flags; /* Table-specific flags */
@ -281,15 +284,130 @@ struct rte_storage {
#define REF_MODIFY 16 /* Route is scheduled for modify */
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
static inline int rte_is_valid_rte(rte *r) { return r && !(r->flags & REF_FILTERED); }
static inline int rte_is_valid_storage(struct rte_storage *r) { return r && rte_is_valid_rte(&r->rte); }
static inline int rte_is_valid_rte(const rte *r) { return r && !(r->flags & REF_FILTERED); }
static inline int rte_is_valid_storage(const struct rte_storage *r) { return r && rte_is_valid_rte(&r->rte); }
#define rte_is_valid(r) _Generic((*r), rte: rte_is_valid_rte, struct rte_storage: rte_is_valid_storage)(r)
/* Route just has REF_FILTERED flag */
static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
static inline int rte_is_filtered(const rte *r) { return !!(r->flags & REF_FILTERED); }
/* Table-channel connections */
struct rt_import_request {
struct rt_import_hook *hook; /* The table part of importer */
char *name;
u8 trace_routes;
void (*dump_req)(struct rt_import_request *req);
void (*log_state_change)(struct rt_import_request *req, u8 state);
/* Preimport is called when the @new route is just-to-be inserted, replacing @old.
* Return a route (may be different or modified in-place) to continue or NULL to withdraw. */
struct rte *(*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old);
struct rte *(*rte_modify)(struct rte *, struct linpool *);
};
struct rt_import_hook {
node n;
rtable *table; /* The connected table */
struct rt_import_request *req; /* The requestor */
struct rt_import_stats {
/* Import - from protocol to core */
u32 pref; /* Number of routes selected as best in the (adjacent) routing table */
u32 updates_ignored; /* Number of route updates rejected as already in route table */
u32 updates_accepted; /* Number of route updates accepted and imported */
u32 withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
} stats;
btime last_state_change; /* Time of last state transition */
u8 import_state; /* IS_* */
void (*stopped)(struct rt_import_request *); /* Stored callback when import is stopped */
};
struct rt_pending_export {
struct rte_storage *new, *new_best, *old, *old_best;
};
struct rt_export_request {
struct rt_export_hook *hook; /* Table part of the export */
char *name;
u8 trace_routes;
/* There are two methods of export. You can either request feeding every single change
* or feeding the whole route feed. In case of regular export, &export_one is preferred.
* Anyway, when feeding, &export_bulk is preferred, falling back to &export_one.
* Thus, for RA_OPTIMAL, &export_one is only set,
* for RA_MERGED and RA_ACCEPTED, &export_bulk is only set
* and for RA_ANY, both are set to accomodate for feeding all routes but receiving single changes
*/
void (*export_one)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
void (*export_bulk)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
void (*dump_req)(struct rt_export_request *req);
void (*log_state_change)(struct rt_export_request *req, u8);
};
struct rt_export_hook {
node n;
rtable *table; /* The connected table */
pool *pool;
linpool *lp;
struct rt_export_request *req; /* The requestor */
struct rt_export_stats {
/* Export - from core to protocol */
u32 updates_received; /* Number of route updates received */
u32 withdraws_received; /* Number of route withdraws received */
} stats;
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
btime last_state_change; /* Time of last state transition */
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
u8 export_state; /* Route export state (TES_*, see below) */
struct event *event; /* Event running all the export operations */
void (*stopped)(struct rt_export_request *); /* Stored callback when export is stopped */
};
#define TIS_DOWN 0
#define TIS_UP 1
#define TIS_STOP 2
#define TIS_FLUSHING 3
#define TIS_WAITING 4
#define TIS_CLEARED 5
#define TIS_MAX 6
#define TES_DOWN 0
#define TES_HUNGRY 1
#define TES_FEEDING 2
#define TES_READY 3
#define TES_STOP 4
#define TES_MAX 5
void rt_request_import(rtable *tab, struct rt_import_request *req);
void rt_request_export(rtable *tab, struct rt_export_request *req);
void rt_stop_import(struct rt_import_request *, void (*stopped)(struct rt_import_request *));
void rt_stop_export(struct rt_export_request *, void (*stopped)(struct rt_export_request *));
const char *rt_import_state_name(u8 state);
const char *rt_export_state_name(u8 state);
static inline u8 rt_import_get_state(struct rt_import_hook *ih) { return ih ? ih->import_state : TIS_DOWN; }
static inline u8 rt_export_get_state(struct rt_export_hook *eh) { return eh ? eh->export_state : TES_DOWN; }
void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
/* Types of route announcement, also used as flags */
#define RA_UNDEF 0 /* Undefined RA type */
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
@ -303,6 +421,7 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
#define RIC_REJECT -1 /* Rejected by protocol */
#define RIC_DROP -2 /* Silently dropped by protocol */
#define rte_update channel_rte_import
/**
* rte_update - enter a new update to a routing table
* @c: channel doing the update
@ -362,23 +481,24 @@ net *net_get(rtable *tab, const net_addr *addr);
net *net_route(rtable *tab, const net_addr *n);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged_show(struct channel *c, net *n, linpool *pool);
void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c);
void rt_modify_stale(rtable *t, struct channel *c);
rte *rt_export_merged(struct channel *c, rte ** feed, uint count, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct rt_import_request *);
void rt_refresh_end(rtable *t, struct rt_import_request *);
void rt_modify_stale(rtable *t, struct rt_import_request *);
void rt_schedule_prune(rtable *t);
void rte_dump(struct rte_storage *);
void rte_free(struct rte_storage *, rtable *);
struct rte_storage *rte_store(const rte *, net *net, rtable *);
void rt_dump(rtable *);
void rt_dump_all(void);
int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c);
void rt_dump_hooks(rtable *);
void rt_dump_hooks_all(void);
int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(struct channel *c);
void rt_refeed_channel(struct channel *c);
void rt_prune_sync(rtable *t, int all);
int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, struct rte_storage **old_exported);
int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
int rte_update_out(struct channel *c, const net_addr *n, rte *new, const rte *old, struct rte_storage **old_exported);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
static inline int rt_is_ip(rtable *tab)
@ -418,6 +538,7 @@ struct rt_show_data {
struct channel *export_channel;
struct config *running_on_config;
struct krt_proto *kernel;
struct rt_export_hook *kernel_export_hook;
int export_mode, addr_mode, primary_only, filtered, stats;
int table_open; /* Iteration (fit) is open */

View File

@ -99,6 +99,29 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
rta_show(c, a);
}
static uint
rte_feed_count(net *n)
{
uint count = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
count++;
return count;
}
static void
rte_feed_obtain(net *n, rte **feed, uint count)
{
uint i = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
{
ASSERT_DIE(i < count);
feed[i++] = &e->rte;
}
ASSERT_DIE(i == count);
}
static void
rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
@ -128,7 +151,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
struct rte e = er->rte;
/* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN))
if (ec && !ec->out_req.hook)
goto skip;
if (d->export_mode == RSEM_EXPORTED)
@ -143,7 +166,14 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
/* Special case for merged export */
pass = 1;
rte *em = rt_export_merged_show(ec, n, c->show_pool);
uint count = rte_feed_count(n);
if (!count)
goto skip;
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(n, feed, count);
rte *em = rt_export_merged(ec, feed, count, c->show_pool, 1);
if (em)
e = *em;
else
@ -369,7 +399,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
{
WALK_LIST(c, d->export_protocol->channels)
{
if (c->export_state == ES_DOWN)
if (!c->out_req.hook)
continue;
tab = rt_show_add_table(d, c->table);

File diff suppressed because it is too large Load Diff

View File

@ -760,25 +760,25 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
{
case BGP_GRS_NONE:
c->gr_active = BGP_GRS_ACTIVE;
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
break;
case BGP_GRS_ACTIVE:
rt_refresh_end(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
rt_refresh_begin(c->c.table, &c->c.in_req);
break;
case BGP_GRS_LLGR:
rt_refresh_begin(c->c.table, &c->c);
rt_modify_stale(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
rt_modify_stale(c->c.table, &c->c.in_req);
break;
}
}
else
{
/* Just flush the routes */
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
rt_refresh_end(c->c.table, &c->c.in_req);
}
/* Reset bucket and prefix tables */
@ -819,7 +819,7 @@ bgp_graceful_restart_done(struct bgp_channel *c)
BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
tm_stop(c->stale_timer);
rt_refresh_end(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
}
/**
@ -861,7 +861,7 @@ bgp_graceful_restart_timeout(timer *t)
/* Channel is in GR, and supports LLGR -> start LLGR */
c->gr_active = BGP_GRS_LLGR;
tm_start(c->stale_timer, c->stale_time S);
rt_modify_stale(c->c.table, &c->c);
rt_modify_stale(c->c.table, &c->c.in_req);
}
}
else
@ -899,10 +899,10 @@ bgp_refresh_begin(struct bgp_channel *c)
{ log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; }
c->load_state = BFS_REFRESHING;
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
if (c->c.in_table)
rt_refresh_begin(c->c.in_table, &c->c);
rt_refresh_begin(c->c.in_table, &c->c.in_req);
}
/**
@ -923,7 +923,7 @@ bgp_refresh_end(struct bgp_channel *c)
{ log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; }
c->load_state = BFS_NONE;
rt_refresh_end(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
if (c->c.in_table)
rt_prune_sync(c->c.in_table, 0);

View File

@ -82,7 +82,7 @@ pipe_preexport(struct channel *c, rte *e)
struct pipe_proto *p = (void *) c->proto;
/* Avoid direct loopbacks */
if (e->sender == c)
if (e->sender == c->in_req.hook)
return -1;
/* Indirection check */
@ -212,10 +212,18 @@ pipe_get_status(struct proto *P, byte *buf)
static void
pipe_show_stats(struct pipe_proto *p)
{
struct import_stats *s1i = &p->pri->import_stats;
struct export_stats *s1e = &p->pri->export_stats;
struct import_stats *s2i = &p->sec->import_stats;
struct export_stats *s2e = &p->sec->export_stats;
struct channel_import_stats *s1i = &p->pri->import_stats;
struct channel_export_stats *s1e = &p->pri->export_stats;
struct channel_import_stats *s2i = &p->sec->import_stats;
struct channel_export_stats *s2e = &p->sec->export_stats;
struct rt_import_stats *rs1i = p->pri->in_req.hook ? &p->pri->in_req.hook->stats : NULL;
struct rt_export_stats *rs1e = p->pri->out_req.hook ? &p->pri->out_req.hook->stats : NULL;
struct rt_import_stats *rs2i = p->sec->in_req.hook ? &p->sec->in_req.hook->stats : NULL;
struct rt_export_stats *rs2e = p->sec->out_req.hook ? &p->sec->out_req.hook->stats : NULL;
u32 pri_routes = p->pri->in_limit.count;
u32 sec_routes = p->sec->in_limit.count;
/*
* Pipe stats (as anything related to pipes) are a bit tricky. There
@ -239,24 +247,22 @@ pipe_show_stats(struct pipe_proto *p)
*/
cli_msg(-1006, " Routes: %u imported, %u exported",
s1i->routes, s2i->routes);
pri_routes, sec_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
s2e->updates_received, s2e->updates_rejected + s1i->updates_invalid,
s2e->updates_filtered, s1i->updates_ignored, s1i->updates_accepted);
rs2e->updates_received, s2e->updates_rejected + s1i->updates_invalid,
s2e->updates_filtered, rs1i->updates_ignored, rs1i->updates_accepted);
cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
s2e->withdraws_received, s1i->withdraws_invalid,
s1i->withdraws_ignored, s1i->withdraws_accepted);
rs2e->withdraws_received, s1i->withdraws_invalid,
rs1i->withdraws_ignored, rs1i->withdraws_accepted);
cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
s1e->updates_received, s1e->updates_rejected + s2i->updates_invalid,
s1e->updates_filtered, s2i->updates_ignored, s2i->updates_accepted);
rs1e->updates_received, s1e->updates_rejected + s2i->updates_invalid,
s1e->updates_filtered, rs2i->updates_ignored, rs2i->updates_accepted);
cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
s1e->withdraws_received, s2i->withdraws_invalid,
s2i->withdraws_ignored, s2i->withdraws_accepted);
rs1e->withdraws_received, s2i->withdraws_invalid,
rs2i->withdraws_ignored, rs2i->withdraws_accepted);
}
static const char *pipe_feed_state[] = { [ES_DOWN] = "down", [ES_FEEDING] = "feed", [ES_READY] = "up" };
static void
pipe_show_proto_info(struct proto *P)
{
@ -265,13 +271,17 @@ pipe_show_proto_info(struct proto *P)
cli_msg(-1006, " Channel %s", "main");
cli_msg(-1006, " Table: %s", p->pri->table->name);
cli_msg(-1006, " Peer table: %s", p->sec->table->name);
cli_msg(-1006, " Import state: %s", pipe_feed_state[p->sec->export_state]);
cli_msg(-1006, " Export state: %s", pipe_feed_state[p->pri->export_state]);
cli_msg(-1006, " Import state: %s", rt_export_state_name(rt_export_get_state(p->sec->out_req.hook)));
cli_msg(-1006, " Export state: %s", rt_export_state_name(rt_export_get_state(p->pri->out_req.hook)));
cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
channel_show_limit(&p->pri->in_limit, "Import limit:");
channel_show_limit(&p->sec->in_limit, "Export limit:");
channel_show_limit(&p->pri->in_limit, "Import limit:",
(p->pri->limit_active & (1 << PLD_IN)), p->pri->limit_actions[PLD_IN]);
channel_show_limit(&p->sec->in_limit, "Export limit:",
(p->sec->limit_active & (1 << PLD_IN)), p->sec->limit_actions[PLD_IN]);
if (P->proto_state != PS_DOWN)
pipe_show_stats(p);

View File

@ -661,9 +661,9 @@ rpki_handle_cache_response_pdu(struct rpki_cache *cache, const struct pdu_cache_
* a refresh cycle.
*/
if (cache->p->roa4_channel)
rt_refresh_begin(cache->p->roa4_channel->table, cache->p->roa4_channel);
rt_refresh_begin(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req);
if (cache->p->roa6_channel)
rt_refresh_begin(cache->p->roa6_channel->table, cache->p->roa6_channel);
rt_refresh_begin(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req);
cache->p->refresh_channels = 1;
}
@ -846,9 +846,9 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
{
cache->p->refresh_channels = 0;
if (cache->p->roa4_channel)
rt_refresh_end(cache->p->roa4_channel->table, cache->p->roa4_channel);
rt_refresh_end(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req);
if (cache->p->roa6_channel)
rt_refresh_end(cache->p->roa6_channel->table, cache->p->roa6_channel);
rt_refresh_end(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req);
}
cache->last_update = current_time();
@ -924,6 +924,9 @@ rpki_rx_hook(struct birdsock *sk, uint size)
struct rpki_cache *cache = sk->data;
struct rpki_proto *p = cache->p;
if ((p->p.proto_state == PS_DOWN) || (p->cache != cache))
return 0;
byte *pkt_start = sk->rbuf;
byte *end = pkt_start + size;
@ -980,6 +983,8 @@ rpki_err_hook(struct birdsock *sk, int error_num)
CACHE_TRACE(D_EVENTS, cache, "The other side closed a connection");
}
if (cache->p->cache != cache)
return;
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
}
@ -999,6 +1004,9 @@ rpki_tx_hook(sock *sk)
{
struct rpki_cache *cache = sk->data;
if (cache->p->cache != cache)
return;
while (rpki_fire_tx(cache) > 0)
;
}
@ -1008,6 +1016,9 @@ rpki_connected_hook(sock *sk)
{
struct rpki_cache *cache = sk->data;
if (cache->p->cache != cache)
return;
CACHE_TRACE(D_EVENTS, cache, "Connected");
proto_notify_state(&cache->p->p, PS_UP);

View File

@ -384,6 +384,9 @@ rpki_refresh_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
if (cache->p->cache != cache)
return;
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
switch (cache->state)
@ -430,6 +433,9 @@ rpki_retry_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
if (cache->p->cache != cache)
return;
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
switch (cache->state)
@ -475,6 +481,9 @@ rpki_expire_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
if (cache->p->cache != cache)
return;
if (!cache->last_update)
return;

View File

@ -507,19 +507,13 @@ static_shutdown(struct proto *P)
WALK_LIST(r, cf->routes)
static_reset_rte(p, r);
return PS_DOWN;
}
static void
static_cleanup(struct proto *P)
{
struct static_proto *p = (void *) P;
if (p->igp_table_ip4)
rt_unlock_table(p->igp_table_ip4);
if (p->igp_table_ip6)
rt_unlock_table(p->igp_table_ip6);
return PS_DOWN;
}
static void
@ -777,7 +771,6 @@ struct protocol proto_static = {
.dump = static_dump,
.start = static_start,
.shutdown = static_shutdown,
.cleanup = static_cleanup,
.reconfigure = static_reconfigure,
.copy_config = static_copy_config,
.get_route_info = static_get_route_info,

View File

@ -542,21 +542,27 @@ krt_is_installed(struct krt_proto *p, net *n)
return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->rte.id);
}
static void
krt_flush_routes(struct krt_proto *p)
static uint
rte_feed_count(net *n)
{
struct rtable *t = p->p.main_channel->table;
uint count = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
count++;
return count;
}
KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
FIB_WALK(&t->fib, net, n)
static void
rte_feed_obtain(net *n, rte **feed, uint count)
{
if (krt_is_installed(p, n))
uint i = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
{
/* FIXME: this does not work if gw is changed in export filter */
krt_replace_rte(p, n->n.addr, NULL, &n->routes->rte);
ASSERT_DIE(i < count);
feed[i++] = &e->rte;
}
}
FIB_WALK_END;
ASSERT_DIE(i == count);
}
static struct rte *
@ -566,7 +572,15 @@ krt_export_net(struct krt_proto *p, net *net)
const struct filter *filter = c->out_filter;
if (c->ra_mode == RA_MERGED)
return rt_export_merged_show(c, net, krt_filter_lp);
{
uint count = rte_feed_count(net);
if (!count)
return NULL;
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(net, feed, count);
return rt_export_merged(c, feed, count, krt_filter_lp, 1);
}
static _Thread_local rte rt;
rt = net->routes->rte;
@ -637,6 +651,9 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src)
#endif
/* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
/* Deleting all routes if flush is requested */
if (p->flush_routes)
goto delete;
/* We wait for the initial feed to have correct installed state */
if (!p->ready)
@ -729,6 +746,17 @@ krt_prune(struct krt_proto *p)
p->initialized = 1;
}
static void
krt_flush_routes(struct krt_proto *p)
{
KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
p->flush_routes = 1;
krt_init_scan(p);
krt_do_scan(p);
/* No prune! */
p->flush_routes = 0;
}
void
krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
{

View File

@ -66,6 +66,7 @@ struct krt_proto {
byte ready; /* Initial feed has been finished */
byte initialized; /* First scan has been finished */
byte reload; /* Next scan is doing reload */
byte flush_routes; /* Scanning to flush */
};
extern pool *krt_pool;