On Tue, Feb 18, 2025 at 03:02:45PM +0100, Felix Huettner via dev wrote:
> Add support for I+P handling of en-learned-route-sync for the
> sb_learned_routes input.
>
> Signed-off-by: Felix Huettner <[email protected]>
Recheck-request: github-robot
> ---
> v1-v2: Fix test error
>
> northd/en-learned-route-sync.c | 123 ++++++++++++++++++++++++++++++---
> northd/en-learned-route-sync.h | 25 +++++++
> northd/inc-proc-northd.c | 6 +-
> northd/northd.c | 5 +-
> northd/northd.h | 31 +++++----
> tests/ovn-northd.at | 16 ++---
> 6 files changed, 170 insertions(+), 36 deletions(-)
>
> diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c
> index 4e87b3265..312141208 100644
> --- a/northd/en-learned-route-sync.c
> +++ b/northd/en-learned-route-sync.c
> @@ -60,6 +60,19 @@ learned_route_sync_northd_change_handler(struct
> engine_node *node,
> return true;
> }
>
> +static void
> +routes_sync_clear_tracked(struct learned_route_sync_data *data)
> +{
> + hmapx_clear(&data->trk_data.trk_created_parsed_route);
> + struct hmapx_node *hmapx_node;
> + HMAPX_FOR_EACH_SAFE (hmapx_node,
> + &data->trk_data.trk_deleted_parsed_route) {
> + parsed_route_free(hmapx_node->data);
> + hmapx_delete(&data->trk_data.trk_deleted_parsed_route, hmapx_node);
> + }
> + data->trk_data.type = LEARNED_ROUTES_TRACKED_NONE;
> +}
> +
> static void
> routes_sync_clear(struct learned_route_sync_data *data)
> {
> @@ -67,12 +80,16 @@ routes_sync_clear(struct learned_route_sync_data *data)
> HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> parsed_route_free(r);
> }
> + routes_sync_clear_tracked(data);
> }
>
> static void
> routes_sync_init(struct learned_route_sync_data *data)
> {
> hmap_init(&data->parsed_routes);
> + hmapx_init(&data->trk_data.trk_created_parsed_route);
> + hmapx_init(&data->trk_data.trk_deleted_parsed_route);
> + data->trk_data.type = LEARNED_ROUTES_TRACKED_NONE;
> }
>
> static void
> @@ -80,6 +97,8 @@ routes_sync_destroy(struct learned_route_sync_data *data)
> {
> routes_sync_clear(data);
> hmap_destroy(&data->parsed_routes);
> + hmapx_destroy(&data->trk_data.trk_created_parsed_route);
> + hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);
> }
>
> void *
> @@ -97,6 +116,12 @@ en_learned_route_sync_cleanup(void *data)
> routes_sync_destroy(data);
> }
>
> +void
> +en_learned_route_sync_clear_tracked_data(void *data)
> +{
> + routes_sync_clear_tracked(data);
> +}
> +
> void
> en_learned_route_sync_run(struct engine_node *node, void *data)
> {
> @@ -122,7 +147,7 @@ en_learned_route_sync_run(struct engine_node *node, void
> *data)
> }
>
>
> -static void
> +static struct parsed_route *
> parse_route_from_sbrec_route(struct hmap *parsed_routes_out,
> const struct hmap *lr_ports,
> const struct hmap *lr_datapaths,
> @@ -132,7 +157,7 @@ parse_route_from_sbrec_route(struct hmap
> *parsed_routes_out,
> NULL, lr_datapaths, route->datapath);
>
> if (!od || ovn_datapath_is_stale(od)) {
> - return;
> + return NULL;
> }
>
> /* Verify that the next hop is an IP address with an all-ones mask. */
> @@ -144,7 +169,7 @@ parse_route_from_sbrec_route(struct hmap
> *parsed_routes_out,
> UUID_FMT, route->nexthop,
> UUID_ARGS(&route->header_.uuid));
> free(nexthop);
> - return;
> + return NULL;
> }
> if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||
> (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {
> @@ -153,7 +178,7 @@ parse_route_from_sbrec_route(struct hmap
> *parsed_routes_out,
> UUID_FMT, route->nexthop,
> UUID_ARGS(&route->header_.uuid));
> free(nexthop);
> - return;
> + return NULL;
> }
>
> /* Parse ip_prefix */
> @@ -164,7 +189,7 @@ parse_route_from_sbrec_route(struct hmap
> *parsed_routes_out,
> UUID_FMT, route->ip_prefix,
> UUID_ARGS(&route->header_.uuid));
> free(nexthop);
> - return;
> + return NULL;
> }
>
> /* Verify that ip_prefix and nexthop are on the same network. */
> @@ -175,14 +200,18 @@ parse_route_from_sbrec_route(struct hmap
> *parsed_routes_out,
> IN6_IS_ADDR_V4MAPPED(&prefix),
> false,
> &out_port, &lrp_addr_s)) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> + VLOG_WARN_RL(&rl, "could not find output port %s for learned route "
> + UUID_FMT, route->logical_port->logical_port,
> + UUID_ARGS(&route->header_.uuid));
> free(nexthop);
> - return;
> + return NULL;
> }
>
> - parsed_route_add(od, nexthop, &prefix, plen, false, lrp_addr_s,
> - out_port, 0, false, false, NULL,
> - ROUTE_SOURCE_LEARNED, &route->header_, NULL,
> - parsed_routes_out);
> + return parsed_route_add(od, nexthop, &prefix, plen, false, lrp_addr_s,
> + out_port, 0, false, false, NULL,
> + ROUTE_SOURCE_LEARNED, &route->header_, NULL,
> + parsed_routes_out);
> }
>
> static void
> @@ -212,3 +241,77 @@ routes_table_sync(
> parsed_route_hash(route));
> }
> }
> +
> +static struct parsed_route *
> +find_learned_route(const struct sbrec_learned_route *learned_route,
> + const struct ovn_datapaths *lr_datapaths,
> + const struct hmap *routes)
> +{
> + const struct ovn_datapath *od = ovn_datapath_from_sbrec(
> + NULL, &lr_datapaths->datapaths, learned_route->datapath);
> + if (!od) {
> + return NULL;
> + }
> + size_t hash = uuid_hash(&od->key);
> + struct parsed_route *route;
> + HMAP_FOR_EACH_WITH_HASH (route, key_node, hash, routes) {
> + if (route->source == ROUTE_SOURCE_LEARNED &&
> + uuid_equals(&route->source_hint->uuid,
> + &learned_route->header_.uuid)) {
> + return route;
> + }
> + }
> + return NULL;
> +}
> +
> +bool
> +learned_route_sync_learned_route_change_handler(struct engine_node *node,
> + void *data_ OVS_UNUSED)
> +{
> + struct learned_route_sync_data *data = data_;
> +
> + const struct sbrec_learned_route_table *sbrec_learned_route_table =
> + EN_OVSDB_GET(engine_get_input("SB_learned_route", node));
> + struct northd_data *northd_data = engine_get_input_data("northd", node);
> +
> + const struct sbrec_learned_route *changed_route;
> + SBREC_LEARNED_ROUTE_TABLE_FOR_EACH_TRACKED (changed_route,
> + sbrec_learned_route_table) {
> + if (sbrec_learned_route_is_new(changed_route)) {
> + struct parsed_route *route = parse_route_from_sbrec_route(
> + &data->parsed_routes, &northd_data->lr_ports,
> + &northd_data->lr_datapaths.datapaths, changed_route);
> + if (route) {
> + hmapx_add(&data->trk_data.trk_created_parsed_route, route);
> + data->trk_data.type |= LEARNED_ROUTES_TRACKED_LEARNED;
> + continue;
> + }
> + }
> +
> + if (sbrec_learned_route_is_deleted(changed_route)) {
> + struct parsed_route *route = find_learned_route(
> + changed_route, &northd_data->lr_datapaths,
> + &data->parsed_routes);
> + if (!route) {
> + goto fail;
> + }
> + hmap_remove(&data->parsed_routes, &route->key_node);
> + hmapx_add(&data->trk_data.trk_deleted_parsed_route, route);
> + data->trk_data.type |= LEARNED_ROUTES_TRACKED_LEARNED;
> + continue;
> + }
> +
> + /* The learned routes should never be updated, so we just fail the
> + * handler here if that happens. */
> + goto fail;
> + }
> +
> + if ((data->trk_data.type & LEARNED_ROUTES_TRACKED_LEARNED) != 0) {
> + engine_set_node_state(node, EN_UPDATED);
> + }
> +
> + return true;
> +fail:
> + routes_sync_clear_tracked(data);
> + return false;
> +}
> diff --git a/northd/en-learned-route-sync.h b/northd/en-learned-route-sync.h
> index 1a5d6ff23..34baadaa1 100644
> --- a/northd/en-learned-route-sync.h
> +++ b/northd/en-learned-route-sync.h
> @@ -18,15 +18,40 @@
>
> #include "lib/inc-proc-eng.h"
> #include "openvswitch/hmap.h"
> +#include "hmapx.h"
> +
> +enum learned_routes_tracked_data_type {
> + LEARNED_ROUTES_TRACKED_NONE,
> + LEARNED_ROUTES_TRACKED_LEARNED = (1 << 0),
> +};
> +
> +/* Track what's changed in the learned routes engine node.
> + * For now only tracks changed learned routes. */
> +struct learned_routes_tracked_data {
> + /* Indicates the type of data tracked. One or all of ROUTES_TRACKED_*.
> */
> + enum learned_routes_tracked_data_type type;
> +
> + /* Tracked created routes based on sb_learned_routes.
> + * hmapx node is 'struct parsed_route *'. */
> + struct hmapx trk_created_parsed_route;
> +
> + /* Tracked deleted routes based on sb_learned_routes.
> + * hmapx node is 'struct parsed_route *'. */
> + struct hmapx trk_deleted_parsed_route;
> +};
>
> struct learned_route_sync_data {
> struct hmap parsed_routes;
> + struct learned_routes_tracked_data trk_data;
> };
>
> bool learned_route_sync_northd_change_handler(struct engine_node *,
> void *data);
> +bool learned_route_sync_learned_route_change_handler(struct engine_node *,
> + void *data);
> void *en_learned_route_sync_init(struct engine_node *, struct engine_arg *);
> void en_learned_route_sync_cleanup(void *data);
> +void en_learned_route_sync_clear_tracked_data(void *data);
> void en_learned_route_sync_run(struct engine_node *, void *data);
>
> #endif /* EN_LEARNED_ROUTE_SYNC_H */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 3fa99f637..bf0fbc62b 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -174,7 +174,8 @@ static ENGINE_NODE(ecmp_nexthop, "ecmp_nexthop");
> 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_WITH_CLEAR_TRACK_DATA(learned_route_sync,
> + "learned_route_sync");
> static ENGINE_NODE(dynamic_routes, "dynamic_routes");
>
> void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> @@ -307,7 +308,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> advertised_route_sync_northd_change_handler);
>
> engine_add_input(&en_learned_route_sync, &en_routes, NULL);
> - engine_add_input(&en_learned_route_sync, &en_sb_learned_route, NULL);
> + engine_add_input(&en_learned_route_sync, &en_sb_learned_route,
> + learned_route_sync_learned_route_change_handler);
> engine_add_input(&en_learned_route_sync, &en_northd,
> learned_route_sync_northd_change_handler);
>
> diff --git a/northd/northd.c b/northd/northd.c
> index f1f1ede43..52bb67c0e 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -11079,7 +11079,7 @@ parsed_route_free(struct parsed_route *pr) {
> * in there.
> * Takes ownership of the provided nexthop. All other parameters are cloned.
> * Elements of the routes hmap need to be freed using parsed_route_free. */
> -void
> +struct parsed_route *
> parsed_route_add(const struct ovn_datapath *od,
> struct in6_addr *nexthop,
> const struct in6_addr *prefix,
> @@ -11108,9 +11108,11 @@ parsed_route_add(const struct ovn_datapath *od,
> struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> if (!pr) {
> hmap_insert(routes, &new_pr->key_node, hash);
> + return new_pr;
> } else {
> pr->stale = false;
> parsed_route_free(new_pr);
> + return pr;
> }
> }
>
> @@ -19116,6 +19118,7 @@ routes_destroy(struct routes_data *data)
>
> simap_destroy(&data->route_tables);
> __bfd_destroy(&data->bfd_active_connections);
> +
> }
>
> void
> diff --git a/northd/northd.h b/northd/northd.h
> index 1fbc93c8e..9402e4a4b 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -783,21 +783,22 @@ struct parsed_route *parsed_route_clone(const struct
> parsed_route *);
> size_t parsed_route_hash(const struct parsed_route *);
> void parsed_route_free(struct parsed_route *);
>
> -void parsed_route_add(const struct ovn_datapath *od,
> - struct in6_addr *nexthop,
> - const struct in6_addr *prefix,
> - unsigned int plen,
> - bool is_discard_route,
> - const char *lrp_addr_s,
> - const struct ovn_port *out_port,
> - uint32_t route_table_id,
> - bool is_src_route,
> - bool ecmp_symmetric_reply,
> - const struct sset *ecmp_selection_fields,
> - enum route_source source,
> - const struct ovsdb_idl_row *source_hint,
> - const struct ovn_port *tracked_port,
> - struct hmap *routes);
> +struct parsed_route * parsed_route_add(
> + const struct ovn_datapath *od,
> + struct in6_addr *nexthop,
> + const struct in6_addr *prefix,
> + unsigned int plen,
> + bool is_discard_route,
> + const char *lrp_addr_s,
> + const struct ovn_port *out_port,
> + uint32_t route_table_id,
> + bool is_src_route,
> + bool ecmp_symmetric_reply,
> + const struct sset *ecmp_selection_fields,
> + enum route_source source,
> + const struct ovsdb_idl_row *source_hint,
> + const struct ovn_port *tracked_port,
> + struct hmap *routes);
>
> bool
> find_route_outport(const struct hmap *lr_ports, const char *output_port,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 6a58ccc27..bb77f2abc 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -15324,8 +15324,8 @@ ovn-sbctl dump-flows lr0 > lr0flows
> AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
> table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;)
> table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra),
> action=(drop;)
> - table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
> == 192.168.1.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
> reg8[[16..31]] = select(1, 2);)
> - table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
> == 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 2;
> reg8[[16..31]] = select(1, 2);)
> + table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
> == 192.168.1.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 2;
> reg8[[16..31]] = select(1, 2);)
> + table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
> == 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
> reg8[[16..31]] = select(1, 2);)
> table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
> 10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
> 10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback =
> 1; reg9[[9]] = 1; next;)
> table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
> 10.0.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
> 10.0.1.1; eth.src = 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback =
> 1; reg9[[9]] = 1; next;)
> table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw0"
> && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 =
> ip6.dst; xxreg1 = fe80::200:ff:fe00:ff01; eth.src = 00:00:00:00:ff:01;
> outport = "lr0-sw0"; flags.loopback = 1; reg9[[9]] = 0; next;)
> @@ -15333,10 +15333,10 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
> ovn_strip_lflows], [0], [dnl
> ])
> AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed -e
> 's/10\.0\..\./10.0.??./g' -e 's/lr0-sw./lr0-sw??/' -e 's/00:ff:0./00:ff:0?/'
> | ovn_strip_lflows], [0], [dnl
> table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;)
> - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
> && reg8[[16..31]] == 1), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
> && reg8[[16..31]] == 2), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
> && reg8[[16..31]] == 1), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
> && reg8[[16..31]] == 2), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
> && reg8[[16..31]] == 1), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
> && reg8[[16..31]] == 2), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
> && reg8[[16..31]] == 1), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
> && reg8[[16..31]] == 2), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src
> = 00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
> table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] ==
> 0), action=(next;)
> ])
>
> @@ -15630,7 +15630,7 @@ check ovn-nbctl --wait=sb sync
> check_engine_compute northd unchanged
> check_engine_compute routes unchanged
> check_engine_compute advertised_route_sync unchanged
> -check_engine_compute learned_route_sync recompute
> +check_engine_compute learned_route_sync incremental
> check_engine_compute lflow recompute
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
>
> @@ -15640,7 +15640,7 @@ check ovn-nbctl --wait=sb sync
> check_engine_compute northd unchanged
> check_engine_compute routes unchanged
> check_engine_compute advertised_route_sync unchanged
> -check_engine_compute learned_route_sync recompute
> +check_engine_compute learned_route_sync incremental
> check_engine_compute lflow recompute
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
>
> --
> 2.48.1
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev