This version attempts to split northd engine processing of advertised routes. The main motivation is to avoid logical flow recomputation when NAT/LB routes change.
Signed-off-by: Martin Kalcok <martin.kal...@canonical.com> --- lib/stopwatch-names.h | 1 + northd/en-advertised-route-sync.c | 167 +++++++++++++++++++++++------- northd/en-advertised-route-sync.h | 4 + northd/en-northd-output.c | 8 ++ northd/en-northd-output.h | 2 + northd/inc-proc-northd.c | 8 ++ northd/northd.c | 38 +++++++ northd/northd.h | 15 ++- 8 files changed, 205 insertions(+), 38 deletions(-) diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h index c12dd28d0..13aa5e7bf 100644 --- a/lib/stopwatch-names.h +++ b/lib/stopwatch-names.h @@ -36,5 +36,6 @@ #define LS_STATEFUL_RUN_STOPWATCH_NAME "ls_stateful" #define ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME "advertised_route_sync" #define LEARNED_ROUTE_SYNC_RUN_STOPWATCH_NAME "learned_route_sync" +#define DYNAMIC_ROUTES_RUN_STOPWATCH_NAME "dynamic_routes" #endif diff --git a/northd/en-advertised-route-sync.c b/northd/en-advertised-route-sync.c index 9e8636806..9d4fb308d 100644 --- a/northd/en-advertised-route-sync.c +++ b/northd/en-advertised-route-sync.c @@ -25,12 +25,14 @@ #include "openvswitch/hmap.h" #include "ovn-util.h" + static void advertised_route_table_sync( struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_advertised_route_table *sbrec_advertised_route_table, const struct lr_stateful_table *lr_stateful_table, - const struct hmap *parsed_routes, + const struct hmap *routes, + const struct hmap *dynamic_routes, struct advertised_route_sync_data *data); bool @@ -141,6 +143,8 @@ en_advertised_route_sync_run(struct engine_node *node, void *data OVS_UNUSED) struct advertised_route_sync_data *routes_sync_data = data; struct routes_data *routes_data = engine_get_input_data("routes", node); + struct dynamic_routes_data *dynamic_routes_data + = engine_get_input_data("dynamic_routes", node); const struct engine_context *eng_ctx = engine_get_context(); const struct sbrec_advertised_route_table *sbrec_advertised_route_table = EN_OVSDB_GET(engine_get_input("SB_advertised_route", node)); @@ -153,12 +157,75 @@ en_advertised_route_sync_run(struct engine_node *node, void *data OVS_UNUSED) sbrec_advertised_route_table, &lr_stateful_data->table, &routes_data->parsed_routes, + &dynamic_routes_data->parsed_routes, routes_sync_data); stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, time_msec()); engine_set_node_state(node, EN_UPDATED); } +void +*en_dynamic_routes_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + struct dynamic_routes_data *data = xmalloc(sizeof *data); + *data = (struct dynamic_routes_data) { + .parsed_routes = HMAP_INITIALIZER(&data->parsed_routes), + }; + + return data; +} + +void +en_dynamic_routes_cleanup(void *data_) +{ + struct dynamic_routes_data *data = data_; + + struct parsed_route *r; + HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) { + parsed_route_free(r); + } + hmap_destroy(&data->parsed_routes); +} + +void +en_dynamic_routes_run(struct engine_node *node, void *data) +{ + struct dynamic_routes_data *dynamic_routes_data = data; + struct northd_data *northd_data = engine_get_input_data("northd", node); + struct ed_type_lr_stateful *lr_stateful_data = + engine_get_input_data("lr_stateful", node); + + const struct lr_stateful_record *lr_stateful_rec; + HMAP_FOR_EACH (lr_stateful_rec, key_node, + &lr_stateful_data->table.entries) { + const struct ovn_datapath *od = + ovn_datapaths_find_by_index(&northd_data->lr_datapaths, + lr_stateful_rec->lr_index); + if (!od->dynamic_routing) { + continue; + } + build_lb_nat_parsed_routes(od, lr_stateful_rec->lrnat_rec, + &dynamic_routes_data->parsed_routes); + + } + engine_set_node_state(node, EN_UPDATED); +} + +bool +dynamic_routes_change_handler(struct engine_node *node OVS_UNUSED, + void *data OVS_UNUSED) +{ + /* XXX: It was suggested to iterate through data in lr_stateful node + * (ed_type_lr_stateful). However the trk_data is only populated when NAT/LB + * changes. While this works for us when NAT/LB is is changed, we also need + * this handler to trigger when dynamic routing options are changed. + * I didn't find a node that would give us only the LR on which options + * changed, so for now I set it to recompute every time. */ + return false; +} + + struct ar_entry { struct hmap_node hmap_node; @@ -335,12 +402,59 @@ publish_host_routes(struct hmap *sync_routes, } } +static void +advertised_route_table_sync_route_add( + const struct lr_stateful_table *lr_stateful_table, + struct advertised_route_sync_data *data, + struct uuidset *host_route_lrps, + struct hmap *sync_routes, + const struct parsed_route *route) +{ + if (route->is_discard_route) { + return; + } + if (prefix_is_link_local(&route->prefix, route->plen)) { + return; + } + if (!route->od->dynamic_routing) { + return; + } + + enum dynamic_routing_redistribute_mode drr = + route->out_port->dynamic_routing_redistribute; + if (route->source == ROUTE_SOURCE_CONNECTED) { + if ((drr & DRRM_CONNECTED) == 0) { + return; + } + /* If we advertise host routes, we only need to do so once per + * LRP. */ + const struct uuid *lrp_uuid = &route->out_port->nbrp->header_.uuid; + if (drr & DRRM_CONNECTED_AS_HOST && + !uuidset_contains(host_route_lrps, lrp_uuid)) { + uuidset_insert(host_route_lrps, lrp_uuid); + publish_host_routes(sync_routes, lr_stateful_table, route, data); + return; + } + } + if (route->source == ROUTE_SOURCE_STATIC && (drr & DRRM_STATIC) == 0) { + return; + } + if (route->source == ROUTE_SOURCE_NAT && (drr & DRRM_NAT) == 0) { + return; + } + + char *ip_prefix = normalize_v46_prefix(&route->prefix, route->plen); + ar_add_entry(sync_routes, route->od->sb, route->out_port->sb, + ip_prefix, NULL); +} + static void advertised_route_table_sync( struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_advertised_route_table *sbrec_advertised_route_table, const struct lr_stateful_table *lr_stateful_table, - const struct hmap *parsed_routes, + const struct hmap *routes, + const struct hmap *dynamic_routes, struct advertised_route_sync_data *data) { struct hmap sync_routes = HMAP_INITIALIZER(&sync_routes); @@ -348,46 +462,25 @@ advertised_route_table_sync( const struct parsed_route *route; struct ar_entry *route_e; - const struct sbrec_advertised_route *sb_route; - HMAP_FOR_EACH (route, key_node, parsed_routes) { - if (route->is_discard_route) { - continue; - } - if (prefix_is_link_local(&route->prefix, route->plen)) { - continue; - } - if (!route->od->dynamic_routing) { - continue; - } - enum dynamic_routing_redistribute_mode drr = - route->out_port->dynamic_routing_redistribute; - if (route->source == ROUTE_SOURCE_CONNECTED) { - if ((drr & DRRM_CONNECTED) == 0) { - continue; - } - /* If we advertise host routes, we only need to do so once per - * LRP. */ - const struct uuid *lrp_uuid = - &route->out_port->nbrp->header_.uuid; - if (drr & DRRM_CONNECTED_AS_HOST && - !uuidset_contains(&host_route_lrps, lrp_uuid)) { - uuidset_insert(&host_route_lrps, lrp_uuid); - publish_host_routes(&sync_routes, lr_stateful_table, - route, data); - continue; - } - } - if (route->source == ROUTE_SOURCE_STATIC && (drr & DRRM_STATIC) == 0) { - continue; - } + /* First build the set of non-dynamic routes that need sync-ing. */ + HMAP_FOR_EACH (route, key_node, routes) { + advertised_route_table_sync_route_add(lr_stateful_table, + data, &host_route_lrps, + &sync_routes, + route); + } - char *ip_prefix = normalize_v46_prefix(&route->prefix, route->plen); - route_e = ar_add_entry(&sync_routes, route->od->sb, - route->out_port->sb, ip_prefix, NULL); + /* First add the set of dynamic routes that need sync-ing. */ + HMAP_FOR_EACH (route, key_node, dynamic_routes) { + advertised_route_table_sync_route_add(lr_stateful_table, + data, &host_route_lrps, + &sync_routes, + route); } uuidset_destroy(&host_route_lrps); + const struct sbrec_advertised_route *sb_route; SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH_SAFE (sb_route, sbrec_advertised_route_table) { route_e = ar_find(&sync_routes, sb_route->datapath, diff --git a/northd/en-advertised-route-sync.h b/northd/en-advertised-route-sync.h index 1f24fd329..94eee0eee 100644 --- a/northd/en-advertised-route-sync.h +++ b/northd/en-advertised-route-sync.h @@ -36,4 +36,8 @@ void *en_advertised_route_sync_init(struct engine_node *, struct engine_arg *); void en_advertised_route_sync_cleanup(void *data); void en_advertised_route_sync_run(struct engine_node *, void *data); +bool dynamic_routes_change_handler(struct engine_node *, void *data); +void *en_dynamic_routes_init(struct engine_node *, struct engine_arg *); +void en_dynamic_routes_cleanup(void *data); +void en_dynamic_routes_run(struct engine_node *, void *data); #endif /* EN_ADVERTISED_ROUTE_SYNC_H */ diff --git a/northd/en-northd-output.c b/northd/en-northd-output.c index 715abd3ab..81c796974 100644 --- a/northd/en-northd-output.c +++ b/northd/en-northd-output.c @@ -97,3 +97,11 @@ northd_output_advertised_route_sync_handler(struct engine_node *node, return true; } +bool +northd_output_dynamic_routes_handler(struct engine_node *node, + void *data OVS_UNUSED) +{ + engine_set_node_state(node, EN_UPDATED); + return true; +} + diff --git a/northd/en-northd-output.h b/northd/en-northd-output.h index 783587cb6..689640118 100644 --- a/northd/en-northd-output.h +++ b/northd/en-northd-output.h @@ -23,5 +23,7 @@ bool northd_output_acl_id_handler(struct engine_node *node, void *data OVS_UNUSED); bool northd_output_advertised_route_sync_handler(struct engine_node *node, void *data OVS_UNUSED); +bool northd_output_dynamic_routes_handler(struct engine_node *node, + void *data OVS_UNUSED); #endif diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index ce7a89ec4..dc88ebd99 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -175,6 +175,7 @@ static ENGINE_NODE(multicast_igmp, "multicast_igmp"); static ENGINE_NODE(acl_id, "acl_id"); static ENGINE_NODE(advertised_route_sync, "advertised_route_sync"); static ENGINE_NODE(learned_route_sync, "learned_route_sync"); +static ENGINE_NODE(dynamic_routes, "dynamic_routes"); void inc_proc_northd_init(struct ovsdb_idl_loop *nb, struct ovsdb_idl_loop *sb) @@ -287,7 +288,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_ecmp_nexthop, &en_sb_mac_binding, ecmp_nexthop_mac_binding_handler); + engine_add_input(&en_dynamic_routes, &en_lr_stateful, + dynamic_routes_change_handler); + engine_add_input(&en_dynamic_routes, &en_northd, engine_noop_handler); + engine_add_input(&en_advertised_route_sync, &en_routes, NULL); + engine_add_input(&en_advertised_route_sync, &en_dynamic_routes, NULL); engine_add_input(&en_advertised_route_sync, &en_sb_advertised_route, NULL); engine_add_input(&en_advertised_route_sync, &en_lr_stateful, @@ -399,6 +405,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, northd_output_acl_id_handler); engine_add_input(&en_northd_output, &en_advertised_route_sync, northd_output_advertised_route_sync_handler); + engine_add_input(&en_northd_output, &en_dynamic_routes, + northd_output_dynamic_routes_handler); struct engine_arg engine_arg = { .nb_idl = nb->idl, diff --git a/northd/northd.c b/northd/northd.c index a6eb70948..4b5708059 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -846,6 +846,14 @@ parse_dynamic_routing_redistribute( out |= DRRM_STATIC; continue; } + if (!strcmp(token, "nat")) { + out |= DRRM_NAT; + continue; + } + if (!strcmp(token, "lb")) { + out |= DRRM_LB; + continue; + } static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "unkown dynamic-routing-redistribute option '%s'", token); @@ -11272,6 +11280,34 @@ build_parsed_routes(const struct ovn_datapath *od, const struct hmap *lr_ports, } } +void +build_lb_nat_parsed_routes(const struct ovn_datapath *od, + const struct lr_nat_record *lr_nat, + struct hmap *routes) +{ + const struct ovn_port *op; + HMAP_FOR_EACH (op, dp_node, &od->ports) { + enum dynamic_routing_redistribute_mode drrm = op->dynamic_routing_redistribute; + if (!(drrm & DRRM_NAT)) { + continue; + } + /* I'm thinking of extending parsed_route struct with "tracked_port". + * Since we are already parsing/iterating NATs here, it feels more + * efficinet to figure out the tracked_port here, rather than + * re-parsing NATs down the line in the advertised_route_table_sync + * function before calling "ar_add_entry".*/ + for (size_t i = 0; i < lr_nat->n_nat_entries; i++) { + const struct ovn_nat *nat = &lr_nat->nat_entries[i]; + int plen = nat_entry_is_v6(nat) ? 128 : 32; + struct in6_addr prefix; + ip46_parse(nat->nb->external_ip, &prefix); + parsed_route_add(od, NULL, &prefix, plen, false, + nat->nb->external_ip, op, 0, false, false, + NULL, ROUTE_SOURCE_NAT, &op->nbrp->header_, routes); + } + } + +} struct ecmp_route_list_node { struct ovs_list list_node; uint16_t id; /* starts from 1 */ @@ -11441,6 +11477,8 @@ route_source_to_offset(enum route_source source) { switch (source) { case ROUTE_SOURCE_CONNECTED: + case ROUTE_SOURCE_NAT: + case ROUTE_SOURCE_LB: return ROUTE_PRIO_OFFSET_CONNECTED; case ROUTE_SOURCE_STATIC: return ROUTE_PRIO_OFFSET_STATIC; diff --git a/northd/northd.h b/northd/northd.h index 11efa0136..95f2fe010 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -186,11 +186,15 @@ struct route_policy { }; struct routes_data { - struct hmap parsed_routes; + struct hmap parsed_routes; /* Stores struct parsed_route. */ struct simap route_tables; struct hmap bfd_active_connections; }; +struct dynamic_routes_data { + struct hmap parsed_routes; /* Stores struct parsed_route. */ +}; + struct route_policies_data { struct hmap route_policies; struct hmap bfd_active_connections; @@ -312,6 +316,8 @@ enum dynamic_routing_redistribute_mode_bits { DRRM_CONNECTED_BIT = 0, DRRM_CONNECTED_AS_HOST_BIT = 1, DRRM_STATIC_BIT = 2, + DRRM_NAT_BIT = 3, + DRRM_LB_BIT = 4, }; enum dynamic_routing_redistribute_mode { @@ -319,6 +325,8 @@ enum dynamic_routing_redistribute_mode { DRRM_CONNECTED = (1 << DRRM_CONNECTED_BIT), DRRM_CONNECTED_AS_HOST = (1 << DRRM_CONNECTED_AS_HOST_BIT), DRRM_STATIC = (1 << DRRM_STATIC_BIT), + DRRM_NAT = (1 << DRRM_NAT_BIT), + DRRM_LB = (1 << DRRM_LB_BIT), }; /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or @@ -730,6 +738,10 @@ enum route_source { ROUTE_SOURCE_STATIC, /* The route is dynamically learned by an ovn-controller. */ ROUTE_SOURCE_LEARNED, + /* The route is derived from a NAT's external IP. */ + ROUTE_SOURCE_NAT, + /* The route is derived from a LB's VIP. */ + ROUTE_SOURCE_LB, }; struct parsed_route { @@ -811,6 +823,7 @@ void route_policies_destroy(struct route_policies_data *); void build_parsed_routes(const struct ovn_datapath *, const struct hmap *, const struct hmap *, struct hmap *, struct simap *, struct hmap *); +void build_lb_nat_parsed_routes(const struct ovn_datapath *, const struct lr_nat_record *, struct hmap *); uint32_t get_route_table_id(struct simap *, const char *); void routes_init(struct routes_data *); void routes_destroy(struct routes_data *); -- 2.43.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev