Blob Blame History Raw
From 85136b292d698a76c5317ec10622bcc015165770 Mon Sep 17 00:00:00 2001
From: Dumitru Ceara <dceara@redhat.com>
Date: Wed, 4 Dec 2019 17:28:28 +0100
Subject: [PATCH ovn 4/4] ovn-controller: Fix use of dangling pointers in I-P
 runtime_data.

The incremental processing engine might stop a run before the
en_runtime_data node is processed. In such cases the ed_runtime_data
fields might contain pointers to already deleted SB records. For
example, if a port binding corresponding to a patch port is removed from
the SB database and the incremental processing engine aborts before the
en_runtime_data node is processed then the corresponding local_datapath
hashtable entry in ed_runtime_data is stale and will store a pointer to
the already freed sbrec_port_binding record.

This will cause invalid memory accesses in various places (e.g.,
pinctrl_run() -> prepare_ipv6_ras()).

To fix the issue we introduce the engine_get_data() API which must be
called in order to safely access internal node data. If the node is in
state EN_STALE or EN_ABORTED, engine_get_data() returns NULL as the
references might be stale.

This commit also adds an "is_valid()" method to engine nodes to allow
users to override the default behavior of determining if data is valid in a
node (e.g., for the ct-zones node the data is always safe to access).

Also, all interactions with node data outside inc-proc-eng.c are now
performed through APIs and never by directly accessing the node->data
field. This makes it easier to ensure that we don't access invalid
(stale) data.

CC: Han Zhou <hzhou@ovn.org>
Fixes: ca278d98a4f5 ("ovn-controller: Initial use of incremental engine - quiet mode.")
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Han Zhou <hzhou@ovn.org>

(cherry picked from upstream commit 94cbc59dc0f1cb56e56d1551956efe5824561864)

Change-Id: Icdb3d38e4b43e36c4c1924509e2461707cdb57e6
---
 ovn/controller/ovn-controller.c | 457 +++++++++++++++++++++-------------------
 ovn/lib/inc-proc-eng.c          |  59 +++++-
 ovn/lib/inc-proc-eng.h          | 115 +++++++---
 3 files changed, 370 insertions(+), 261 deletions(-)

diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 20e5ce4..cb23bc8 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -702,26 +702,25 @@ struct ed_type_ofctrl_is_connected {
     bool connected;
 };
 
-static void
-en_ofctrl_is_connected_init(struct engine_node *node)
+static void *
+en_ofctrl_is_connected_init(struct engine_node *node OVS_UNUSED,
+                            struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_ofctrl_is_connected *data =
-        (struct ed_type_ofctrl_is_connected *)node->data;
-    data->connected = false;
+    struct ed_type_ofctrl_is_connected *data = xzalloc(sizeof *data);
+    return data;
 }
 
 static void
-en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED)
+en_ofctrl_is_connected_cleanup(void *data OVS_UNUSED)
 {
 }
 
 static void
-en_ofctrl_is_connected_run(struct engine_node *node)
+en_ofctrl_is_connected_run(struct engine_node *node, void *data)
 {
-    struct ed_type_ofctrl_is_connected *data =
-        (struct ed_type_ofctrl_is_connected *)node->data;
-    if (data->connected != ofctrl_is_connected()) {
-        data->connected = !data->connected;
+    struct ed_type_ofctrl_is_connected *of_data = data;
+    if (of_data->connected != ofctrl_is_connected()) {
+        of_data->connected = !of_data->connected;
         engine_set_node_state(node, EN_UPDATED);
         return;
     }
@@ -736,21 +735,24 @@ struct ed_type_addr_sets {
     struct sset updated;
 };
 
-static void
-en_addr_sets_init(struct engine_node *node)
+static void *
+en_addr_sets_init(struct engine_node *node OVS_UNUSED,
+                  struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
+    struct ed_type_addr_sets *as = xzalloc(sizeof *as);
+
     shash_init(&as->addr_sets);
     as->change_tracked = false;
     sset_init(&as->new);
     sset_init(&as->deleted);
     sset_init(&as->updated);
+    return as;
 }
 
 static void
-en_addr_sets_cleanup(struct engine_node *node)
+en_addr_sets_cleanup(void *data)
 {
-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
+    struct ed_type_addr_sets *as = data;
     expr_const_sets_destroy(&as->addr_sets);
     shash_destroy(&as->addr_sets);
     sset_destroy(&as->new);
@@ -759,9 +761,9 @@ en_addr_sets_cleanup(struct engine_node *node)
 }
 
 static void
-en_addr_sets_run(struct engine_node *node)
+en_addr_sets_run(struct engine_node *node, void *data)
 {
-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
+    struct ed_type_addr_sets *as = data;
 
     sset_clear(&as->new);
     sset_clear(&as->deleted);
@@ -779,9 +781,9 @@ en_addr_sets_run(struct engine_node *node)
 }
 
 static bool
-addr_sets_sb_address_set_handler(struct engine_node *node)
+addr_sets_sb_address_set_handler(struct engine_node *node, void *data)
 {
-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
+    struct ed_type_addr_sets *as = data;
 
     sset_clear(&as->new);
     sset_clear(&as->deleted);
@@ -813,21 +815,24 @@ struct ed_type_port_groups{
     struct sset updated;
 };
 
-static void
-en_port_groups_init(struct engine_node *node)
+static void *
+en_port_groups_init(struct engine_node *node OVS_UNUSED,
+                    struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
+    struct ed_type_port_groups *pg = xzalloc(sizeof *pg);
+
     shash_init(&pg->port_groups);
     pg->change_tracked = false;
     sset_init(&pg->new);
     sset_init(&pg->deleted);
     sset_init(&pg->updated);
+    return pg;
 }
 
 static void
-en_port_groups_cleanup(struct engine_node *node)
+en_port_groups_cleanup(void *data)
 {
-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
+    struct ed_type_port_groups *pg = data;
     expr_const_sets_destroy(&pg->port_groups);
     shash_destroy(&pg->port_groups);
     sset_destroy(&pg->new);
@@ -836,9 +841,9 @@ en_port_groups_cleanup(struct engine_node *node)
 }
 
 static void
-en_port_groups_run(struct engine_node *node)
+en_port_groups_run(struct engine_node *node, void *data)
 {
-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
+    struct ed_type_port_groups *pg = data;
 
     sset_clear(&pg->new);
     sset_clear(&pg->deleted);
@@ -856,9 +861,9 @@ en_port_groups_run(struct engine_node *node)
 }
 
 static bool
-port_groups_sb_port_group_handler(struct engine_node *node)
+port_groups_sb_port_group_handler(struct engine_node *node, void *data)
 {
-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
+    struct ed_type_port_groups *pg = data;
 
     sset_clear(&pg->new);
     sset_clear(&pg->deleted);
@@ -899,46 +904,45 @@ struct ed_type_runtime_data {
     struct sset active_tunnels;
 };
 
-static void
-en_runtime_data_init(struct engine_node *node)
+static void *
+en_runtime_data_init(struct engine_node *node OVS_UNUSED,
+                     struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)node->data;
+    struct ed_type_runtime_data *data = xzalloc(sizeof *data);
 
     hmap_init(&data->local_datapaths);
     sset_init(&data->local_lports);
     sset_init(&data->local_lport_ids);
     sset_init(&data->active_tunnels);
+    return data;
 }
 
 static void
-en_runtime_data_cleanup(struct engine_node *node)
+en_runtime_data_cleanup(void *data)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)node->data;
+    struct ed_type_runtime_data *rt_data = data;
 
-    sset_destroy(&data->local_lports);
-    sset_destroy(&data->local_lport_ids);
-    sset_destroy(&data->active_tunnels);
+    sset_destroy(&rt_data->local_lports);
+    sset_destroy(&rt_data->local_lport_ids);
+    sset_destroy(&rt_data->active_tunnels);
     struct local_datapath *cur_node, *next_node;
     HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
-                        &data->local_datapaths) {
+                        &rt_data->local_datapaths) {
         free(cur_node->peer_ports);
-        hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
+        hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node);
         free(cur_node);
     }
-    hmap_destroy(&data->local_datapaths);
+    hmap_destroy(&rt_data->local_datapaths);
 }
 
 static void
-en_runtime_data_run(struct engine_node *node)
+en_runtime_data_run(struct engine_node *node, void *data)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)node->data;
-    struct hmap *local_datapaths = &data->local_datapaths;
-    struct sset *local_lports = &data->local_lports;
-    struct sset *local_lport_ids = &data->local_lport_ids;
-    struct sset *active_tunnels = &data->active_tunnels;
+    struct ed_type_runtime_data *rt_data = data;
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct sset *local_lports = &rt_data->local_lports;
+    struct sset *local_lport_ids = &rt_data->local_lport_ids;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
 
     static bool first_run = true;
     if (first_run) {
@@ -981,8 +985,7 @@ en_runtime_data_run(struct engine_node *node)
     ovs_assert(chassis);
 
     struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
-        (struct ed_type_ofctrl_is_connected *)engine_get_input(
-            "ofctrl_is_connected", node)->data;
+        engine_get_input_data("ofctrl_is_connected", node);
     if (ed_ofctrl_is_connected->connected) {
         /* Calculate the active tunnels only if have an an active
          * OpenFlow connection to br-int.
@@ -1036,12 +1039,11 @@ en_runtime_data_run(struct engine_node *node)
 }
 
 static bool
-runtime_data_sb_port_binding_handler(struct engine_node *node)
+runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)node->data;
-    struct sset *local_lports = &data->local_lports;
-    struct sset *active_tunnels = &data->active_tunnels;
+    struct ed_type_runtime_data *rt_data = data;
+    struct sset *local_lports = &rt_data->local_lports;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
 
     struct ovsrec_open_vswitch_table *ovs_table =
         (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
@@ -1080,10 +1082,10 @@ struct ed_type_ct_zones {
     struct simap current;
 };
 
-static void
-en_ct_zones_init(struct engine_node *node)
+static void *
+en_ct_zones_init(struct engine_node *node, struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_ct_zones *data = node->data;
+    struct ed_type_ct_zones *data = xzalloc(sizeof *data);
     struct ovsrec_open_vswitch_table *ovs_table =
         (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
             engine_get_input("OVS_open_vswitch", node));
@@ -1097,56 +1099,63 @@ en_ct_zones_init(struct engine_node *node)
     memset(data->bitmap, 0, sizeof data->bitmap);
     bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */
     restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap);
+    return data;
 }
 
 static void
-en_ct_zones_cleanup(struct engine_node *node)
+en_ct_zones_cleanup(void *data)
 {
-    struct ed_type_ct_zones *data = node->data;
+    struct ed_type_ct_zones *ct_zones_data = data;
 
-    simap_destroy(&data->current);
-    shash_destroy(&data->pending);
+    simap_destroy(&ct_zones_data->current);
+    shash_destroy(&ct_zones_data->pending);
 }
 
 static void
-en_ct_zones_run(struct engine_node *node)
+en_ct_zones_run(struct engine_node *node, void *data)
 {
-    struct ed_type_ct_zones *data = node->data;
+    struct ed_type_ct_zones *ct_zones_data = data;
     struct ed_type_runtime_data *rt_data =
-        (struct ed_type_runtime_data *)engine_get_input(
-            "runtime_data", node)->data;
+        engine_get_input_data("runtime_data", node);
 
     update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths,
-                    &data->current, data->bitmap, &data->pending);
+                    &ct_zones_data->current, ct_zones_data->bitmap,
+                    &ct_zones_data->pending);
 
     engine_set_node_state(node, EN_UPDATED);
 }
 
