diff options
-rw-r--r-- | doc/bird.sgml | 10 | ||||
-rw-r--r-- | nest/config.Y | 11 | ||||
-rw-r--r-- | nest/proto.c | 26 | ||||
-rw-r--r-- | nest/protocol.h | 3 | ||||
-rw-r--r-- | nest/route.h | 1 | ||||
-rw-r--r-- | nest/rt-table.c | 90 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 4 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 1 | ||||
-rw-r--r-- | proto/bgp/config.Y | 1 |
9 files changed, 146 insertions, 1 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 16d03028..d1b90a1d 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2705,6 +2705,16 @@ be used in explicit configuration. 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-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> 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 diff --git a/nest/config.Y b/nest/config.Y index e1a932d4..c62501a3 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -567,6 +567,17 @@ r_args: rt_show_add_table($$, c->in_table); $$->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 { $$ = $1; if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice"); diff --git a/nest/proto.c b/nest/proto.c index 18cf214b..54955bdd 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -317,6 +317,13 @@ channel_reset_import(struct channel *c) 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 */ void 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); } +/* 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 channel_do_start(struct channel *c) @@ -376,6 +395,7 @@ channel_do_down(struct channel *c) c->in_table = NULL; c->reload_event = NULL; + c->out_table = NULL; CALL(c->channel->cleanup, c); @@ -411,6 +431,9 @@ channel_set_state(struct channel *c, uint state) if (c->in_table && (cs == CS_UP)) channel_reset_import(c); + if (c->out_table && (cs == CS_UP)) + channel_reset_export(c); + break; case CS_UP: @@ -433,6 +456,9 @@ channel_set_state(struct channel *c, uint state) if (c->in_table && (cs == CS_UP)) channel_reset_import(c); + if (c->out_table && (cs == CS_UP)) + channel_reset_export(c); + channel_do_flush(c); break; diff --git a/nest/protocol.h b/nest/protocol.h index 297afba7..17af2e17 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -539,6 +539,8 @@ struct channel { 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 */ + + 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_setup_in_table(struct channel *c); +void channel_setup_out_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); } diff --git a/nest/route.h b/nest/route.h index 8d799454..f4f32ce0 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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); void rt_reload_channel_abort(struct channel *c); 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); diff --git a/nest/rt-table.c b/nest/rt-table.c index 53f5d979..ff995cce 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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_stats *stats = &c->stats; - /* * 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) stats->exp_updates_accepted++; @@ -2507,6 +2508,10 @@ rt_feed_channel_abort(struct channel *c) } +/* + * Import table + */ + int 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 hc_hash(ip_addr a, rtable *dep) { diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index c08e5ee9..01f61e81 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1721,6 +1721,9 @@ bgp_channel_start(struct channel *C) if (c->cf->import_table) 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->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->add_path != old->add_path) || (new->import_table != old->import_table) || + (new->export_table != old->export_table) || (IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) || (IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6))) return 0; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 2651677c..f9a19d53 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -150,6 +150,7 @@ struct bgp_channel_config { 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 */ + 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_ip6; /* Table for recursive IPv6 next hop lookups */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index bbc7d9a4..1407315d 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -255,6 +255,7 @@ bgp_channel_item: | 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; } + | EXPORT TABLE bool { BGP_CC->export_table = $3; } | IGP TABLE rtable { if (BGP_CC->desc->no_igp) cf_error("IGP table not allowed here"); |