0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-20 04:15:19 +00:00

Merge commit '56c8f2f0' into haugesund

Conflicts:
	nest/route.h
	nest/rt-table.c
This commit is contained in:
Maria Matejka 2022-03-09 13:49:31 +01:00
commit 4eeae48214
9 changed files with 103 additions and 51 deletions

View File

@ -45,6 +45,7 @@ struct config {
int cli_debug; /* Tracing of CLI connections and commands */ int cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */ 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 latency_limit; /* Events with longer duration are logged (us) */
u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */
u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */ u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */

View File

@ -4211,6 +4211,14 @@ include standard channel config options; see the example below.
<tag><label id="pipe-peer-table">peer table <m/table/</tag> <tag><label id="pipe-peer-table">peer table <m/table/</tag>
Defines secondary routing table to connect to. The primary one is Defines secondary routing table to connect to. The primary one is
selected by the <cf/table/ keyword. 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> </descrip>
<sect1>Attributes <sect1>Attributes

View File

@ -112,7 +112,7 @@ proto_postconfig(void)
CF_DECLS 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(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(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI) CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
@ -371,6 +371,7 @@ debug_default:
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; } DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; } | DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
| DEBUG COMMANDS expr { new_config->cli_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 */ /* MRTDUMP PROTOCOLS is in systep/unix/config.Y */

View File

@ -167,7 +167,6 @@ typedef struct rtable {
char *name; /* Name of this table */ char *name; /* Name of this table */
list channels; /* List of attached channels (struct channel) */ list channels; /* List of attached channels (struct channel) */
uint addr_type; /* Type of address data stored in table (NET_*) */ 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 */ int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */ u32 rt_count; /* Number of routes in the table */
@ -195,6 +194,7 @@ typedef struct rtable {
struct f_trie *trie_old; /* Old prefix trie waiting to be freed */ struct f_trie *trie_old; /* Old prefix trie waiting to be freed */
u32 trie_lock_count; /* Prefix trie locked by walks */ u32 trie_lock_count; /* Prefix trie locked by walks */
u32 trie_old_lock_count; /* Old prefix trie locked by walks */ u32 trie_old_lock_count; /* Old prefix trie locked by walks */
struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
list subscribers; /* Subscribers for notifications */ list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */ struct timer *settle_timer; /* Settle time for notifications */
@ -262,6 +262,9 @@ typedef struct rte {
u32 id; /* Table specific route id */ u32 id; /* Table specific route id */
byte flags; /* Table-specific flags */ byte flags; /* Table-specific flags */
byte pflags; /* Protocol-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; } rte;
struct rte_storage { struct rte_storage {
@ -373,8 +376,9 @@ int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c); void rt_feed_channel_abort(struct channel *c);
int rt_reload_channel(struct channel *c); int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(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); 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 refeed); int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, struct rte_storage **old_exported);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
static inline int rt_is_ip(rtable *tab) static inline int rt_is_ip(rtable *tab)

View File

@ -739,8 +739,11 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref
struct rte_storage *old_exported = NULL; struct rte_storage *old_exported = NULL;
if (c->out_table) if (c->out_table)
{ {
if (!rte_update_out(c, net, new, old, &old_exported, refeed)) if (!rte_update_out(c, net, new, old, &old_exported))
{
rte_trace_out(D_ROUTES, c, new, "idempotent");
return; return;
}
} }
if (new) if (new)
@ -1129,7 +1132,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
struct proto *p = c->proto; struct proto *p = c->proto;
struct rtable *table = c->table; struct rtable *table = c->table;
struct proto_stats *stats = &c->stats; 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; struct rte_storage *old_best_stored = net->routes, *old_stored = NULL;
rte *old_best = old_best_stored ? &old_best_stored->rte : NULL; rte *old_best = old_best_stored ? &old_best_stored->rte : NULL;
rte *old = NULL; rte *old = NULL;
@ -1141,22 +1143,22 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
{ {
old = &(old_stored = (*before_old))->rte; old = &(old_stored = (*before_old))->rte;
/* If there is the same route in the routing table but from /* If there is the same route in the routing table but from
* a different sender, then there are two paths from the * a different sender, then there are two paths from the
* source protocol to this routing table through transparent * source protocol to this routing table through transparent
* pipes, which is not allowed. * pipes, which is not allowed.
* * We log that and ignore the route. */
* We log that and ignore the route. If it is withdraw, we if (old->sender->proto != p)
* ignore it completely (there might be 'spurious withdraws', {
* see FIXME in do_rte_announce()) 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",
if (old->sender->proto != p) c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
{
if (new) log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u",
log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s", c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
net->n.addr, table->name);
return; return;
} }
if (new && rte_same(old, new)) if (new && rte_same(old, new))
{ {
@ -1167,14 +1169,15 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
if (!rte_is_filtered(new)) if (!rte_is_filtered(new))
{ {
stats->imp_updates_ignored++; 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) if (!old && !new)
@ -1420,7 +1423,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)) if (c->in_table && !rte_update_in(c, n, new, src))
return; return;
// struct proto *p = c->proto;
struct proto_stats *stats = &c->stats; struct proto_stats *stats = &c->stats;
const struct filter *filter = c->in_filter; const struct filter *filter = c->in_filter;
net *nn; net *nn;
@ -1978,6 +1980,8 @@ rt_setup(pool *pp, struct rtable_config *cf)
t->rt_event = ev_new_init(p, rt_event, t); t->rt_event = ev_new_init(p, rt_event, t);
t->last_rt_change = t->gc_time = current_time(); t->last_rt_change = t->gc_time = current_time();
t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
if (rt_is_flow(t)) if (rt_is_flow(t))
{ {
t->flowspec_trie = f_new_trie(lp_new_default(p), 0); t->flowspec_trie = f_new_trie(lp_new_default(p), 0);
@ -3065,7 +3069,7 @@ again:
*/ */
int int
rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct rte_storage **old_exported, int refeed) rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct rte_storage **old_exported)
{ {
struct rtable *tab = c->out_table; struct rtable *tab = c->out_table;
struct rte_src *src; struct rte_src *src;
@ -3082,7 +3086,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct
src = old0->src; src = old0->src;
if (!net) if (!net)
goto drop_withdraw; goto drop;
} }
/* Find the old rte */ /* Find the old rte */
@ -3092,7 +3096,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct
if (old = *pos) if (old = *pos)
{ {
if (new && rte_same(&(*pos)->rte, new)) if (new && rte_same(&(*pos)->rte, new))
goto drop_update; goto drop;
/* Remove the old rte */ /* Remove the old rte */
*pos = old->next; *pos = old->next;
@ -3103,7 +3107,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct
if (!new) if (!new)
{ {
if (!old) if (!old)
goto drop_withdraw; goto drop;
if (!net->routes) if (!net->routes)
fib_delete(&tab->fib, net); fib_delete(&tab->fib, net);
@ -3119,13 +3123,36 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct
tab->rt_count++; tab->rt_count++;
return 1; return 1;
drop_update: drop:
return refeed;
drop_withdraw:
return 0; return 0;
} }
void
rt_refeed_channel(struct channel *c)
{
if (!c->out_table)
{
channel_request_feeding(c);
return;
}
ASSERT_DIE(c->ra_mode != RA_ANY);
c->proto->feed_begin(c, 0);
FIB_WALK(&c->out_table->fib, net, n)
{
if (!n->routes)
continue;
rte e = n->routes->rte;
c->proto->rt_notify(c->proto, c, n->n.addr, &e, NULL);
}
FIB_WALK_END;
c->proto->feed_end(c);
}
/* /*
* Hostcache * Hostcache

View File

@ -2732,7 +2732,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len)
{ {
case BGP_RR_REQUEST: case BGP_RR_REQUEST:
BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
channel_request_feeding(&c->c); rt_refeed_channel(&c->c);
break; break;
case BGP_RR_BEGIN: case BGP_RR_BEGIN:

View File

@ -16,7 +16,7 @@ CF_DEFINES
CF_DECLS CF_DECLS
CF_KEYWORDS(PIPE, PEER, TABLE) CF_KEYWORDS(PIPE, PEER, TABLE, MAX, GENERATION)
CF_GRAMMAR CF_GRAMMAR
@ -25,6 +25,7 @@ proto: pipe_proto '}' { this_channel = NULL; } ;
pipe_proto_start: proto_start PIPE pipe_proto_start: proto_start PIPE
{ {
this_proto = proto_config_new(&proto_pipe, $1); this_proto = proto_config_new(&proto_pipe, $1);
PIPE_CFG->max_generation = 16;
} }
proto_name proto_name
{ {
@ -41,6 +42,10 @@ pipe_proto:
| pipe_proto proto_item ';' | pipe_proto proto_item ';'
| pipe_proto channel_item_ ';' | pipe_proto channel_item_ ';'
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; } | 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 CF_CODE

View File

@ -56,15 +56,6 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
if (!new && !old) if (!new && !old)
return; 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) if (new)
{ {
rta *a = alloca(rta_size(new->attrs)); 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 = { rte e0 = {
.attrs = a, .attrs = a,
.src = new->src, .src = new->src,
.generation = new->generation + 1,
}; };
rte_update(dst, n, &e0, new->src); rte_update(dst, n, &e0, new->src);
} }
else else
rte_update(dst, n, NULL, old->src); rte_update(dst, n, NULL, old->src);
src_ch->table->pipe_busy = 0;
} }
static int static int
pipe_preexport(struct channel *c, rte *e) pipe_preexport(struct channel *c, rte *e)
{ {
struct proto *pp = e->sender->proto; struct pipe_proto *p = (void *) c->proto;
if (pp == c->proto) /* Avoid direct loopbacks */
return -1; /* Avoid local loops automatically */ if (e->sender == c)
return -1;
/* 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; return 0;
} }
@ -177,6 +179,8 @@ pipe_init(struct proto_config *CF)
P->preexport = pipe_preexport; P->preexport = pipe_preexport;
P->reload_routes = pipe_reload_routes; P->reload_routes = pipe_reload_routes;
p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
pipe_configure_channels(p, cf); pipe_configure_channels(p, cf);
return P; return P;

View File

@ -12,12 +12,14 @@
struct pipe_config { struct pipe_config {
struct proto_config c; struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */ struct rtable_config *peer; /* Table we're connected to */
u8 max_generation;
}; };
struct pipe_proto { struct pipe_proto {
struct proto p; struct proto p;
struct channel *pri; struct channel *pri;
struct channel *sec; struct channel *sec;
struct tbf rl_gen;
}; };
#endif #endif