en_route_policies was mostly defined in en-northd.h and en-northd.c. It
calls out to functions and uses structures that are defined in northd.h
and northd.c. In many cases, these functions were one-off uses that did
not need to be exported in a header file.

To reduce the annoyingness factor, this commit moves en-route-policies
to its own file. Along with it, this commit moves certain functions out
of northd and into en-route-policies.c as static functions.

There are no functionality changes in this commit. This only moves
functions around so they are better organized.

Signed-off-by: Mark Michelson <[email protected]>
---
 northd/automake.mk         |   2 +
 northd/en-lflow.c          |   1 +
 northd/en-northd.c         | 117 +----------
 northd/en-northd.h         |  11 -
 northd/en-route-policies.c | 408 +++++++++++++++++++++++++++++++++++++
 northd/en-route-policies.h |  56 +++++
 northd/inc-proc-northd.c   |   1 +
 northd/northd.c            | 313 +---------------------------
 northd/northd.h            |  48 +++--
 9 files changed, 515 insertions(+), 442 deletions(-)
 create mode 100644 northd/en-route-policies.c
 create mode 100644 northd/en-route-policies.h

diff --git a/northd/automake.mk b/northd/automake.mk
index 1d3044930..06c35276f 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -34,6 +34,8 @@ northd_ovn_northd_SOURCES = \
        northd/en-northd-output.h \
        northd/en-port-group.c \
        northd/en-port-group.h \
+       northd/en-route-policies.c \
+       northd/en-route-policies.h \
        northd/en-sync-sb.c \
        northd/en-sync-sb.h \
        northd/en-sync-from-sb.c \
diff --git a/northd/en-lflow.c b/northd/en-lflow.c
index f2ae40ec8..aa1cab3df 100644
--- a/northd/en-lflow.c
+++ b/northd/en-lflow.c
@@ -29,6 +29,7 @@
 #include "en-sampling-app.h"
 #include "en-group-ecmp-route.h"
 #include "en-datapath-sync.h"
+#include "en-route-policies.h"
 #include "lflow-mgr.h"
 
 #include "lib/inc-proc-eng.h"
diff --git a/northd/en-northd.c b/northd/en-northd.c
index 5286dbbb2..db0d4e413 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -1,4 +1,4 @@
-    /*
+/*
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
@@ -32,6 +32,7 @@
 #include "lib/util.h"
 #include "openvswitch/vlog.h"
 #include "en-datapath-logical-router.h"
+#include "en-route-policies.h"
 
 VLOG_DEFINE_THIS_MODULE(en_northd);
 COVERAGE_DEFINE(northd_run);
@@ -278,94 +279,6 @@ northd_nb_port_group_handler(struct engine_node *node, 
void *data)
     return EN_HANDLED_UNCHANGED;
 }
 
-enum engine_input_handler_result
-route_policies_northd_change_handler(struct engine_node *node,
-                                     void *data OVS_UNUSED)
-{
-    struct northd_data *northd_data = engine_get_input_data("northd", node);
-    if (!northd_has_tracked_data(&northd_data->trk_data)) {
-        return EN_UNHANDLED;
-    }
-
-    /* This node uses the below data from the en_northd engine node.
-     * See (lr_stateful_get_input_data())
-     *   1. northd_data->lr_datapaths
-     *      This data gets updated when a logical router or logical router port
-     *      is created or deleted.
-     *      Northd engine node presently falls back to full recompute when
-     *      this happens and so does this node.
-     *
-     */
-
-    return EN_HANDLED_UNCHANGED;
-}
-
-enum engine_input_handler_result
-route_policies_datapath_synced_logical_router_handler(struct engine_node *node,
-                                                      void *data OVS_UNUSED)
-{
-    const struct ovn_synced_logical_router_map *synced_lrs =
-        engine_get_input_data("datapath_synced_logical_router", node);
-
-    if (hmapx_is_empty(&synced_lrs->new) &&
-        hmapx_is_empty(&synced_lrs->updated) &&
-        hmapx_is_empty(&synced_lrs->deleted)) {
-        return EN_UNHANDLED;
-    }
-
-    struct hmapx_node *lr_node;
-    HMAPX_FOR_EACH (lr_node, &synced_lrs->deleted) {
-        const struct ovn_synced_logical_router *lr = lr_node->data;
-        if (lr->nb->n_policies > 0) {
-            return EN_UNHANDLED;
-        }
-    }
-
-    HMAPX_FOR_EACH (lr_node, &synced_lrs->new) {
-        const struct ovn_synced_logical_router *lr = lr_node->data;
-        if (lr->nb->n_policies > 0) {
-            return EN_UNHANDLED;
-        }
-    }
-
-    HMAPX_FOR_EACH (lr_node, &synced_lrs->updated) {
-        const struct ovn_synced_logical_router *lr = lr_node->data;
-        if (nbrec_logical_router_is_updated(
-                lr->nb, NBREC_LOGICAL_ROUTER_COL_POLICIES)) {
-            return EN_UNHANDLED;
-        }
-        for (size_t i = 0; i < lr->nb->n_policies; i++) {
-            if (nbrec_logical_router_policy_row_get_seqno(lr->nb->policies[i],
-                                    OVSDB_IDL_CHANGE_MODIFY) > 0) {
-                return EN_UNHANDLED;
-            }
-        }
-    }
-
-    return EN_HANDLED_UNCHANGED;
-}
-
-enum engine_node_state
-en_route_policies_run(struct engine_node *node, void *data)
-{
-    struct northd_data *northd_data = engine_get_input_data("northd", node);
-    struct bfd_data *bfd_data = engine_get_input_data("bfd", node);
-    struct route_policies_data *route_policies_data = data;
-
-    route_policies_destroy(data);
-    route_policies_init(data);
-
-    struct ovn_datapath *od;
-    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
-        build_route_policies(od, &bfd_data->bfd_connections,
-                             &route_policies_data->route_policies,
-                             &route_policies_data->bfd_active_connections,
-                             &route_policies_data->chain_ids);
-    }
-
-    return EN_UPDATED;
-}
-
 enum engine_input_handler_result
 routes_northd_change_handler(struct engine_node *node,
                              void *data OVS_UNUSED)
