BGP: implement Adj-RIB-Out

The patch implements optional internal export table to a channel and
hooks it to BGP so it can be used as Adj-RIB-Out. When enabled, all
exported (post-filtered) routes are stored there. An export table can be
examined using e.g. 'show route export table bgp1.ipv4'.
This commit is contained in:
Ondrej Zajicek (work) 2019-08-13 18:22:07 +02:00
parent dfe63ed84d
commit b7d7599ce3
9 changed files with 146 additions and 1 deletions

View file

@ -2705,6 +2705,16 @@ be used in explicit configuration.
be examined later by <cf/show route/, and can be used to reconfigure be examined later by <cf/show route/, and can be used to reconfigure
import filters without full route refresh. Default: off. import filters without full route refresh. Default: off.
<tag><label id="bgp-export-table">export table <m/switch/</tag>
A BGP export table contains all routes sent to given BGP neighbor, after
application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
terminology. BIRD BGP by default operates without export tables, in
which case routes from master table are just processed by export filters
and then announced by BGP. Enabling <cf/export table/ allows to store
routes after export filter processing, so they can be examined later by
<cf/show route/, and can be used to eliminate unnecessary updates or
withdraws. Default: off.
<tag><label id="bgp-secondary">secondary <m/switch/</tag> <tag><label id="bgp-secondary">secondary <m/switch/</tag>
Usually, if an export filter rejects a selected route, no other route is Usually, if an export filter rejects a selected route, no other route is
propagated for that network. This option allows to try the next route in propagated for that network. This option allows to try the next route in

View file

@ -567,6 +567,17 @@ r_args:
rt_show_add_table($$, c->in_table); rt_show_add_table($$, c->in_table);
$$->tables_defined_by = RSD_TDB_DIRECT; $$->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 { | r_args FILTER filter {
$$ = $1; $$ = $1;
if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice"); if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice");

View file

@ -317,6 +317,13 @@ channel_reset_import(struct channel *c)
rt_prune_sync(c->in_table, 1); 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 */ /* Called by protocol to activate in_table */
void void
channel_setup_in_table(struct channel *c) 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); 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 static void
channel_do_start(struct channel *c) channel_do_start(struct channel *c)
@ -376,6 +395,7 @@ channel_do_down(struct channel *c)
c->in_table = NULL; c->in_table = NULL;
c->reload_event = NULL; c->reload_event = NULL;
c->out_table = NULL;
CALL(c->channel->cleanup, c); CALL(c->channel->cleanup, c);
@ -411,6 +431,9 @@ channel_set_state(struct channel *c, uint state)
if (c->in_table && (cs == CS_UP)) if (c->in_table && (cs == CS_UP))
channel_reset_import(c); channel_reset_import(c);
if (c->out_table && (cs == CS_UP))
channel_reset_export(c);
break; break;
case CS_UP: case CS_UP:
@ -433,6 +456,9 @@ channel_set_state(struct channel *c, uint state)
if (c->in_table && (cs == CS_UP)) if (c->in_table && (cs == CS_UP))
channel_reset_import(c); channel_reset_import(c);
if (c->out_table && (cs == CS_UP))
channel_reset_export(c);
channel_do_flush(c); channel_do_flush(c);
break; break;

View file

@ -539,6 +539,8 @@ struct channel {
struct event *reload_event; /* Event responsible for reloading from in_table */ struct event *reload_event; /* Event responsible for reloading from in_table */
struct fib_iterator reload_fit; /* Iterator in in_table used during reloading */ struct fib_iterator reload_fit; /* Iterator in in_table used during reloading */
u8 reload_active; /* Iterator reload_fit is linked */ 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_set_state(struct channel *c, uint state);
void channel_setup_in_table(struct channel *c); void channel_setup_in_table(struct channel *c);
void channel_setup_out_table(struct channel *c);
void channel_schedule_reload(struct channel *c); void channel_schedule_reload(struct channel *c);
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); } static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }

View file

@ -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); 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_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 *old0, int refeed);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);

View file

@ -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 *p = c->proto;
struct proto_stats *stats = &c->stats; struct proto_stats *stats = &c->stats;
/* /*
* First, apply export limit. * 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) if (new)
stats->exp_updates_accepted++; stats->exp_updates_accepted++;
@ -2507,6 +2508,10 @@ rt_feed_channel_abort(struct channel *c)
} }
/*
* Import table
*/
int int
rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) 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 static inline u32
hc_hash(ip_addr a, rtable *dep) hc_hash(ip_addr a, rtable *dep)
{ {

View file

@ -1721,6 +1721,9 @@ bgp_channel_start(struct channel *C)
if (c->cf->import_table) if (c->cf->import_table)
channel_setup_in_table(C); channel_setup_in_table(C);
if (c->cf->export_table)
channel_setup_out_table(C);
c->stale_timer = tm_new_init(c->pool, bgp_long_lived_stale_timeout, c, 0, 0); c->stale_timer = tm_new_init(c->pool, bgp_long_lived_stale_timeout, c, 0, 0);
c->next_hop_addr = c->cf->next_hop_addr; c->next_hop_addr = c->cf->next_hop_addr;
@ -2067,6 +2070,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
(new->ext_next_hop != old->ext_next_hop) || (new->ext_next_hop != old->ext_next_hop) ||
(new->add_path != old->add_path) || (new->add_path != old->add_path) ||
(new->import_table != old->import_table) || (new->import_table != old->import_table) ||
(new->export_table != old->export_table) ||
(IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) || (IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) ||
(IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6))) (IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6)))
return 0; return 0;

View file

@ -150,6 +150,7 @@ struct bgp_channel_config {
u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */ u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
u8 add_path; /* Use ADD-PATH extension [RFC 7911] */ u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
u8 import_table; /* Use c.in_table as Adj-RIB-In */ u8 import_table; /* Use c.in_table as Adj-RIB-In */
u8 export_table; /* Use c.out_table as Adj-RIB-Out */
struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */ struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */ struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */

View file

@ -255,6 +255,7 @@ bgp_channel_item:
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; } | ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; } | ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
| IMPORT TABLE bool { BGP_CC->import_table = $3; } | IMPORT TABLE bool { BGP_CC->import_table = $3; }
| EXPORT TABLE bool { BGP_CC->export_table = $3; }
| IGP TABLE rtable { | IGP TABLE rtable {
if (BGP_CC->desc->no_igp) if (BGP_CC->desc->no_igp)
cf_error("IGP table not allowed here"); cf_error("IGP table not allowed here");