mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
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:
parent
b357f25932
commit
0d1966cc1d
@ -2563,6 +2563,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
|
||||||
|
@ -573,6 +573,16 @@ 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 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->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");
|
||||||
|
26
nest/proto.c
26
nest/proto.c
@ -315,6 +315,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)
|
||||||
@ -329,6 +336,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;
|
||||||
|
|
||||||
|
@ -520,6 +520,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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -588,6 +590,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); }
|
||||||
|
@ -329,6 +329,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);
|
||||||
void cmd_show_table_stats(struct rtable_config *tab);
|
void cmd_show_table_stats(struct rtable_config *tab);
|
||||||
|
|
||||||
|
@ -566,7 +566,6 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int
|
|||||||
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.
|
||||||
*
|
*
|
||||||
@ -612,6 +611,8 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
@ -2416,6 +2417,10 @@ ptr_hash(void *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
{
|
{
|
||||||
@ -2568,6 +2573,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->route_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->route_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)
|
||||||
{
|
{
|
||||||
|
@ -1539,6 +1539,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->next_hop_addr = c->cf->next_hop_addr;
|
c->next_hop_addr = c->cf->next_hop_addr;
|
||||||
c->link_addr = IPA_NONE;
|
c->link_addr = IPA_NONE;
|
||||||
c->packets_to_send = 0;
|
c->packets_to_send = 0;
|
||||||
@ -1841,8 +1844,9 @@ 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) ||
|
||||||
(IGP_TABLE(old, ip4) != IGP_TABLE(new, ip4)) ||
|
(new->export_table != old->export_table) ||
|
||||||
(IGP_TABLE(old, ip6) != IGP_TABLE(new, ip6)))
|
(IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) ||
|
||||||
|
(IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (new->gw_mode != old->gw_mode)
|
if (new->gw_mode != old->gw_mode)
|
||||||
|
@ -143,6 +143,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 */
|
||||||
|
@ -219,6 +219,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");
|
||||||
|
Loading…
Reference in New Issue
Block a user