Add support for I+P handling of en-learned-route-sync for the
sb_learned_routes input.

Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
---
v2->v3:
  * Addressed review comments.
  * Ignore routes that are added and deleted in the same handler run.
v1-v2: Fix test error

 northd/en-learned-route-sync.c | 121 ++++++++++++++++++++++++++++++---
 northd/en-learned-route-sync.h |  21 ++++++
 northd/inc-proc-northd.c       |   6 +-
 northd/northd.c                |  24 ++++++-
 northd/northd.h                |  34 +++++----
 tests/ovn-northd.at            |  16 ++---
 6 files changed, 186 insertions(+), 36 deletions(-)

diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c
index 4e87b3265..bde9ffa79 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->tracked = false;
+}
+
 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->tracked = false;
 }
 
 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,75 @@ 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;
+    }
+    return parsed_route_lookup_by_source(od, ROUTE_SOURCE_LEARNED,
+                                         &learned_route->header_, routes);
+}
+
+bool
+learned_route_sync_sb_learned_route_change_handler(struct engine_node *node,
+                                                   void *data_)
+{
+    struct learned_route_sync_data *data = data_;
+    data->tracked = true;
+
+    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) &&
+            sbrec_learned_route_is_deleted(changed_route)) {
+            continue;
+        }
+
+        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);
+                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);
+            continue;
+        }
+
+        /* The learned routes should never be updated, so we just fail the
+         * handler here if that happens. */
+        goto fail;
+    }
+
+    if (!hmapx_is_empty(&data->trk_data.trk_created_parsed_route) ||
+        !hmapx_is_empty(&data->trk_data.trk_deleted_parsed_route)) {
+        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..b2d079621 100644
--- a/northd/en-learned-route-sync.h
+++ b/northd/en-learned-route-sync.h
@@ -18,15 +18,36 @@
 
 #include "lib/inc-proc-eng.h"
 #include "openvswitch/hmap.h"
+#include "hmapx.h"
+
+/* Track what's changed in the learned routes engine node.
+ * For now only tracks changed learned routes. */
+struct learned_routes_tracked_data {
+    /* 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;
+
+    /* 'tracked' is set to true if there is information available for
+     * incremental processing. If true then trk_data is valid. */
+    bool tracked;
+    struct learned_routes_tracked_data trk_data;
 };
 
 bool learned_route_sync_northd_change_handler(struct engine_node *,
                                               void *data);
+bool learned_route_sync_sb_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..b22e5fef3 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_sb_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 a91e48ac2..cfa2cf915 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -11074,6 +11074,26 @@ parsed_route_clone(const struct parsed_route *pr)
     return new_pr;
 }
 
+/* Searches for a parsed_route in a hmap based on datapath, source and
+ * source_hint. */
+struct parsed_route *
+parsed_route_lookup_by_source(const struct ovn_datapath *od,
+                              enum route_source source,
+                              const struct ovsdb_idl_row *source_hint,
+                              const struct hmap *routes)
+{
+    size_t hash = uuid_hash(&od->key);
+    struct parsed_route *route;
+    HMAP_FOR_EACH_WITH_HASH (route, key_node, hash, routes) {
+        if (route->source == source &&
+                uuid_equals(&route->source_hint->uuid,
+                            &source_hint->uuid)) {
+            return route;
+        }
+    }
+    return NULL;
+}
+
 /* This hash needs to be equal to the one used in
  * build_route_flows_for_lrouter to iterate over all routes of a datapath.
  * This is distinct from route_hash which is stored in parsed_route->hash. */
@@ -11097,7 +11117,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,
@@ -11126,9 +11146,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;
     }
 }
 
diff --git a/northd/northd.h b/northd/northd.h
index de0d924c5..f74719cc9 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -782,24 +782,28 @@ struct parsed_route {
 };
 
 struct parsed_route *parsed_route_clone(const struct parsed_route *);
+struct parsed_route *parsed_route_lookup_by_source(
+    const struct ovn_datapath *od, enum route_source source,
+    const struct ovsdb_idl_row *source_hint, const struct hmap *routes);
 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 c74d57f7e..65f2c9950 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -15370,8 +15370,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;)
@@ -15379,10 +15379,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;)
 ])
 
@@ -15676,7 +15676,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
 
@@ -15686,7 +15686,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
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to