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:
parent
1e5dcb37ac
commit
210dbc16c2
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
59
nest/proto.c
59
nest/proto.c
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
155
nest/rt-table.c
155
nest/rt-table.c
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user