summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaria Matejka <mq@ucw.cz>2020-05-01 22:26:24 +0200
committerMaria Matejka <mq@ucw.cz>2021-11-09 19:20:41 +0100
commit56c8f2f03a8631417dc3b730625c08ffca42ead2 (patch)
tree665f5a2c3e0acfe8872c741a69c2485ced3e2700
parent1c2f66f2bd9b2996c8cba0604e7ac38738399000 (diff)
Nest: Route generations and explicit tracking route propagion through pipes
-rw-r--r--conf/conf.h1
-rw-r--r--doc/bird.sgml8
-rw-r--r--nest/config.Y3
-rw-r--r--nest/route.h5
-rw-r--r--nest/rt-table.c47
-rw-r--r--proto/pipe/config.Y7
-rw-r--r--proto/pipe/pipe.c32
-rw-r--r--proto/pipe/pipe.h2
8 files changed, 65 insertions, 40 deletions
diff --git a/conf/conf.h b/conf/conf.h
index 55cb9c58..69ef8a10 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -45,6 +45,7 @@ struct config {
int cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */
+ int pipe_debug; /* Track route propagation through pipes */
u32 latency_limit; /* Events with longer duration are logged (us) */
u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */
u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */
diff --git a/doc/bird.sgml b/doc/bird.sgml
index a2138b55..d1a3b70f 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -4124,6 +4124,14 @@ include standard channel config options; see the example below.
<tag><label id="pipe-peer-table">peer table <m/table/</tag>
Defines secondary routing table to connect to. The primary one is
selected by the <cf/table/ keyword.
+
+ <tag><label id="pipe-max-generation">max generation <m/expr/</tag>
+ Sets maximal generation of route that may pass through this pipe.
+ The generation value is increased by one by each pipe on its path.
+ Not meeting this requirement causes an error message complaining about
+ an overpiped route. If you have long chains of pipes, you probably want
+ to raise this value; anyway the default of 16 should be enough for even
+ most strange uses. Maximum is 254.
</descrip>
<sect1>Attributes
diff --git a/nest/config.Y b/nest/config.Y
index ef3e27c0..a1d901ab 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -111,7 +111,7 @@ proto_postconfig(void)
CF_DECLS
-CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
+CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
@@ -348,6 +348,7 @@ debug_default:
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
| DEBUG COMMANDS expr { new_config->cli_debug = $3; }
+ | DEBUG PIPE bool { new_config->pipe_debug = $3; }
;
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
diff --git a/nest/route.h b/nest/route.h
index 98d605c8..9baaeda0 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -162,7 +162,6 @@ typedef struct rtable {
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 pipe_busy; /* Pipe loop detection */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
@@ -185,6 +184,7 @@ typedef struct rtable {
byte nhu_state; /* Next Hop Update state */
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 */
list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */
@@ -243,6 +243,9 @@ typedef struct rte {
u32 id; /* Table specific route id */
byte flags; /* Table-specific flags */
byte pflags; /* Protocol-specific flags */
+ u8 generation; /* If this route import is based on other previously exported route,
+ this value should be 1 + MAX(generation of the parent routes).
+ Otherwise the route is independent and this value is zero. */
} rte;
struct rte_storage {
diff --git a/nest/rt-table.c b/nest/rt-table.c
index b005f6f3..6851b4bc 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -837,7 +837,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
struct proto *p = c->proto;
struct rtable *table = c->table;
struct proto_stats *stats = &c->stats;
- static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
struct rte_storage *old_best_stored = net->routes, *old_stored = NULL;
rte *old_best = old_best_stored ? &old_best_stored->rte : NULL;
rte *old = NULL;
@@ -849,22 +848,22 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
{
old = &(old_stored = (*before_old))->rte;
- /* If there is the same route in the routing table but from
- * a different sender, then there are two paths from the
- * source protocol to this routing table through transparent
- * pipes, which is not allowed.
- *
- * We log that and ignore the route. If it is withdraw, we
- * ignore it completely (there might be 'spurious withdraws',
- * see FIXME in do_rte_announce())
- */
- if (old->sender->proto != p)
- {
- if (new)
- log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
- net->n.addr, table->name);
- return;
- }
+ /* If there is the same route in the routing table but from
+ * a different sender, then there are two paths from the
+ * source protocol to this routing table through transparent
+ * pipes, which is not allowed.
+ * We log that and ignore the route. */
+ if (old->sender->proto != p)
+ {
+ if (!old->generation && !new->generation)
+ bug("Two protocols claim to author a route with the same rte_src in table %s: %N %s/%u:%u",
+ c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
+
+ log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u",
+ c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
+
+ return;
+ }
if (new && rte_same(old, new))
{
@@ -875,14 +874,15 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
if (!rte_is_filtered(new))
{
stats->imp_updates_ignored++;
- rte_trace_in(D_ROUTES, c, new, "ignored");
+ rte_trace_in(D_ROUTES, c, new, "ignored");
}
- return;
- }
+ return;
+ }
- *before_old = (*before_old)->next;
- table->rt_count--;
+
+ *before_old = (*before_old)->next;
+ table->rt_count--;
}
if (!old && !new)
@@ -1128,7 +1128,6 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
if (c->in_table && !rte_update_in(c, n, new, src))
return;
- // struct proto *p = c->proto;
struct proto_stats *stats = &c->stats;
const struct filter *filter = c->in_filter;
net *nn;
@@ -1595,6 +1594,8 @@ rt_setup(pool *pp, struct rtable_config *cf)
t->rt_event = ev_new_init(p, rt_event, t);
t->last_rt_change = t->gc_time = current_time();
+
+ t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
}
return t;
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 1202c169..c869de9f 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -16,7 +16,7 @@ CF_DEFINES
CF_DECLS
-CF_KEYWORDS(PIPE, PEER, TABLE)
+CF_KEYWORDS(PIPE, PEER, TABLE, MAX, GENERATION)
CF_GRAMMAR
@@ -25,6 +25,7 @@ proto: pipe_proto '}' { this_channel = NULL; } ;
pipe_proto_start: proto_start PIPE
{
this_proto = proto_config_new(&proto_pipe, $1);
+ PIPE_CFG->max_generation = 16;
}
proto_name
{
@@ -41,6 +42,10 @@ pipe_proto:
| pipe_proto proto_item ';'
| pipe_proto channel_item_ ';'
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
+ | pipe_proto MAX GENERATION expr ';' {
+ if (($4 < 1) || ($4 > 254)) cf_error("Max generation must be in range 1..254, got %u", $4);
+ PIPE_CFG->max_generation = $4;
+ }
;
CF_CODE
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 3caa85d0..7604cc79 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -56,15 +56,6 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
if (!new && !old)
return;
- if (dst->table->pipe_busy)
- {
- log(L_ERR "Pipe loop detected when sending %N to table %s",
- n, dst->table->name);
- return;
- }
-
- src_ch->table->pipe_busy = 1;
-
if (new)
{
rta *a = alloca(rta_size(new->attrs));
@@ -76,23 +67,34 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
rte e0 = {
.attrs = a,
.src = new->src,
+ .generation = new->generation + 1,
};
rte_update(dst, n, &e0, new->src);
}
else
rte_update(dst, n, NULL, old->src);
-
- src_ch->table->pipe_busy = 0;
}
static int
pipe_preexport(struct channel *c, rte *e)
{
- struct proto *pp = e->sender->proto;
+ struct pipe_proto *p = (void *) c->proto;
+
+ /* Avoid direct loopbacks */
+ if (e->sender == c)
+ return -1;
- if (pp == c->proto)
- return -1; /* Avoid local loops automatically */
+ /* Indirection check */
+ uint max_generation = ((struct pipe_config *) p->p.cf)->max_generation;
+ if (e->generation >= max_generation)
+ {
+ log_rl(&p->rl_gen, L_ERR "Route overpiped (%u hops of %u configured in %s) in table %s: %N %s/%u:%u",
+ e->generation, max_generation, c->proto->name,
+ c->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id);
+
+ return -1;
+ }
return 0;
}
@@ -177,6 +179,8 @@ pipe_init(struct proto_config *CF)
P->preexport = pipe_preexport;
P->reload_routes = pipe_reload_routes;
+ p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
+
pipe_configure_channels(p, cf);
return P;
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index 038c6666..60c857eb 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -12,12 +12,14 @@
struct pipe_config {
struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */
+ u8 max_generation;
};
struct pipe_proto {
struct proto p;
struct channel *pri;
struct channel *sec;
+ struct tbf rl_gen;
};
#endif