@@ -553,6 +466,12 @@ en_routes_run(struct engine_node *node, void *data)
     return EN_UPDATED;
 }
 
+static void
+destroy_bfd_data(struct bfd_data *data)
+{
+    bfd_destroy(&data->bfd_connections);
+}
+
 enum engine_node_state
 en_bfd_run(struct engine_node *node, void *data)
 {
@@ -562,7 +481,7 @@ en_bfd_run(struct engine_node *node, void *data)
     const struct sbrec_bfd_table *sbrec_bfd_table =
         EN_OVSDB_GET(engine_get_input("SB_bfd", node));
 
-    bfd_destroy(data);
+    destroy_bfd_data(data);
     bfd_init(data);
     build_bfd_map(nbrec_bfd_table, sbrec_bfd_table,
                   &bfd_data->bfd_connections);
@@ -661,16 +580,6 @@ void
     return data;
 }
 
-void
-*en_route_policies_init(struct engine_node *node OVS_UNUSED,
-                        struct engine_arg *arg OVS_UNUSED)
-{
-    struct route_policies_data *data = xzalloc(sizeof *data);
-
-    route_policies_init(data);
-    return data;
-}
-
 void
 *en_routes_init(struct engine_node *node OVS_UNUSED,
                       struct engine_arg *arg OVS_UNUSED)
@@ -758,12 +667,6 @@ northd_sb_fdb_change_handler(struct engine_node *node, 
void *data)
     return EN_HANDLED_UNCHANGED;
 }
 