+/* The data in the ct_zones node is always valid (i.e., no stale pointers). */
+static bool
+en_ct_zones_is_valid(struct engine_node *node OVS_UNUSED)
+{
+    return true;
+}
+
 struct ed_type_mff_ovn_geneve {
     enum mf_field_id mff_ovn_geneve;
 };
 
-static void
-en_mff_ovn_geneve_init(struct engine_node *node)
+static void *
+en_mff_ovn_geneve_init(struct engine_node *node OVS_UNUSED,
+                       struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_mff_ovn_geneve *data =
-        (struct ed_type_mff_ovn_geneve *)node->data;
-    data->mff_ovn_geneve = 0;
+    struct ed_type_mff_ovn_geneve *data = xzalloc(sizeof *data);
+    return data;
 }
 
 static void
-en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED)
+en_mff_ovn_geneve_cleanup(void *data OVS_UNUSED)
 {
 }
 
 static void
-en_mff_ovn_geneve_run(struct engine_node *node)
+en_mff_ovn_geneve_run(struct engine_node *node, void *data)
 {
-    struct ed_type_mff_ovn_geneve *data =
-        (struct ed_type_mff_ovn_geneve *)node->data;
+    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = data;
     enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id();
-    if (data->mff_ovn_geneve != mff_ovn_geneve) {
-        data->mff_ovn_geneve = mff_ovn_geneve;
+    if (ed_mff_ovn_geneve->mff_ovn_geneve != mff_ovn_geneve) {
+        ed_mff_ovn_geneve->mff_ovn_geneve = mff_ovn_geneve;
         engine_set_node_state(node, EN_UPDATED);
         return;
     }
@@ -1166,48 +1175,46 @@ struct ed_type_flow_output {
     struct lflow_resource_ref lflow_resource_ref;
 };
 
-static void
-en_flow_output_init(struct engine_node *node)
+static void *
+en_flow_output_init(struct engine_node *node OVS_UNUSED,
+                    struct engine_arg *arg OVS_UNUSED)
 {
-    struct ed_type_flow_output *data =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *data = xzalloc(sizeof *data);
+
     ovn_desired_flow_table_init(&data->flow_table);
     ovn_extend_table_init(&data->group_table);
     ovn_extend_table_init(&data->meter_table);
     data->conj_id_ofs = 1;
     lflow_resource_init(&data->lflow_resource_ref);
+    return data;
 }
 
 static void
-en_flow_output_cleanup(struct engine_node *node)
+en_flow_output_cleanup(void *data)
 {
-    struct ed_type_flow_output *data =
-        (struct ed_type_flow_output *)node->data;
-    ovn_desired_flow_table_destroy(&data->flow_table);
-    ovn_extend_table_destroy(&data->group_table);
-    ovn_extend_table_destroy(&data->meter_table);
-    lflow_resource_destroy(&data->lflow_resource_ref);
+    struct ed_type_flow_output *flow_output_data = data;
+    ovn_desired_flow_table_destroy(&flow_output_data->flow_table);
+    ovn_extend_table_destroy(&flow_output_data->group_table);
+    ovn_extend_table_destroy(&flow_output_data->meter_table);
+    lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
 }
 
 static void
-en_flow_output_run(struct engine_node *node)
+en_flow_output_run(struct engine_node *node, void *data)
 {
     struct ed_type_runtime_data *rt_data =
-        (struct ed_type_runtime_data *)engine_get_input(
-            "runtime_data", node)->data;
+        engine_get_input_data("runtime_data", node);
     struct hmap *local_datapaths = &rt_data->local_datapaths;
     struct sset *local_lports = &rt_data->local_lports;
     struct sset *local_lport_ids = &rt_data->local_lport_ids;
     struct sset *active_tunnels = &rt_data->active_tunnels;
 
     struct ed_type_ct_zones *ct_zones_data =
-        (struct ed_type_ct_zones *)engine_get_input(
-            "ct_zones", node)->data;
+        engine_get_input_data("ct_zones", node);
     struct simap *ct_zones = &ct_zones_data->current;
 
     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
-            "mff_ovn_geneve", node)->data;
+        engine_get_input_data("mff_ovn_geneve", node);
     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
 
     struct ovsrec_open_vswitch_table *ovs_table =
@@ -1224,12 +1231,11 @@ en_flow_output_run(struct engine_node *node)
                 engine_get_input("SB_chassis", node),
                 "name");
     struct ed_type_addr_sets *as_data =
-        (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
+        engine_get_input_data("addr_sets", node);
     struct shash *addr_sets = &as_data->addr_sets;
 
     struct ed_type_port_groups *pg_data =
-        (struct ed_type_port_groups *)engine_get_input(
-            "port_groups", node)->data;
+        engine_get_input_data("port_groups", node);
     struct shash *port_groups = &pg_data->port_groups;
 
     const struct sbrec_chassis *chassis = NULL;
@@ -1239,8 +1245,7 @@ en_flow_output_run(struct engine_node *node)
 
     ovs_assert(br_int && chassis);
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
     struct ovn_extend_table *group_table = &fo->group_table;
     struct ovn_extend_table *meter_table = &fo->meter_table;
@@ -1320,21 +1325,19 @@ en_flow_output_run(struct engine_node *node)
 }
 
 static bool
-flow_output_sb_logical_flow_handler(struct engine_node *node)
-{
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)engine_get_input(
-                "runtime_data", node)->data;
-    struct hmap *local_datapaths = &data->local_datapaths;
-    struct sset *local_lport_ids = &data->local_lport_ids;
-    struct sset *active_tunnels = &data->active_tunnels;
+flow_output_sb_logical_flow_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct sset *local_lport_ids = &rt_data->local_lport_ids;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
     struct ed_type_addr_sets *as_data =
-        (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
+        engine_get_input_data("addr_sets", node);
     struct shash *addr_sets = &as_data->addr_sets;
 
     struct ed_type_port_groups *pg_data =
-        (struct ed_type_port_groups *)engine_get_input(
-            "port_groups", node)->data;
+        engine_get_input_data("port_groups", node);
     struct shash *port_groups = &pg_data->port_groups;
 
     struct ovsrec_open_vswitch_table *ovs_table =
@@ -1358,8 +1361,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node)
 
     ovs_assert(br_int && chassis);
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
     struct ovn_extend_table *group_table = &fo->group_table;
     struct ovn_extend_table *meter_table = &fo->meter_table;
@@ -1403,7 +1405,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node)
 }
 
 static bool
-flow_output_sb_mac_binding_handler(struct engine_node *node)
+flow_output_sb_mac_binding_handler(struct engine_node *node, void *data)
 {
     struct ovsdb_idl_index *sbrec_port_binding_by_name =
         engine_ovsdb_node_get_index(
@@ -1414,8 +1416,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node)
         (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
             engine_get_input("SB_mac_binding", node));
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
 
     lflow_handle_changed_neighbors(sbrec_port_binding_by_name,
@@ -1426,22 +1427,19 @@ flow_output_sb_mac_binding_handler(struct engine_node *node)
 }
 
 static bool
-flow_output_sb_port_binding_handler(struct engine_node *node)
+flow_output_sb_port_binding_handler(struct engine_node *node, void *data)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)engine_get_input(
-                "runtime_data", node)->data;
-    struct hmap *local_datapaths = &data->local_datapaths;
-    struct sset *active_tunnels = &data->active_tunnels;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
 
     struct ed_type_ct_zones *ct_zones_data =
-        (struct ed_type_ct_zones *)engine_get_input(
-            "ct_zones", node)->data;
+        engine_get_input_data("ct_zones", node);
     struct simap *ct_zones = &ct_zones_data->current;
 
     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
-            "mff_ovn_geneve", node)->data;
+        engine_get_input_data("mff_ovn_geneve", node);
     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
 
     struct ovsrec_open_vswitch_table *ovs_table =
@@ -1463,8 +1461,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
     }
     ovs_assert(br_int && chassis);
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
 
     struct ovsdb_idl_index *sbrec_port_binding_by_name =
@@ -1534,21 +1531,18 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
 }
 
 static bool
-flow_output_sb_multicast_group_handler(struct engine_node *node)
+flow_output_sb_multicast_group_handler(struct engine_node *node, void *data)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)engine_get_input(
-                "runtime_data", node)->data;
-    struct hmap *local_datapaths = &data->local_datapaths;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
 
     struct ed_type_ct_zones *ct_zones_data =
-        (struct ed_type_ct_zones *)engine_get_input(
-            "ct_zones", node)->data;
+        engine_get_input_data("ct_zones", node);
     struct simap *ct_zones = &ct_zones_data->current;
 
     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
-            "mff_ovn_geneve", node)->data;
+        engine_get_input_data("mff_ovn_geneve", node);
     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
 
     struct ovsrec_open_vswitch_table *ovs_table =
@@ -1570,8 +1564,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
     }
     ovs_assert(br_int && chassis);
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
 
     struct sbrec_multicast_group_table *multicast_group_table =
@@ -1588,23 +1581,21 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
 }
 
 static bool
-_flow_output_resource_ref_handler(struct engine_node *node,
-                                 enum ref_type ref_type)
+_flow_output_resource_ref_handler(struct engine_node *node, void *data,
+                                  enum ref_type ref_type)
 {
-    struct ed_type_runtime_data *data =
-        (struct ed_type_runtime_data *)engine_get_input(
-                "runtime_data", node)->data;
-    struct hmap *local_datapaths = &data->local_datapaths;
-    struct sset *local_lport_ids = &data->local_lport_ids;
-    struct sset *active_tunnels = &data->active_tunnels;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct sset *local_lport_ids = &rt_data->local_lport_ids;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
 
     struct ed_type_addr_sets *as_data =
-        (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
+        engine_get_input_data("addr_sets", node);
     struct shash *addr_sets = &as_data->addr_sets;
 
     struct ed_type_port_groups *pg_data =
-        (struct ed_type_port_groups *)engine_get_input(
-            "port_groups", node)->data;
+        engine_get_input_data("port_groups", node);
     struct shash *port_groups = &pg_data->port_groups;
 
     struct ovsrec_open_vswitch_table *ovs_table =
@@ -1627,8 +1618,7 @@ _flow_output_resource_ref_handler(struct engine_node *node,
 
     ovs_assert(br_int && chassis);
 
-    struct ed_type_flow_output *fo =
-        (struct ed_type_flow_output *)node->data;
+    struct ed_type_flow_output *fo = data;
     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
     struct ovn_extend_table *group_table = &fo->group_table;
     struct ovn_extend_table *meter_table = &fo->meter_table;
@@ -1735,15 +1725,15 @@ _flow_output_resource_ref_handler(struct engine_node *node,
 }
 
 static bool
-flow_output_addr_sets_handler(struct engine_node *node)
+flow_output_addr_sets_handler(struct engine_node *node, void *data)
 {
-    return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET);
+    return _flow_output_resource_ref_handler(node, data, REF_TYPE_ADDRSET);
 }
 
 static bool
-flow_output_port_groups_handler(struct engine_node *node)
+flow_output_port_groups_handler(struct engine_node *node, void *data)
 {
-    return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP);
+    return _flow_output_resource_ref_handler(node, data, REF_TYPE_PORTGROUP);
 }
 
 struct ovn_controller_exit_args {
@@ -1851,15 +1841,7 @@ main(int argc, char *argv[])
     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
 
     /* Define inc-proc-engine nodes. */
-    struct ed_type_ct_zones ed_ct_zones;
-    struct ed_type_runtime_data ed_runtime_data;
-    struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve;
-    struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected;
-    struct ed_type_flow_output ed_flow_output;
-    struct ed_type_addr_sets ed_addr_sets;
-    struct ed_type_port_groups ed_port_groups;
-
-    ENGINE_NODE(ct_zones, "ct_zones");
+    ENGINE_NODE_CUSTOM_DATA(ct_zones, "ct_zones");
     ENGINE_NODE(runtime_data, "runtime_data");
     ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
     ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
@@ -1875,18 +1857,6 @@ main(int argc, char *argv[])
     OVS_NODES
 #undef OVS_NODE
 
-    engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name);
-    engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath",
-                                sbrec_multicast_group_by_name_datapath);
-    engine_ovsdb_node_add_index(&en_sb_port_binding, "name",
-                                sbrec_port_binding_by_name);
-    engine_ovsdb_node_add_index(&en_sb_port_binding, "key",
-                                sbrec_port_binding_by_key);
-    engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath",
-                                sbrec_port_binding_by_datapath);
-    engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key",
-                                sbrec_datapath_binding_by_key);
-
     /* Add dependencies between inc-proc-engine nodes. */
 
     engine_add_input(&en_addr_sets, &en_sb_address_set,
@@ -1935,20 +1905,45 @@ main(int argc, char *argv[])
     engine_add_input(&en_runtime_data, &en_sb_port_binding,
                      runtime_data_sb_port_binding_handler);
 
-    engine_init(&en_flow_output);
+    struct engine_arg engine_arg = {
+        .sb_idl = ovnsb_idl_loop.idl,
+        .ovs_idl = ovs_idl_loop.idl,
+    };
+    engine_init(&en_flow_output, &engine_arg);
 
-    ofctrl_init(&ed_flow_output.group_table,
-                &ed_flow_output.meter_table,
+    engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name);
+    engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath",
+                                sbrec_multicast_group_by_name_datapath);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "name",
+                                sbrec_port_binding_by_name);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "key",
+                                sbrec_port_binding_by_key);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath",
+                                sbrec_port_binding_by_datapath);
+    engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key",
+                                sbrec_datapath_binding_by_key);
+
+    struct ed_type_flow_output *flow_output_data =
+        engine_get_internal_data(&en_flow_output);
+    struct ed_type_ct_zones *ct_zones_data =
+        engine_get_internal_data(&en_ct_zones);
+    struct ed_type_runtime_data *runtime_data = NULL;
+
+    ofctrl_init(&flow_output_data->group_table,
+                &flow_output_data->meter_table,
                 get_ofctrl_probe_interval(ovs_idl_loop.idl));
 
     unixctl_command_register("group-table-list", "", 0, 0,
-                             group_table_list, &ed_flow_output.group_table);
+                             group_table_list,
+                             &flow_output_data->group_table);
 
     unixctl_command_register("meter-table-list", "", 0, 0,
-                             meter_table_list, &ed_flow_output.meter_table);
+                             meter_table_list,
+                             &flow_output_data->meter_table);
 
     unixctl_command_register("ct-zone-list", "", 0, 0,
-                             ct_zone_list, &ed_ct_zones.current);
+                             ct_zone_list,
+                             &ct_zones_data->current);
 
     struct pending_pkt pending_pkt = { .conn = NULL };
     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
@@ -2024,7 +2019,10 @@ main(int argc, char *argv[])
             }
 
             if (br_int) {
-                ofctrl_run(br_int, &ed_ct_zones.pending);
+                ct_zones_data = engine_get_data(&en_ct_zones);
+                if (ct_zones_data) {
+                    ofctrl_run(br_int, &ct_zones_data->pending);
+                }
 
                 if (chassis) {
                     patch_run(ovs_idl_txn,
@@ -2064,41 +2062,50 @@ main(int argc, char *argv[])
                     }
                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
                                    time_msec());
+                    ct_zones_data = engine_get_data(&en_ct_zones);
                     if (ovs_idl_txn) {
-                        commit_ct_zones(br_int, &ed_ct_zones.pending);
+                        if (ct_zones_data) {
+                            commit_ct_zones(br_int, &ct_zones_data->pending);
+                        }
                         bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl),
                                 br_int, chassis,
                                 sbrec_ha_chassis_group_table_get(
                                     ovnsb_idl_loop.idl),
                                 sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
                     }
-                    ofctrl_put(&ed_flow_output.flow_table,
-                               &ed_ct_zones.pending,
-                               sbrec_meter_table_get(ovnsb_idl_loop.idl),
-                               get_nb_cfg(sbrec_sb_global_table_get(
-                                              ovnsb_idl_loop.idl)),
-                               engine_node_changed(&en_flow_output));
-                    pinctrl_run(ovnsb_idl_txn,
-                                sbrec_datapath_binding_by_key,
-                                sbrec_port_binding_by_datapath,
-                                sbrec_port_binding_by_key,
-                                sbrec_port_binding_by_name,
-                                sbrec_mac_binding_by_lport_ip,
-                                sbrec_igmp_group,
-                                sbrec_ip_multicast,
-                                sbrec_dns_table_get(ovnsb_idl_loop.idl),
-                                sbrec_controller_event_table_get(
-                                    ovnsb_idl_loop.idl),
-                                sbrec_service_monitor_table_get(
-                                    ovnsb_idl_loop.idl),
-                                br_int, chassis,
-                                &ed_runtime_data.local_datapaths,
-                                &ed_runtime_data.active_tunnels);
 
