0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

BGP: implement Adj-RIB-In

The patch implements optional internal import table to a channel and
hooks it to BGP so it can be used as Adj-RIB-In. When enabled, all
received (pre-filtered) routes are stored there and import filters can
be re-evaluated without explicit route refresh. An import table can be
examined using e.g. 'show route import table bgp1.ipv4'.
This commit is contained in:
Ondrej Zajicek (work) 2019-02-17 18:46:28 +01:00
parent 1e5dcb37ac
commit 210dbc16c2
11 changed files with 286 additions and 9 deletions

View File

@ -2553,6 +2553,16 @@ be used in explicit configuration.
for every allowed table type. Default: the same as the main table
the channel is connected to (if eligible).
<tag><label id="bgp-import-table">import table <m/switch/</tag>
A BGP import table contain all received routes from given BGP neighbor,
before application of import filters. It is also called <em/Adj-RIB-In/
in BGP terminology. BIRD BGP by default operates without import tables,
in which case received routes are just processed by import filters,
accepted ones are stored in the master table, and the rest is forgotten.
Enabling <cf/import table/ allows to store unprocessed routes, which can
be examined later by <cf/show route/, and can be used to reconfigure
import filters without full route refresh. Default: off.
<tag><label id="bgp-secondary">secondary <m/switch/</tag>
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

View File

@ -36,5 +36,14 @@ ev_active(event *e)
return e->n.next != NULL;
}
static inline event*
ev_new_init(pool *p, void (*hook)(void *), void *data)
{
event *e = ev_new(p);
e->hook = hook;
e->data = data;
return e;
}
#endif

View File