-void
-en_route_policies_cleanup(void *data)
-{
-    route_policies_destroy(data);
-}
-
 void
 en_routes_cleanup(void *data)
 {
@@ -779,7 +682,7 @@ en_routes_clear_tracked_data(void *data)
 void
 en_bfd_cleanup(void *data)
 {
-    bfd_destroy(data);
+    destroy_bfd_data(data);
 }
 
 void
diff --git a/northd/en-northd.h b/northd/en-northd.h
index 706b49e45..6fbf36749 100644
--- a/northd/en-northd.h
+++ b/northd/en-northd.h
@@ -31,17 +31,6 @@ enum engine_input_handler_result
 northd_sb_fdb_change_handler(struct engine_node *node, void *data);
 void *en_routes_init(struct engine_node *node OVS_UNUSED,
                             struct engine_arg *arg OVS_UNUSED);
-void en_route_policies_cleanup(void *data);
-enum engine_input_handler_result
-route_policies_northd_change_handler(struct engine_node *node,
-                                     void *data OVS_UNUSED);
-enum engine_input_handler_result
-route_policies_datapath_synced_logical_router_handler(struct engine_node *node,
-                                                      void *data OVS_UNUSED);
-enum engine_node_state en_route_policies_run(struct engine_node *node,
-                                             void *data);
-void *en_route_policies_init(struct engine_node *node OVS_UNUSED,
-                             struct engine_arg *arg OVS_UNUSED);
 void en_routes_clear_tracked_data(void *data);
 void en_routes_cleanup(void *data);
 enum engine_input_handler_result
diff --git a/northd/en-route-policies.c b/northd/en-route-policies.c
new file mode 100644
index 000000000..dd6d182e8
--- /dev/null
+++ b/northd/en-route-policies.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2026, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <config.h>
+
+#include "en-datapath-logical-router.h"
+#include "en-route-policies.h"
+#include "northd.h"
+#include "ovn-nb-idl.h"
+
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(en_route_policies);
+
+static struct route_policy *
+route_policies_lookup(struct hmap *route_policies, size_t hash,
+                      struct route_policy *new_rp)
+{
+    struct route_policy *rp;
+    HMAP_FOR_EACH_WITH_HASH (rp, key_node, hash, route_policies) {
+        if (rp->rule != new_rp->rule) {
+            continue;
+        }
+
+        if (rp->chain_id != new_rp->chain_id) {
+            continue;
+        }
+
+        if (rp->jump_chain_id != new_rp->jump_chain_id) {
+            continue;
+        }
+
+        if (rp->n_valid_nexthops != new_rp->n_valid_nexthops) {
+            continue;
+        }
+
+        size_t i;
+        for (i = 0; i < new_rp->n_valid_nexthops; i++) {
+            size_t j;
+
+            for (j = 0; j < rp->n_valid_nexthops; j++) {
+                if (!strcmp(new_rp->valid_nexthops[i],
+                            rp->valid_nexthops[j])) {
+                    break;
+                }
+            }
+
+            if (j == rp->n_valid_nexthops) {
+                break;
+            }
+        }
+
+        if (i == new_rp->n_valid_nexthops) {
+            return rp;
+        }
+    }
+
+    return NULL;
+}
+
+static bool
+policy_chain_id(struct simap *chain_ids, const char *chain_name, uint32_t *id)
+{
+    if (chain_name && *chain_name) {
+        *id = simap_get(chain_ids, chain_name);
+        return true;
+    }
+
+    return false;
+}
+
+static void
+policy_chain_add(struct simap *chain_ids, const char *chain_name)
+{
+    uint32_t id = simap_count(chain_ids) + 1;
+
+    if (id == UINT16_MAX) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "Too many policy chains for Logical Router.");
+        return;
+    }
+
+    if (!simap_put(chain_ids, chain_name, id)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "Policy chain id unexpectedly appeared");
+    }
+}
+
+static bool
+check_bfd_state(const struct nbrec_logical_router_policy *rule,
+                struct ovn_port *out_port, const char *nexthop,
+                const struct hmap *bfd_connections,
+                struct hmap *bfd_active_connections)
+{
+    struct in6_addr nexthop_v6;
+    bool is_nexthop_v6 = ipv6_parse(nexthop, &nexthop_v6);
+
+    for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
+        /* Check if there is a BFD session associated to the reroute
+         * policy. */
+        const struct nbrec_bfd *nb_bt = rule->bfd_sessions[i];
+        struct in6_addr dst_ipv6;
+        bool is_dst_v6 = ipv6_parse(nb_bt->dst_ip, &dst_ipv6);
+
+        if (is_nexthop_v6 ^ is_dst_v6) {
+            continue;
+        }
+
+        if ((is_nexthop_v6 && !ipv6_addr_equals(&nexthop_v6, &dst_ipv6)) ||
+            strcmp(nb_bt->dst_ip, nexthop)) {
+            continue;
+        }
+
+        if (strcmp(nb_bt->logical_port, out_port->key)) {
+            continue;
+        }
+
+        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
+                                                  nb_bt->logical_port,
+                                                  nb_bt->dst_ip);
+        if (!bfd_e) {
+            continue;
+        }
+
+        /* This route policy is linked to an active bfd session. */
+        struct bfd_entry *bfd_rp = bfd_port_lookup(bfd_active_connections,
+                                                   nb_bt->logical_port,
+                                                   nb_bt->dst_ip);
+        if (!bfd_rp) {
+            bfd_rp = bfd_alloc_entry(bfd_active_connections,
+                                     nb_bt->logical_port, nb_bt->dst_ip,
+                                     bfd_e->status);
+        }
+
+        if (!strcmp(bfd_e->status, "admin_down")) {
+            bfd_set_status(bfd_rp, "down");
+        }
+
+        return strcmp(bfd_rp->status, "down");
+    }
+
+    return true;
+}
+
+static void
+build_route_policies(struct ovn_datapath *od,
+                     const struct hmap *bfd_connections,
+                     struct hmap *route_policies,
+                     struct hmap *bfd_active_connections,
+                     struct simap *chain_ids)
+{
+    struct route_policy *rp;
+
+    /* Create chain numeric ids for policies with chain name set */
+    for (int i = 0; i < od->nbr->n_policies; i++) {
+        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
+        uint32_t id;
+
+        if (policy_chain_id(chain_ids, rule->chain, &id) && id == 0) {
+            policy_chain_add(chain_ids, rule->chain);
+        }
+    }
+
+    for (int i = 0; i < od->nbr->n_policies; i++) {
+        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
+
+        if (rule->nexthop && rule->nexthop[0]) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "Logical router: %s, policy uses deprecated"
+                         " column \"nexthop\", this column is ignored. Please"
+                         "use \"nexthops\" column instead.", od->nbr->name);
+            continue;
+        }
+
+        size_t n_valid_nexthops = 0;
+        char **valid_nexthops = NULL;
+        uint32_t chain_id = 0;
+        uint32_t jump_chain_id = 0;
+
+        /* Skip policy if chain name is set but id was not created above */
+        if (policy_chain_id(chain_ids, rule->chain, &chain_id)
+            && chain_id == 0) {
+            continue;
+        }
+
+        if (!strcmp(rule->action, "jump")) {
+            /* Skip policy if action is 'jump' but no target chain is set */
+            if (!policy_chain_id(chain_ids, rule->jump_chain,
+                                 &jump_chain_id)) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+                VLOG_WARN_RL(&rl,
+                         "Logical router: %s, policy action 'jump'"
+                         " has empty target",
+                         od->nbr->name);
+                continue;
+            }
+
+            /* Skip policy if action is 'jump' but target chain name
+               is not resolved to numeric id */
+            if (jump_chain_id == 0) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+                VLOG_WARN_RL(&rl,
+                         "Logical router: %s, policy action 'jump'"
+                         " follows to non-existent chain %s",
+                         od->nbr->name, rule->jump_chain);
+                continue;
+            }
+        }
+
+        if (simap_is_empty(chain_ids)) {
+            chain_id = -1;
+        }
+
+        if (!strcmp(rule->action, "reroute")) {
+            if (rule->output_port && rule->n_nexthops != 1) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+                VLOG_WARN_RL(&rl,
+                             "Logical router: %s, policy "
+                             "(chain: '%s', match: '%s', priority %"PRId64"): "
+                             "output_port only supported on non-ECMP "
+                             "reroute policies",
+                             od->nbr->name,
+                             rule->chain ? rule->chain : "<Default>",
+                             rule->match, rule->priority);
+                continue;
+            }
+
+            valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
+            for (size_t j = 0; j < rule->n_nexthops; j++) {
+                char *nexthop = rule->nexthops[j];
+                if (!nexthop || !nexthop[0]) {
+                    continue;
+                }
+
+                struct ovn_port *out_port = NULL;
+                bool is_ipv4 = strchr(nexthop, '.') ? true : false;
+
+                if (!find_policy_outport(od, rule, nexthop, is_ipv4, NULL,
+                                         &out_port)) {
+                    continue;
+                }
+                if (!check_bfd_state(rule, out_port, nexthop,
+                                     bfd_connections,
+                                     bfd_active_connections)) {
+                    continue;
+                }
+                valid_nexthops[n_valid_nexthops++] = nexthop;
+            }
+
+            if (!n_valid_nexthops) {
+                free(valid_nexthops);
+                continue;
+            }
+        }
+
+        struct route_policy *new_rp = xzalloc(sizeof *new_rp);
+        new_rp->rule = rule;
+        new_rp->n_valid_nexthops = n_valid_nexthops;
+        new_rp->valid_nexthops = valid_nexthops;
+        new_rp->chain_id = chain_id;
+        new_rp->jump_chain_id = jump_chain_id;
+
+        size_t hash = uuid_hash(&od->key);
+        rp = route_policies_lookup(route_policies, hash, new_rp);
+        if (!rp) {
+            hmap_insert(route_policies, &new_rp->key_node, hash);
+        } else {
+            free(valid_nexthops);
+            free(new_rp);
+        }
+    }
+}
+
+static void
+route_policies_init(struct route_policies_data *data)
+{
+    hmap_init(&data->route_policies);
+    hmap_init(&data->bfd_active_connections);
+    simap_init(&data->chain_ids);
+}
+
+static void
+route_policies_destroy(struct route_policies_data *data)
+{
+    struct route_policy *rp;
+    HMAP_FOR_EACH_POP (rp, key_node, &data->route_policies) {
+        free(rp->valid_nexthops);
+        free(rp);
+    };
+    hmap_destroy(&data->route_policies);
+    bfd_destroy(&data->bfd_active_connections);
+    simap_destroy(&data->chain_ids);
+}
+
+enum engine_node_state
+en_route_policies_run(struct engine_node *node, void *data)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    struct bfd_data *bfd_data = engine_get_input_data("bfd", node);
+    struct route_policies_data *route_policies_data = data;
+
+    route_policies_destroy(data);
+    route_policies_init(data);
+
+    struct ovn_datapath *od;
+    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
+        build_route_policies(od, &bfd_data->bfd_connections,
+                             &route_policies_data->route_policies,
+                             &route_policies_data->bfd_active_connections,
+                             &route_policies_data->chain_ids);
+    }
+
+    return EN_UPDATED;
+}
+
+void
+*en_route_policies_init(struct engine_node *node OVS_UNUSED,
+                        struct engine_arg *arg OVS_UNUSED)
+{
+    struct route_policies_data *data = xzalloc(sizeof *data);
+
+    route_policies_init(data);
+    return data;
+}
+
+void
+en_route_policies_cleanup(void *data)
+{
+    route_policies_destroy(data);
+}
+
+enum engine_input_handler_result
+route_policies_northd_change_handler(struct engine_node *node,
+                                     void *data OVS_UNUSED)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    if (!northd_has_tracked_data(&northd_data->trk_data)) {
+        return EN_UNHANDLED;
+    }
+
+    /* This node uses the below data from the en_northd engine node.
+     * See (lr_stateful_get_input_data())
+     *   1. northd_data->lr_datapaths
+     *      This data gets updated when a logical router or logical router port
+     *      is created or deleted.
+     *      Northd engine node presently falls back to full recompute when
+     *      this happens and so does this node.
+     */
+
+    return EN_HANDLED_UNCHANGED;
+}
+
+enum engine_input_handler_result
+route_policies_datapath_synced_logical_router_handler(struct engine_node *node,
+                                                      void *data OVS_UNUSED)
+{
+    const struct ovn_synced_logical_router_map *synced_lrs =
+        engine_get_input_data("datapath_synced_logical_router", node);
+
+    if (hmapx_is_empty(&synced_lrs->new) &&
+        hmapx_is_empty(&synced_lrs->updated) &&
+        hmapx_is_empty(&synced_lrs->deleted)) {
+        return EN_UNHANDLED;
+    }
+
+    struct hmapx_node *lr_node;
+    HMAPX_FOR_EACH (lr_node, &synced_lrs->deleted) {
+        const struct ovn_synced_logical_router *lr = lr_node->data;
+        if (lr->nb->n_policies > 0) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    HMAPX_FOR_EACH (lr_node, &synced_lrs->new) {
+        const struct ovn_synced_logical_router *lr = lr_node->data;
+        if (lr->nb->n_policies > 0) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    HMAPX_FOR_EACH (lr_node, &synced_lrs->updated) {
+        const struct ovn_synced_logical_router *lr = lr_node->data;
+        if (nbrec_logical_router_is_updated(
+                lr->nb, NBREC_LOGICAL_ROUTER_COL_POLICIES)) {
+            return EN_UNHANDLED;
+        }
+        for (size_t i = 0; i < lr->nb->n_policies; i++) {
+            if (nbrec_logical_router_policy_row_get_seqno(lr->nb->policies[i],
+                                    OVSDB_IDL_CHANGE_MODIFY) > 0) {
+                return EN_UNHANDLED;
+            }
+        }
+    }
+
+    return EN_HANDLED_UNCHANGED;
+}
diff --git a/northd/en-route-policies.h b/northd/en-route-policies.h
new file mode 100644
index 000000000..1d2288e46
--- /dev/null
+++ b/northd/en-route-policies.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2026, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EN_ROUTE_POLICIES_H
+#define EN_ROUTE_POLICIES_H
+
+#include "inc-proc-eng.h"
+
+#include "lib/simap.h"
+#include "openvswitch/hmap.h"
+
+/* Represents the data associated with an instance of a northbound
+ * Logical Router Policy for a particular Logical Router.
+ */
+struct route_policy {
+    struct hmap_node key_node;
+    const struct nbrec_logical_router_policy *rule;
+    size_t n_valid_nexthops;
+    char **valid_nexthops;
+    uint32_t chain_id;
+    uint32_t jump_chain_id;
+};
+
+/* Global route policy data exported by en-route-policies. */
+struct route_policies_data {
+    struct hmap route_policies;
+    struct hmap bfd_active_connections;
+    struct simap chain_ids;
+};
+
+void en_route_policies_cleanup(void *data);
+enum engine_input_handler_result
+route_policies_northd_change_handler(struct engine_node *node,
+                                     void *data OVS_UNUSED);
+enum engine_input_handler_result
+route_policies_datapath_synced_logical_router_handler(struct engine_node *node,
+                                                      void *data OVS_UNUSED);
+enum engine_node_state en_route_policies_run(struct engine_node *node,
+                                             void *data);
+void *en_route_policies_init(struct engine_node *node OVS_UNUSED,
+                             struct engine_arg *arg OVS_UNUSED);
+
+#endif /* EN_ROUTE_POLICIES_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index e28cd9d97..84c40fdd2 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -51,6 +51,7 @@
 #include "en-datapath-logical-router.h"
 #include "en-datapath-logical-switch.h"
 #include "en-datapath-sync.h"
+#include "en-route-policies.h"
 #include "unixctl.h"
 #include "util.h"
 
diff --git a/northd/northd.c b/northd/northd.c
index 5aaf320d1..c23b4ffc1 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -54,6 +54,7 @@
 #include "en-sampling-app.h"
 #include "en-datapath-logical-switch.h"
 #include "en-datapath-logical-router.h"
+#include "en-route-policies.h"
 #include "lib/ovn-parallel-hmap.h"
 #include "ovn/actions.h"
 #include "ovn/features.h"
@@ -11880,19 +11881,7 @@ build_lswitch_ip_unicast_lookup_for_nats(
     }
 }
 
-struct bfd_entry {
-    struct hmap_node hmap_node;
-
-    const struct nbrec_bfd *nb_bt;
-    const struct sbrec_bfd *sb_bt;
-
-    char *logical_port;
-    char *dst_ip;
-    char *status;
-    bool stale;
-};
-
-static struct bfd_entry *
+struct bfd_entry *
 bfd_alloc_entry(struct hmap *bfd_connections,
                 const char *logical_port, const char *dst_ip,
                 const char *status)
@@ -11908,7 +11897,7 @@ bfd_alloc_entry(struct hmap *bfd_connections,
     return bfd_e;
 }
 
-static void
+void
 bfd_erase_entry(struct bfd_entry *bfd_e)
 {
     free(bfd_e->logical_port);
@@ -11917,14 +11906,14 @@ bfd_erase_entry(struct bfd_entry *bfd_e)
     free(bfd_e);
 }
 
-static void
+void
 bfd_set_status(struct bfd_entry *bfd_e, const char *status)
 {
     free(bfd_e->status);
     bfd_e->status = xstrdup(status);
 }
 
-static struct bfd_entry *
+struct bfd_entry *
 bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
                 const char *dst_ip)
 {
@@ -12205,7 +12194,7 @@ lrp_find_member_ip(const struct ovn_port *op, const 
char *ip_s)
  * this policy could be determined.  Stores a pointer to the output port
  * in 'p_output_port' and a pointer to the router IP address to be used for
  * this policy, in 'p_lrp_addr_s'. */
-static bool
+bool
 find_policy_outport(struct ovn_datapath *od,
                     const struct nbrec_logical_router_policy *policy,
                     const char *nexthop, bool is_ipv4,
@@ -12256,61 +12245,6 @@ find_policy_outport(struct ovn_datapath *od,
     return true;
 }
 
-static bool check_bfd_state(const struct nbrec_logical_router_policy *rule,
-                            struct ovn_port *out_port, const char *nexthop,
-                            const struct hmap *bfd_connections,
-                            struct hmap *bfd_active_connections)
-{
-    struct in6_addr nexthop_v6;
-    bool is_nexthop_v6 = ipv6_parse(nexthop, &nexthop_v6);
-
-    for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
-        /* Check if there is a BFD session associated to the reroute
-         * policy. */
-        const struct nbrec_bfd *nb_bt = rule->bfd_sessions[i];
-        struct in6_addr dst_ipv6;
-        bool is_dst_v6 = ipv6_parse(nb_bt->dst_ip, &dst_ipv6);
-
-        if (is_nexthop_v6 ^ is_dst_v6) {
-            continue;
-        }
-
-        if ((is_nexthop_v6 && !ipv6_addr_equals(&nexthop_v6, &dst_ipv6)) ||
-            strcmp(nb_bt->dst_ip, nexthop)) {
-            continue;
-        }
-
-        if (strcmp(nb_bt->logical_port, out_port->key)) {
-            continue;
-        }
-
-        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
-                                                  nb_bt->logical_port,
-                                                  nb_bt->dst_ip);
-        if (!bfd_e) {
-            continue;
-        }
-
-        /* This route policy is linked to an active bfd session. */
-        struct bfd_entry *bfd_rp = bfd_port_lookup(bfd_active_connections,
-                                                   nb_bt->logical_port,
-                                                   nb_bt->dst_ip);
-        if (!bfd_rp) {
-            bfd_rp = bfd_alloc_entry(bfd_active_connections,
-                                     nb_bt->logical_port, nb_bt->dst_ip,
-                                     bfd_e->status);
-        }
-
-        if (!strcmp(bfd_e->status, "admin_down")) {
-            bfd_set_status(bfd_rp, "down");
-        }
-
-        return strcmp(bfd_rp->status, "down");
-    }
-
-    return true;
-}
-
 static void
 build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
                           struct route_policy *rp,