-                    if (engine_node_changed(&en_runtime_data)) {
-                        update_sb_monitors(ovnsb_idl_loop.idl, chassis,
-                                           &ed_runtime_data.local_lports,
-                                           &ed_runtime_data.local_datapaths);
+                    flow_output_data = engine_get_data(&en_flow_output);
+                    if (flow_output_data && ct_zones_data) {
+                        ofctrl_put(&flow_output_data->flow_table,
+                                   &ct_zones_data->pending,
+                                   sbrec_meter_table_get(ovnsb_idl_loop.idl),
+                                   get_nb_cfg(sbrec_sb_global_table_get(
+                                                   ovnsb_idl_loop.idl)),
+                                   engine_node_changed(&en_flow_output));
+                    }
+                    runtime_data = engine_get_data(&en_runtime_data);
+                    if (runtime_data) {
+                        pinctrl_run(ovnsb_idl_txn,
+                                    sbrec_datapath_binding_by_key,
+                                    sbrec_port_binding_by_datapath,
+                                    sbrec_port_binding_by_key,
+                                    sbrec_port_binding_by_name,
+                                    sbrec_mac_binding_by_lport_ip,
+                                    sbrec_igmp_group,
+                                    sbrec_ip_multicast,
+                                    sbrec_dns_table_get(ovnsb_idl_loop.idl),
+                                    sbrec_controller_event_table_get(
+                                        ovnsb_idl_loop.idl),
+                                    sbrec_service_monitor_table_get(
+                                        ovnsb_idl_loop.idl),
+                                    br_int, chassis,
+                                    &runtime_data->local_datapaths,
+                                    &runtime_data->active_tunnels);
+                        if (engine_node_changed(&en_runtime_data)) {
+                            update_sb_monitors(ovnsb_idl_loop.idl, chassis,
+                                               &runtime_data->local_lports,
+                                               &runtime_data->local_datapaths);
+                        }
                     }
                 }
 