@ -562,6 +562,16 @@ r_args:
$$ = $1;
$$->fit.flags |= FIF_ORDERED;
}
| r_args IMPORT TABLE SYM '.' r_args_channel {
$$ = $1;
struct proto_config *cf = (void *) $4->def;
if ($4->class != SYM_PROTO || !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->in_table) cf_error("No import table in channel %s.%s", $4->name, $6);
rt_show_add_table($$, c->in_table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args FILTER filter {
$$ = $1;
if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice");

View File

@ -282,6 +282,54 @@ channel_stop_export(struct channel *c)
c->stats.exp_routes = 0;
}
/* Called by protocol for reload from in_table */
void
channel_schedule_reload(struct channel *c)
{
ASSERT(c->channel_state == CS_UP);
rt_reload_channel_abort(c);
ev_schedule(c->reload_event);
}
static void
channel_reload_loop(void *ptr)
{
struct channel *c = ptr;
if (!rt_reload_channel(c))
{
ev_schedule(c->reload_event);
return;
}
}
static void
channel_reset_import(struct channel *c)
{
/* Need to abort feeding */
ev_postpone(c->reload_event);
rt_reload_channel_abort(c);
rt_prune_sync(c->in_table, 1);
}
/* Called by protocol to activate in_table */
void
channel_setup_in_table(struct channel *c)
{
struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
cf->name = "import";
cf->addr_type = c->net_type;
c->in_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
rt_setup(c->proto->pool, c->in_table, cf);
c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
}
static void
channel_do_start(struct channel *c)
{
@ -315,6 +363,8 @@ channel_do_flush(struct channel *c)
static void
channel_do_down(struct channel *c)
{
ASSERT(!c->feed_active && !c->reload_active);
rem_node(&c->table_node);
rt_unlock_table(c->table);
c->proto->active_channels--;
@ -324,6 +374,9 @@ channel_do_down(struct channel *c)
memset(&c->stats, 0, sizeof(struct proto_stats));
c->in_table = NULL;
c->reload_event = NULL;
CALL(c->channel->cleanup, c);
/* Schedule protocol shutddown */
@ -355,6 +408,9 @@ channel_set_state(struct channel *c, uint state)
if (es != ES_DOWN)
channel_stop_export(c);
if (c->in_table && (cs == CS_UP))
channel_reset_import(c);
break;
case CS_UP:
@ -374,6 +430,9 @@ channel_set_state(struct channel *c, uint state)
if (es != ES_DOWN)
channel_stop_export(c);
if (c->in_table && (cs == CS_UP))
channel_reset_import(c);
channel_do_flush(c);
break;

View File

@ -282,8 +282,6 @@ rte_make_tmp_attrs(struct rte *rt, struct linpool *pool)
return mta ? mta(rt, pool) : NULL;
}
/* Moved from route.h to avoid dependency conflicts */
static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
extern pool *proto_pool;
extern list proto_list;
@ -514,6 +512,11 @@ struct channel {
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
btime last_state_change; /* Time of last state transition */
struct rtable *in_table; /* Internal table for received routes */
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 */
};
@ -581,6 +584,8 @@ struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
void channel_set_state(struct channel *c, uint state);
void channel_setup_in_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); }
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
@ -592,4 +597,17 @@ void *channel_config_get(const struct channel_class *cc, const char *name, uint
int channel_reconfigure(struct channel *c, struct channel_config *cf);
/* Moved from route.h to avoid dependency conflicts */
static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
static inline void
rte_update3(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;
rte_update2(c, n, new, src);
}
#endif

View File

@ -322,6 +322,10 @@ void rt_dump(rtable *);
void rt_dump_all(void);
int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c);
int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(struct channel *c);
void rt_prune_sync(rtable *t, int all);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
void cmd_show_table_stats(struct rtable_config *tab);

View File

@ -1080,7 +1080,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
int old_ok = rte_is_ok(old);
struct channel_limit *l = &c->rx_limit;
if (l->action && !old && new)
if (l->action && !old && new && !c->in_table)
{
u32 all_routes = stats->imp_routes + stats->filt_routes;
@ -2262,6 +2262,159 @@ ptr_hash(void *ptr)
return p ^ (p << 8) ^ (p >> 16);
}
int
rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
struct rtable *tab = c->in_table;
rte *old, **pos;
net *net;
if (new)
{
net = net_get(tab, n);
if (!new->pref)
new->pref = c->preference;
if (!rta_is_cached(new->attrs))
new->attrs = rta_lookup(new->attrs);
}
else
{
net = net_find(tab, n);
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))
goto drop_update;
/* Remove the old rte */
*pos = old->next;
rte_free_quick(old);
tab->route_count--;
break;
}
if (!new)
{
if (!old)
goto drop_withdraw;
return 1;
}
struct channel_limit *l = &c->rx_limit;
if (l->action && !old)
{
if (tab->route_count >= l->limit)
channel_notify_limit(c, l, PLD_RX, tab->route_count);
if (l->state == PLS_BLOCKED)
{
rte_trace_in(D_FILTERS, c->proto, new, "ignored [limit]");
goto drop_update;
}
}
/* 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->route_count++;
return 1;
drop_update:
c->stats.imp_updates_received++;
c->stats.imp_updates_ignored++;
rte_free(new);
return 0;
drop_withdraw:
c->stats.imp_withdraws_received++;
c->stats.imp_withdraws_ignored++;
return 0;
}
int
rt_reload_channel(struct channel *c)
{
struct rtable *tab = c->in_table;
struct fib_iterator *fit = &c->reload_fit;
int max_feed = 64;
ASSERT(c->channel_state == CS_UP);
if (!c->reload_active)
{
FIB_ITERATE_INIT(fit, &tab->fib);
c->reload_active = 1;
}
FIB_ITERATE_START(&tab->fib, fit, net, n)
{
if (max_feed <= 0)
{
FIB_ITERATE_PUT(fit);
return 0;
}
for (rte *e = n->routes; e; e = e->next)
{
rte_update2(c, n->n.addr, rte_do_cow(e), e->attrs->src);
max_feed--;
}
}
FIB_ITERATE_END;
c->reload_active = 0;
return 1;
}
void
rt_reload_channel_abort(struct channel *c)
{
if (c->reload_active)
{
/* Unlink the iterator */
fit_get(&c->in_table->fib, &c->reload_fit);
c->reload_active = 0;
}
}
void
rt_prune_sync(rtable *t, int all)
{
FIB_WALK(&t->fib, net, n)
{
rte *e, **ee = &n->routes;
while (e = *ee)
{
if (all || (e->flags & (REF_STALE | REF_DISCARD)))
{
*ee = e->next;
rte_free_quick(e);
t->route_count--;
}
else
ee = &e->next;
}
}
FIB_WALK_END;
}
static inline u32
hc_hash(ip_addr a, rtable *dep)
{

View File

@ -539,7 +539,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
int active = loc->ready && rem->ready;
c->c.disabled = !active;
c->c.reloadable = p->route_refresh;
c->c.reloadable = p->route_refresh || c->cf->import_table;
c->index = active ? num++ : 0;
@ -762,6 +762,9 @@ bgp_refresh_begin(struct bgp_channel *c)
c->load_state = BFS_REFRESHING;
rt_refresh_begin(c->c.table, &c->c);
if (c->c.in_table)
rt_refresh_begin(c->c.in_table, &c->c);
}
/**
@ -783,6 +786,9 @@ bgp_refresh_end(struct bgp_channel *c)
c->load_state = BFS_NONE;
rt_refresh_end(c->c.table, &c->c);
if (c->c.in_table)
rt_prune_sync(c->c.in_table, 0);
}
@ -1199,9 +1205,12 @@ bgp_reload_routes(struct channel *C)
struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C;
ASSERT(p->conn && p->route_refresh);
ASSERT(p->conn && (p->route_refresh || c->c.in_table));
bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
if (c->c.in_table)
channel_schedule_reload(C);
else
bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
}
static void
@ -1505,6 +1514,9 @@ bgp_channel_start(struct channel *C)
bgp_init_bucket_table(c);
bgp_init_prefix_table(c);
if (c->cf->import_table)
channel_setup_in_table(C);
c->next_hop_addr = c->cf->next_hop_addr;
c->link_addr = IPA_NONE;
c->packets_to_send = 0;

View File

@ -142,6 +142,7 @@ struct bgp_channel_config {
u8 gr_able; /* Allow full graceful restart for the channel */
u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
u8 import_table; /* Use c.in_table as Adj-RIB-In */
uint rest[0]; /* Remaining items are reconfigured separately */
struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */

View File

@ -28,7 +28,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6)
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, IMPORT)
%type <i32> bgp_afi
@ -218,6 +218,7 @@ bgp_channel_item:
| ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
| IMPORT TABLE bool { BGP_CC->import_table = $3; }
| IGP TABLE rtable {
if (BGP_CC->desc->no_igp)
cf_error("IGP table not allowed here");

View File

@ -1108,7 +1108,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
if (!a0)
{
/* Route withdraw */
rte_update2(&s->channel->c, n, NULL, s->last_src);
rte_update3(&s->channel->c, n, NULL, s->last_src);
return;
}
@ -1128,7 +1128,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
e->pflags = 0;
e->u.bgp.suppressed = 0;
rte_update2(&s->channel->c, n, e, s->last_src);
rte_update3(&s->channel->c, n, e, s->last_src);
}
static void