@@ -15796,208 +15730,6 @@ build_mcast_lookup_flows_for_lrouter(struct 
ovn_datapath *od,
     }
 }
 
-static struct route_policy *
-route_policies_lookup(struct hmap *route_policies, size_t hash,
-                      struct route_policy *new_rp)
-{
-    struct route_policy *rp;
-    HMAP_FOR_EACH_WITH_HASH (rp, key_node, hash, route_policies) {
-        if (rp->rule != new_rp->rule) {
-            continue;
-        }
-
-        if (rp->chain_id != new_rp->chain_id) {
-            continue;
-        }
-
-        if (rp->jump_chain_id != new_rp->jump_chain_id) {
-            continue;
-        }
-
-        if (rp->n_valid_nexthops != new_rp->n_valid_nexthops) {
-            continue;
-        }
-
-        size_t i;
-        for (i = 0; i < new_rp->n_valid_nexthops; i++) {
-            size_t j;
-
-            for (j = 0; j < rp->n_valid_nexthops; j++) {
-                if (!strcmp(new_rp->valid_nexthops[i],
-                            rp->valid_nexthops[j])) {
-                    break;
-                }
-            }
-
-            if (j == rp->n_valid_nexthops) {
-                break;
-            }
-        }
-
-        if (i == new_rp->n_valid_nexthops) {
-            return rp;
-        }
-    }
-
-    return NULL;
-}
-
-static bool
-policy_chain_id(struct simap *chain_ids, const char *chain_name, uint32_t *id)
-{
-    if (chain_name && *chain_name) {
-        *id = simap_get(chain_ids, chain_name);
-        return true;
-    }
-
-    return false;
-}
-
-static void
-policy_chain_add(struct simap *chain_ids, const char *chain_name)
-{
-    uint32_t id = simap_count(chain_ids) + 1;
-
-    if (id == UINT16_MAX) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-        VLOG_WARN_RL(&rl, "Too many policy chains for Logical Router.");
-        return;
-    }
-
-    if (!simap_put(chain_ids, chain_name, id)) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-        VLOG_WARN_RL(&rl, "Policy chain id unexpectedly appeared");
-    }
-}
-
-void
-build_route_policies(struct ovn_datapath *od,
-                     const struct hmap *bfd_connections,
-                     struct hmap *route_policies,
-                     struct hmap *bfd_active_connections,
-                     struct simap *chain_ids)
-{
-    struct route_policy *rp;
-
-    /* Create chain numeric ids for policies with chain name set */
-    for (int i = 0; i < od->nbr->n_policies; i++) {
-        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
-        uint32_t id;
-
-        if (policy_chain_id(chain_ids, rule->chain, &id) && id == 0) {
-            policy_chain_add(chain_ids, rule->chain);
-        }
-    }
-
-    for (int i = 0; i < od->nbr->n_policies; i++) {
-        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
-
-        if (rule->nexthop && rule->nexthop[0]) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-            VLOG_WARN_RL(&rl, "Logical router: %s, policy uses deprecated"
-                         " column \"nexthop\", this column is ignored. Please"
-                         "use \"nexthops\" column instead.", od->nbr->name);
-            continue;
-        }
-
-        size_t n_valid_nexthops = 0;
-        char **valid_nexthops = NULL;
-        uint32_t chain_id = 0;
-        uint32_t jump_chain_id = 0;
-
-        /* Skip policy if chain name is set but id was not created above */
-        if (policy_chain_id(chain_ids, rule->chain, &chain_id)
-            && chain_id == 0) {
-            continue;
-        }
-
-        if (!strcmp(rule->action, "jump")) {
-            /* Skip policy if action is 'jump' but no target chain is set */
-            if (!policy_chain_id(chain_ids, rule->jump_chain,
-                                 &jump_chain_id)) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-                VLOG_WARN_RL(&rl,
-                         "Logical router: %s, policy action 'jump'"
-                         " has empty target",
-                         od->nbr->name);
-                continue;
-            }
-
-            /* Skip policy if action is 'jump' but target chain name
-               is not resolved to numeric id */
-            if (jump_chain_id == 0) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-                VLOG_WARN_RL(&rl,
-                         "Logical router: %s, policy action 'jump'"
-                         " follows to non-existent chain %s",
-                         od->nbr->name, rule->jump_chain);
-                continue;
-            }
-        }
-
-        if (simap_is_empty(chain_ids)) {
-            chain_id = -1;
-        }
-
-        if (!strcmp(rule->action, "reroute")) {
-            if (rule->output_port && rule->n_nexthops != 1) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-                VLOG_WARN_RL(&rl,
-                             "Logical router: %s, policy "
-                             "(chain: '%s', match: '%s', priority %"PRId64"): "
-                             "output_port only supported on non-ECMP "
-                             "reroute policies",
-                             od->nbr->name,
-                             rule->chain ? rule->chain : "<Default>",
-                             rule->match, rule->priority);
-                continue;
-            }
-
-            valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
-            for (size_t j = 0; j < rule->n_nexthops; j++) {
-                char *nexthop = rule->nexthops[j];
-                if (!nexthop || !nexthop[0]) {
-                    continue;
-                }
-
-                struct ovn_port *out_port = NULL;
-                bool is_ipv4 = strchr(nexthop, '.') ? true : false;
-
-                if (!find_policy_outport(od, rule, nexthop, is_ipv4, NULL,
-                                         &out_port)) {
-                    continue;
-                }
-                if (!check_bfd_state(rule, out_port, nexthop,
-                                     bfd_connections,
-                                     bfd_active_connections)) {
-                    continue;
-                }
-                valid_nexthops[n_valid_nexthops++] = nexthop;
-            }
-
-            if (!n_valid_nexthops) {
-                free(valid_nexthops);
-                continue;
-            }
-        }
-
-        struct route_policy *new_rp = xzalloc(sizeof *new_rp);
-        new_rp->rule = rule;
-        new_rp->n_valid_nexthops = n_valid_nexthops;
-        new_rp->valid_nexthops = valid_nexthops;
-        new_rp->chain_id = chain_id;
-        new_rp->jump_chain_id = jump_chain_id;
-
-        size_t hash = uuid_hash(&od->key);
-        rp = route_policies_lookup(route_policies, hash, new_rp);
-        if (!rp) {
-            hmap_insert(route_policies, &new_rp->key_node, hash);
-        } else {
-            free(valid_nexthops);
-            free(new_rp);
-        }
-    }
-}
 
 /* Logical router ingress table POLICY: Policy.
  *
@@ -21823,14 +21555,6 @@ northd_init(struct northd_data *data)
     init_northd_tracked_data(data);
 }
 
-void
-route_policies_init(struct route_policies_data *data)
-{
-    hmap_init(&data->route_policies);
-    hmap_init(&data->bfd_active_connections);
-    simap_init(&data->chain_ids);
-}
-
 void
 routes_init(struct routes_data *data)
 {
@@ -21912,8 +21636,8 @@ northd_destroy(struct northd_data *data)
     destroy_northd_tracked_data(data);
 }
 
-static void
-__bfd_destroy(struct hmap *bfd_connections)
+void
+bfd_destroy(struct hmap *bfd_connections)
 {
     struct bfd_entry *bfd_e;
 
@@ -21923,12 +21647,6 @@ __bfd_destroy(struct hmap *bfd_connections)
     hmap_destroy(bfd_connections);
 }
 
-void
-bfd_destroy(struct bfd_data *data)
-{
-    __bfd_destroy(&data->bfd_connections);
-}
-
 static void
 __ic_learned_svcs_cleanup(struct hmap *ic_learned_svc_monitors_map)
 {
@@ -21946,19 +21664,6 @@ ic_learned_svc_monitors_cleanup(struct 
ic_learned_svc_monitors_data *data)
     lflow_ref_destroy(data->lflow_ref);
 }
 
-void
-route_policies_destroy(struct route_policies_data *data)
-{
-    struct route_policy *rp;
-    HMAP_FOR_EACH_POP (rp, key_node, &data->route_policies) {
-        free(rp->valid_nexthops);
-        free(rp);
-    };
-    hmap_destroy(&data->route_policies);
-    __bfd_destroy(&data->bfd_active_connections);
-    simap_destroy(&data->chain_ids);
-}
-
 void
 routes_destroy(struct routes_data *data)
 {
@@ -21969,7 +21674,7 @@ routes_destroy(struct routes_data *data)
     hmap_destroy(&data->parsed_routes);
 
     simap_destroy(&data->route_tables);
-    __bfd_destroy(&data->bfd_active_connections);
+    bfd_destroy(&data->bfd_active_connections);
     hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);
     hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);
 }
diff --git a/northd/northd.h b/northd/northd.h
index c4bfae177..81ebbf0f6 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -210,15 +210,6 @@ struct northd_data {
     struct northd_tracked_data trk_data;
 };
 
-struct route_policy {
-    struct hmap_node key_node;
-    const struct nbrec_logical_router_policy *rule;
-    size_t n_valid_nexthops;
-    char **valid_nexthops;
-    uint32_t chain_id;
-    uint32_t jump_chain_id;
-};
-
 struct route_tracked_data {
     struct hmapx trk_crupdated_parsed_route;
     struct hmapx trk_deleted_parsed_route;
@@ -232,12 +223,6 @@ struct routes_data {
     bool tracked;
 };
 
-struct route_policies_data {
-    struct hmap route_policies;
-    struct hmap bfd_active_connections;
-    struct simap chain_ids;
-};
-
 struct bfd_data {
     struct hmap bfd_connections;
 };
@@ -949,8 +934,6 @@ void northd_init(struct northd_data *data);
 void northd_indices_create(struct northd_data *data,
                            struct ovsdb_idl *ovnsb_idl);
 
-void route_policies_init(struct route_policies_data *);
-void route_policies_destroy(struct route_policies_data *);
 void build_parsed_routes(const struct ovn_datapath *, const struct hmap *,
                          struct hmap *, struct simap *, struct hmap *);
 uint32_t get_route_table_id(struct simap *, const char *);
@@ -958,8 +941,29 @@ void routes_init(struct routes_data *);
 void routes_destroy(struct routes_data *);
 void routes_clear_tracked(struct routes_data *);
 
+struct bfd_entry {
+    struct hmap_node hmap_node;
+
+    const struct nbrec_bfd *nb_bt;
+    const struct sbrec_bfd *sb_bt;
+
+    char *logical_port;
+    char *dst_ip;
+    char *status;
+    bool stale;
+};
+
+struct bfd_entry *bfd_alloc_entry(struct hmap *bfd_connections,
+                                  const char *logical_port, const char *dst_ip,
+                                  const char *status);
+void bfd_erase_entry(struct bfd_entry *bfd_e);
+void bfd_set_status(struct bfd_entry *bfd_e, const char *status);
+struct bfd_entry *bfd_port_lookup(const struct hmap *bfd_map,
+                                  const char *logical_port,
+                                  const char *dst_ip);
+void bfd_destroy(struct hmap *bfd_connections);
+
 void bfd_init(struct bfd_data *);
-void bfd_destroy(struct bfd_data *);
 
 void bfd_sync_init(struct bfd_sync_data *);
 void bfd_sync_swap(struct bfd_sync_data *, struct sset *bfd_ports);
@@ -1016,8 +1020,12 @@ bool northd_handle_lb_data_changes(struct 
tracked_lb_data *,
                                    const struct hmap *lr_lb_map,
                                    struct northd_tracked_data *);
 
-void build_route_policies(struct ovn_datapath *, const struct hmap *,
-                          struct hmap *, struct hmap *, struct simap *);
+bool find_policy_outport(struct ovn_datapath *od,
+                         const struct nbrec_logical_router_policy *policy,
+                         const char *nexthop, bool is_ipv4,
+                         const char **p_lrp_addr_s,
+                         struct ovn_port **p_out_port);
+
 void bfd_table_sync(struct ovsdb_idl_txn *, const struct nbrec_bfd_table *,
                     const struct hmap *, const struct hmap *,
                     const struct hmap *, const struct hmap *,
-- 
2.55.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to