mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Table import and export are now explicit hooks.
Channels have now included rt_import_req and rt_export_req to hook into the table instead of just one list node. This will (in future) allow for: * channel import and export bound to different tables * more efficient pipe code (dropping most of the channel code) * conversion of 'show route' to a special kind of export * temporary static routes from CLI The import / export states are also updated to the new algorithms.
This commit is contained in:
parent
3a8197a9dc
commit
f81702b7e4
@ -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 {
|
||||
debug("\n");
|
||||
f_dump_line(c->in_filter->root, 2);
|
||||
}
|
||||
channel_filter_dump(c->in_filter);
|
||||
debug(" EXPORT", c->name, net_label[c->net_type]);
|
||||
channel_filter_dump(c->out_filter);
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -820,8 +820,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]])
|
||||
|
521
nest/proto.c
521
nest/proto.c
@ -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;
|
||||
|
||||
@ -55,12 +54,14 @@ static char *proto_state_name(struct proto *p);
|
||||
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,10 +69,46 @@ 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->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
|
||||
@ -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,6 +211,7 @@ 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;
|
||||
@ -182,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;
|
||||
|
||||
@ -204,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);
|
||||
}
|
||||
@ -224,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
|
||||
@ -233,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
|
||||
@ -244,69 +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 */
|
||||
if (c->refeeding &&
|
||||
(c->limit_active & (1 << PLD_OUT)) &&
|
||||
(c->refeed_count <= c->out_limit.max))
|
||||
{
|
||||
log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, c->out_limit.max);
|
||||
channel_reset_limit(c, &c->out_limit, PLD_OUT);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@ -325,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" : "");
|
||||
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 */
|
||||
@ -444,34 +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);
|
||||
if (c->out_req.hook)
|
||||
{
|
||||
log(L_WARN "%s.%s: Attempted to start channel's already started export", c->proto->name, c->name);
|
||||
return;
|
||||
}
|
||||
|
||||
c->export_state = ES_DOWN;
|
||||
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);
|
||||
bmap_reset(&c->export_map, 1024);
|
||||
bmap_reset(&c->export_reject_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)
|
||||
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, &c->rx_limit, PLD_RX);
|
||||
channel_reset_limit(c, &c->in_limit, PLD_IN);
|
||||
channel_reset_limit(c, &c->out_limit, PLD_OUT);
|
||||
|
||||
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->in_limit.count || c->rx_limit.count)
|
||||
bug("%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");
|
||||
@ -861,6 +955,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
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;
|
||||
|
||||
@ -975,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);
|
||||
@ -1528,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 */
|
||||
@ -1618,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)
|
||||
@ -1628,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))
|
||||
@ -1933,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))
|
||||
@ -2029,8 +2129,16 @@ 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;
|
||||
@ -2038,24 +2146,31 @@ channel_show_stats(struct channel *c)
|
||||
|
||||
if (c->in_keep_filtered)
|
||||
cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
|
||||
in_routes, (rx_routes - in_routes), out_routes, is->pref);
|
||||
in_routes, (rx_routes - in_routes), out_routes, SRI(pref));
|
||||
else
|
||||
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
|
||||
in_routes, out_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
|
||||
@ -2073,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));
|
||||
|
@ -77,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_*) */
|
||||
@ -133,30 +132,6 @@ struct proto_config {
|
||||
};
|
||||
|
||||
/* Protocol statistics */
|
||||
struct import_stats {
|
||||
/* Import - from protocol to core */
|
||||
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 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 */
|
||||
@ -512,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;
|
||||
@ -531,10 +505,28 @@ struct channel {
|
||||
u8 limit_actions[PLD_MAX]; /* Limit actions enum */
|
||||
u8 limit_active; /* Flags for active limits */
|
||||
|
||||
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 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 */
|
||||
|
||||
@ -548,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 */
|
||||
@ -599,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)
|
||||
@ -644,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);
|
||||
|
145
nest/route.h
145
nest/route.h
@ -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;
|
||||
@ -160,12 +162,12 @@ typedef struct rtable {
|
||||
struct slab *rte_slab; /* Slab to allocate route objects */
|
||||
struct fib 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;
|
||||
@ -182,6 +184,7 @@ typedef struct rtable {
|
||||
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||
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 tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
|
||||
@ -238,7 +241,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 */
|
||||
@ -262,12 +265,127 @@ 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 *r) { return r && !(r->flags & REF_FILTERED); }
|
||||
static inline int rte_is_valid(const rte *r) { return r && !(r->flags & REF_FILTERED); }
|
||||
|
||||
/* 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 */
|
||||
@ -281,6 +399,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
|
||||
@ -335,23 +454,24 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
|
||||
void *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);
|
||||
|
||||
|
||||
@ -377,6 +497,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, primary_only, filtered, stats, show_for;
|
||||
|
||||
int table_open; /* Iteration (fit) is open */
|
||||
|
@ -98,6 +98,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
|
||||
@ -315,7 +345,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);
|
||||
|
849
nest/rt-table.c
849
nest/rt-table.c
File diff suppressed because it is too large
Load Diff
@ -775,25 +775,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 */
|
||||
@ -834,7 +834,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -876,7 +876,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
|
||||
@ -914,10 +914,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -938,7 +938,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);
|
||||
|
@ -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,15 @@ 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;
|
||||
@ -245,21 +250,19 @@ pipe_show_stats(struct pipe_proto *p)
|
||||
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)
|
||||
{
|
||||
@ -268,8 +271,8 @@ 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));
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
@ -819,9 +819,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();
|
||||
|
@ -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,
|
||||
|
@ -542,6 +542,29 @@ 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 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 struct rte *
|
||||
krt_export_net(struct krt_proto *p, net *net)
|
||||
{
|
||||
@ -549,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;
|
||||
|
Loading…
Reference in New Issue
Block a user