summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/config.Y6
-rw-r--r--nest/proto.c8
-rw-r--r--nest/protocol.h2
-rw-r--r--nest/rt-table.c89
-rw-r--r--nest/rt.h3
5 files changed, 91 insertions, 17 deletions
diff --git a/nest/config.Y b/nest/config.Y
index ea7e1266..c3f2532f 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -306,6 +306,12 @@ channel_item_:
this_channel->table = $2;
}
| IMPORT imexport { this_channel->in_filter = $2; }
+ | EXPORT IN net_any imexport {
+ if (this_channel->net_type && ($3->type != this_channel->net_type))
+ cf_error("Incompatible export prefilter type");
+ this_channel->out_subprefix = $3;
+ this_channel->out_filter = $4;
+ }
| EXPORT imexport { this_channel->out_filter = $2; }
| RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
| IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
diff --git a/nest/proto.c b/nest/proto.c
index 8513b9cb..3792fde4 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -214,6 +214,7 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
+ c->out_subprefix = cf->out_subprefix;
channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
@@ -467,6 +468,7 @@ channel_start_export(struct channel *c)
c->out_req = (struct rt_export_request) {
.name = rn,
+ .addr_in = c->out_subprefix,
.trace_routes = c->debug | c->proto->debug,
.dump_req = channel_dump_export_req,
.log_state_change = channel_export_log_state_change,
@@ -922,7 +924,10 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* FIXME: better handle these changes, also handle in_keep_filtered */
if ((c->table != cf->table->table) ||
(cf->ra_mode && (c->ra_mode != cf->ra_mode)) ||
- (cf->in_keep != c->in_keep))
+ (cf->in_keep != c->in_keep) ||
+ cf->out_subprefix && c->out_subprefix &&
+ !net_equal(cf->out_subprefix, c->out_subprefix) ||
+ (!cf->out_subprefix != !c->out_subprefix))
return 0;
/* Note that filter_same() requires arguments in (new, old) order */
@@ -947,6 +952,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
// c->ra_mode = cf->ra_mode;
c->merge_limit = cf->merge_limit;
c->preference = cf->preference;
+ c->out_req.addr_in = c->out_subprefix = cf->out_subprefix;
c->debug = cf->debug;
c->in_req.trace_routes = c->out_req.trace_routes = c->debug | c->proto->debug;
c->rpki_reload = cf->rpki_reload;
diff --git a/nest/protocol.h b/nest/protocol.h
index b482ed99..a1701a7e 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -452,6 +452,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 */
+ const net_addr *out_subprefix; /* Export only subprefixes of this net */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep & RIK_REJECTED) */
@@ -477,6 +478,7 @@ struct channel {
struct rtable *table;
const struct filter *in_filter; /* Input filter */
const struct filter *out_filter; /* Output filter */
+ const net_addr *out_subprefix; /* Export only subprefixes of this net */
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 */
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 14c80138..afafef62 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -126,7 +126,9 @@ static inline void rt_flowspec_resolve_rte(rte *r, struct channel *c);
static inline void rt_prune_table(rtable *tab);
static inline void rt_schedule_notify(rtable *tab);
static void rt_flowspec_notify(rtable *tab, net *net);
-static void rt_feed_channel(void *);
+static void rt_feed_by_fib(void *);
+static void rt_feed_by_trie(void *);
+static uint rt_feed_net(struct rt_export_hook *c, net *n);
const char *rt_import_state_name_array[TIS_MAX] = {
[TIS_DOWN] = "DOWN",
@@ -1187,6 +1189,9 @@ rte_announce(rtable *tab, net *net, struct rte_storage *new, struct rte_storage
if (eh->export_state == TES_STOP)
continue;
+ if (eh->req->addr_in && !net_in_netX(net->n.addr, eh->req->addr_in))
+ continue;
+
if (new)
eh->stats.updates_received++;
else
@@ -1768,13 +1773,21 @@ rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
hook->lp = lp_new_default(p);
/* stats zeroed by mb_allocz */
-
- FIB_ITERATE_INIT(&hook->feed_fit, &tab->fib);
+ if (tab->trie && req->addr_in && net_val_match(tab->addr_type, NB_IP))
+ {
+ hook->walk_state = mb_allocz(p, sizeof (struct f_trie_walk_state));
+ hook->walk_lock = rt_lock_trie(tab);
+ trie_walk_init(hook->walk_state, tab->trie, req->addr_in);
+ hook->event = ev_new_init(p, rt_feed_by_trie, hook);
+ }
+ else
+ {
+ FIB_ITERATE_INIT(&hook->feed_fit, &tab->fib);
+ hook->event = ev_new_init(p, rt_feed_by_fib, hook);
+ }
DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count);
- hook->event = ev_new_init(p, rt_feed_channel, hook);
-
return hook;
}
@@ -3154,7 +3167,7 @@ rt_commit(struct config *new, struct config *old)
}
/**
- * rt_feed_channel - advertise all routes to a channel
+ * rt_feed_by_fib - advertise all routes to a channel by walking a fib
* @c: channel to be fed
*
* This function performs one pass of advertisement of routes to a channel that
@@ -3163,7 +3176,7 @@ rt_commit(struct config *new, struct config *old)
* order not to monopolize CPU time.)
*/
static void
-rt_feed_channel(void *data)
+rt_feed_by_fib(void *data)
{
struct rt_export_hook *c = data;
@@ -3183,9 +3196,55 @@ rt_feed_channel(void *data)
return;
}
- if (c->export_state != TES_FEEDING)
- goto done;
+ ASSERT(c->export_state == TES_FEEDING);
+
+ if (!c->req->addr_in || net_in_netX(n->n.addr, c->req->addr_in))
+ max_feed -= rt_feed_net(c, n);
+ }
+ FIB_ITERATE_END;
+
+ rt_set_export_state(c, TES_READY);
+}
+
+static void
+rt_feed_by_trie(void *data)
+{
+ struct rt_export_hook *c = data;
+ rtable *tab = SKIP_BACK(rtable, exporter, c->table);
+
+ ASSERT_DIE(c->walk_state);
+ struct f_trie_walk_state *ws = c->walk_state;
+
+ int max_feed = 256;
+
+ ASSERT_DIE(c->export_state == TES_FEEDING);
+
+ net_addr addr;
+ while (trie_walk_next(ws, &addr))
+ {
+ net *n = net_find(tab, &addr);
+ if (!n)
+ continue;
+
+ if ((max_feed -= rt_feed_net(c, n)) <= 0)
+ return;
+
+ ASSERT_DIE(c->export_state == TES_FEEDING);
+ }
+ rt_unlock_trie(tab, c->walk_lock);
+ c->walk_lock = NULL;
+
+ mb_free(c->walk_state);
+ c->walk_state = NULL;
+
+ rt_set_export_state(c, TES_READY);
+}
+
+
+static uint
+rt_feed_net(struct rt_export_hook *c, net *n)
+{
if (c->req->export_bulk)
{
uint count = rte_feed_count(n);
@@ -3196,23 +3255,21 @@ rt_feed_channel(void *data)
rte_feed_obtain(n, feed, count);
struct rt_pending_export rpe = { .new_best = n->routes };
c->req->export_bulk(c->req, n->n.addr, &rpe, feed, count);
- max_feed -= count;
rte_update_unlock();
}
+ return count;
}
- else if (n->routes && rte_is_valid(&n->routes->rte))
+
+ if (n->routes && rte_is_valid(&n->routes->rte))
{
rte_update_lock();
struct rt_pending_export rpe = { .new = n->routes, .new_best = n->routes };
c->req->export_one(c->req, n->n.addr, &rpe);
- max_feed--;
rte_update_unlock();
+ return 1;
}
- }
- FIB_ITERATE_END;
-done:
- rt_set_export_state(c, TES_READY);
+ return 0;
}
diff --git a/nest/rt.h b/nest/rt.h
index 0f5a5ba7..57e117da 100644
--- a/nest/rt.h
+++ b/nest/rt.h
@@ -212,6 +212,7 @@ struct rt_pending_export {
struct rt_export_request {
struct rt_export_hook *hook; /* Table part of the export */
+ const net_addr *addr_in; /* Subnet export request */
char *name;
u8 trace_routes;
@@ -245,6 +246,8 @@ struct rt_export_hook {
} stats;
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
+ struct f_trie_walk_state *walk_state; /* Iterator over networks in trie */
+ struct f_trie *walk_lock; /* Locked trie for walking */
btime last_state_change; /* Time of last state transition */