static void
channel_reset_export(struct channel *c)
{
+ if (c->out_flush_refeed)
+ rt_out_flush(c);
+
/* Just free the routes */
rt_prune_sync(c->out_table, 1);
+ bmap_reset(&c->out_seen_map, 1024);
}
/* Called by protocol to activate in_table */
bmap_init(&c->export_map, c->proto->pool, 1024);
bmap_init(&c->export_reject_map, c->proto->pool, 1024);
+
+ if (c->out_table)
+ bmap_init(&c->out_seen_map, c->proto->pool, 1024);
+
memset(&c->stats, 0, sizeof(struct proto_stats));
channel_reset_limit(&c->rx_limit);
/* 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);
+ if (c->out_table)
+ bmap_free(&c->out_seen_map);
c->in_table = NULL;
c->reload_event = NULL;
c->out_table = NULL;
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 bmap out_seen_map; /* Keeps track which routes have been seen by out_sync */
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 */
u8 reload_pending; /* Reloading and another reload is scheduled */
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
u8 rpki_reload; /* RPKI changes trigger channel reload */
+ u8 out_flush_refeed; /* Feed by withdrawals on export reset */
list net_feed; /* Active net feeders (struct channel_net_feed) */
int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(struct channel *c);
void rt_prune_sync(rtable *t, int all);
-int rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, int refeed);
+int rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, u32 id, int refeed);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
+void rt_out_sync_start(struct channel *c);
+_Bool rt_out_sync_mark(struct channel *c, struct rte_export *e);
+void rt_out_sync_finish(struct channel *c);
+void rt_out_flush(struct channel *c);
/* Default limit for ECMP next hops, defined in sysdep code */
/* Prepare new merged route */
if (e->new_best)
{
- ep->new_id = e->new->id;
+ ep->new_id = e->new_best->id;
if (!rt_export_merged(c, e->net, &ep->new, rte_update_pool, 0))
ep->new.attrs = NULL;
}
/* Apply export table */
if (c->out_table)
{
- if (!rte_update_out(c, &(e->pub.new), &(e->pub.old), &(e->old_stored), e->refeed))
+ if (!rte_update_out(c, &(e->pub.new), &(e->pub.old), &(e->old_stored), e->pub.new_id, e->refeed))
return 0;
}
else if (c->out_filter != FILTER_ACCEPT)
*/
int
-rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, int refeed)
+rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, u32 id, int refeed)
{
struct rtable *tab = c->out_table;
struct rte_storage **pos;
struct rte_storage *e = rte_store(new, net);
e->sender = c;
e->lastmod = current_time();
+ e->id = id;
e->next = *pos;
*pos = e;
tab->rt_count++;
return 0;
}
+void
+rt_out_sync_start(struct channel *c)
+{
+ ASSERT_DIE(c->out_table);
+ ASSERT_DIE(c->ra_mode != RA_ANY);
+ bmap_reset(&c->out_seen_map, 1024);
+}
+
+_Bool
+rt_out_sync_mark(struct channel *c, struct rte_export *e)
+{
+ ASSERT_DIE(c->out_table);
+ ASSERT_DIE(c->ra_mode != RA_ANY);
+
+ net *n = net_find(c->out_table, e->old.net);
+ if (!n || !n->routes)
+ return 1;
+
+ e->new = rte_copy(n->routes);
+ e->new_id = n->routes->id;
+
+ if (bmap_test(&c->out_seen_map, n->routes->id))
+ return 0;
+
+ bmap_set(&c->out_seen_map, n->routes->id);
+ return 1;
+}
+
+void
+rt_out_sync_finish(struct channel *c)
+{
+ ASSERT_DIE(c->out_table);
+ ASSERT_DIE(c->ra_mode != RA_ANY);
+
+ FIB_WALK(&c->out_table->fib, net, n)
+ {
+ if (!n->routes)
+ continue;
+
+ if (!bmap_test(&c->out_seen_map, n->routes->id))
+ {
+ struct rte_export ex = {
+ .new_id = n->routes->id,
+ .new = rte_copy(n->routes),
+ };
+
+ c->proto->rt_notify(c, &ex);
+ }
+ }
+ FIB_WALK_END;
+ bmap_reset(&c->out_seen_map, 1024);
+}
+
+void
+rt_out_flush(struct channel *c)
+{
+ ASSERT_DIE(c->out_table);
+ ASSERT_DIE(c->ra_mode != RA_ANY);
+
+ FIB_WALK(&c->out_table->fib, net, n)
+ {
+ if (!n->routes)
+ continue;
+
+ struct rte_export ex = {
+ .old_id = n->routes->id,
+ .old = rte_copy(n->routes),
+ };
+
+ c->proto->rt_notify(c, &ex);
+ }
+ FIB_WALK_END;
+}
/*
* Hostcache
*/
pool *krt_pool;
-static linpool *krt_filter_lp;
static list krt_proto_list;
void
krt_io_init(void)
{
krt_pool = rp_new(&root_pool, "Kernel Syncer");
- krt_filter_lp = lp_new_default(krt_pool);
init_list(&krt_proto_list);
krt_sys_io_init();
}
* Routes
*/
-static inline int
-krt_is_installed(struct krt_proto *p, net *n)
-{
- return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->id);
-}
-
-static void
-krt_flush_routes(struct krt_proto *p)
-{
- struct rtable *t = p->p.main_channel->table;
-
- KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
- FIB_WALK(&t->fib, net, n)
- {
- if (krt_is_installed(p, n))
- {
- struct rte_export e = {
- .old = rte_copy(n->routes),
- .old_id = n->routes->id,
- };
-
- /* FIXME: this does not work if gw is changed in export filter */
- krt_replace_rte(p, &e);
- }
- }
- FIB_WALK_END;
-}
-
-static _Bool
-krt_export_net(struct krt_proto *p, net *net, rte *rt)
-{
- struct channel *c = p->p.main_channel;
- const struct filter *filter = c->out_filter;
-
- if (c->ra_mode == RA_MERGED)
- return rt_export_merged(c, net, rt, krt_filter_lp, 1);
-
- if (!rte_is_valid(net->routes))
- return 0;
-
- if (filter == FILTER_REJECT)
- return 0;
-
- /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
- *rt = rte_copy(net->routes);
-
- if (filter == FILTER_ACCEPT)
- return 1;
-
- if (f_run(filter, rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
- return 0;
-
- return 1;
-}
-
static int
krt_same_dest(rte *k, rte *e)
{
if (!p->ready)
goto ignore;
- net *n = net_find(p->p.main_channel->table, e->net);
-
- if (!n || !krt_is_installed(p, n))
- goto delete;
-
- /* Rejected by filters */
- if (!krt_export_net(p, n, &ex.new))
- goto delete;
-
- /* Route to this destination was already seen. Strange, but it happens... */
- if (bmap_test(&p->seen_map, n->routes->id))
+ if (!rt_out_sync_mark(p->p.main_channel, &ex))
goto aseen;
- /* Mark route as seen */
- bmap_set(&p->seen_map, n->routes->id);
+ if (!ex.new.attrs)
+ goto delete;
/* TODO: There also may be changes in route eattrs, we ignore that for now. */
- if (!bmap_test(&p->sync_map, n->routes->id) || !krt_same_dest(e, &ex.new))
+ if (!bmap_test(&p->sync_map, ex.new_id) || !krt_same_dest(e, &ex.new))
goto update;
goto seen;
update:
krt_trace_in(p, &ex.new, "updating");
- ex.new_id = n->routes->id;
krt_replace_rte(p, &ex);
goto done;
goto done;
done:
- lp_flush(krt_filter_lp);
+ return;
}
static void
krt_init_scan(struct krt_proto *p)
{
- bmap_reset(&p->seen_map, 1024);
+ rt_out_sync_start(p->p.main_channel);
}
static void
krt_prune(struct krt_proto *p)
{
- struct rtable *t = p->p.main_channel->table;
-
- KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
- FIB_WALK(&t->fib, net, n)
- {
- if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
- {
- struct rte_export ex = {
- .new_id = n->routes->id
- };
+ KRT_TRACE(p, D_EVENTS, "Sync finished, pruning table %s", p->p.main_channel->table->name);
- if (krt_export_net(p, n, &ex.new))
- {
- krt_trace_in(p, &ex.new, "installing");
- krt_replace_rte(p, &ex);
- }
-
- lp_flush(krt_filter_lp);
- }
- }
- FIB_WALK_END;
+ p->pruning = 1;
+ rt_out_sync_finish(p->p.main_channel);
+ p->pruning = 0;
#ifdef KRT_ALLOW_LEARN
if (KRT_CF->learn)
return;
#endif
- if (p->initialized) /* Before first scan we don't touch the routes */
+ if (p->initialized || p->ready && p->pruning) /* Before first scan we don't touch the routes */
krt_replace_rte(p, e);
}
// struct krt_config *cf = (void *) CF;
p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
+ p->p.main_channel->out_flush_refeed = 1;
p->p.preexport = krt_preexport;
p->p.rt_notify = krt_rt_notify;
}
/* If it is needed, setup out table automagically */
- if (!TRIVIAL_FILTER(p->p.main_channel->out_filter))
+ /* For now, setup out table always.
+ if (!TRIVIAL_FILTER(p->p.main_channel->out_filter)) */
channel_setup_out_table(p->p.main_channel);
-
bmap_init(&p->sync_map, p->p.pool, 1024);
- bmap_init(&p->seen_map, p->p.pool, 1024);
add_tail(&krt_proto_list, &p->krt_node);
#ifdef KRT_ALLOW_LEARN
/* FIXME we should flush routes even when persist during reconfiguration */
if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
- krt_flush_routes(p);
+ rt_out_flush(p->p.main_channel);
p->ready = 0;
p->initialized = 0;
}
static int
-krt_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed UNUSED, int *export_changed)
+krt_channel_reconfigure(struct channel *C UNUSED, struct channel_config *CC UNUSED, int *import_changed UNUSED, int *export_changed)
{
- if (!*export_changed)
- return 1;
-
- return (TRIVIAL_FILTER(C->out_filter) == TRIVIAL_FILTER(CC->out_filter));
+ return !*export_changed;
}
static int
#endif
struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */
- struct bmap seen_map; /* Routes seen during last periodic scan */
node krt_node; /* Node in krt_proto_list */
byte af; /* Kernel address family (AF_*) */
byte ready; /* Initial feed has been finished */
byte initialized; /* First scan has been finished */
+ byte pruning; /* Pruning */
byte reload; /* Next scan is doing reload */
};