From 6d87cf4be7536433d263609828414e687e1d4f08 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Fri, 1 Oct 2021 14:11:13 +0200 Subject: [PATCH 001/431] Kernel routes are flushed on shutdown by kernel scan, not by table scan --- sysdep/unix/krt.c | 31 ++++++++++++++----------------- sysdep/unix/krt.h | 1 + 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index b98e7ec0..32bfe7fc 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -542,23 +542,6 @@ krt_is_installed(struct krt_proto *p, net *n) return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->rte.id); } -static void -krt_flush_routes(struct krt_proto *p) -{ - struct rtable *t = p->p.main_channel->table; - - KRT_TRACE(p, D_EVENTS, "Flushing kernel routes"); - FIB_WALK(&t->fib, net, n) - { - if (krt_is_installed(p, n)) - { - /* FIXME: this does not work if gw is changed in export filter */ - krt_replace_rte(p, n->n.addr, NULL, &n->routes->rte); - } - } - FIB_WALK_END; -} - static struct rte * krt_export_net(struct krt_proto *p, net *net) { @@ -637,6 +620,9 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src) #endif /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */ + /* Deleting all routes if flush is requested */ + if (p->flush_routes) + goto delete; /* We wait for the initial feed to have correct installed state */ if (!p->ready) @@ -729,6 +715,17 @@ krt_prune(struct krt_proto *p) p->initialized = 1; } +static void +krt_flush_routes(struct krt_proto *p) +{ + KRT_TRACE(p, D_EVENTS, "Flushing kernel routes"); + p->flush_routes = 1; + krt_init_scan(p); + krt_do_scan(p); + /* No prune! */ + p->flush_routes = 0; +} + void krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src) { diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index cd4bd07d..f6ad6fde 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -66,6 +66,7 @@ struct krt_proto { byte ready; /* Initial feed has been finished */ byte initialized; /* First scan has been finished */ byte reload; /* Next scan is doing reload */ + byte flush_routes; /* Scanning to flush */ }; extern pool *krt_pool; From 13ebe7717685aaa64bbbe09a2e6cc0c6da2bd6bd Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sat, 30 Oct 2021 14:56:55 +0000 Subject: [PATCH 002/431] RPKI shouldn't process more packets when being stopped --- proto/rpki/packets.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index dd11f997..2c37df76 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -897,6 +897,9 @@ rpki_rx_hook(struct birdsock *sk, uint size) struct rpki_cache *cache = sk->data; struct rpki_proto *p = cache->p; + if ((p->p.proto_state == PS_DOWN) || (p->cache != cache)) + return 0; + byte *pkt_start = sk->rbuf; byte *end = pkt_start + size; From 46739f007a8a21c7887a29a465db0c2520fb4a13 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sat, 30 Oct 2021 15:50:16 +0000 Subject: [PATCH 003/431] RPKI: Do nothing when protocol is stopping --- proto/rpki/packets.c | 8 ++++++++ proto/rpki/rpki.c | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index 2c37df76..a711858c 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -956,6 +956,8 @@ rpki_err_hook(struct birdsock *sk, int error_num) CACHE_TRACE(D_EVENTS, cache, "The other side closed a connection"); } + if (cache->p->cache != cache) + return; rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT); } @@ -975,6 +977,9 @@ rpki_tx_hook(sock *sk) { struct rpki_cache *cache = sk->data; + if (cache->p->cache != cache) + return; + while (rpki_fire_tx(cache) > 0) ; } @@ -984,6 +989,9 @@ rpki_connected_hook(sock *sk) { struct rpki_cache *cache = sk->data; + if (cache->p->cache != cache) + return; + CACHE_TRACE(D_EVENTS, cache, "Connected"); proto_notify_state(&cache->p->p, PS_UP); diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index 72fbc967..95066499 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -384,6 +384,9 @@ rpki_refresh_hook(timer *tm) { struct rpki_cache *cache = tm->data; + if (cache->p->cache != cache) + return; + CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state)); switch (cache->state) @@ -430,6 +433,9 @@ rpki_retry_hook(timer *tm) { struct rpki_cache *cache = tm->data; + if (cache->p->cache != cache) + return; + CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state)); switch (cache->state) @@ -475,6 +481,9 @@ rpki_expire_hook(timer *tm) { struct rpki_cache *cache = tm->data; + if (cache->p->cache != cache) + return; + if (!cache->last_update) return; From 1e76f6e954e8043ec08c917c3f53e20f7dd06c99 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Thu, 30 Sep 2021 11:54:11 +0200 Subject: [PATCH 004/431] Suppressed MRT unused static function warning --- proto/mrt/mrt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/mrt/mrt.c b/proto/mrt/mrt.c index 03f0d59e..5da3c7c6 100644 --- a/proto/mrt/mrt.c +++ b/proto/mrt/mrt.c @@ -113,13 +113,13 @@ mrt_buffer_flush(buffer *b) } #define MRT_DEFINE_TYPE(S, T) \ - static inline void mrt_put_##S##_(buffer *b, T x) \ + UNUSED static inline void mrt_put_##S##_(buffer *b, T x) \ { \ put_##S(b->pos, x); \ b->pos += sizeof(T); \ } \ \ - static inline void mrt_put_##S(buffer *b, T x) \ + UNUSED static inline void mrt_put_##S(buffer *b, T x) \ { \ mrt_buffer_need(b, sizeof(T)); \ put_##S(b->pos, x); \ From 1c2f66f2bd9b2996c8cba0604e7ac38738399000 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Fri, 14 May 2021 16:23:18 +0200 Subject: [PATCH 005/431] Refeed is done from export table when appropriate --- nest/route.h | 3 ++- nest/rt-table.c | 44 +++++++++++++++++++++++++++++++++++--------- proto/bgp/packets.c | 2 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/nest/route.h b/nest/route.h index ade14857..98d605c8 100644 --- a/nest/route.h +++ b/nest/route.h @@ -346,8 +346,9 @@ int rt_feed_channel(struct channel *c); void rt_feed_channel_abort(struct channel *c); int rt_reload_channel(struct channel *c); void rt_reload_channel_abort(struct channel *c); +void rt_refeed_channel(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 *old, struct rte_storage **old_exported, int refeed); +int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, struct rte_storage **old_exported); 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 837e0ab9..b005f6f3 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -451,8 +451,11 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref struct rte_storage *old_exported = NULL; if (c->out_table) { - if (!rte_update_out(c, net, new, old, &old_exported, refeed)) + if (!rte_update_out(c, net, new, old, &old_exported)) + { + rte_trace_out(D_ROUTES, c, new, "idempotent"); return; + } } if (new) @@ -2406,7 +2409,7 @@ again: */ int -rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct rte_storage **old_exported, int refeed) +rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct rte_storage **old_exported) { struct rtable *tab = c->out_table; struct rte_src *src; @@ -2423,7 +2426,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct src = old0->src; if (!net) - goto drop_withdraw; + goto drop; } /* Find the old rte */ @@ -2433,7 +2436,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct if (old = *pos) { if (new && rte_same(&(*pos)->rte, new)) - goto drop_update; + goto drop; /* Remove the old rte */ *pos = old->next; @@ -2444,7 +2447,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct if (!new) { if (!old) - goto drop_withdraw; + goto drop; if (!net->routes) fib_delete(&tab->fib, net); @@ -2460,13 +2463,36 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct tab->rt_count++; return 1; -drop_update: - return refeed; - -drop_withdraw: +drop: return 0; } +void +rt_refeed_channel(struct channel *c) +{ + if (!c->out_table) + { + channel_request_feeding(c); + return; + } + + ASSERT_DIE(c->ra_mode != RA_ANY); + + c->proto->feed_begin(c, 0); + + FIB_WALK(&c->out_table->fib, net, n) + { + if (!n->routes) + continue; + + rte e = n->routes->rte; + c->proto->rt_notify(c->proto, c, n->n.addr, &e, NULL); + } + FIB_WALK_END; + + c->proto->feed_end(c); +} + /* * Hostcache diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 647551e5..f1e6d7d2 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -2695,7 +2695,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len) { case BGP_RR_REQUEST: BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); - channel_request_feeding(&c->c); + rt_refeed_channel(&c->c); break; case BGP_RR_BEGIN: From c56752e4367733c03a05e65ba62ccd2e54f7aadd Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 21 Jun 2021 19:11:42 +0200 Subject: [PATCH 006/431] Protocol stats split to import and export --- nest/proto.c | 45 +++++++++++++------------ nest/protocol.h | 46 +++++++++++++------------ nest/rt-table.c | 86 +++++++++++++++++++++++------------------------ proto/pipe/pipe.c | 24 +++++++------ 4 files changed, 105 insertions(+), 96 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index 7cfb1555..631e4b60 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -284,7 +284,7 @@ channel_feed_loop(void *ptr) if (c->refeeding && (l->state == PLS_BLOCKED) && (c->refeed_count <= l->limit) && - (c->stats.exp_routes <= l->limit)) + (c->export_stats.routes <= l->limit)) { log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit); channel_reset_limit(&c->out_limit); @@ -461,7 +461,7 @@ channel_stop_export(struct channel *c) rt_feed_channel_abort(c); c->export_state = ES_DOWN; - c->stats.exp_routes = 0; + c->export_stats.routes = 0; bmap_reset(&c->export_map, 1024); } @@ -551,7 +551,8 @@ channel_do_start(struct channel *c) c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c); bmap_init(&c->export_map, c->proto->pool, 1024); - memset(&c->stats, 0, sizeof(struct proto_stats)); + memset(&c->export_stats, 0, sizeof(struct export_stats)); + memset(&c->import_stats, 0, sizeof(struct import_stats)); channel_reset_limit(&c->rx_limit); channel_reset_limit(&c->in_limit); @@ -600,11 +601,12 @@ channel_do_down(struct channel *c) rt_unlock_table(c->table); c->proto->active_channels--; - if ((c->stats.imp_routes + c->stats.filt_routes) != 0) + if ((c->import_stats.routes + c->import_stats.filtered) != 0) log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name); // bmap_free(&c->export_map); - memset(&c->stats, 0, sizeof(struct proto_stats)); + memset(&c->import_stats, 0, sizeof(struct import_stats)); + memset(&c->export_stats, 0, sizeof(struct export_stats)); c->in_table = NULL; c->reload_event = NULL; @@ -1842,19 +1844,19 @@ static void channel_verify_limits(struct channel *c) { struct channel_limit *l; - u32 all_routes = c->stats.imp_routes + c->stats.filt_routes; + u32 all_routes = c->import_stats.routes + c->import_stats.filtered; l = &c->rx_limit; if (l->action && (all_routes > l->limit)) channel_notify_limit(c, l, PLD_RX, all_routes); l = &c->in_limit; - if (l->action && (c->stats.imp_routes > l->limit)) - channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes); + if (l->action && (c->import_stats.routes > l->limit)) + channel_notify_limit(c, l, PLD_IN, c->import_stats.routes); l = &c->out_limit; - if (l->action && (c->stats.exp_routes > l->limit)) - channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes); + if (l->action && (c->export_stats.routes > l->limit)) + channel_notify_limit(c, l, PLD_OUT, c->export_stats.routes); } static inline void @@ -2009,28 +2011,29 @@ proto_state_name(struct proto *p) static void channel_show_stats(struct channel *c) { - struct proto_stats *s = &c->stats; + struct import_stats *is = &c->import_stats; + struct export_stats *es = &c->export_stats; if (c->in_keep_filtered) cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", - s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes); + is->routes, is->filtered, es->routes, is->pref); else cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", - s->imp_routes, s->exp_routes, s->pref_routes); + is->routes, es->routes, is->pref); cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", - s->imp_updates_received, s->imp_updates_invalid, - s->imp_updates_filtered, s->imp_updates_ignored, - s->imp_updates_accepted); + is->updates_received, is->updates_invalid, + is->updates_filtered, is->updates_ignored, + is->updates_accepted); cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", - s->imp_withdraws_received, s->imp_withdraws_invalid, - s->imp_withdraws_ignored, s->imp_withdraws_accepted); + is->withdraws_received, is->withdraws_invalid, + is->withdraws_ignored, is->withdraws_accepted); cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", - s->exp_updates_received, s->exp_updates_rejected, - s->exp_updates_filtered, s->exp_updates_accepted); + es->updates_received, es->updates_rejected, + es->updates_filtered, es->updates_accepted); cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", - s->exp_withdraws_received, s->exp_withdraws_accepted); + es->withdraws_received, es->withdraws_accepted); } void diff --git a/nest/protocol.h b/nest/protocol.h index 80b4509b..9be8e531 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -132,29 +132,31 @@ struct proto_config { }; /* Protocol statistics */ -struct proto_stats { +struct import_stats { /* Import - from protocol to core */ - u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */ - u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */ - u32 pref_routes; /* Number of routes selected as best in the (adjacent) routing table */ - u32 imp_updates_received; /* Number of route updates received */ - u32 imp_updates_invalid; /* Number of route updates rejected as invalid */ - u32 imp_updates_filtered; /* Number of route updates rejected by filters */ - u32 imp_updates_ignored; /* Number of route updates rejected as already in route table */ - u32 imp_updates_accepted; /* Number of route updates accepted and imported */ - u32 imp_withdraws_received; /* Number of route withdraws received */ - u32 imp_withdraws_invalid; /* Number of route withdraws rejected as invalid */ - u32 imp_withdraws_ignored; /* Number of route withdraws rejected as already not in route table */ - u32 imp_withdraws_accepted; /* Number of route withdraws accepted and processed */ + u32 routes; /* Number of routes successfully imported to the (adjacent) routing table */ + u32 filtered; /* Number of routes rejected in import filter but kept in the routing table */ + u32 pref; /* Number of routes selected as best in the (adjacent) routing table */ + u32 updates_received; /* Number of route updates received */ + u32 updates_invalid; /* Number of route updates rejected as invalid */ + u32 updates_filtered; /* Number of route updates rejected by filters */ + u32 updates_ignored; /* Number of route updates rejected as already in route table */ + u32 updates_accepted; /* Number of route updates accepted and imported */ + u32 withdraws_received; /* Number of route withdraws received */ + u32 withdraws_invalid; /* Number of route withdraws rejected as invalid */ + u32 withdraws_ignored; /* Number of route withdraws rejected as already not in route table */ + u32 withdraws_accepted; /* Number of route withdraws accepted and processed */ +}; +struct export_stats { /* Export - from core to protocol */ - u32 exp_routes; /* Number of routes successfully exported to the protocol */ - u32 exp_updates_received; /* Number of route updates received */ - u32 exp_updates_rejected; /* Number of route updates rejected by protocol */ - u32 exp_updates_filtered; /* Number of route updates rejected by filters */ - u32 exp_updates_accepted; /* Number of route updates accepted and exported */ - u32 exp_withdraws_received; /* Number of route withdraws received */ - u32 exp_withdraws_accepted; /* Number of route withdraws accepted and processed */ + u32 routes; /* Number of routes successfully exported to the protocol */ + u32 updates_received; /* Number of route updates received */ + u32 updates_rejected; /* Number of route updates rejected by protocol */ + u32 updates_filtered; /* Number of route updates rejected by filters */ + u32 updates_accepted; /* Number of route updates accepted and exported */ + u32 withdraws_received; /* Number of route withdraws received */ + u32 withdraws_accepted; /* Number of route withdraws accepted and processed */ }; struct proto { @@ -516,7 +518,9 @@ struct channel { struct event *feed_event; /* Event responsible for feeding */ struct fib_iterator feed_fit; /* Routing table iterator used during feeding */ - struct proto_stats stats; /* Per-channel protocol statistics */ + struct import_stats import_stats; /* Import statistics */ + struct export_stats export_stats; /* Export statistics */ + u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */ u8 net_type; /* Routing table network type (NET_*), 0 for undefined */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 6851b4bc..0b6351e9 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -375,7 +375,7 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) { struct proto *p = c->proto; const struct filter *filter = c->out_filter; - struct proto_stats *stats = &c->stats; + struct export_stats *stats = &c->export_stats; int v; v = p->preexport ? p->preexport(c, rt) : 0; @@ -384,7 +384,7 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) if (silent) goto reject; - stats->exp_updates_rejected++; + stats->updates_rejected++; if (v == RIC_REJECT) rte_trace_out(D_FILTERS, c, rt, "rejected by protocol"); goto reject; @@ -404,7 +404,7 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) if (silent) goto reject; - stats->exp_updates_filtered++; + stats->updates_filtered++; rte_trace_out(D_FILTERS, c, rt, "filtered out"); goto reject; } @@ -427,7 +427,7 @@ static void do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int refeed) { struct proto *p = c->proto; - struct proto_stats *stats = &c->stats; + struct export_stats *stats = &c->export_stats; if (refeed && new) c->refeed_count++; @@ -436,12 +436,12 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref struct channel_limit *l = &c->out_limit; if (l->action && !old && new) { - if (stats->exp_routes >= l->limit) - channel_notify_limit(c, l, PLD_OUT, stats->exp_routes); + if (stats->routes >= l->limit) + channel_notify_limit(c, l, PLD_OUT, stats->routes); if (l->state == PLS_BLOCKED) { - stats->exp_updates_rejected++; + stats->updates_rejected++; rte_trace_out(D_FILTERS, c, new, "rejected [limit]"); return; } @@ -459,20 +459,20 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref } if (new) - stats->exp_updates_accepted++; + stats->updates_accepted++; else - stats->exp_withdraws_accepted++; + stats->withdraws_accepted++; if (old) { bmap_clear(&c->export_map, old->id); - stats->exp_routes--; + stats->routes--; } if (new) { bmap_set(&c->export_map, new->id); - stats->exp_routes++; + stats->routes++; } if (p->debug & D_ROUTES) @@ -495,9 +495,9 @@ static void rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old, int refeed) { if (new) - c->stats.exp_updates_received++; + c->export_stats.updates_received++; else - c->stats.exp_withdraws_received++; + c->export_stats.withdraws_received++; if (new) new = export_filter(c, new, 0); @@ -537,9 +537,9 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang */ if (net->routes) - c->stats.exp_updates_received++; + c->export_stats.updates_received++; else - c->stats.exp_withdraws_received++; + c->export_stats.withdraws_received++; /* Find old_best - either old_changed, or route for net->routes */ if (old_changed && bmap_test(&c->export_map, old_changed->id)) @@ -655,9 +655,9 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed return; if (new_best) - c->stats.exp_updates_received++; + c->export_stats.updates_received++; else - c->stats.exp_withdraws_received++; + c->export_stats.withdraws_received++; /* Prepare new merged route */ if (new_best) @@ -735,9 +735,9 @@ rte_announce(rtable *tab, uint type, net *net, struct rte_storage *new, struct r if (new_best != old_best) { if (new_best) - new_best->rte.sender->stats.pref_routes++; + new_best->rte.sender->import_stats.pref++; if (old_best) - old_best->rte.sender->stats.pref_routes--; + old_best->rte.sender->import_stats.pref--; if (tab->hostcache) rt_notify_hostcache(tab, net); @@ -836,7 +836,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) { struct proto *p = c->proto; struct rtable *table = c->table; - struct proto_stats *stats = &c->stats; + struct import_stats *stats = &c->import_stats; struct rte_storage *old_best_stored = net->routes, *old_stored = NULL; rte *old_best = old_best_stored ? &old_best_stored->rte : NULL; rte *old = NULL; @@ -873,7 +873,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) if (!rte_is_filtered(new)) { - stats->imp_updates_ignored++; + stats->updates_ignored++; rte_trace_in(D_ROUTES, c, new, "ignored"); } @@ -887,7 +887,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) if (!old && !new) { - stats->imp_withdraws_ignored++; + stats->withdraws_ignored++; return; } @@ -897,7 +897,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) struct channel_limit *l = &c->rx_limit; if (l->action && !old && new && !c->in_table) { - u32 all_routes = stats->imp_routes + stats->filt_routes; + u32 all_routes = stats->routes + stats->filtered; if (all_routes >= l->limit) channel_notify_limit(c, l, PLD_RX, all_routes); @@ -907,7 +907,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) /* In receive limit the situation is simple, old is NULL so we just free new and exit like nothing happened */ - stats->imp_updates_ignored++; + stats->updates_ignored++; rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); return; } @@ -916,8 +916,8 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) l = &c->in_limit; if (l->action && !old_ok && new_ok) { - if (stats->imp_routes >= l->limit) - channel_notify_limit(c, l, PLD_IN, stats->imp_routes); + if (stats->routes >= l->limit) + channel_notify_limit(c, l, PLD_IN, stats->routes); if (l->state == PLS_BLOCKED) { @@ -928,7 +928,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) if both are NULL as this case is probably assumed to be already handled. */ - stats->imp_updates_ignored++; + stats->updates_ignored++; rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); if (c->in_keep_filtered) @@ -948,11 +948,11 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) } if (new_ok) - stats->imp_updates_accepted++; + stats->updates_accepted++; else if (old_ok) - stats->imp_withdraws_accepted++; + stats->withdraws_accepted++; else - stats->imp_withdraws_ignored++; + stats->withdraws_ignored++; if (old_ok || new_ok) table->last_rt_change = current_time(); @@ -961,9 +961,9 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) struct rte_storage *new_stored = new ? rte_store(new, net, table) : NULL; if (new) - rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; + rte_is_filtered(new) ? stats->filtered++ : stats->routes++; if (old) - rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--; + rte_is_filtered(old) ? stats->filtered-- : stats->routes--; if (table->config->sorted) { @@ -1128,7 +1128,7 @@ rte_update(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; - struct proto_stats *stats = &c->stats; + struct import_stats *stats = &c->import_stats; const struct filter *filter = c->in_filter; net *nn; @@ -1140,17 +1140,17 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) new->net = n; new->sender = c; - stats->imp_updates_received++; + stats->updates_received++; if (!rte_validate(new)) { rte_trace_in(D_FILTERS, c, new, "invalid"); - stats->imp_updates_invalid++; + stats->updates_invalid++; goto drop; } if (filter == FILTER_REJECT) { - stats->imp_updates_filtered++; + stats->updates_filtered++; rte_trace_in(D_FILTERS, c, new, "filtered out"); if (! c->in_keep_filtered) @@ -1164,7 +1164,7 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) int fr = f_run(filter, new, rte_update_pool, 0); if (fr > F_ACCEPT) { - stats->imp_updates_filtered++; + stats->updates_filtered++; rte_trace_in(D_FILTERS, c, new, "filtered out"); if (! c->in_keep_filtered) @@ -1180,11 +1180,11 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) } else { - stats->imp_withdraws_received++; + stats->withdraws_received++; if (!(nn = net_find(c->table, n)) || !src) { - stats->imp_withdraws_ignored++; + stats->withdraws_ignored++; rte_update_unlock(); return; } @@ -2297,8 +2297,8 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr return 1; drop_update: - c->stats.imp_updates_received++; - c->stats.imp_updates_ignored++; + c->import_stats.updates_received++; + c->import_stats.updates_ignored++; if (!net->routes) fib_delete(&tab->fib, net); @@ -2306,8 +2306,8 @@ drop_update: return 0; drop_withdraw: - c->stats.imp_withdraws_received++; - c->stats.imp_withdraws_ignored++; + c->import_stats.withdraws_received++; + c->import_stats.withdraws_ignored++; return 0; } diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 7604cc79..55a0b526 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -212,8 +212,10 @@ pipe_get_status(struct proto *P, byte *buf) static void pipe_show_stats(struct pipe_proto *p) { - struct proto_stats *s1 = &p->pri->stats; - struct proto_stats *s2 = &p->sec->stats; + struct import_stats *s1i = &p->pri->import_stats; + struct export_stats *s1e = &p->pri->export_stats; + struct import_stats *s2i = &p->sec->import_stats; + struct export_stats *s2e = &p->sec->export_stats; /* * Pipe stats (as anything related to pipes) are a bit tricky. There @@ -237,20 +239,20 @@ pipe_show_stats(struct pipe_proto *p) */ cli_msg(-1006, " Routes: %u imported, %u exported", - s1->imp_routes, s2->imp_routes); + s1i->routes, s2i->routes); cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", - s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid, - s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted); + s2e->updates_received, s2e->updates_rejected + s1i->updates_invalid, + s2e->updates_filtered, s1i->updates_ignored, s1i->updates_accepted); cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", - s2->exp_withdraws_received, s1->imp_withdraws_invalid, - s1->imp_withdraws_ignored, s1->imp_withdraws_accepted); + s2e->withdraws_received, s1i->withdraws_invalid, + s1i->withdraws_ignored, s1i->withdraws_accepted); cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u", - s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid, - s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted); + s1e->updates_received, s1e->updates_rejected + s2i->updates_invalid, + s1e->updates_filtered, s2i->updates_ignored, s2i->updates_accepted); cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u", - s1->exp_withdraws_received, s2->imp_withdraws_invalid, - s2->imp_withdraws_ignored, s2->imp_withdraws_accepted); + s1e->withdraws_received, s2i->withdraws_invalid, + s2i->withdraws_ignored, s2i->withdraws_accepted); } static const char *pipe_feed_state[] = { [ES_DOWN] = "down", [ES_FEEDING] = "feed", [ES_READY] = "up" }; From 3a8197a9dce6fc5d38b089a291ac79d8d394fea1 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sat, 6 Nov 2021 20:34:16 +0100 Subject: [PATCH 007/431] Limit containment --- nest/limit.h | 49 ++++++++++ nest/proto.c | 225 +++++++++++++++++++++++++--------------------- nest/protocol.h | 40 ++++++--- nest/rt-table.c | 144 +++++++++++++---------------- proto/pipe/pipe.c | 13 ++- 5 files changed, 269 insertions(+), 202 deletions(-) create mode 100644 nest/limit.h diff --git a/nest/limit.h b/nest/limit.h new file mode 100644 index 00000000..5838ad3b --- /dev/null +++ b/nest/limit.h @@ -0,0 +1,49 @@ +/* + * BIRD Internet Routing Daemon -- Limits + * + * (c) 1998--2000 Martin Mares + * (c) 2021 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_LIMIT_H_ +#define _BIRD_LIMIT_H_ + +struct limit { + u32 max; + u32 count; + int (*action)(struct limit *, void *data); +}; + +static inline int limit_do_action(struct limit *l, void *data) +{ + return l->action ? l->action(l, data) : 1; +} + +static inline int limit_push(struct limit *l, void *data) +{ + if ((l->count >= l->max) && limit_do_action(l, data)) + return 1; + + l->count++; + return 0; +} + +static inline void limit_pop(struct limit *l) +{ + --l->count; +} + +static inline void limit_reset(struct limit *l) +{ + l->count = 0; +} + +static inline void limit_update(struct limit *l, void *data, u32 max) +{ + if (l->count > (l->max = max)) + limit_do_action(l, data); +} + +#endif diff --git a/nest/proto.c b/nest/proto.c index c7e25209..2009ff1f 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -52,9 +52,9 @@ static void channel_request_reload(struct channel *c); static void proto_shutdown_loop(timer *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); -static void channel_verify_limits(struct channel *c); -static inline void channel_reset_limit(struct channel_limit *l); - +static void channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf); +static void channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf); +static void channel_reset_limit(struct channel *c, struct limit *l, int dir); static inline int proto_is_done(struct proto *p) { return (p->proto_state == PS_DOWN) && (p->active_channels == 0); } @@ -168,9 +168,10 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->in_filter = cf->in_filter; c->out_filter = cf->out_filter; - c->rx_limit = cf->rx_limit; - c->in_limit = cf->in_limit; - c->out_limit = cf->out_limit; + + channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit); + channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit); + channel_init_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit); c->net_type = cf->net_type; c->ra_mode = cf->ra_mode; @@ -280,14 +281,12 @@ channel_feed_loop(void *ptr) } /* Reset export limit if the feed ended with acceptable number of exported routes */ - struct channel_limit *l = &c->out_limit; if (c->refeeding && - (l->state == PLS_BLOCKED) && - (c->refeed_count <= l->limit) && - (c->export_stats.routes <= l->limit)) + (c->limit_active & (1 << PLD_OUT)) && + (c->refeed_count <= c->out_limit.max)) { - log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit); - channel_reset_limit(&c->out_limit); + log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, c->out_limit.max); + channel_reset_limit(c, &c->out_limit, PLD_OUT); /* Continue in feed - it will process routing table again from beginning */ c->refeed_count = 0; @@ -461,7 +460,8 @@ channel_stop_export(struct channel *c) rt_feed_channel_abort(c); c->export_state = ES_DOWN; - c->export_stats.routes = 0; + + channel_reset_limit(c, &c->out_limit, PLD_OUT); bmap_reset(&c->export_map, 1024); bmap_reset(&c->export_reject_map, 1024); } @@ -556,9 +556,9 @@ channel_do_start(struct channel *c) memset(&c->export_stats, 0, sizeof(struct export_stats)); memset(&c->import_stats, 0, sizeof(struct import_stats)); - channel_reset_limit(&c->rx_limit); - channel_reset_limit(&c->in_limit); - channel_reset_limit(&c->out_limit); + channel_reset_limit(c, &c->rx_limit, PLD_RX); + channel_reset_limit(c, &c->in_limit, PLD_IN); + channel_reset_limit(c, &c->out_limit, PLD_OUT); CALL(c->channel->start, c); } @@ -604,8 +604,8 @@ channel_do_down(struct channel *c) rt_unlock_table(c->table); c->proto->active_channels--; - if ((c->import_stats.routes + c->import_stats.filtered) != 0) - log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name); + if (c->in_limit.count || c->rx_limit.count) + bug("%s: Channel %s is down but still has some routes", c->proto->name, c->name); // bmap_free(&c->export_map); memset(&c->import_stats, 0, sizeof(struct import_stats)); @@ -748,8 +748,8 @@ channel_request_reload(struct channel *c) * Should this be done before reload_routes() hook? * Perhaps, but routes are updated asynchronously. */ - channel_reset_limit(&c->rx_limit); - channel_reset_limit(&c->in_limit); + channel_reset_limit(c, &c->rx_limit, PLD_RX); + channel_reset_limit(c, &c->in_limit, PLD_IN); } const struct channel_class channel_basic = { @@ -852,9 +852,10 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) /* Reconfigure channel fields */ c->in_filter = cf->in_filter; c->out_filter = cf->out_filter; - c->rx_limit = cf->rx_limit; - c->in_limit = cf->in_limit; - c->out_limit = cf->out_limit; + + channel_update_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit); + channel_update_limit(c, &c->in_limit, PLD_IN, &cf->in_limit); + channel_update_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit); // c->ra_mode = cf->ra_mode; c->merge_limit = cf->merge_limit; @@ -863,8 +864,6 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) c->in_keep_filtered = cf->in_keep_filtered; c->rpki_reload = cf->rpki_reload; - channel_verify_limits(c); - /* Execute channel-specific reconfigure hook */ if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed)) return 0; @@ -1785,88 +1784,104 @@ proto_set_message(struct proto *p, char *msg, int len) } -static const char * -channel_limit_name(struct channel_limit *l) -{ - const char *actions[] = { - [PLA_WARN] = "warn", - [PLA_BLOCK] = "block", - [PLA_RESTART] = "restart", - [PLA_DISABLE] = "disable", - }; +static const char * channel_limit_name[] = { + [PLA_WARN] = "warn", + [PLA_BLOCK] = "block", + [PLA_RESTART] = "restart", + [PLA_DISABLE] = "disable", +}; - return actions[l->action]; -} -/** - * channel_notify_limit: notify about limit hit and take appropriate action - * @c: channel - * @l: limit being hit - * @dir: limit direction (PLD_*) - * @rt_count: the number of routes - * - * The function is called by the route processing core when limit @l - * is breached. It activates the limit and tooks appropriate action - * according to @l->action. - */ -void -channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count) +static void +channel_log_limit(struct channel *c, struct limit *l, int dir) { const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; - const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; - struct proto *p = c->proto; - - if (l->state == PLS_BLOCKED) - return; - - /* For warning action, we want the log message every time we hit the limit */ - if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) - log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", - p->name, dir_name[dir], l->limit, channel_limit_name(l)); - - switch (l->action) - { - case PLA_WARN: - l->state = PLS_ACTIVE; - break; - - case PLA_BLOCK: - l->state = PLS_BLOCKED; - break; - - case PLA_RESTART: - case PLA_DISABLE: - l->state = PLS_BLOCKED; - if (p->proto_state == PS_UP) - proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); - break; - } + log(L_WARN "Channel %s.%s hits route %s limit (%d), action: %s", + c->proto->name, c->name, dir_name[dir], l->max, channel_limit_name[c->limit_actions[dir]]); } static void -channel_verify_limits(struct channel *c) +channel_activate_limit(struct channel *c, struct limit *l, int dir) { - struct channel_limit *l; - u32 all_routes = c->import_stats.routes + c->import_stats.filtered; + if (c->limit_active & (1 << dir)) + return; - l = &c->rx_limit; - if (l->action && (all_routes > l->limit)) - channel_notify_limit(c, l, PLD_RX, all_routes); - - l = &c->in_limit; - if (l->action && (c->import_stats.routes > l->limit)) - channel_notify_limit(c, l, PLD_IN, c->import_stats.routes); - - l = &c->out_limit; - if (l->action && (c->export_stats.routes > l->limit)) - channel_notify_limit(c, l, PLD_OUT, c->export_stats.routes); + c->limit_active |= (1 << dir); + channel_log_limit(c, l, dir); } -static inline void -channel_reset_limit(struct channel_limit *l) +static int +channel_limit_warn(struct limit *l, void *data) { - if (l->action) - l->state = PLS_INITIAL; + struct channel_limit_data *cld = data; + struct channel *c = cld->c; + int dir = cld->dir; + + channel_log_limit(c, l, dir); + + return 0; +} + +static int +channel_limit_block(struct limit *l, void *data) +{ + struct channel_limit_data *cld = data; + struct channel *c = cld->c; + int dir = cld->dir; + + channel_activate_limit(c, l, dir); + + return 1; +} + +static const byte chl_dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; + +static int +channel_limit_down(struct limit *l, void *data) +{ + struct channel_limit_data *cld = data; + struct channel *c = cld->c; + struct proto *p = c->proto; + int dir = cld->dir; + + channel_activate_limit(c, l, dir); + + if (p->proto_state == PS_UP) + proto_schedule_down(p, c->limit_actions[dir] == PLA_RESTART, chl_dir_down[dir]); + + return 1; +} + +static int (*channel_limit_action[])(struct limit *, void *) = { + [PLA_NONE] = NULL, + [PLA_WARN] = channel_limit_warn, + [PLA_BLOCK] = channel_limit_block, + [PLA_RESTART] = channel_limit_down, + [PLA_DISABLE] = channel_limit_down, +}; + +static void +channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf) +{ + l->action = channel_limit_action[cf->action]; + c->limit_actions[dir] = cf->action; + + struct channel_limit_data cld = { .c = c, .dir = dir }; + limit_update(l, &cld, cf->action ? cf->limit : ~((u32) 0)); +} + +static void +channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf) +{ + channel_reset_limit(c, l, dir); + channel_update_limit(c, l, dir, cf); +} + +static void +channel_reset_limit(struct channel *c, struct limit *l, int dir) +{ + limit_reset(l); + c->limit_active &= ~(1 << dir); } static inline void @@ -2017,12 +2032,16 @@ channel_show_stats(struct channel *c) struct import_stats *is = &c->import_stats; struct export_stats *es = &c->export_stats; + u32 rx_routes = c->rx_limit.count; + u32 in_routes = c->in_limit.count; + u32 out_routes = c->out_limit.count; + if (c->in_keep_filtered) cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", - is->routes, is->filtered, es->routes, is->pref); + in_routes, (rx_routes - in_routes), out_routes, is->pref); else cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", - is->routes, es->routes, is->pref); + in_routes, out_routes, is->pref); cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", @@ -2040,13 +2059,13 @@ channel_show_stats(struct channel *c) } void -channel_show_limit(struct channel_limit *l, const char *dsc) +channel_show_limit(struct limit *l, const char *dsc, int active, int action) { if (!l->action) return; - cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : ""); - cli_msg(-1006, " Action: %s", channel_limit_name(l)); + cli_msg(-1006, " %-16s%d%s", dsc, l->max, active ? " [HIT]" : ""); + cli_msg(-1006, " Action: %s", channel_limit_name[action]); } void @@ -2064,9 +2083,9 @@ channel_show_info(struct channel *c) c->gr_lock ? " pending" : "", c->gr_wait ? " waiting" : ""); - channel_show_limit(&c->rx_limit, "Receive limit:"); - channel_show_limit(&c->in_limit, "Import limit:"); - channel_show_limit(&c->out_limit, "Export limit:"); + channel_show_limit(&c->rx_limit, "Receive limit:", c->limit_active & (1 << PLD_RX), c->limit_actions[PLD_RX]); + channel_show_limit(&c->in_limit, "Import limit:", c->limit_active & (1 << PLD_IN), c->limit_actions[PLD_IN]); + channel_show_limit(&c->out_limit, "Export limit:", c->limit_active & (1 << PLD_OUT), c->limit_actions[PLD_OUT]); if (c->channel_state != CS_DOWN) channel_show_stats(c); diff --git a/nest/protocol.h b/nest/protocol.h index 4c87c72d..c1978914 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -13,6 +13,7 @@ #include "lib/resource.h" #include "lib/event.h" #include "nest/route.h" +#include "nest/limit.h" #include "conf/conf.h" struct iface; @@ -134,8 +135,6 @@ struct proto_config { /* Protocol statistics */ struct import_stats { /* Import - from protocol to core */ - u32 routes; /* Number of routes successfully imported to the (adjacent) routing table */ - u32 filtered; /* Number of routes rejected in import filter but kept in the routing table */ u32 pref; /* Number of routes selected as best in the (adjacent) routing table */ u32 updates_received; /* Number of route updates received */ u32 updates_invalid; /* Number of route updates rejected as invalid */ @@ -150,7 +149,6 @@ struct import_stats { struct export_stats { /* Export - from core to protocol */ - u32 routes; /* Number of routes successfully exported to the protocol */ u32 updates_received; /* Number of route updates received */ u32 updates_rejected; /* Number of route updates rejected by protocol */ u32 updates_filtered; /* Number of route updates rejected by filters */ @@ -277,7 +275,7 @@ void channel_graceful_restart_unlock(struct channel *c); #define DEFAULT_GR_WAIT 240 -void channel_show_limit(struct channel_limit *l, const char *dsc); +void channel_show_limit(struct limit *l, const char *dsc, int active, int action); void channel_show_info(struct channel *c); void channel_cmd_debug(struct channel *c, uint mask); @@ -432,18 +430,29 @@ extern struct proto_config *cf_dev_proto; #define PLA_RESTART 4 /* Force protocol restart */ #define PLA_DISABLE 5 /* Shutdown and disable protocol */ -#define PLS_INITIAL 0 /* Initial limit state after protocol start */ -#define PLS_ACTIVE 1 /* Limit was hit */ -#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */ - struct channel_limit { u32 limit; /* Maximum number of prefixes */ u8 action; /* Action to take (PLA_*) */ - u8 state; /* State of limit (PLS_*) */ }; -void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count); +struct channel_limit_data { + struct channel *c; + int dir; +}; +#define CLP__RX(_c) (&(_c)->rx_limit) +#define CLP__IN(_c) (&(_c)->in_limit) +#define CLP__OUT(_c) (&(_c)->out_limit) + + +#if 0 +#define CHANNEL_LIMIT_LOG(_c, _dir, _op) log(L_TRACE "%s.%s: %s limit %s %u", (_c)->proto->name, (_c)->name, #_dir, _op, (CLP__##_dir(_c))->count) +#else +#define CHANNEL_LIMIT_LOG(_c, _dir, _op) +#endif + +#define CHANNEL_LIMIT_PUSH(_c, _dir) ({ CHANNEL_LIMIT_LOG(_c, _dir, "push from"); struct channel_limit_data cld = { .c = (_c), .dir = PLD_##_dir }; limit_push(CLP__##_dir(_c), &cld); }) +#define CHANNEL_LIMIT_POP(_c, _dir) ({ limit_pop(CLP__##_dir(_c)); CHANNEL_LIMIT_LOG(_c, _dir, "pop to"); }) /* * Channels @@ -486,6 +495,7 @@ struct channel_config { struct proto_config *parent; /* Where channel is defined (proto or template) */ struct rtable_config *table; /* Table we're attached to */ const struct filter *in_filter, *out_filter; /* Attached filters */ + struct channel_limit rx_limit; /* Limit for receiving routes from protocol (relevant when in_keep_filtered is active) */ struct channel_limit in_limit; /* Limit for importing routes from protocol */ @@ -513,9 +523,13 @@ struct channel { const struct filter *out_filter; /* Output filter */ struct bmap export_map; /* Keeps track which routes were really exported */ struct bmap export_reject_map; /* Keeps track which routes were rejected by export filter */ - struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */ - struct channel_limit in_limit; /* Input limit */ - struct channel_limit out_limit; /* Output limit */ + + struct limit rx_limit; /* Receive limit (for in_keep_filtered) */ + struct limit in_limit; /* Input limit */ + struct limit out_limit; /* Output limit */ + + u8 limit_actions[PLD_MAX]; /* Limit actions enum */ + u8 limit_active; /* Flags for active limits */ struct event *feed_event; /* Event responsible for feeding */ struct fib_iterator feed_fit; /* Routing table iterator used during feeding */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 146734f4..66e63acf 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -473,20 +473,16 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref if (refeed && new) c->refeed_count++; - /* Apply export limit */ - struct channel_limit *l = &c->out_limit; - if (l->action && !old && new) - { - if (stats->routes >= l->limit) - channel_notify_limit(c, l, PLD_OUT, stats->routes); - - if (l->state == PLS_BLOCKED) + if (!old && new) + if (CHANNEL_LIMIT_PUSH(c, OUT)) { stats->updates_rejected++; rte_trace_out(D_FILTERS, c, new, "rejected [limit]"); return; } - } + + if (!new && old) + CHANNEL_LIMIT_POP(c, OUT); /* Apply export table */ struct rte_storage *old_exported = NULL; @@ -505,16 +501,10 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref stats->withdraws_accepted++; if (old) - { bmap_clear(&c->export_map, old->id); - stats->routes--; - } if (new) - { bmap_set(&c->export_map, new->id); - stats->routes++; - } if (p->debug & D_ROUTES) { @@ -973,58 +963,53 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) int new_ok = rte_is_ok(new); int old_ok = rte_is_ok(old); - struct channel_limit *l = &c->rx_limit; - if (l->action && !old && new && !c->in_table) + if (!c->in_table) + { + if (!old && new) + if (CHANNEL_LIMIT_PUSH(c, RX)) + { + /* In receive limit the situation is simple, old is NULL so + we just free new and exit like nothing happened */ + + stats->updates_ignored++; + rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); + return; + } + + if (old && !new) + CHANNEL_LIMIT_POP(c, RX); + } + + if (!old_ok && new_ok) + if (CHANNEL_LIMIT_PUSH(c, IN)) { - u32 all_routes = stats->routes + stats->filtered; + /* In import limit the situation is more complicated. We + shouldn't just drop the route, we should handle it like + it was filtered. We also have to continue the route + processing if old or new is non-NULL, but we should exit + if both are NULL as this case is probably assumed to be + already handled. */ - if (all_routes >= l->limit) - channel_notify_limit(c, l, PLD_RX, all_routes); + stats->updates_ignored++; + rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - if (l->state == PLS_BLOCKED) - { - /* In receive limit the situation is simple, old is NULL so - we just free new and exit like nothing happened */ + if (c->in_keep_filtered) + new->flags |= REF_FILTERED; + else + new = NULL; - stats->updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - return; - } + /* Note that old && !new could be possible when + c->in_keep_filtered changed in the recent past. */ + + if (!old && !new) + return; + + new_ok = 0; + goto skip_stats1; } - l = &c->in_limit; - if (l->action && !old_ok && new_ok) - { - if (stats->routes >= l->limit) - channel_notify_limit(c, l, PLD_IN, stats->routes); - - if (l->state == PLS_BLOCKED) - { - /* In import limit the situation is more complicated. We - shouldn't just drop the route, we should handle it like - it was filtered. We also have to continue the route - processing if old or new is non-NULL, but we should exit - if both are NULL as this case is probably assumed to be - already handled. */ - - stats->updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - - if (c->in_keep_filtered) - new->flags |= REF_FILTERED; - else - new = NULL; - - /* Note that old && !new could be possible when - c->in_keep_filtered changed in the recent past. */ - - if (!old && !new) - return; - - new_ok = 0; - goto skip_stats1; - } - } + if (old_ok && !new_ok) + CHANNEL_LIMIT_POP(c, IN); if (new_ok) stats->updates_accepted++; @@ -1039,11 +1024,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) skip_stats1:; struct rte_storage *new_stored = new ? rte_store(new, net, table) : NULL; - if (new) - rte_is_filtered(new) ? stats->filtered++ : stats->routes++; - if (old) - rte_is_filtered(old) ? stats->filtered-- : stats->routes--; - if (table->config->sorted) { /* If routes are sorted, just insert new route to appropriate position */ @@ -2332,6 +2312,9 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr goto drop_update; } + if (!new) + CHANNEL_LIMIT_POP(c, RX); + /* Move iterator if needed */ if (*pos == c->reload_next_rte) c->reload_next_rte = (*pos)->next; @@ -2342,7 +2325,18 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr rte_free(del, tab); tab->rt_count--; } - else if (!new) + else if (new) + { + if (CHANNEL_LIMIT_PUSH(c, RX)) + { + /* Required by rte_trace_in() */ + new->net = n; + + rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); + goto drop_update; + } + } + else goto drop_withdraw; if (!new) @@ -2353,22 +2347,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr return 1; } - struct channel_limit *l = &c->rx_limit; - if (l->action && !*pos) - { - if (tab->rt_count >= l->limit) - channel_notify_limit(c, l, PLD_RX, tab->rt_count); - - if (l->state == PLS_BLOCKED) - { - /* Required by rte_trace_in() */ - new->net = n; - - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - goto drop_update; - } - } - /* Insert the new rte */ struct rte_storage *e = rte_store(new, net, tab); e->rte.sender = c; diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 55a0b526..74d0e518 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -217,6 +217,9 @@ pipe_show_stats(struct pipe_proto *p) struct import_stats *s2i = &p->sec->import_stats; struct export_stats *s2e = &p->sec->export_stats; + u32 pri_routes = p->pri->in_limit.count; + u32 sec_routes = p->sec->in_limit.count; + /* * Pipe stats (as anything related to pipes) are a bit tricky. There * are two sets of stats - s1 for ahook to the primary routing and @@ -239,7 +242,7 @@ pipe_show_stats(struct pipe_proto *p) */ cli_msg(-1006, " Routes: %u imported, %u exported", - s1i->routes, s2i->routes); + pri_routes, sec_routes); cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", s2e->updates_received, s2e->updates_rejected + s1i->updates_invalid, @@ -270,8 +273,12 @@ pipe_show_proto_info(struct proto *P) cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter)); cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter)); - channel_show_limit(&p->pri->in_limit, "Import limit:"); - channel_show_limit(&p->sec->in_limit, "Export limit:"); + + + channel_show_limit(&p->pri->in_limit, "Import limit:", + (p->pri->limit_active & (1 << PLD_IN)), p->pri->limit_actions[PLD_IN]); + channel_show_limit(&p->sec->in_limit, "Export limit:", + (p->sec->limit_active & (1 << PLD_IN)), p->sec->limit_actions[PLD_IN]); if (P->proto_state != PS_DOWN) pipe_show_stats(p); From 575da88f7a6cac54d204839d2b2cfc1809811ba3 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Thu, 25 Feb 2021 21:52:49 +0100 Subject: [PATCH 008/431] Recursive route nexthop updates now announced with valid new_best/old_best information --- nest/rt-table.c | 84 +++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index ee69d7c4..837e0ab9 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -349,7 +349,7 @@ rte_mergable(rte *pri, rte *sec) } static void -rte_trace(struct channel *c, rte *e, int dir, char *msg) +rte_trace(struct channel *c, rte *e, int dir, const char *msg) { log(L_TRACE "%s.%s %c %s %N %uL %uG %s", c->proto->name, c->name ?: "?", dir, msg, e->net, e->src->private_id, e->src->global_id, @@ -357,14 +357,14 @@ rte_trace(struct channel *c, rte *e, int dir, char *msg) } static inline void -rte_trace_in(uint flag, struct channel *c, rte *e, char *msg) +rte_trace_in(uint flag, struct channel *c, rte *e, const char *msg) { if ((c->debug & flag) || (c->proto->debug & flag)) rte_trace(c, e, '>', msg); } static inline void -rte_trace_out(uint flag, struct channel *c, rte *e, char *msg) +rte_trace_out(uint flag, struct channel *c, rte *e, const char *msg) { if ((c->debug & flag) || (c->proto->debug & flag)) rte_trace(c, e, '<', msg); @@ -1870,44 +1870,50 @@ rt_next_hop_update_rte(rtable *tab, net *n, rte *old) static inline int rt_next_hop_update_net(rtable *tab, net *n) { - struct rte_storage **k, *e, *new, *old_best, **new_best; + struct rte_storage *new; int count = 0; - int free_old_best = 0; - old_best = n->routes; + struct rte_storage *old_best = n->routes; if (!old_best) return 0; - for (k = &n->routes; e = *k; k = &e->next) + for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next) if (rta_next_hop_outdated(e->rte.attrs)) - { - new = rt_next_hop_update_rte(tab, n, &e->rte); - new->next = e->next; - *k = new; - - rte_trace_in(D_ROUTES, new->rte.sender, &new->rte, "updated"); - rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL); - - /* Call a pre-comparison hook */ - /* Not really an efficient way to compute this */ - if (e->rte.src->proto->rte_recalculate) - e->rte.src->proto->rte_recalculate(tab, n, &new->rte, &e->rte, NULL); - - if (e != old_best) - rte_free(e, tab); - else /* Freeing of the old best rte is postponed */ - free_old_best = 1; - - e = new; - count++; - } + count++; if (!count) return 0; + struct rte_multiupdate { + struct rte_storage *old, *new; + } *updates = alloca(sizeof(struct rte_multiupdate) * count); + + int pos = 0; + for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next) + if (rta_next_hop_outdated(e->rte.attrs)) + { + struct rte_storage *new = rt_next_hop_update_rte(tab, n, &e->rte); + + /* Call a pre-comparison hook */ + /* Not really an efficient way to compute this */ + if (e->rte.src->proto->rte_recalculate) + e->rte.src->proto->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte); + + updates[pos++] = (struct rte_multiupdate) { + .old = e, + .new = new, + }; + + /* Replace the route in the list */ + new->next = e->next; + *k = e = new; + } + + ASSERT_DIE(pos == count); + /* Find the new best route */ - new_best = NULL; - for (k = &n->routes; e = *k; k = &e->next) + struct rte_storage **new_best = NULL; + for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next) { if (!new_best || rte_better(&e->rte, &(*new_best)->rte)) new_best = k; @@ -1922,15 +1928,17 @@ rt_next_hop_update_net(rtable *tab, net *n) n->routes = new; } - /* Announce the new best route */ - if (new != old_best) - rte_trace_in(D_ROUTES, new->rte.sender, &new->rte, "updated [best]"); + /* Announce the changes */ + for (int i=0; irte.sender, &updates[i].new->rte, best_indicator[nb][ob]); + rte_announce_i(tab, RA_UNDEF, n, updates[i].new, updates[i].old, new, old_best); + } - /* Propagate changes */ - rte_announce_i(tab, RA_UNDEF, n, NULL, NULL, n->routes, old_best); - - if (free_old_best) - rte_free(old_best, tab); + for (int i=0; i Date: Fri, 1 May 2020 22:26:24 +0200 Subject: [PATCH 009/431] Nest: Route generations and explicit tracking route propagion through pipes --- conf/conf.h | 1 + doc/bird.sgml | 8 ++++++++ nest/config.Y | 3 ++- nest/route.h | 5 ++++- nest/rt-table.c | 47 +++++++++++++++++++++++---------------------- proto/pipe/config.Y | 7 ++++++- proto/pipe/pipe.c | 32 ++++++++++++++++-------------- proto/pipe/pipe.h | 2 ++ 8 files changed, 65 insertions(+), 40 deletions(-) diff --git a/conf/conf.h b/conf/conf.h index 55cb9c58..69ef8a10 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -45,6 +45,7 @@ struct config { int cli_debug; /* Tracing of CLI connections and commands */ int latency_debug; /* I/O loop tracks duration of each event */ + int pipe_debug; /* Track route propagation through pipes */ u32 latency_limit; /* Events with longer duration are logged (us) */ u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */ diff --git a/doc/bird.sgml b/doc/bird.sgml index a2138b55..d1a3b70f 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -4124,6 +4124,14 @@ include standard channel config options; see the example below.