summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/config.Y11
-rw-r--r--nest/proto.c26
-rw-r--r--nest/protocol.h3
-rw-r--r--nest/route.h1
-rw-r--r--nest/rt-table.c90
5 files changed, 130 insertions, 1 deletions
diff --git a/nest/config.Y b/nest/config.Y
index e1a932d4..c62501a3 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -567,6 +567,17 @@ r_args:
rt_show_add_table($$, c->in_table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
+ | r_args EXPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
+ cf_assert_symbol($4, SYM_PROTO);
+ $$ = $1;
+ struct proto_config *cf = $4->proto;
+ if (!cf->proto) cf_error("%s is not a protocol", $4->name);
+ struct channel *c = proto_find_channel_by_name(cf->proto, $6);
+ if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
+ if (!c->out_table) cf_error("No export table in channel %s.%s", $4->name, $6);
+ rt_show_add_table($$, c->out_table);
+ $$->tables_defined_by = RSD_TDB_DIRECT;
+ }
| r_args FILTER filter {
$$ = $1;
if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice");
diff --git a/nest/proto.c b/nest/proto.c
index 18cf214b..54955bdd 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -317,6 +317,13 @@ channel_reset_import(struct channel *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)
@@ -331,6 +338,18 @@ channel_setup_in_table(struct channel *c)
c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
}
+/* Called by protocol to activate out_table */
+void
+channel_setup_out_table(struct channel *c)
+{
+ struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
+ cf->name = "export";
+ cf->addr_type = c->net_type;
+
+ c->out_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
+ rt_setup(c->proto->pool, c->out_table, cf);
+}
+
static void
channel_do_start(struct channel *c)
@@ -376,6 +395,7 @@ channel_do_down(struct channel *c)
c->in_table = NULL;
c->reload_event = NULL;
+ c->out_table = NULL;
CALL(c->channel->cleanup, c);
@@ -411,6 +431,9 @@ channel_set_state(struct channel *c, uint state)
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:
@@ -433,6 +456,9 @@ channel_set_state(struct channel *c, uint state)
if (c->in_table && (cs == CS_UP))
channel_reset_import(c);
+ if (c->out_table && (cs == CS_UP))
+ channel_reset_export(c);
+
channel_do_flush(c);
break;
diff --git a/nest/protocol.h b/nest/protocol.h
index 297afba7..17af2e17 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -539,6 +539,8 @@ struct channel {
struct event *reload_event; /* Event responsible for reloading from in_table */
struct fib_iterator reload_fit; /* Iterator in in_table used during reloading */
u8 reload_active; /* Iterator reload_fit is linked */
+
+ struct rtable *out_table; /* Internal table for exported routes */
};
@@ -607,6 +609,7 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
void channel_set_state(struct channel *c, uint state);
void channel_setup_in_table(struct channel *c);
+void channel_setup_out_table(struct channel *c);
void channel_schedule_reload(struct channel *c);
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
diff --git a/nest/route.h b/nest/route.h
index 8d799454..f4f32ce0 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -320,6 +320,7 @@ int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src
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, const net_addr *n, rte *new, rte *old0, int refeed);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 53f5d979..ff995cce 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -633,7 +633,6 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
struct proto *p = c->proto;
struct proto_stats *stats = &c->stats;
-
/*
* First, apply export limit.
*
@@ -679,6 +678,8 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
}
}
+ if (c->out_table && !rte_update_out(c, net->n.addr, new, old, refeed))
+ return;
if (new)
stats->exp_updates_accepted++;
@@ -2507,6 +2508,10 @@ rt_feed_channel_abort(struct channel *c)
}
+/*
+ * Import table
+ */
+
int
rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
@@ -2668,6 +2673,89 @@ rt_prune_sync(rtable *t, int all)
}
+/*
+ * Export table
+ */
+
+int
+rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed)
+{
+ struct rtable *tab = c->out_table;
+ struct rte_src *src;
+ rte *old, **pos;
+ net *net;
+
+ if (new)
+ {
+ net = net_get(tab, n);
+ src = new->attrs->src;
+ }
+ else
+ {
+ net = net_find(tab, n);
+ src = old0->attrs->src;
+
+ if (!net)
+ goto drop_withdraw;
+ }
+
+ /* Find the old rte */
+ for (pos = &net->routes; old = *pos; pos = &old->next)
+ if (old->attrs->src == src)
+ {
+ if (new && rte_same(old, new))
+ {
+ /* REF_STALE / REF_DISCARD not used in export table */
+ /*
+ if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
+ {
+ old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+ return 1;
+ }
+ */
+
+ goto drop_update;
+ }
+
+ /* Remove the old rte */
+ *pos = old->next;
+ rte_free_quick(old);
+ tab->rt_count--;
+
+ break;
+ }
+
+ if (!new)
+ {
+ if (!old)
+ goto drop_withdraw;
+
+ return 1;
+ }
+
+ /* Insert the new rte */
+ rte *e = rte_do_cow(new);
+ e->flags |= REF_COW;
+ e->net = net;
+ e->sender = c;
+ e->lastmod = current_time();
+ e->next = *pos;
+ *pos = e;
+ tab->rt_count++;
+ return 1;
+
+drop_update:
+ return refeed;
+
+drop_withdraw:
+ return 0;
+}
+
+
+/*
+ * Hostcache
+ */
+
static inline u32
hc_hash(ip_addr a, rtable *dep)
{