@@ -2133,9 +2140,13 @@ main(int argc, char *argv[])
 
 
             if (pending_pkt.conn) {
-                if (br_int && chassis) {
+                struct ed_type_addr_sets *as_data =
+                    engine_get_data(&en_addr_sets);
+                struct ed_type_port_groups *pg_data =
+                    engine_get_data(&en_port_groups);
+                if (br_int && chassis && as_data && pg_data) {
                     char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s,
-                        &ed_addr_sets.addr_sets, &ed_port_groups.port_groups);
+                        &as_data->addr_sets, &pg_data->port_groups);
                     if (error) {
                         unixctl_command_reply_error(pending_pkt.conn, error);
                         free(error);
@@ -2173,12 +2184,16 @@ main(int argc, char *argv[])
         }
 
         if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) {
-            struct shash_node *iter, *iter_next;
-            SHASH_FOR_EACH_SAFE (iter, iter_next, &ed_ct_zones.pending) {
-                struct ct_zone_pending_entry *ctzpe = iter->data;
-                if (ctzpe->state == CT_ZONE_DB_SENT) {
-                    shash_delete(&ed_ct_zones.pending, iter);
-                    free(ctzpe);
+            ct_zones_data = engine_get_data(&en_ct_zones);
+            if (ct_zones_data) {
+                struct shash_node *iter, *iter_next;
+                SHASH_FOR_EACH_SAFE (iter, iter_next,
+                                     &ct_zones_data->pending) {
+                    struct ct_zone_pending_entry *ctzpe = iter->data;
+                    if (ctzpe->state == CT_ZONE_DB_SENT) {
+                        shash_delete(&ct_zones_data->pending, iter);
+                        free(ctzpe);
+                    }
                 }
             }
         }
diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c
index 59b5cac..9b1479a 100644
--- a/ovn/lib/inc-proc-eng.c
+++ b/ovn/lib/inc-proc-eng.c
@@ -103,13 +103,16 @@ engine_get_nodes(struct engine_node *node, size_t *n_count)
 }
 
 void
-engine_init(struct engine_node *node)
+engine_init(struct engine_node *node, struct engine_arg *arg)
 {
     engine_nodes = engine_get_nodes(node, &engine_n_nodes);
 
     for (size_t i = 0; i < engine_n_nodes; i++) {
         if (engine_nodes[i]->init) {
-            engine_nodes[i]->init(engine_nodes[i]);
+            engine_nodes[i]->data =
+                engine_nodes[i]->init(engine_nodes[i], arg);
+        } else {
+            engine_nodes[i]->data = NULL;
         }
     }
 }
@@ -119,8 +122,9 @@ engine_cleanup(void)
 {
     for (size_t i = 0; i < engine_n_nodes; i++) {
         if (engine_nodes[i]->cleanup) {
-            engine_nodes[i]->cleanup(engine_nodes[i]);
+            engine_nodes[i]->cleanup(engine_nodes[i]->data);
         }
+        free(engine_nodes[i]->data);
     }
     free(engine_nodes);
     engine_nodes = NULL;
@@ -140,9 +144,16 @@ engine_get_input(const char *input_name, struct engine_node *node)
     return NULL;
 }
 
+void *
+engine_get_input_data(const char *input_name, struct engine_node *node)
+{
+    struct engine_node *input_node = engine_get_input(input_name, node);
+    return engine_get_data(input_node);
+}
+
 void
 engine_add_input(struct engine_node *node, struct engine_node *input,
-                 bool (*change_handler)(struct engine_node *))
+                 bool (*change_handler)(struct engine_node *, void *))
 {
     ovs_assert(node->n_inputs < ENGINE_MAX_INPUT);
     node->inputs[node->n_inputs].node = input;
@@ -153,7 +164,7 @@ engine_add_input(struct engine_node *node, struct engine_node *input,
 struct ovsdb_idl_index *
 engine_ovsdb_node_get_index(struct engine_node *node, const char *name)
 {
-    struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
+    struct ed_type_ovsdb_table *ed = node->data;
     for (size_t i = 0; i < ed->n_indexes; i++) {
         if (!strcmp(ed->indexes[i].name, name)) {
             return ed->indexes[i].index;
@@ -167,7 +178,7 @@ void
 engine_ovsdb_node_add_index(struct engine_node *node, const char *name,
                             struct ovsdb_idl_index *index)
 {
-    struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
+    struct ed_type_ovsdb_table *ed = node->data;
     ovs_assert(ed->n_indexes < ENGINE_MAX_OVSDB_INDEX);
 
     ed->indexes[ed->n_indexes].name = name;
@@ -192,6 +203,19 @@ engine_set_node_state_at(struct engine_node *node,
     node->state = state;
 }
 
+static bool
+engine_node_valid(struct engine_node *node)
+{
+    if (node->state == EN_UPDATED || node->state == EN_VALID) {
+        return true;
+    }
+
+    if (node->is_valid) {
+        return node->is_valid(node);
+    }
+    return false;
+}
+
 bool
 engine_node_changed(struct engine_node *node)
 {
@@ -215,6 +239,21 @@ engine_aborted(void)
     return engine_run_aborted;
 }
 
+void *
+engine_get_data(struct engine_node *node)
+{
+    if (engine_node_valid(node)) {
+        return node->data;
+    }
+    return NULL;
+}
+
+void *
+engine_get_internal_data(struct engine_node *node)
+{
+    return node->data;
+}
+
 void
 engine_init_run(void)
 {
@@ -240,7 +279,7 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed)
     }
 
     /* Run the node handler which might change state. */
-    node->run(node);
+    node->run(node, node->data);
 }
 
 /* Return true if the node could be computed, false otherwise. */
@@ -256,7 +295,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed)
             /* If the input change can't be handled incrementally, run
              * the node handler.
              */
-            if (!node->inputs[i].change_handler(node)) {
+            if (!node->inputs[i].change_handler(node, node->data)) {
                 VLOG_DBG("node: %s, can't handle change for input %s, "
                          "fall back to recompute",
                          node->name, node->inputs[i].node->name);
@@ -273,7 +312,7 @@ engine_run_node(struct engine_node *node, bool recompute_allowed)
 {
     if (!node->n_inputs) {
         /* Run the node handler which might change state. */
-        node->run(node);
+        node->run(node, node->data);
         return;
     }
 
@@ -345,7 +384,7 @@ engine_need_run(void)
             continue;
         }
 
-        engine_nodes[i]->run(engine_nodes[i]);
+        engine_nodes[i]->run(engine_nodes[i], engine_nodes[i]->data);
         VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name,
                  engine_node_state_name[engine_nodes[i]->state]);
         if (engine_nodes[i]->state == EN_UPDATED) {
diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
index 2f3ff62..9cc99a3 100644
--- a/ovn/lib/inc-proc-eng.h
+++ b/ovn/lib/inc-proc-eng.h
@@ -68,6 +68,12 @@ struct engine_context {
     struct ovsdb_idl_txn *ovnsb_idl_txn;
 };
 
+/* Arguments to be passed to the engine at engine_init(). */
+struct engine_arg {
+    struct ovsdb_idl *sb_idl;
+    struct ovsdb_idl *ovs_idl;
+};
+
 struct engine_node;
 
 struct engine_node_input {
@@ -79,7 +85,7 @@ struct engine_node_input {
      *  - true: if change can be handled
      *  - false: if change cannot be handled (indicating full recompute needed)
      */
-    bool (*change_handler)(struct engine_node *node);
+    bool (*change_handler)(struct engine_node *node, void *data);
 };
 
 enum engine_node_state {
@@ -106,30 +112,42 @@ struct engine_node {
     /* Inputs of this node. */
     struct engine_node_input inputs[ENGINE_MAX_INPUT];
 
-    /* Data of this node. It is vague and interpreted by the related functions.
-     * The content of the data should be changed only by the change_handlers
-     * and run() function of the current node. Users should ensure that the
-     * data is read-only in change-handlers of the nodes that depends on this
-     * node. */
+    /* A pointer to node internal data. The data is safely accessible to
+     * users through the engine_get_data() API. For special cases, when the
+     * data is known to be valid (e.g., at init time), users can also call
+     * engine_get_internal_data().
+     */
     void *data;
 
     /* State of the node after the last engine run. */
     enum engine_node_state state;
 
-    /* Method to initialize data. It may be NULL. */
-    void (*init)(struct engine_node *);
+    /* Method to allocate and initialize node data. It may be NULL.
+     * The user supplied argument 'arg' is passed from the call to
+     * engine_init().
+     */
+    void *(*init)(struct engine_node *node, struct engine_arg *arg);
 
     /* Method to clean up data. It may be NULL. */
-    void (*cleanup)(struct engine_node *);
+    void (*cleanup)(void *data);
 
     /* Fully processes all inputs of this node and regenerates the data
-     * of this node */
-    void (*run)(struct engine_node *);
+     * of this node. The pointer to the node's data is passed as argument.
+     */
+    void (*run)(struct engine_node *node, void *data);
+
+    /* Method to validate if the 'internal_data' is valid. This allows users
+     * to customize when 'data' can be used (e.g., even if the node
+     * hasn't been refreshed in the last iteration, if 'data'
+     * doesn't store pointers to DB records it's still safe to use).
+     */
+    bool (*is_valid)(struct engine_node *);
 };
 
 /* Initialize the data for the engine nodes. It calls each node's
- * init() method if not NULL. It should be called before the main loop. */
-void engine_init(struct engine_node *node);
+ * init() method if not NULL passing the user supplied 'arg'.
+ * It should be called before the main loop. */
+void engine_init(struct engine_node *node, struct engine_arg *arg);
 
 /* Initialize the engine nodes for a new run. It should be called in the
  * main processing loop before every potential engine_run().
@@ -155,12 +173,15 @@ bool engine_need_run(void);
 struct engine_node * engine_get_input(const char *input_name,
                                       struct engine_node *);
 
+/* Get the data from the input node with <name> for <node> */
+void *engine_get_input_data(const char *input_name, struct engine_node *);
+
 /* Add an input (dependency) for <node>, with corresponding change_handler,
  * which can be NULL. If the change_handler is NULL, the engine will not
  * be able to process the change incrementally, and will fall back to call
  * the run method to recompute. */
 void engine_add_input(struct engine_node *node, struct engine_node *input,
-                      bool (*change_handler)(struct engine_node *));
+                      bool (*change_handler)(struct engine_node *, void *));
 
 /* Force the engine to recompute everything if set to true. It is used
  * in circumstances when we are not sure there is change or not, or
@@ -185,6 +206,25 @@ bool engine_has_run(void);
 /* Returns true if during the last engine run we had to abort processing. */
 bool engine_aborted(void);
 
+/* Return a pointer to node data accessible for users outside the processing
+ * engine. If the node data is not valid (e.g., last engine_run() failed or
+ * didn't happen), the node's is_valid() method is used to determine if the
+ * data can be safely accessed. If it's not the case, the function returns
+ * NULL.
+ * The content of the data should be changed only by the change_handlers
+ * and run() function of the current node. Users should ensure that the
+ * data is read-only in change-handlers of the nodes that depends on this
+ * node.
+ */
+void *engine_get_data(struct engine_node *node);
+
+/* Return a pointer to node data *without* performing any sanity checks on
+ * the state of the node. This may be used only in specific cases when data
+ * is guaranteed to be valid, e.g., immediately after initialization and
+ * before the first engine_run().
+ */
+void *engine_get_internal_data(struct engine_node *node);
+
 /* Set the state of the node and log changes. */
 #define engine_set_node_state(node, state) \
     engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR)
@@ -201,30 +241,42 @@ struct ed_type_ovsdb_table {
 };
 
 #define EN_OVSDB_GET(NODE) \
-    (((struct ed_type_ovsdb_table *)NODE->data)->table)
+    (((struct ed_type_ovsdb_table *)(NODE)->data)->table)
 
 struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *,
                                                      const char *name);
 
+/* Adds an OVSDB IDL index to the node. This should be called only after
+ * engine_init() as the index is stored in the node data.
+ */
 void engine_ovsdb_node_add_index(struct engine_node *, const char *name,
                                  struct ovsdb_idl_index *);
 
 /* Macro to define an engine node. */
-#define ENGINE_NODE(NAME, NAME_STR) \
+#define ENGINE_NODE_DEF(NAME, NAME_STR) \
     struct engine_node en_##NAME = { \
         .name = NAME_STR, \
-        .data = &ed_##NAME, \
+        .data = NULL, \
         .state = EN_STALE, \
         .init = en_##NAME##_init, \
         .run = en_##NAME##_run, \
         .cleanup = en_##NAME##_cleanup, \
+        .is_valid = en_##NAME##_is_valid, \
     };
 
+#define ENGINE_NODE_CUSTOM_DATA(NAME, NAME_STR) \
+    ENGINE_NODE_DEF(NAME, NAME_STR)
+
+#define ENGINE_NODE(NAME, NAME_STR) \
+    static bool (*en_##NAME##_is_valid)(struct engine_node *node) = NULL; \
+    ENGINE_NODE_DEF(NAME, NAME_STR)
+
 /* Macro to define member functions of an engine node which represents
  * a table of OVSDB */
 #define ENGINE_FUNC_OVSDB(DB_NAME, TBL_NAME) \
 static void \
-en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
+en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node, \
+                                void *data OVS_UNUSED) \
 { \
     const struct DB_NAME##rec_##TBL_NAME##_table *table = \
         EN_OVSDB_GET(node); \
@@ -234,10 +286,18 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
     } \
     engine_set_node_state(node, EN_VALID); \
 } \
-static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \
-            = NULL; \
-static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \
-            = NULL;
+static void *en_##DB_NAME##_##TBL_NAME##_init( \
+    struct engine_node *node OVS_UNUSED, \
+    struct engine_arg *arg) \
+{ \
+    struct ovsdb_idl *idl = arg->DB_NAME##_idl; \
+    struct ed_type_ovsdb_table *data = xzalloc(sizeof *data); \
+    data->table = DB_NAME##rec_##TBL_NAME##_table_get(idl); \
+    return data; \
+} \
+static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \
+{ \
+}
 
 /* Macro to define member functions of an engine node which represents
  * a table of OVN SB DB */
@@ -250,21 +310,16 @@ static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \
     ENGINE_FUNC_OVSDB(ovs, TBL_NAME)
 
 /* Macro to define an engine node which represents a table of OVSDB */
-#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR, IDL) \
-    struct ed_type_ovsdb_table ed_##DB_NAME##_##TBL_NAME; \
-    memset(&ed_##DB_NAME##_##TBL_NAME, 0, sizeof ed_##DB_NAME##_##TBL_NAME); \
-    ovs_assert(IDL); \
-    ed_##DB_NAME##_##TBL_NAME.table = \
-        DB_NAME##rec_##TBL_NAME##_table_get(IDL); \
+#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR) \
     ENGINE_NODE(DB_NAME##_##TBL_NAME, DB_NAME_STR"_"TBL_NAME_STR)
 
 /* Macro to define an engine node which represents a table of OVN SB DB */
 #define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \
-    ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR, ovnsb_idl_loop.idl);
+    ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR);
 
 /* Macro to define an engine node which represents a table of open_vswitch
  * DB */
 #define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \
-    ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR, ovs_idl_loop.idl);
+    ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR);
 
 #endif /* ovn/lib/inc-proc-eng.h */
-- 
1.8.3.1