From f4deef89bebae6e41654217e20f2a7531c65bf49 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 18 Aug 2023 03:53:58 +0200 Subject: BMP: Refactor route monitoring - Manage BMP state through bmp_peer, bmp_stream, bmp_table structures - Use channels and rt_notify() hook for route announcements - Add support for post-policy monitoring - Send End-of-RIB even when there is no routes - Remove rte_update_in_notify() hook from import tables - Update import tables to support channels - Add bmp_hack (no feed / no flush) flag to channels --- nest/proto.c | 11 ++++++--- nest/protocol.h | 7 ++++-- nest/rt-table.c | 69 ++++++++++++++++++++++++++++++++------------------------- 3 files changed, 52 insertions(+), 35 deletions(-) (limited to 'nest') diff --git a/nest/proto.c b/nest/proto.c index 885a0b75..16245dca 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -179,6 +179,7 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->merge_limit = cf->merge_limit; c->in_keep_filtered = cf->in_keep_filtered; c->rpki_reload = cf->rpki_reload; + c->bmp_hack = cf->bmp_hack; c->channel_state = CS_DOWN; c->export_state = ES_DOWN; @@ -450,7 +451,10 @@ channel_start_export(struct channel *c) ASSERT(c->channel_state == CS_UP); ASSERT(c->export_state == ES_DOWN); - channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ + if (!c->bmp_hack) + channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ + else + c->export_state = ES_READY; } static void @@ -523,7 +527,7 @@ channel_setup_in_table(struct channel *c) cf->addr_type = c->net_type; cf->internal = 1; - c->in_table = rt_setup(c->proto->pool, cf); + c->in_table = cf->table = rt_setup(c->proto->pool, cf); c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c); } @@ -574,7 +578,8 @@ channel_do_up(struct channel *c) static void channel_do_flush(struct channel *c) { - rt_schedule_prune(c->table); + if (!c->bmp_hack) + rt_schedule_prune(c->table); c->gr_wait = 0; if (c->gr_lock) diff --git a/nest/protocol.h b/nest/protocol.h index da6d434e..d94a11bc 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -214,7 +214,6 @@ struct proto { void (*if_notify)(struct proto *, unsigned flags, struct iface *i); void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old); - void (*rte_update_in_notify)(struct channel *, const net_addr *, const struct rte *, const struct rte_src *); void (*neigh_notify)(struct neighbor *neigh); int (*preexport)(struct channel *, struct rte *rt); void (*reload_routes)(struct channel *); @@ -477,7 +476,8 @@ struct channel_class { #endif }; -extern struct channel_class channel_bgp; +extern const struct channel_class channel_basic; +extern const struct channel_class channel_bgp; struct channel_config { node n; @@ -500,6 +500,7 @@ struct channel_config { u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */ u8 in_keep_filtered; /* Routes rejected in import filter are kept */ u8 rpki_reload; /* RPKI changes trigger channel reload */ + u8 bmp_hack; /* No feed, no flush */ }; struct channel { @@ -552,6 +553,7 @@ struct channel { u8 reload_pending; /* Reloading and another reload is scheduled */ u8 refeed_pending; /* Refeeding and another refeed is scheduled */ u8 rpki_reload; /* RPKI changes trigger channel reload */ + u8 bmp_hack; /* No feed, no flush */ struct rtable *out_table; /* Internal table for exported routes */ @@ -620,6 +622,7 @@ static inline struct channel_config *proto_cf_main_channel(struct proto_config * struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t); struct channel *proto_find_channel_by_name(struct proto *p, const char *n); struct channel *proto_add_channel(struct proto *p, struct channel_config *cf); +void proto_remove_channel(struct proto *p, struct channel *c); int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf); void channel_set_state(struct channel *c, uint state); diff --git a/nest/rt-table.c b/nest/rt-table.c index 2b065032..e8478c36 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -3063,6 +3063,23 @@ rt_feed_channel_abort(struct channel *c) * Import table */ +static void +rte_announce_in(struct rtable *tab, struct network *net, struct rte *new, struct rte *old) +{ + struct channel *c; node *n; + WALK_LIST2(c, n, tab->channels, table_node) + { + if (c->export_state == ES_DOWN) + continue; + + if (c->ra_mode != RA_ANY) + continue; + + struct proto *p = c->proto; + p->rt_notify(p, c, net, new, old); + } +} + int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { @@ -3096,9 +3113,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr { old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY); - if (c->proto->rte_update_in_notify) - c->proto->rte_update_in_notify(c, n, old, src); - return 1; } @@ -3111,28 +3125,15 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr /* Remove the old rte */ *pos = old->next; - rte_free_quick(old); tab->rt_count--; - break; } - if (!new) - { - if (!old) - goto drop_withdraw; - - if (!net->routes) - fib_delete(&tab->fib, net); - - if (c->proto->rte_update_in_notify) - c->proto->rte_update_in_notify(c, n, NULL, src); - - return 1; - } + if (!old && !new) + goto drop_withdraw; struct channel_limit *l = &c->rx_limit; - if (l->action && !old) + if (l->action && !old && new) { if (tab->rt_count >= l->limit) channel_notify_limit(c, l, PLD_RX, tab->rt_count); @@ -3147,18 +3148,26 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr } } - /* 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++; + if (new) + { + /* 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 = new = e; + tab->rt_count++; + } - if (c->proto->rte_update_in_notify) - c->proto->rte_update_in_notify(c, n, e, src); + rte_announce_in(tab, net, new, old); + + if (old) + rte_free_quick(old); + + if (!net->routes) + fib_delete(&tab->fib, net); return 1; -- cgit v1.2.3 From ef6ab5ce86af925ee10687fde4e62912fb892433 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 21 Aug 2023 04:17:21 +0200 Subject: Nest: Use generic rte_announce() also for import tables Remove special rte_announce_in(), so we can use generic rte_announce() for bot feed and notifications. --- nest/rt-table.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'nest') diff --git a/nest/rt-table.c b/nest/rt-table.c index e8478c36..23c4bbb2 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2148,11 +2148,11 @@ rt_setup(pool *pp, struct rtable_config *cf) init_list(&t->flowspec_links); init_list(&t->subscribers); + hmap_init(&t->id_map, p, 1024); + hmap_set(&t->id_map, 0); + if (!(t->internal = cf->internal)) { - hmap_init(&t->id_map, p, 1024); - hmap_set(&t->id_map, 0); - t->rt_event = ev_new_init(p, rt_event, t); t->prune_timer = tm_new_init(p, rt_prune_timer, t, 0, 0); t->last_rt_change = t->gc_time = current_time(); @@ -3063,23 +3063,6 @@ rt_feed_channel_abort(struct channel *c) * Import table */ -static void -rte_announce_in(struct rtable *tab, struct network *net, struct rte *new, struct rte *old) -{ - struct channel *c; node *n; - WALK_LIST2(c, n, tab->channels, table_node) - { - if (c->export_state == ES_DOWN) - continue; - - if (c->ra_mode != RA_ANY) - continue; - - struct proto *p = c->proto; - p->rt_notify(p, c, net, new, old); - } -} - int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { @@ -3159,12 +3142,25 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr e->next = *pos; *pos = new = e; tab->rt_count++; + + if (!old) + { + new->id = hmap_first_zero(&tab->id_map); + hmap_set(&tab->id_map, new->id); + } + else + new->id = old->id; } - rte_announce_in(tab, net, new, old); + rte_announce(tab, RA_ANY, net, new, old, NULL, NULL); if (old) + { + if (!new) + hmap_clear(&tab->id_map, old->id); + rte_free_quick(old); + } if (!net->routes) fib_delete(&tab->fib, net); -- cgit v1.2.3 From 52641e086675832e9f43f86237b0cddbd5ec551e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 21 Aug 2023 04:20:32 +0200 Subject: BMP: Use generic channel feed instead of direct walk over rtable Now we use rt_notify() and channels for both feed and notifications, in both import tables (pre-policy) and regular tables (post-policy). Remove direct walk in bmp_route_monitor_snapshot(). --- nest/proto.c | 5 +--- nest/protocol.h | 4 +-- proto/bmp/bmp.c | 77 ++++++++++++++++++++++++++++++++++++--------------------- proto/bmp/bmp.h | 1 + 4 files changed, 53 insertions(+), 34 deletions(-) (limited to 'nest') diff --git a/nest/proto.c b/nest/proto.c index 16245dca..a5325705 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -451,10 +451,7 @@ channel_start_export(struct channel *c) ASSERT(c->channel_state == CS_UP); ASSERT(c->export_state == ES_DOWN); - if (!c->bmp_hack) - channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ - else - c->export_state = ES_READY; + channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ } static void diff --git a/nest/protocol.h b/nest/protocol.h index d94a11bc..dad0b781 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -500,7 +500,7 @@ struct channel_config { u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */ u8 in_keep_filtered; /* Routes rejected in import filter are kept */ u8 rpki_reload; /* RPKI changes trigger channel reload */ - u8 bmp_hack; /* No feed, no flush */ + u8 bmp_hack; /* No flush */ }; struct channel { @@ -553,7 +553,7 @@ struct channel { u8 reload_pending; /* Reloading and another reload is scheduled */ u8 refeed_pending; /* Refeeding and another refeed is scheduled */ u8 rpki_reload; /* RPKI changes trigger channel reload */ - u8 bmp_hack; /* No feed, no flush */ + u8 bmp_hack; /* No flush */ struct rtable *out_table; /* Internal table for exported routes */ diff --git a/proto/bmp/bmp.c b/proto/bmp/bmp.c index 964d44b5..22b9f2fb 100644 --- a/proto/bmp/bmp.c +++ b/proto/bmp/bmp.c @@ -223,6 +223,8 @@ bmp_send_peer_up_notif_msg(struct bmp_proto *p, const struct bgp_proto *bgp, const byte *tx_data, const size_t tx_data_size, const byte *rx_data, const size_t rx_data_size); +static void bmp_route_monitor_end_of_rib(struct bmp_proto *p, struct bmp_stream *bs); + // Stores necessary any data in list struct bmp_data_node { node n; @@ -237,9 +239,6 @@ struct bmp_data_node { bool policy; }; -static void -bmp_route_monitor_snapshot(struct bmp_proto *p, struct bmp_stream *bs); - static void bmp_common_hdr_serialize(buffer *stream, const enum bmp_message_type type, const u32 data_size) { @@ -571,6 +570,7 @@ bmp_add_stream(struct bmp_proto *p, struct bmp_peer *bp, u32 afi, bool policy, s bmp_lock_table(p, bs->table); bs->sender = sender; + bs->sync = false; return bs; } @@ -634,7 +634,7 @@ bmp_remove_peer(struct bmp_proto *p, struct bmp_peer *bp) } static void -bmp_peer_up_(struct bmp_proto *p, struct bgp_proto *bgp, +bmp_peer_up_(struct bmp_proto *p, struct bgp_proto *bgp, bool sync, const byte *tx_open_msg, uint tx_open_length, const byte *rx_open_msg, uint rx_open_length) { @@ -651,9 +651,20 @@ bmp_peer_up_(struct bmp_proto *p, struct bgp_proto *bgp, bmp_send_peer_up_notif_msg(p, bgp, tx_open_msg, tx_open_length, rx_open_msg, rx_open_length); - struct bmp_stream *bs; - WALK_LIST(bs, bp->streams) - bmp_route_monitor_snapshot(p, bs); + /* + * We asssume peer_up() notifications are received before any route + * notifications from that peer. Therefore, peers established after BMP + * session coould be considered synced with empty RIB. + */ + if (sync) + { + struct bmp_stream *bs; + WALK_LIST(bs, bp->streams) + { + bmp_route_monitor_end_of_rib(p, bs); + bs->sync = true; + } + } } void @@ -663,7 +674,7 @@ bmp_peer_up(struct bgp_proto *bgp, { struct bmp_proto *p; node *n; WALK_LIST2(p, n, bmp_proto_list, bmp_node) - bmp_peer_up_(p, bgp, tx_open_msg, tx_open_length, rx_open_msg, rx_open_length); + bmp_peer_up_(p, bgp, true, tx_open_msg, tx_open_length, rx_open_msg, rx_open_length); } static void @@ -675,7 +686,7 @@ bmp_peer_init(struct bmp_proto *p, struct bgp_proto *bgp) !conn->local_open_msg || !conn->remote_open_msg) return; - bmp_peer_up_(p, bgp, conn->local_open_msg, conn->local_open_length, + bmp_peer_up_(p, bgp, false, conn->local_open_msg, conn->local_open_length, conn->remote_open_msg, conn->remote_open_length); } @@ -856,25 +867,6 @@ bmp_route_monitor_end_of_rib(struct bmp_proto *p, struct bmp_stream *bs) bmp_route_monitor_put_update(p, bs, rx_end_payload, pos - rx_end_payload, current_real_time()); } -static void -bmp_route_monitor_snapshot(struct bmp_proto *p, struct bmp_stream *bs) -{ - struct rtable *tab = bs->table->table; - - struct fib_iterator fit = {}; - FIB_ITERATE_INIT(&fit, &tab->fib); - FIB_ITERATE_START(&tab->fib, &fit, net, n) - { - rte *e; - for (e = n->routes; e; e = e->next) - if (e->sender == &bs->sender->c) - bmp_route_monitor_notify(p, bs, n->n.addr, e, e->src); - } - FIB_ITERATE_END; - - bmp_route_monitor_end_of_rib(p, bs); -} - static void bmp_send_peer_down_notif_msg(struct bmp_proto *p, const struct bgp_proto *bgp, const byte *data, const size_t data_size) @@ -1003,6 +995,34 @@ bmp_rt_notify(struct proto *P, struct channel *c, struct network *net, bmp_route_monitor_notify(p, bs, net->n.addr, new, (new ?: old)->src); } +static void +bmp_feed_end(struct channel *c) +{ + struct bmp_proto *p = (void *) c->proto; + + struct bmp_table *bt = bmp_find_table(p, c->table); + if (!bt) + return; + + /* + * Unsynced streams are added in one moment during BMP session establishment, + * therefore we can assume that all unsynced streams (for given channel) + * already received full feed now and are synced. + * + * TODO: Use more efficent way to find bmp_stream from bmp_table + */ + + HASH_WALK(p->stream_map, next, bs) + { + if ((bs->table == bt) && !bs->sync) + { + bmp_route_monitor_end_of_rib(p, bs); + bs->sync = true; + } + } + HASH_WALK_END; +} + /** * bmp_startup - enter established state @@ -1181,6 +1201,7 @@ bmp_init(struct proto_config *CF) P->rt_notify = bmp_rt_notify; P->preexport = bmp_preexport; + P->feed_end = bmp_feed_end; p->cf = cf; p->local_addr = cf->local_addr; diff --git a/proto/bmp/bmp.h b/proto/bmp/bmp.h index 9b4e2a73..e51c2ea0 100644 --- a/proto/bmp/bmp.h +++ b/proto/bmp/bmp.h @@ -87,6 +87,7 @@ struct bmp_stream { node n; struct bgp_proto *bgp; u32 key; + bool sync; struct bmp_stream *next; struct bmp_table *table; struct bgp_channel *sender; -- cgit v1.2.3