diff mbox series

[ovs-dev,v11] northd: Add bfd, static_routes, route_policies and bfd_sync nodes to I-P engine.

Message ID 20aa09c88476688d9850c6adbf3142bf136db6df.1722596944.git.lorenzo.bianconi@redhat.com
State Accepted
Headers show
Series [ovs-dev,v11] northd: Add bfd, static_routes, route_policies and bfd_sync nodes to I-P engine. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Lorenzo Bianconi Aug. 2, 2024, 11:12 a.m. UTC
Introduce bfd, static_routes, route_policies and bfd_sync nodes to northd I-P
engine to track bfd connections and northd static_route/policy_route
changes.

Reported-at: https://issues.redhat.com/browse/FDP-600
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
Changes since v10:
- rename bfd_sync_change_handler in bfd_sync_northd_change_handler
- make dependency between en_lflow and en_bfd_sync explicit
Changes since v9:
- write in I-P private data just in the corresponding I-P node
- move lr_port lookup in bfd_table_sync()
- cosmetics
Changes since v8:
- rebase on top of ovn main branch and fix conflicts
Changes since v7:
- rework node dependencies and move all NB-BFD table update in bfd_sync node
- fix some more comments
Changes since v6:
- remove unnecessary node inputs
- fix comment
Changes since v5:
- remove bfd_mutex
Changes since v4:
- introduce bfd_nb_sync node and ger rid of bfd_consumer routine
Changes since v3:
- fix bfd_northd_change_handler logic
- fix route_policies_northd_change_handler logic
- fix static_routes_northd_change_handler logic
- add unit-tests
- cosmetics
Changes since v2:
- add incremental processing routines
- split bfd_consumer node in static_routes and route_policies nodes
Changes since v1:
- add incremental processing logic for bfd_consumer node to avoid a full
  recompute
---
 northd/en-lflow.c        |  25 +-
 northd/en-northd.c       | 231 ++++++++++++
 northd/en-northd.h       |  22 ++
 northd/inc-proc-northd.c |  26 +-
 northd/northd.c          | 777 +++++++++++++++++++++++++++------------
 northd/northd.h          |  74 +++-
 tests/ovn-northd.at      |  56 +++
 7 files changed, 955 insertions(+), 256 deletions(-)

Comments

Han Zhou Aug. 5, 2024, 7:24 a.m. UTC | #1
On Fri, Aug 2, 2024 at 4:12 AM Lorenzo Bianconi
<lorenzo.bianconi@redhat.com> wrote:
>
> Introduce bfd, static_routes, route_policies and bfd_sync nodes to northd I-P
> engine to track bfd connections and northd static_route/policy_route
> changes.
>
> Reported-at: https://issues.redhat.com/browse/FDP-600
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
> Changes since v10:
> - rename bfd_sync_change_handler in bfd_sync_northd_change_handler
> - make dependency between en_lflow and en_bfd_sync explicit
> Changes since v9:
> - write in I-P private data just in the corresponding I-P node
> - move lr_port lookup in bfd_table_sync()
> - cosmetics
> Changes since v8:
> - rebase on top of ovn main branch and fix conflicts
> Changes since v7:
> - rework node dependencies and move all NB-BFD table update in bfd_sync node
> - fix some more comments
> Changes since v6:
> - remove unnecessary node inputs
> - fix comment
> Changes since v5:
> - remove bfd_mutex
> Changes since v4:
> - introduce bfd_nb_sync node and ger rid of bfd_consumer routine
> Changes since v3:
> - fix bfd_northd_change_handler logic
> - fix route_policies_northd_change_handler logic
> - fix static_routes_northd_change_handler logic
> - add unit-tests
> - cosmetics
> Changes since v2:
> - add incremental processing routines
> - split bfd_consumer node in static_routes and route_policies nodes
> Changes since v1:
> - add incremental processing logic for bfd_consumer node to avoid a full
>   recompute
> ---
>  northd/en-lflow.c        |  25 +-
>  northd/en-northd.c       | 231 ++++++++++++
>  northd/en-northd.h       |  22 ++
>  northd/inc-proc-northd.c |  26 +-
>  northd/northd.c          | 777 +++++++++++++++++++++++++++------------
>  northd/northd.h          |  74 +++-
>  tests/ovn-northd.at      |  56 +++
>  7 files changed, 955 insertions(+), 256 deletions(-)
>
> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
> index c4b927fb8..467a6314b 100644
> --- a/northd/en-lflow.c
> +++ b/northd/en-lflow.c
> @@ -41,6 +41,11 @@ lflow_get_input_data(struct engine_node *node,
>                       struct lflow_input *lflow_input)
>  {
>      struct northd_data *northd_data = engine_get_input_data("northd", node);
> +    struct bfd_data *bfd_data = engine_get_input_data("bfd_sync", node);
> +    struct static_routes_data *static_routes_data =
> +        engine_get_input_data("static_routes", node);
> +    struct route_policies_data *route_policies_data =
> +        engine_get_input_data("route_policies", node);
>      struct port_group_data *pg_data =
>          engine_get_input_data("port_group", node);
>      struct sync_meters_data *sync_meters_data =
> @@ -50,10 +55,6 @@ lflow_get_input_data(struct engine_node *node,
>      struct ed_type_ls_stateful *ls_stateful_data =
>          engine_get_input_data("ls_stateful", node);
>
> -    lflow_input->nbrec_bfd_table =
> -        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> -    lflow_input->sbrec_bfd_table =
> -        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
>      lflow_input->sbrec_logical_flow_table =
>          EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
>      lflow_input->sbrec_multicast_group_table =
> @@ -78,7 +79,10 @@ lflow_get_input_data(struct engine_node *node,
>      lflow_input->meter_groups = &sync_meters_data->meter_groups;
>      lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
>      lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
> -    lflow_input->bfd_connections = NULL;
> +    lflow_input->bfd_connections = &bfd_data->bfd_connections;
> +    lflow_input->parsed_routes = &static_routes_data->parsed_routes;
> +    lflow_input->route_tables = &static_routes_data->route_tables;
> +    lflow_input->route_policies = &route_policies_data->route_policies;
>
>      struct ed_type_global_config *global_config =
>          engine_get_input_data("global_config", node);
> @@ -95,25 +99,14 @@ void en_lflow_run(struct engine_node *node, void *data)
>      struct lflow_input lflow_input;
>      lflow_get_input_data(node, &lflow_input);
>
> -    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
> -    lflow_input.bfd_connections = &bfd_connections;
> -
>      stopwatch_start(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
>
>      struct lflow_data *lflow_data = data;
>      lflow_table_clear(lflow_data->lflow_table);
>      lflow_reset_northd_refs(&lflow_input);
>
> -    build_bfd_table(eng_ctx->ovnsb_idl_txn,
> -                    lflow_input.nbrec_bfd_table,
> -                    lflow_input.sbrec_bfd_table,
> -                    lflow_input.lr_ports,
> -                    &bfd_connections);
>      build_lflows(eng_ctx->ovnsb_idl_txn, &lflow_input,
>                   lflow_data->lflow_table);
> -    bfd_cleanup_connections(lflow_input.nbrec_bfd_table,
> -                            &bfd_connections);
> -    hmap_destroy(&bfd_connections);
>      stopwatch_stop(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
>
>      engine_set_node_state(node, EN_UPDATED);
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index 4479b4aff..dd2b4353d 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -236,6 +236,174 @@ northd_global_config_handler(struct engine_node *node, void *data OVS_UNUSED)
>      return true;
>  }
>
> +bool
> +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 false;
> +    }
> +
> +    /* This node uses the below data from the en_northd engine node.
> +     * See (lr_stateful_get_input_data())
> +     *   1. northd_data->lr_datapaths
> +     *   2. northd_data->lr_ports
> +     *      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.
> +     *      Note: When we add I-P to the created/deleted logical routers or
> +     *      logical router ports, we need to revisit this handler.
> +     *
> +     *      This node also accesses the route policies of the logical router.
> +     *      When these route policies get updated, en_northd engine recomputes
> +     *      and so does this node.
> +     *      Note: When we add I-P to handle route policies changes, we need
> +     *      to revisit this handler.
> +     */
> +    return true;
> +}
> +
> +void
> +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, &northd_data->lr_ports,
> +                             &bfd_data->bfd_connections,
> +                             &route_policies_data->route_policies,
> +                             &route_policies_data->bfd_active_connections);
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +}
> +
> +bool
> +static_routes_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 false;
> +    }
> +
> +    /* This node uses the below data from the en_northd engine node.
> +     * See (lr_stateful_get_input_data())
> +     *   1. northd_data->lr_datapaths
> +     *   2. northd_data->lr_ports
> +     *      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.
> +     *      Note: When we add I-P to the created/deleted logical routers or
> +     *      logical router ports, we need to revisit this handler.
> +     *
> +     *      This node also accesses the static routes of the logical router.
> +     *      When these static routes gets updated, en_northd engine recomputes
> +     *      and so does this node.
> +     *      Note: When we add I-P to handle static routes changes, we need
> +     *      to revisit this handler.
> +     */
> +    return true;
> +}
> +
> +void
> +en_static_routes_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 static_routes_data *static_routes_data = data;
> +
> +    static_routes_destroy(data);
> +    static_routes_init(data);
> +
> +    struct ovn_datapath *od;
> +    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
> +        for (int i = 0; i < od->nbr->n_ports; i++) {
> +            const char *route_table_name =
> +                smap_get(&od->nbr->ports[i]->options, "route_table");
> +            get_route_table_id(&static_routes_data->route_tables,
> +                               route_table_name);
> +        }
> +
> +        build_parsed_routes(od, &northd_data->lr_ports,
> +                            &bfd_data->bfd_connections,
> +                            &static_routes_data->parsed_routes,
> +                            &static_routes_data->route_tables,
> +                            &static_routes_data->bfd_active_connections);
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +}
> +
> +void
> +en_bfd_run(struct engine_node *node, void *data)
> +{
> +    struct bfd_data *bfd_data = data;
> +    const struct nbrec_bfd_table *nbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> +    const struct sbrec_bfd_table *sbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
> +
> +    bfd_destroy(data);
> +    bfd_init(data);
> +    build_bfd_map(nbrec_bfd_table, sbrec_bfd_table,
> +                  &bfd_data->bfd_connections);
> +    engine_set_node_state(node, EN_UPDATED);
> +}
> +
> +bool
> +bfd_sync_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 false;
> +    }
> +
> +    /* This node uses the below data from the en_northd engine node.
> +     * See (lr_stateful_get_input_data())
> +     *   1. northd_data->lr_ports
> +     *      This data gets updated when a logical router port is created or
> +     *      deleted. Northd engine node presently falls back to full recompute
> +     *      when this happens and so does this node.
> +     *      Note: When we add I-P to the created/deleted logical router ports,
> +     *      we need to revisit this handler.
> +     */
> +    return true;
> +}
> +
> +void
> +en_bfd_sync_run(struct engine_node *node, void *data)
> +{
> +    struct northd_data *northd_data = engine_get_input_data("northd", node);
> +    const struct engine_context *eng_ctx = engine_get_context();
> +    struct bfd_data *bfd_data = engine_get_input_data("bfd", node);
> +    struct route_policies_data *route_policies_data
> +        = engine_get_input_data("route_policies", node);
> +    struct static_routes_data *static_routes_data
> +        = engine_get_input_data("static_routes", node);
> +    const struct nbrec_bfd_table *nbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> +    struct bfd_data *bfd_sync_data = data;
> +
> +    bfd_destroy(data);
> +    bfd_init(data);
> +    bfd_table_sync(eng_ctx->ovnsb_idl_txn, nbrec_bfd_table,
> +                   &northd_data->lr_ports, &bfd_data->bfd_connections,
> +                   &route_policies_data->bfd_active_connections,
> +                   &static_routes_data->bfd_active_connections,
> +                   &bfd_sync_data->bfd_connections);
> +    engine_set_node_state(node, EN_UPDATED);
> +}
> +
>  void
>  *en_northd_init(struct engine_node *node OVS_UNUSED,
>                  struct engine_arg *arg OVS_UNUSED)
> @@ -247,6 +415,45 @@ 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_static_routes_init(struct engine_node *node OVS_UNUSED,
> +                      struct engine_arg *arg OVS_UNUSED)
> +{
> +    struct static_routes_data *data = xzalloc(sizeof *data);
> +
> +    static_routes_init(data);
> +    return data;
> +}
> +
> +void
> +*en_bfd_init(struct engine_node *node OVS_UNUSED,
> +             struct engine_arg *arg OVS_UNUSED)
> +{
> +    struct bfd_data *data = xzalloc(sizeof *data);
> +
> +    bfd_init(data);
> +    return data;
> +}
> +
> +void
> +*en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
> +                  struct engine_arg *arg OVS_UNUSED)
> +{
> +    struct bfd_data *data = xzalloc(sizeof *data);
> +    bfd_init(data);
> +    return data;
> +}
> +
>  void
>  en_northd_cleanup(void *data)
>  {
> @@ -259,3 +466,27 @@ en_northd_clear_tracked_data(void *data_)
>      struct northd_data *data = data_;
>      destroy_northd_data_tracked_changes(data);
>  }
> +
> +void
> +en_route_policies_cleanup(void *data)
> +{
> +    route_policies_destroy(data);
> +}
> +
> +void
> +en_static_routes_cleanup(void *data)
> +{
> +    static_routes_destroy(data);
> +}
> +
> +void
> +en_bfd_cleanup(void *data)
> +{
> +    bfd_destroy(data);
> +}
> +
> +void
> +en_bfd_sync_cleanup(void *data)
> +{
> +    bfd_destroy(data);
> +}
> diff --git a/northd/en-northd.h b/northd/en-northd.h
> index 9b7bda32a..f2d0ce8ba 100644
> --- a/northd/en-northd.h
> +++ b/northd/en-northd.h
> @@ -19,5 +19,27 @@ bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
>  bool northd_nb_logical_router_handler(struct engine_node *, void *data);
>  bool northd_sb_port_binding_handler(struct engine_node *, void *data);
>  bool northd_lb_data_handler(struct engine_node *, void *data);
> +void *en_static_routes_init(struct engine_node *node OVS_UNUSED,
> +                            struct engine_arg *arg OVS_UNUSED);
> +void en_route_policies_cleanup(void *data);
> +bool route_policies_northd_change_handler(struct engine_node *node,
> +                                          void *data OVS_UNUSED);
> +void 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_static_routes_cleanup(void *data);
> +bool static_routes_northd_change_handler(struct engine_node *node,
> +                                         void *data OVS_UNUSED);
> +void en_static_routes_run(struct engine_node *node, void *data);
> +void *en_bfd_init(struct engine_node *node OVS_UNUSED,
> +                  struct engine_arg *arg OVS_UNUSED);
> +void en_bfd_cleanup(void *data);
> +void en_bfd_run(struct engine_node *node, void *data);
> +void *en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
> +                       struct engine_arg *arg OVS_UNUSED);
> +bool bfd_sync_northd_change_handler(struct engine_node *node,
> +                                    void *data OVS_UNUSED);
> +void en_bfd_sync_run(struct engine_node *node, void *data);
> +void en_bfd_sync_cleanup(void *data OVS_UNUSED);
>
>  #endif /* EN_NORTHD_H */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 522236ad2..7d4c28b13 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -155,6 +155,10 @@ static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
>  static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_nat, "lr_nat");
>  static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_stateful, "lr_stateful");
>  static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(ls_stateful, "ls_stateful");
> +static ENGINE_NODE(route_policies, "route_policies");
> +static ENGINE_NODE(static_routes, "static_routes");
> +static ENGINE_NODE(bfd, "bfd");
> +static ENGINE_NODE(bfd_sync, "bfd_sync");
>
>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                            struct ovsdb_idl_loop *sb)
> @@ -237,18 +241,36 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_fdb_aging, &en_global_config,
>                       node_global_config_handler);
>
> +    engine_add_input(&en_bfd, &en_nb_bfd, NULL);
> +    engine_add_input(&en_bfd, &en_sb_bfd, NULL);
> +
> +    engine_add_input(&en_route_policies, &en_bfd, NULL);
> +    engine_add_input(&en_route_policies, &en_northd,
> +                     route_policies_northd_change_handler);
> +
> +    engine_add_input(&en_static_routes, &en_bfd, NULL);
> +    engine_add_input(&en_static_routes, &en_northd,
> +                     static_routes_northd_change_handler);
> +
> +    engine_add_input(&en_bfd_sync, &en_bfd, NULL);
> +    engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);
> +    engine_add_input(&en_bfd_sync, &en_static_routes, NULL);
> +    engine_add_input(&en_bfd_sync, &en_route_policies, NULL);
> +    engine_add_input(&en_bfd_sync, &en_northd, bfd_sync_northd_change_handler);
> +
>      engine_add_input(&en_sync_meters, &en_nb_acl, NULL);
>      engine_add_input(&en_sync_meters, &en_nb_meter, NULL);
>      engine_add_input(&en_sync_meters, &en_sb_meter, NULL);
>
> -    engine_add_input(&en_lflow, &en_nb_bfd, NULL);
>      engine_add_input(&en_lflow, &en_nb_acl, NULL);
>      engine_add_input(&en_lflow, &en_sync_meters, NULL);
> -    engine_add_input(&en_lflow, &en_sb_bfd, NULL);
>      engine_add_input(&en_lflow, &en_sb_logical_flow, NULL);
>      engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
>      engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
>      engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);
> +    engine_add_input(&en_lflow, &en_bfd_sync, NULL);
> +    engine_add_input(&en_lflow, &en_route_policies, NULL);
> +    engine_add_input(&en_lflow, &en_static_routes, NULL);
>      engine_add_input(&en_lflow, &en_global_config,
>                       node_global_config_handler);
>      engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
> diff --git a/northd/northd.c b/northd/northd.c
> index a8a0b6f94..e0b06ff9a 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -9702,11 +9702,47 @@ 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;
>
> -    bool ref;
> +    char *logical_port;
> +    char *dst_ip;
> +    char *status;
> +    bool stale;
>  };
>
> +static struct bfd_entry *
> +bfd_alloc_entry(struct hmap *bfd_connections,
> +                const char *logical_port, const char *dst_ip,
> +                const char *status)
> +{
> +    struct bfd_entry *bfd_e = xzalloc(sizeof *bfd_e);
> +    bfd_e->logical_port = xstrdup(logical_port);
> +    bfd_e->dst_ip = xstrdup(dst_ip);
> +    bfd_e->status = xstrdup(status);
> +    uint32_t hash = hash_string(dst_ip, 0);
> +    hash = hash_string(logical_port, hash);
> +    hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> +
> +    return bfd_e;
> +}
> +
> +static void
> +bfd_erase_entry(struct bfd_entry *bfd_e)
> +{
> +    free(bfd_e->logical_port);
> +    free(bfd_e->dst_ip);
> +    free(bfd_e->status);
> +    free(bfd_e);
> +}
> +
> +static 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 *
>  bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
>                  const char *dst_ip)
> @@ -9717,38 +9753,28 @@ bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
>      hash = hash_string(dst_ip, 0);
>      hash = hash_string(logical_port, hash);
>      HMAP_FOR_EACH_WITH_HASH (bfd_e, hmap_node, hash, bfd_map) {
> -        if (!strcmp(bfd_e->sb_bt->logical_port, logical_port) &&
> -            !strcmp(bfd_e->sb_bt->dst_ip, dst_ip)) {
> +        if (!strcmp(bfd_e->logical_port, logical_port) &&
> +            !strcmp(bfd_e->dst_ip, dst_ip)) {
>              return bfd_e;
>          }
>      }
> +
>      return NULL;
>  }
>
> -void
> -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> -                        struct hmap *bfd_map)
> +static bool
> +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
>  {
> -    const struct nbrec_bfd *nb_bt;
>      struct bfd_entry *bfd_e;
> -
> -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> -        if (!bfd_e) {
> -            continue;
> -        }
> -
> -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> -            /* no user for this bfd connection */
> -            nbrec_bfd_set_status(nb_bt, "admin_down");
> +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {

I like the idea of checking the existence of the bfd entry to decide
if a port has bfd instead of relying on the has_bfd status. However, I
have performance concerns for this loop. The bfd_port_lookup()
searches by hashing both logical_port and dst_ip, but here for
logical_port it just iterates all bfd entries, which looks
unreasonable. It may be better to add a hindex for logical_port.

> +        if (!strcmp(bfd_e->logical_port, port)) {
> +            return true;
>          }
>      }
> -
> -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> -        free(bfd_e);
> -    }
> +    return false;
>  }
>
> +
>  #define BFD_DEF_MINTX       1000 /* 1s */
>  #define BFD_DEF_MINRX       1000 /* 1s */
>  #define BFD_DEF_DETECT_MULT 5
> @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
>      return port + BFD_UDP_SRC_PORT_START;
>  }
>
> -void
> -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> -                const struct nbrec_bfd_table *nbrec_bfd_table,
> -                const struct sbrec_bfd_table *sbrec_bfd_table,
> -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> +static char *
> +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> +                          const struct hmap *rp_bfd_connections,
> +                          const struct hmap *sr_bfd_connections)
>  {
> -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> -    const struct sbrec_bfd *sb_bt;
> -    unsigned long *bfd_src_ports;
> -    struct bfd_entry *bfd_e;
> -    uint32_t hash;
> +    struct bfd_entry *bfd_rp, *bfd_sr;
>
> -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> +                             nb_bt->dst_ip);
> +    if (!bfd_rp) {
> +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> +                                 nb_bt->dst_ip);
> +        if (!bfd_sr) {
> +            return "admin_down";
> +        }
> +    }
>
> -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> -        bfd_e = xmalloc(sizeof *bfd_e);
> -        bfd_e->sb_bt = sb_bt;
> -        hash = hash_string(sb_bt->dst_ip, 0);
> -        hash = hash_string(sb_bt->logical_port, hash);
> -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> +}
> +
> +void
> +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> +               const struct nbrec_bfd_table *nbrec_bfd_table,
> +               const struct hmap *lr_ports,
> +               const struct hmap *bfd_connections,
> +               const struct hmap *rp_bfd_connections,
> +               const struct hmap *sr_bfd_connections,
> +               struct hmap *sync_bfd_connections)
> +{
> +    if (!ovnsb_txn) {
> +        return;
> +    }
> +
> +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> +
> +    struct bfd_entry *bfd_e;
> +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> +                                              bfd_e->logical_port,
> +                                              bfd_e->dst_ip, bfd_e->status);

It looks like we are blindly allocating a new entry for each entry in
bfd_connections without checking if it already exists. Did I
misunderstand?

Thanks,
Han

> +        e->nb_bt = bfd_e->nb_bt;
> +        e->sb_bt = bfd_e->sb_bt;
> +        e->stale = true;
> +        /* we need to check if this entry is even in the BFD nb db table */
> +        if (bfd_e->sb_bt) {
> +            bitmap_set1(bfd_src_ports,
> +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> +        }
>      }
>
>      const struct nbrec_bfd *nb_bt;
>      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> -        if (!nb_bt->status) {
> -            /* default state is admin_down */
> -            nbrec_bfd_set_status(nb_bt, "admin_down");
> +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> +                                nb_bt->dst_ip);
> +        if (!bfd_e) {
> +            continue;
>          }
>
>          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> -        if (!bfd_e) {
> +        if (!op || !op->sb) {
> +            /* skip not bounded ports */
> +            continue;
> +        }
> +
> +        nbrec_bfd_set_status(nb_bt,
> +                             bfd_get_connection_status(nb_bt,
> +                                                       rp_bfd_connections,
> +                                                       sr_bfd_connections));
> +        if (!bfd_e->sb_bt) {
>              int udp_src = bfd_get_unused_port(bfd_src_ports);
>              if (udp_src < 0) {
>                  continue;
>              }
>
> -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> +            /* Add entry to bfd sb table. */
> +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
>              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
>              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
>              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
>              sbrec_bfd_set_src_port(sb_bt, udp_src);
>              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> -            if (op && op->sb && op->sb->chassis) {
> +            if (op->sb->chassis) {
>                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
>              }
>
> @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
>                  }
>              }
> +
>              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> -            if (op && op->sb && op->sb->chassis &&
> -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> +                                           bfd_e->sb_bt->chassis_name)) {
>                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
>                                             op->sb->chassis->name);
>              }
> -
> -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> -            bfd_e->ref = false;
> -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
>          }
>
> -        if (op) {
> -            op->has_bfd = true;
> -        }
> +        bfd_e->stale = false;
>      }
>
> -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> -        struct ovn_port *op = ovn_port_find(lr_ports,
> -                                            bfd_e->sb_bt->logical_port);
> -        if (op) {
> -            op->has_bfd = false;
> +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> +        if (bfd_e->stale) {
> +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> +            sbrec_bfd_delete(bfd_e->sb_bt);
> +            bfd_erase_entry(bfd_e);
>          }
> -        sbrec_bfd_delete(bfd_e->sb_bt);
> -        free(bfd_e);
>      }
> -    hmap_destroy(&sb_only);
>
>      bitmap_free(bfd_src_ports);
>  }
>
> +void
> +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> +              const struct sbrec_bfd_table *sbrec_bfd_table,
> +              struct hmap *bfd_connections)
> +{
> +    struct bfd_entry *bfd_e;
> +
> +    /* align bfd_map to sb db */
> +    const struct sbrec_bfd *sb_bt;
> +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> +                                sb_bt->dst_ip);
> +        if (!bfd_e) {
> +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> +                                    sb_bt->dst_ip,  sb_bt->status);
> +        }
> +        bfd_e->sb_bt = sb_bt;
> +    }
> +
> +    const struct nbrec_bfd *nb_bt;
> +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> +                                nb_bt->dst_ip);
> +        if (!bfd_e) {
> +            /* brand new entry. */
> +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> +                                    nb_bt->dst_ip, "admin_down");
> +        }
> +        bfd_e->nb_bt = nb_bt;
> +    }
> +}
> +
>  /* Returns a string of the IP address of the router port 'op' that
>   * overlaps with 'ip_s".  If one is not found, returns NULL.
>   *
> @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
>      return NULL;
>  }
>
> -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> -
> -static bool check_bfd_state(
> -        const struct nbrec_logical_router_policy *rule,
> -        const struct hmap *bfd_connections,
> -        struct ovn_port *out_port,
> -        const char *nexthop)
> +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);
> -    bool ret = true;
>
>      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
>          /* Check if there is a BFD session associated to the reroute
> @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
>          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
>                                                    nb_bt->logical_port,
>                                                    nb_bt->dst_ip);
> -        ovs_mutex_lock(&bfd_lock);
> -        if (bfd_e) {
> -            bfd_e->ref = true;
> +        if (!bfd_e) {
> +            continue;
>          }
>
> -        if (!strcmp(nb_bt->status, "admin_down")) {
> -            nbrec_bfd_set_status(nb_bt, "down");
> +        /* 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);
>          }
>
> -        ret = strcmp(nb_bt->status, "down");
> -        ovs_mutex_unlock(&bfd_lock);
> -        break;
> +        if (!strcmp(bfd_e->status, "admin_down")) {
> +            bfd_set_status(bfd_rp, "down");
> +        }
> +
> +        return strcmp(bfd_rp->status, "down");
>      }
>
> -    return ret;
> +    return true;
>  }
>
>  static void
>  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> -                          const struct hmap *lr_ports,
> -                          const struct nbrec_logical_router_policy *rule,
> -                          const struct hmap *bfd_connections,
> +                          const struct hmap *lr_ports, struct route_policy *rp,
>                            const struct ovsdb_idl_row *stage_hint,
>                            struct lflow_ref *lflow_ref)
>  {
> +    const struct nbrec_logical_router_policy *rule = rp->rule;
>      struct ds match = DS_EMPTY_INITIALIZER;
>      struct ds actions = DS_EMPTY_INITIALIZER;
>
>      if (!strcmp(rule->action, "reroute")) {
>          ovs_assert(rule->n_nexthops <= 1);
>
> -        char *nexthop =
> -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> +        if (!rp->n_valid_nexthops) {
> +            return;
> +        }
> +
> +        char *nexthop = rp->valid_nexthops[0];
>          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
>               od, lr_ports, rule->priority, nexthop);
>          if (!out_port) {
> @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>              return;
>          }
>
> -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> -            return;
> -        }
> -
>          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
>          if (pkt_mark) {
>              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> @@ -10070,19 +10156,18 @@ static void
>  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>                                  struct ovn_datapath *od,
>                                  const struct hmap *lr_ports,
> -                                const struct nbrec_logical_router_policy *rule,
> -                                const struct hmap *bfd_connections,
> +                                struct route_policy *rp,
>                                  uint16_t ecmp_group_id,
>                                  struct lflow_ref *lflow_ref)
>  {
> -    ovs_assert(rule->n_nexthops > 1);
> -
>      bool nexthops_is_ipv4 = true;
> +    const struct nbrec_logical_router_policy *rule = rp->rule;
> +    ovs_assert(rule->n_nexthops > 1);
>
>      /* Check that all the nexthops belong to the same addr family before
>       * adding logical flows. */
> -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
>
>          if (i == 0) {
>              nexthops_is_ipv4 = is_ipv4;
> @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
>                           "the match [%s] do not belong to the same address "
>                           "family as other next hops",
> -                         rule->nexthops[i], rule->match);
> +                         rp->valid_nexthops[i], rule->match);
>              return;
>          }
>      }
> @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>      struct ds match = DS_EMPTY_INITIALIZER;
>      struct ds actions = DS_EMPTY_INITIALIZER;
>
> -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> -    size_t n_valid_nexthops = 0;
> -
> -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
>          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> -             od, lr_ports, rule->priority, rule->nexthops[i]);
> +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
>          if (!out_port) {
>              goto cleanup;
>          }
>
>          const char *lrp_addr_s =
> -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
>          if (!lrp_addr_s) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
>                              " priority %"PRId64" nexthop %s",
> -                            rule->priority, rule->nexthops[i]);
> +                            rule->priority, rp->valid_nexthops[i]);
>              goto cleanup;
>          }
>
> -        if (!check_bfd_state(rule, bfd_connections, out_port,
> -                             rule->nexthops[i])) {
> -            continue;
> -        }
> -
> -        valid_nexthops[n_valid_nexthops++] = i + 1;
> -
>          ds_clear(&actions);
>          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
>          if (pkt_mark) {
>              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
>          }
>
> -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
>
>          ds_put_format(&actions, "%s = %s; "
>                        "%s = %s; "
> @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>                        "flags.loopback = 1; "
>                        "next;",
>                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> -                      rule->nexthops[i],
> +                      rp->valid_nexthops[i],
>                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
>                        lrp_addr_s,
>                        out_port->lrp_networks.ea_s,
> @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>                                  lflow_ref);
>      }
>
> -    if (!n_valid_nexthops) {
> -        goto cleanup;
> -    }
> -
>      ds_clear(&actions);
> -    if (n_valid_nexthops > 1) {
> +    if (rp->n_valid_nexthops > 1) {
>          ds_put_format(&actions, "%s = %"PRIu16
>                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
>                        REG_ECMP_MEMBER_ID);
>
> -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
>              if (i > 0) {
>                  ds_put_cstr(&actions, ", ");
>              }
>
> -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
>          }
>          ds_put_cstr(&actions, ");");
>      } else {
>          ds_put_format(&actions, "%s = %"PRIu16
> -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> -                      valid_nexthops[0]);
> +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
>      }
>      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
>                              rule->priority, rule->match,
>                              ds_cstr(&actions), &rule->header_,
>                              lflow_ref);
> -
>  cleanup:
> -    free(valid_nexthops);
>      ds_destroy(&match);
>      ds_destroy(&actions);
>  }
> @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
>      return rtb_id;
>  }
>
> -static uint32_t
> +uint32_t
>  get_route_table_id(struct simap *route_tables, const char *route_table_name)
>  {
>      if (!route_table_name || !route_table_name[0]) {
> @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
>      ds_destroy(&actions);
>  }
>
> -struct parsed_route {
> -    struct ovs_list list_node;
> -    struct in6_addr prefix;
> -    unsigned int plen;
> -    bool is_src_route;
> -    uint32_t route_table_id;
> -    uint32_t hash;
> -    const struct nbrec_logical_router_static_route *route;
> -    bool ecmp_symmetric_reply;
> -    bool is_discard_route;
> -};
> -
>  static uint32_t
>  route_hash(struct parsed_route *route)
>  {
> @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
>
>  /* Parse and validate the route. Return the parsed route if successful.
>   * Otherwise return NULL. */
> +
>  static struct parsed_route *
> +parsed_route_lookup(struct hmap *routes, size_t hash,
> +                    struct parsed_route *new_pr)
> +{
> +    struct parsed_route *pr;
> +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> +        if (pr->plen != new_pr->plen) {
> +            continue;
> +        }
> +
> +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> +            continue;
> +        }
> +
> +        if (pr->is_src_route != new_pr->is_src_route) {
> +            continue;
> +        }
> +
> +        if (pr->route_table_id != new_pr->route_table_id) {
> +            continue;
> +        }
> +
> +        if (pr->route != new_pr->route) {
> +            continue;
> +        }
> +
> +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> +            continue;
> +        }
> +
> +        if (pr->is_discard_route != new_pr->is_discard_route) {
> +            continue;
> +        }
> +
> +        return pr;
> +    }
> +
> +    return NULL;
> +}
> +
> +static void
>  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> -                  struct ovs_list *routes, struct simap *route_tables,
>                    const struct nbrec_logical_router_static_route *route,
> -                  const struct hmap *bfd_connections)
> +                  struct hmap *bfd_connections,
> +                  struct hmap *routes, struct simap *route_tables,
> +                  struct hmap *bfd_active_connections)
>  {
>      /* Verify that the next hop is an IP address with an all-ones mask. */
>      struct in6_addr nexthop;
> @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
>                           UUID_FMT, route->nexthop,
>                           UUID_ARGS(&route->header_.uuid));
> -            return NULL;
> +            return;
>          }
>          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
>              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
>                           UUID_FMT, route->nexthop,
>                           UUID_ARGS(&route->header_.uuid));
> -            return NULL;
> +            return;
>          }
>      }
>
> @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
>                       UUID_FMT, route->ip_prefix,
>                       UUID_ARGS(&route->header_.uuid));
> -        return NULL;
> +        return;
>      }
>
>      /* Verify that ip_prefix and nexthop have same address familiy. */
> @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>                           " %s and 'nexthop' %s in static route "UUID_FMT,
>                           route->ip_prefix, route->nexthop,
>                           UUID_ARGS(&route->header_.uuid));
> -            return NULL;
> +            return;
>          }
>      }
>
> @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>          !find_static_route_outport(od, lr_ports, route,
>                                     IN6_IS_ADDR_V4MAPPED(&prefix),
>                                     NULL, NULL)) {
> -        return NULL;
> +        return;
>      }
>
>      const struct nbrec_bfd *nb_bt = route->bfd;
>      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> -        struct bfd_entry *bfd_e;
> +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> +                                                  nb_bt->logical_port,
> +                                                  nb_bt->dst_ip);
> +        if (!bfd_e) {
> +            return;
> +        }
>
> -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> -                                nb_bt->dst_ip);
> -        ovs_mutex_lock(&bfd_lock);
> -        if (bfd_e) {
> -            bfd_e->ref = true;
> +        /* This static route is linked to an active bfd session. */
> +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> +                                                   nb_bt->logical_port,
> +                                                   nb_bt->dst_ip);
> +        if (!bfd_sr) {
> +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> +                                     nb_bt->logical_port, nb_bt->dst_ip,
> +                                     bfd_e->status);
>          }
>
> -        if (!strcmp(nb_bt->status, "admin_down")) {
> -            nbrec_bfd_set_status(nb_bt, "down");
> +        if (!strcmp(bfd_e->status, "admin_down")) {
> +            bfd_set_status(bfd_sr, "down");
>          }
>
> -        if (!strcmp(nb_bt->status, "down")) {
> -            ovs_mutex_unlock(&bfd_lock);
> -            return NULL;
> +
> +        if (!strcmp(bfd_sr->status, "down")) {
> +            return;
>          }
> -        ovs_mutex_unlock(&bfd_lock);
>      }
>
> -    struct parsed_route *pr = xzalloc(sizeof *pr);
> -    pr->prefix = prefix;
> -    pr->plen = plen;
> -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> -                                                 "src-ip"));
> -    pr->hash = route_hash(pr);
> -    pr->route = route;
> -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> -                                             "ecmp_symmetric_reply", false);
> -    pr->is_discard_route = is_discard_route;
> -    ovs_list_insert(routes, &pr->list_node);
> -    return pr;
> +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> +    new_pr->prefix = prefix;
> +    new_pr->plen = plen;
> +    new_pr->route_table_id = get_route_table_id(route_tables,
> +                                                route->route_table);
> +    new_pr->is_src_route = (route->policy &&
> +                            !strcmp(route->policy, "src-ip"));
> +    new_pr->hash = route_hash(new_pr);
> +    new_pr->route = route;
> +    new_pr->nbr = od->nbr;
> +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> +                                                 "ecmp_symmetric_reply",
> +                                                 false);
> +    new_pr->is_discard_route = is_discard_route;
> +
> +    size_t hash = uuid_hash(&od->key);
> +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> +    if (!pr) {
> +        hmap_insert(routes, &new_pr->key_node, hash);
> +    } else {
> +        pr->stale = false;
> +        free(new_pr);
> +    }
>  }
>
> -static void
> -parsed_routes_destroy(struct ovs_list *routes)
> +void
> +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> +                    struct hmap *bfd_connections, struct hmap *routes,
> +                    struct simap *route_tables,
> +                    struct hmap *bfd_active_connections)
>  {
>      struct parsed_route *pr;
> -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> -        ovs_list_remove(&pr->list_node);
> +    HMAP_FOR_EACH (pr, key_node, routes) {
> +        if (pr->nbr == od->nbr) {
> +            pr->stale = true;
> +        }
> +    }
> +
> +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> +                          bfd_connections, routes, route_tables,
> +                          bfd_active_connections);
> +    }
> +
> +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> +        if (!pr->stale) {
> +            continue;
> +        }
> +
> +        hmap_remove(routes, &pr->key_node);
>          free(pr);
>      }
>  }
> @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
>            const struct ovn_port *op, const char *lrp_addr_s,
>            const char *network_s, int plen, const char *gateway,
>            bool is_src_route, const uint32_t rtb_id,
> +          const struct hmap *bfd_connections,
>            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
>            int ofs, struct lflow_ref *lflow_ref)
>  {
> @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
>                              priority, ds_cstr(&match),
>                              ds_cstr(&actions), stage_hint,
>                              lflow_ref);
> -    if (op && op->has_bfd) {
> +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
>          ds_put_format(&match, " && udp.dst == 3784");
>          ovn_lflow_add_with_hint(lflows, op->od,
>                                  S_ROUTER_IN_IP_ROUTING,
> @@ -10895,6 +11030,7 @@ static void
>  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>                          const struct hmap *lr_ports,
>                          const struct parsed_route *route_,
> +                        const struct hmap *bfd_connections,
>                          struct lflow_ref *lflow_ref)
>  {
>      const char *lrp_addr_s = NULL;
> @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
>      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
>                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> -              route_->is_src_route, route_->route_table_id, &route->header_,
> -              route_->is_discard_route, ofs, lflow_ref);
> +              route_->is_src_route, route_->route_table_id,
> +              bfd_connections, &route->header_, route_->is_discard_route,
> +              ofs, lflow_ref);
>
>      free(prefix_s);
>  }
> @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
>  static void
>  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
>                          const struct shash *meter_groups,
> +                        const struct hmap *bfd_connections,
>                          struct lflow_ref *lflow_ref)
>  {
> -    if (!op->has_bfd) {
> +    if (!bfd_is_port_running(bfd_connections, op->key)) {
>          return;
>      }
>
> @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
>   * LRP 'op'.
>   */
>  static void
> -build_ip_routing_flows_for_lrp(
> -        struct ovn_port *op, struct lflow_table *lflows,
> -        struct lflow_ref *lflow_ref)
> +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> +                               const struct hmap *bfd_connections,
> +                               struct lflow_table *lflows,
> +                               struct lflow_ref *lflow_ref)
>  {
>      ovs_assert(op->nbrp);
>      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
>                    op->lrp_networks.ipv4_addrs[i].network_s,
>                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> -                  lflow_ref);
> +                  bfd_connections, &op->nbrp->header_, false,
> +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
>      }
>
>      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
>          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
>                    op->lrp_networks.ipv6_addrs[i].network_s,
>                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> -                  lflow_ref);
> +                  bfd_connections, &op->nbrp->header_, false,
> +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
>      }
>  }
>
>  static void
>  build_static_route_flows_for_lrouter(
>          struct ovn_datapath *od, struct lflow_table *lflows,
> -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> +        struct simap *route_tables, const struct hmap *bfd_connections,
>          struct lflow_ref *lflow_ref)
>  {
>      ovs_assert(od->nbr);
> @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
>
>      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
>      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
>      struct ecmp_groups_node *group;
>
>      for (int i = 0; i < od->nbr->n_ports; i++) {
>          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> -                                &route_tables, lflow_ref);
> +                                route_tables, lflow_ref);
>      }
>
> -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> -        struct parsed_route *route =
> -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> -                              od->nbr->static_routes[i], bfd_connections);
> -        if (!route) {
> -            continue;
> -        }
> +    struct parsed_route *route;
> +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> +                             parsed_routes) {
>          group = ecmp_groups_find(&ecmp_groups, route);
>          if (group) {
>              ecmp_groups_add_route(group, route);
> @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
>      }
>      const struct unique_routes_node *ur;
>      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> +                                bfd_connections, lflow_ref);
>      }
>      ecmp_groups_destroy(&ecmp_groups);
>      unique_routes_destroy(&unique_routes);
> -    parsed_routes_destroy(&parsed_routes);
> -    simap_destroy(&route_tables);
>  }
>
>  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
>      }
>  }
>
> +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->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;
> +}
> +
> +void
> +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> +                     const struct hmap *bfd_connections,
> +                     struct hmap *route_policies,
> +                     struct hmap *bfd_active_connections)
> +{
> +    struct route_policy *rp;
> +
> +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> +        if (rp->nbr == od->nbr) {
> +            rp->stale = true;
> +        }
> +    }
> +
> +    for (int i = 0; i < od->nbr->n_policies; i++) {
> +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> +        size_t n_valid_nexthops = 0;
> +        char **valid_nexthops = NULL;
> +
> +        if (!strcmp(rule->action, "reroute")) {
> +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> +
> +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> +            for (size_t j = 0; j < n_nexthops; j++) {
> +                char *nexthop = rule->n_nexthops
> +                    ? rule->nexthops[j] : rule->nexthop;
> +                struct ovn_port *out_port =
> +                    get_outport_for_routing_policy_nexthop(
> +                            od, lr_ports, rule->priority, nexthop);
> +                if (!out_port || !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->nbr = od->nbr;
> +
> +        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 {
> +            rp->stale = false;
> +            free(valid_nexthops);
> +            free(new_rp);
> +        }
> +    }
> +
> +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> +        if (!rp->stale) {
> +            continue;
> +        }
> +
> +        hmap_remove(route_policies, &rp->key_node);
> +        free(rp->valid_nexthops);
> +        free(rp);
> +    }
> +}
> +
>  /* Logical router ingress table POLICY: Policy.
>   *
>   * A packet that arrives at this table is an IP packet that should be
> @@ -12862,7 +13104,7 @@ static void
>  build_ingress_policy_flows_for_lrouter(
>          struct ovn_datapath *od, struct lflow_table *lflows,
>          const struct hmap *lr_ports,
> -        const struct hmap *bfd_connections,
> +        struct hmap *route_policies,
>          struct lflow_ref *lflow_ref)
>  {
>      ovs_assert(od->nbr);
> @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
>
>      /* Convert routing policies to flows. */
>      uint16_t ecmp_group_id = 1;
> -    for (int i = 0; i < od->nbr->n_policies; i++) {
> -        const struct nbrec_logical_router_policy *rule
> -            = od->nbr->policies[i];
> +    struct route_policy *rp;
> +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> +                             route_policies) {
> +        const struct nbrec_logical_router_policy *rule = rp->rule;
>          bool is_ecmp_reroute =
>              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
>
>          if (is_ecmp_reroute) {
> -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> -                                            bfd_connections, ecmp_group_id,
> -                                            lflow_ref);
> +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> +                                            ecmp_group_id, lflow_ref);
>              ecmp_group_id++;
>          } else {
> -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> -                                      bfd_connections, &rule->header_,
> -                                      lflow_ref);
> +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> +                                      &rule->header_, lflow_ref);
>          }
>      }
>  }
> @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                              struct lflow_table *lflows,
>                              struct ds *match, struct ds *actions,
>                              const struct shash *meter_groups,
> +                            const struct hmap *bfd_connections,
>                              struct lflow_ref *lflow_ref)
>  {
>      ovs_assert(op->nbrp);
> @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>      }
>
>      /* BFD msg handling */
> -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> +                            lflow_ref);
>
>      /* ICMP time exceeded */
>      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
>  static void
>  build_routable_flows_for_router_port(
>      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> +    const struct hmap *bfd_connections,
>      struct lflow_table *lflows,
>      struct ds *match,
>      struct ds *actions)
> @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
>                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
>                                laddrs->ipv4_addrs[k].network_s,
>                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> -                              &router_port->nbrp->header_, false,
> -                              ROUTE_PRIO_OFFSET_CONNECTED,
> +                              bfd_connections, &router_port->nbrp->header_,
> +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
>                                lrp->stateful_lflow_ref);
>                  }
>              }
> @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
>  static void
>  build_lbnat_lflows_iterate_by_lrp(
>      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> -    const struct shash *meter_groups, struct ds *match,
> -    struct ds *actions, struct lflow_table *lflows)
> +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
>  {
>      ovs_assert(op->nbrp);
>
> @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
>      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
>                                  actions, lflows);
>
> -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> -                                         actions);
> +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> +                                         lflows, match, actions);
>  }
>
>  static void
> @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
>      struct ds actions;
>      size_t thread_lflow_counter;
>      const char *svc_monitor_mac;
> +    struct hmap *parsed_routes;
> +    struct hmap *route_policies;
> +    struct simap *route_tables;
>  };
>
>  /* Helper function to combine all lflow generation which is iterated by
> @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
>      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
>      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
>      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> -                                         lsi->bfd_connections,
> -                                         NULL);
> +                                         lsi->parsed_routes, lsi->route_tables,
> +                                         lsi->bfd_connections, NULL);
>      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
>                                           &lsi->actions, NULL);
>      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> -                                           lsi->bfd_connections, NULL);
> +                                           lsi->route_policies, NULL);
>      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
>      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
>                                            &lsi->match, &lsi->actions,
> @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
>                                            &lsi->actions, op->lflow_ref);
>      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
>                                                  &lsi->actions, op->lflow_ref);
> -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> +                                   lsi->lflows, op->lflow_ref);
>      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
>                                         &lsi->actions, lsi->meter_groups,
>                                         op->lflow_ref);
> @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
>                                              lsi->meter_groups,
>                                              op->lflow_ref);
>      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> -                                lsi->meter_groups, op->lflow_ref);
> +                                lsi->meter_groups, lsi->bfd_connections,
> +                                op->lflow_ref);
>      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
>                                                   &lsi->actions, op->lflow_ref);
>  }
> @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
>                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
>                      build_lbnat_lflows_iterate_by_lrp(
>                          op, lsi->lr_stateful_table, lsi->meter_groups,
> -                        &lsi->match, &lsi->actions, lsi->lflows);
> +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> +                        lsi->lflows);
>                  }
>              }
>              for (bnum = control->id;
> @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
>      const struct hmap *svc_monitor_map,
>      const struct hmap *bfd_connections,
>      const struct chassis_features *features,
> -    const char *svc_monitor_mac)
> +    const char *svc_monitor_mac,
> +    struct hmap *parsed_routes,
> +    struct hmap *route_policies,
> +    struct simap *route_tables)
>  {
>
>      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
>              lsiv[index].svc_check_match = svc_check_match;
>              lsiv[index].thread_lflow_counter = 0;
>              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> +            lsiv[index].parsed_routes = parsed_routes;
> +            lsiv[index].route_tables = route_tables;
> +            lsiv[index].route_policies = route_policies;
>              ds_init(&lsiv[index].match);
>              ds_init(&lsiv[index].actions);
>
> @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
>              .features = features,
>              .svc_check_match = svc_check_match,
>              .svc_monitor_mac = svc_monitor_mac,
> +            .parsed_routes = parsed_routes,
> +            .route_tables = route_tables,
> +            .route_policies = route_policies,
>              .match = DS_EMPTY_INITIALIZER,
>              .actions = DS_EMPTY_INITIALIZER,
>          };
> @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
>              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
>              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
>                                                lsi.meter_groups,
> +                                              lsi.bfd_connections,
>                                                &lsi.match,
>                                                &lsi.actions,
>                                                lsi.lflows);
> @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
>                                      input_data->svc_monitor_map,
>                                      input_data->bfd_connections,
>                                      input_data->features,
> -                                    input_data->svc_monitor_mac);
> +                                    input_data->svc_monitor_mac,
> +                                    input_data->parsed_routes,
> +                                    input_data->route_policies,
> +                                    input_data->route_tables);
>
>      if (parallelization_state == STATE_INIT_HASH_SIZES) {
>          parallelization_state = STATE_USE_PARALLELIZATION;
> @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
>              build_lbnat_lflows_iterate_by_lrp(op,
>                                                lflow_input->lr_stateful_table,
>                                                lflow_input->meter_groups,
> +                                              lflow_input->bfd_connections,
>                                                &match, &actions,
>                                                lflows);
>
> @@ -17490,6 +17754,27 @@ 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);
> +}
> +
> +void
> +static_routes_init(struct static_routes_data *data)
> +{
> +    hmap_init(&data->parsed_routes);
> +    simap_init(&data->route_tables);
> +    hmap_init(&data->bfd_active_connections);
> +}
> +
> +void
> +bfd_init(struct bfd_data *data)
> +{
> +    hmap_init(&data->bfd_connections);
> +}
> +
>  void
>  northd_destroy(struct northd_data *data)
>  {
> @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
>      destroy_northd_tracked_data(data);
>  }
>
> +static void
> +__bfd_destroy(struct hmap *bfd_connections)
> +{
> +    struct bfd_entry *bfd_e;
> +
> +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> +        bfd_erase_entry(bfd_e);
> +    }
> +    hmap_destroy(bfd_connections);
> +}
> +
> +void
> +bfd_destroy(struct bfd_data *data)
> +{
> +    __bfd_destroy(&data->bfd_connections);
> +}
> +
> +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);
> +}
> +
> +void
> +static_routes_destroy(struct static_routes_data *data)
> +{
> +    struct parsed_route *r;
> +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> +        free(r);
> +    }
> +    hmap_destroy(&data->parsed_routes);
> +
> +    simap_destroy(&data->route_tables);
> +    __bfd_destroy(&data->bfd_active_connections);
> +}
> +
>  void
>  ovnnb_db_run(struct northd_input *input_data,
>               struct northd_data *data,
> diff --git a/northd/northd.h b/northd/northd.h
> index d4a8d75ab..432072f9f 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -23,6 +23,7 @@
>  #include "northd/en-port-group.h"
>  #include "northd/ipam.h"
>  #include "openvswitch/hmap.h"
> +#include "simap.h"
>  #include "ovs-thread.h"
>
>  struct northd_input {
> @@ -160,14 +161,34 @@ 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;
> +    const struct nbrec_logical_router *nbr;
> +    bool stale;
> +};
> +
> +struct static_routes_data {
> +    struct hmap parsed_routes;
> +    struct simap route_tables;
> +    struct hmap bfd_active_connections;
> +};
> +
> +struct route_policies_data {
> +    struct hmap route_policies;
> +    struct hmap bfd_active_connections;
> +};
> +
> +struct bfd_data {
> +    struct hmap bfd_connections;
> +};
> +
>  struct lr_nat_table;
>
>  struct lflow_input {
> -    /* Northbound table references */
> -    const struct nbrec_bfd_table *nbrec_bfd_table;
> -
>      /* Southbound table references */
> -    const struct sbrec_bfd_table *sbrec_bfd_table;
>      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
>      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
>      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> @@ -190,6 +211,9 @@ struct lflow_input {
>      const struct hmap *svc_monitor_map;
>      bool ovn_internal_version_changed;
>      const char *svc_monitor_mac;
> +    struct hmap *parsed_routes;
> +    struct hmap *route_policies;
> +    struct simap *route_tables;
>  };
>
>  extern int parallelization_state;
> @@ -593,8 +617,6 @@ struct ovn_port {
>
>      bool has_unknown; /* If the addresses have 'unknown' defined. */
>
> -    bool has_bfd;
> -
>      /* The port's peer:
>       *
>       *     - A switch port S of type "router" has a router port R as a peer,
> @@ -653,6 +675,20 @@ struct ovn_port {
>      struct lflow_ref *stateful_lflow_ref;
>  };
>
> +struct parsed_route {
> +    struct hmap_node key_node;
> +    struct in6_addr prefix;
> +    unsigned int plen;
> +    bool is_src_route;
> +    uint32_t route_table_id;
> +    uint32_t hash;
> +    const struct nbrec_logical_router_static_route *route;
> +    bool ecmp_symmetric_reply;
> +    bool is_discard_route;
> +    const struct nbrec_logical_router *nbr;
> +    bool stale;
> +};
> +
>  void ovnnb_db_run(struct northd_input *input_data,
>                    struct northd_data *data,
>                    struct ovsdb_idl_txn *ovnnb_txn,
> @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> +                         struct hmap *, struct hmap *, struct simap *,
> +                         struct hmap *);
> +uint32_t get_route_table_id(struct simap *, const char *);
> +void static_routes_init(struct static_routes_data *);
> +void static_routes_destroy(struct static_routes_data *);
> +
> +void bfd_init(struct bfd_data *);
> +void bfd_destroy(struct bfd_data *);
> +
>  struct lflow_table;
>  struct lr_stateful_tracked_data;
>  struct ls_stateful_tracked_data;
> @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
>                                     struct hmap *lbgrp_datapaths_map,
>                                     struct northd_tracked_data *);
>
> -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> -                     const struct nbrec_bfd_table *,
> -                     const struct sbrec_bfd_table *,
> -                     const struct hmap *lr_ports,
> -                     struct hmap *bfd_connections);
> -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> -                             struct hmap *bfd_map);
> +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> +                          const struct hmap *, struct hmap *, struct hmap *);
> +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 *, struct hmap *);
> +void build_bfd_map(const struct nbrec_bfd_table *,
> +                   const struct sbrec_bfd_table *, struct hmap *);
>  void run_update_worker_pool(int n_threads);
>
>  const struct ovn_datapath *northd_get_datapath_for_port(
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 199197f09..710500dce 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
>      ovn-sbctl list meter >> $1
>      ovn-sbctl list meter_band >> $1
>      ovn-sbctl list port_group >> $1
> +    ovn-sbctl list bfd >> $1
>      ovn-sbctl dump-flows > lflows_$1
>  ])
>
> @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
>      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
>  done
>
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
>  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
>  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
>  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
>                       min_rx=1000 min_tx=1000 status=admin_down
>
> +check_engine_stats northd norecompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
>  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
>
> @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
>  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
>  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
>
> +check_engine_stats northd norecompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
>  wait_column down bfd status logical_port=r0-sw1
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
>
> +check_engine_stats northd recompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats static_routes recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
>  wait_column down bfd status logical_port=r0-sw2
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
>  wait_column down bfd status logical_port=r0-sw5
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
>
> +check_engine_stats northd recompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats static_routes recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
>  wait_column down bfd status logical_port=r0-sw6
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
>
> +check_engine_stats northd recompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats route_policies recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
>  wait_column down bfd status logical_port=r0-sw7
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
>  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
>
> +check_engine_stats northd recompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats static_routes recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  check ovn-nbctl lr-policy-del r0
>  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
>
> @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
>  wait_column down bfd status dst_ip=192.168.9.3
>  wait_column down bfd status dst_ip=192.168.9.4
>
> +check_engine_stats northd recompute nocompute
> +check_engine_stats bfd recompute nocompute
> +check_engine_stats route_policies recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +check_engine_stats northd_output norecompute compute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +
>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
>  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
>
> --
> 2.45.2
>
Lorenzo Bianconi Aug. 6, 2024, 4:53 p.m. UTC | #2
[...]
> > -void
> > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > -                        struct hmap *bfd_map)
> > +static bool
> > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> >  {
> > -    const struct nbrec_bfd *nb_bt;
> >      struct bfd_entry *bfd_e;
> > -
> > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > -        if (!bfd_e) {
> > -            continue;
> > -        }
> > -
> > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > -            /* no user for this bfd connection */
> > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> 
> I like the idea of checking the existence of the bfd entry to decide
> if a port has bfd instead of relying on the has_bfd status. However, I
> have performance concerns for this loop. The bfd_port_lookup()
> searches by hashing both logical_port and dst_ip, but here for
> logical_port it just iterates all bfd entries, which looks
> unreasonable. It may be better to add a hindex for logical_port.

Hi Han,

I did not get what you mean here, can you please provide more details?
Do you mean another hmap to link the logical_port name to the bfd_entry?

> 
> > +        if (!strcmp(bfd_e->logical_port, port)) {
> > +            return true;
> >          }
> >      }
> > -
> > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > -        free(bfd_e);
> > -    }
> > +    return false;
> >  }
> >
> > +
> >  #define BFD_DEF_MINTX       1000 /* 1s */
> >  #define BFD_DEF_MINRX       1000 /* 1s */
> >  #define BFD_DEF_DETECT_MULT 5
> > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> >      return port + BFD_UDP_SRC_PORT_START;
> >  }
> >
> > -void
> > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > +static char *
> > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > +                          const struct hmap *rp_bfd_connections,
> > +                          const struct hmap *sr_bfd_connections)
> >  {
> > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > -    const struct sbrec_bfd *sb_bt;
> > -    unsigned long *bfd_src_ports;
> > -    struct bfd_entry *bfd_e;
> > -    uint32_t hash;
> > +    struct bfd_entry *bfd_rp, *bfd_sr;
> >
> > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > +                             nb_bt->dst_ip);
> > +    if (!bfd_rp) {
> > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > +                                 nb_bt->dst_ip);
> > +        if (!bfd_sr) {
> > +            return "admin_down";
> > +        }
> > +    }
> >
> > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > -        bfd_e = xmalloc(sizeof *bfd_e);
> > -        bfd_e->sb_bt = sb_bt;
> > -        hash = hash_string(sb_bt->dst_ip, 0);
> > -        hash = hash_string(sb_bt->logical_port, hash);
> > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > +}
> > +
> > +void
> > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > +               const struct hmap *lr_ports,
> > +               const struct hmap *bfd_connections,
> > +               const struct hmap *rp_bfd_connections,
> > +               const struct hmap *sr_bfd_connections,
> > +               struct hmap *sync_bfd_connections)
> > +{
> > +    if (!ovnsb_txn) {
> > +        return;
> > +    }
> > +
> > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > +
> > +    struct bfd_entry *bfd_e;
> > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > +                                              bfd_e->logical_port,
> > +                                              bfd_e->dst_ip, bfd_e->status);
> 
> It looks like we are blindly allocating a new entry for each entry in
> bfd_connections without checking if it already exists. Did I
> misunderstand?

So far sync_bfd_connections always start from scratch and it is 1:1 with
bfd_connections, so I guess an entry can't already exist. But I prefer to be
more generic and check it. I will fix it.

Regards,
Lorenzo

> 
> Thanks,
> Han
> 
> > +        e->nb_bt = bfd_e->nb_bt;
> > +        e->sb_bt = bfd_e->sb_bt;
> > +        e->stale = true;
> > +        /* we need to check if this entry is even in the BFD nb db table */
> > +        if (bfd_e->sb_bt) {
> > +            bitmap_set1(bfd_src_ports,
> > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > +        }
> >      }
> >
> >      const struct nbrec_bfd *nb_bt;
> >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > -        if (!nb_bt->status) {
> > -            /* default state is admin_down */
> > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > +                                nb_bt->dst_ip);
> > +        if (!bfd_e) {
> > +            continue;
> >          }
> >
> >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > -        if (!bfd_e) {
> > +        if (!op || !op->sb) {
> > +            /* skip not bounded ports */
> > +            continue;
> > +        }
> > +
> > +        nbrec_bfd_set_status(nb_bt,
> > +                             bfd_get_connection_status(nb_bt,
> > +                                                       rp_bfd_connections,
> > +                                                       sr_bfd_connections));
> > +        if (!bfd_e->sb_bt) {
> >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> >              if (udp_src < 0) {
> >                  continue;
> >              }
> >
> > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > +            /* Add entry to bfd sb table. */
> > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > -            if (op && op->sb && op->sb->chassis) {
> > +            if (op->sb->chassis) {
> >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> >              }
> >
> > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> >                  }
> >              }
> > +
> >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > -            if (op && op->sb && op->sb->chassis &&
> > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > +                                           bfd_e->sb_bt->chassis_name)) {
> >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> >                                             op->sb->chassis->name);
> >              }
> > -
> > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > -            bfd_e->ref = false;
> > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> >          }
> >
> > -        if (op) {
> > -            op->has_bfd = true;
> > -        }
> > +        bfd_e->stale = false;
> >      }
> >
> > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > -                                            bfd_e->sb_bt->logical_port);
> > -        if (op) {
> > -            op->has_bfd = false;
> > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > +        if (bfd_e->stale) {
> > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > +            bfd_erase_entry(bfd_e);
> >          }
> > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > -        free(bfd_e);
> >      }
> > -    hmap_destroy(&sb_only);
> >
> >      bitmap_free(bfd_src_ports);
> >  }
> >
> > +void
> > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > +              struct hmap *bfd_connections)
> > +{
> > +    struct bfd_entry *bfd_e;
> > +
> > +    /* align bfd_map to sb db */
> > +    const struct sbrec_bfd *sb_bt;
> > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > +                                sb_bt->dst_ip);
> > +        if (!bfd_e) {
> > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > +                                    sb_bt->dst_ip,  sb_bt->status);
> > +        }
> > +        bfd_e->sb_bt = sb_bt;
> > +    }
> > +
> > +    const struct nbrec_bfd *nb_bt;
> > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > +                                nb_bt->dst_ip);
> > +        if (!bfd_e) {
> > +            /* brand new entry. */
> > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > +                                    nb_bt->dst_ip, "admin_down");
> > +        }
> > +        bfd_e->nb_bt = nb_bt;
> > +    }
> > +}
> > +
> >  /* Returns a string of the IP address of the router port 'op' that
> >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> >   *
> > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> >      return NULL;
> >  }
> >
> > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > -
> > -static bool check_bfd_state(
> > -        const struct nbrec_logical_router_policy *rule,
> > -        const struct hmap *bfd_connections,
> > -        struct ovn_port *out_port,
> > -        const char *nexthop)
> > +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);
> > -    bool ret = true;
> >
> >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> >          /* Check if there is a BFD session associated to the reroute
> > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> >                                                    nb_bt->logical_port,
> >                                                    nb_bt->dst_ip);
> > -        ovs_mutex_lock(&bfd_lock);
> > -        if (bfd_e) {
> > -            bfd_e->ref = true;
> > +        if (!bfd_e) {
> > +            continue;
> >          }
> >
> > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > -            nbrec_bfd_set_status(nb_bt, "down");
> > +        /* 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);
> >          }
> >
> > -        ret = strcmp(nb_bt->status, "down");
> > -        ovs_mutex_unlock(&bfd_lock);
> > -        break;
> > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > +            bfd_set_status(bfd_rp, "down");
> > +        }
> > +
> > +        return strcmp(bfd_rp->status, "down");
> >      }
> >
> > -    return ret;
> > +    return true;
> >  }
> >
> >  static void
> >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > -                          const struct hmap *lr_ports,
> > -                          const struct nbrec_logical_router_policy *rule,
> > -                          const struct hmap *bfd_connections,
> > +                          const struct hmap *lr_ports, struct route_policy *rp,
> >                            const struct ovsdb_idl_row *stage_hint,
> >                            struct lflow_ref *lflow_ref)
> >  {
> > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> >      struct ds match = DS_EMPTY_INITIALIZER;
> >      struct ds actions = DS_EMPTY_INITIALIZER;
> >
> >      if (!strcmp(rule->action, "reroute")) {
> >          ovs_assert(rule->n_nexthops <= 1);
> >
> > -        char *nexthop =
> > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > +        if (!rp->n_valid_nexthops) {
> > +            return;
> > +        }
> > +
> > +        char *nexthop = rp->valid_nexthops[0];
> >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> >               od, lr_ports, rule->priority, nexthop);
> >          if (!out_port) {
> > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> >              return;
> >          }
> >
> > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > -            return;
> > -        }
> > -
> >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> >          if (pkt_mark) {
> >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > @@ -10070,19 +10156,18 @@ static void
> >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> >                                  struct ovn_datapath *od,
> >                                  const struct hmap *lr_ports,
> > -                                const struct nbrec_logical_router_policy *rule,
> > -                                const struct hmap *bfd_connections,
> > +                                struct route_policy *rp,
> >                                  uint16_t ecmp_group_id,
> >                                  struct lflow_ref *lflow_ref)
> >  {
> > -    ovs_assert(rule->n_nexthops > 1);
> > -
> >      bool nexthops_is_ipv4 = true;
> > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > +    ovs_assert(rule->n_nexthops > 1);
> >
> >      /* Check that all the nexthops belong to the same addr family before
> >       * adding logical flows. */
> > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> >
> >          if (i == 0) {
> >              nexthops_is_ipv4 = is_ipv4;
> > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> >                           "the match [%s] do not belong to the same address "
> >                           "family as other next hops",
> > -                         rule->nexthops[i], rule->match);
> > +                         rp->valid_nexthops[i], rule->match);
> >              return;
> >          }
> >      }
> > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> >      struct ds match = DS_EMPTY_INITIALIZER;
> >      struct ds actions = DS_EMPTY_INITIALIZER;
> >
> > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > -    size_t n_valid_nexthops = 0;
> > -
> > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> >          if (!out_port) {
> >              goto cleanup;
> >          }
> >
> >          const char *lrp_addr_s =
> > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> >          if (!lrp_addr_s) {
> >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> >                              " priority %"PRId64" nexthop %s",
> > -                            rule->priority, rule->nexthops[i]);
> > +                            rule->priority, rp->valid_nexthops[i]);
> >              goto cleanup;
> >          }
> >
> > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > -                             rule->nexthops[i])) {
> > -            continue;
> > -        }
> > -
> > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > -
> >          ds_clear(&actions);
> >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> >          if (pkt_mark) {
> >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> >          }
> >
> > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> >
> >          ds_put_format(&actions, "%s = %s; "
> >                        "%s = %s; "
> > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> >                        "flags.loopback = 1; "
> >                        "next;",
> >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > -                      rule->nexthops[i],
> > +                      rp->valid_nexthops[i],
> >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> >                        lrp_addr_s,
> >                        out_port->lrp_networks.ea_s,
> > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> >                                  lflow_ref);
> >      }
> >
> > -    if (!n_valid_nexthops) {
> > -        goto cleanup;
> > -    }
> > -
> >      ds_clear(&actions);
> > -    if (n_valid_nexthops > 1) {
> > +    if (rp->n_valid_nexthops > 1) {
> >          ds_put_format(&actions, "%s = %"PRIu16
> >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> >                        REG_ECMP_MEMBER_ID);
> >
> > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> >              if (i > 0) {
> >                  ds_put_cstr(&actions, ", ");
> >              }
> >
> > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> >          }
> >          ds_put_cstr(&actions, ");");
> >      } else {
> >          ds_put_format(&actions, "%s = %"PRIu16
> > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > -                      valid_nexthops[0]);
> > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> >      }
> >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> >                              rule->priority, rule->match,
> >                              ds_cstr(&actions), &rule->header_,
> >                              lflow_ref);
> > -
> >  cleanup:
> > -    free(valid_nexthops);
> >      ds_destroy(&match);
> >      ds_destroy(&actions);
> >  }
> > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> >      return rtb_id;
> >  }
> >
> > -static uint32_t
> > +uint32_t
> >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> >  {
> >      if (!route_table_name || !route_table_name[0]) {
> > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> >      ds_destroy(&actions);
> >  }
> >
> > -struct parsed_route {
> > -    struct ovs_list list_node;
> > -    struct in6_addr prefix;
> > -    unsigned int plen;
> > -    bool is_src_route;
> > -    uint32_t route_table_id;
> > -    uint32_t hash;
> > -    const struct nbrec_logical_router_static_route *route;
> > -    bool ecmp_symmetric_reply;
> > -    bool is_discard_route;
> > -};
> > -
> >  static uint32_t
> >  route_hash(struct parsed_route *route)
> >  {
> > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> >
> >  /* Parse and validate the route. Return the parsed route if successful.
> >   * Otherwise return NULL. */
> > +
> >  static struct parsed_route *
> > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > +                    struct parsed_route *new_pr)
> > +{
> > +    struct parsed_route *pr;
> > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > +        if (pr->plen != new_pr->plen) {
> > +            continue;
> > +        }
> > +
> > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > +            continue;
> > +        }
> > +
> > +        if (pr->is_src_route != new_pr->is_src_route) {
> > +            continue;
> > +        }
> > +
> > +        if (pr->route_table_id != new_pr->route_table_id) {
> > +            continue;
> > +        }
> > +
> > +        if (pr->route != new_pr->route) {
> > +            continue;
> > +        }
> > +
> > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > +            continue;
> > +        }
> > +
> > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > +            continue;
> > +        }
> > +
> > +        return pr;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +static void
> >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > -                  struct ovs_list *routes, struct simap *route_tables,
> >                    const struct nbrec_logical_router_static_route *route,
> > -                  const struct hmap *bfd_connections)
> > +                  struct hmap *bfd_connections,
> > +                  struct hmap *routes, struct simap *route_tables,
> > +                  struct hmap *bfd_active_connections)
> >  {
> >      /* Verify that the next hop is an IP address with an all-ones mask. */
> >      struct in6_addr nexthop;
> > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> >                           UUID_FMT, route->nexthop,
> >                           UUID_ARGS(&route->header_.uuid));
> > -            return NULL;
> > +            return;
> >          }
> >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> >                           UUID_FMT, route->nexthop,
> >                           UUID_ARGS(&route->header_.uuid));
> > -            return NULL;
> > +            return;
> >          }
> >      }
> >
> > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> >                       UUID_FMT, route->ip_prefix,
> >                       UUID_ARGS(&route->header_.uuid));
> > -        return NULL;
> > +        return;
> >      }
> >
> >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> >                           route->ip_prefix, route->nexthop,
> >                           UUID_ARGS(&route->header_.uuid));
> > -            return NULL;
> > +            return;
> >          }
> >      }
> >
> > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> >          !find_static_route_outport(od, lr_ports, route,
> >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> >                                     NULL, NULL)) {
> > -        return NULL;
> > +        return;
> >      }
> >
> >      const struct nbrec_bfd *nb_bt = route->bfd;
> >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > -        struct bfd_entry *bfd_e;
> > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > +                                                  nb_bt->logical_port,
> > +                                                  nb_bt->dst_ip);
> > +        if (!bfd_e) {
> > +            return;
> > +        }
> >
> > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > -                                nb_bt->dst_ip);
> > -        ovs_mutex_lock(&bfd_lock);
> > -        if (bfd_e) {
> > -            bfd_e->ref = true;
> > +        /* This static route is linked to an active bfd session. */
> > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > +                                                   nb_bt->logical_port,
> > +                                                   nb_bt->dst_ip);
> > +        if (!bfd_sr) {
> > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > +                                     bfd_e->status);
> >          }
> >
> > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > -            nbrec_bfd_set_status(nb_bt, "down");
> > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > +            bfd_set_status(bfd_sr, "down");
> >          }
> >
> > -        if (!strcmp(nb_bt->status, "down")) {
> > -            ovs_mutex_unlock(&bfd_lock);
> > -            return NULL;
> > +
> > +        if (!strcmp(bfd_sr->status, "down")) {
> > +            return;
> >          }
> > -        ovs_mutex_unlock(&bfd_lock);
> >      }
> >
> > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > -    pr->prefix = prefix;
> > -    pr->plen = plen;
> > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > -                                                 "src-ip"));
> > -    pr->hash = route_hash(pr);
> > -    pr->route = route;
> > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > -                                             "ecmp_symmetric_reply", false);
> > -    pr->is_discard_route = is_discard_route;
> > -    ovs_list_insert(routes, &pr->list_node);
> > -    return pr;
> > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > +    new_pr->prefix = prefix;
> > +    new_pr->plen = plen;
> > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > +                                                route->route_table);
> > +    new_pr->is_src_route = (route->policy &&
> > +                            !strcmp(route->policy, "src-ip"));
> > +    new_pr->hash = route_hash(new_pr);
> > +    new_pr->route = route;
> > +    new_pr->nbr = od->nbr;
> > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > +                                                 "ecmp_symmetric_reply",
> > +                                                 false);
> > +    new_pr->is_discard_route = is_discard_route;
> > +
> > +    size_t hash = uuid_hash(&od->key);
> > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > +    if (!pr) {
> > +        hmap_insert(routes, &new_pr->key_node, hash);
> > +    } else {
> > +        pr->stale = false;
> > +        free(new_pr);
> > +    }
> >  }
> >
> > -static void
> > -parsed_routes_destroy(struct ovs_list *routes)
> > +void
> > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > +                    struct hmap *bfd_connections, struct hmap *routes,
> > +                    struct simap *route_tables,
> > +                    struct hmap *bfd_active_connections)
> >  {
> >      struct parsed_route *pr;
> > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > -        ovs_list_remove(&pr->list_node);
> > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > +        if (pr->nbr == od->nbr) {
> > +            pr->stale = true;
> > +        }
> > +    }
> > +
> > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > +                          bfd_connections, routes, route_tables,
> > +                          bfd_active_connections);
> > +    }
> > +
> > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > +        if (!pr->stale) {
> > +            continue;
> > +        }
> > +
> > +        hmap_remove(routes, &pr->key_node);
> >          free(pr);
> >      }
> >  }
> > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> >            const struct ovn_port *op, const char *lrp_addr_s,
> >            const char *network_s, int plen, const char *gateway,
> >            bool is_src_route, const uint32_t rtb_id,
> > +          const struct hmap *bfd_connections,
> >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> >            int ofs, struct lflow_ref *lflow_ref)
> >  {
> > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> >                              priority, ds_cstr(&match),
> >                              ds_cstr(&actions), stage_hint,
> >                              lflow_ref);
> > -    if (op && op->has_bfd) {
> > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> >          ds_put_format(&match, " && udp.dst == 3784");
> >          ovn_lflow_add_with_hint(lflows, op->od,
> >                                  S_ROUTER_IN_IP_ROUTING,
> > @@ -10895,6 +11030,7 @@ static void
> >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> >                          const struct hmap *lr_ports,
> >                          const struct parsed_route *route_,
> > +                        const struct hmap *bfd_connections,
> >                          struct lflow_ref *lflow_ref)
> >  {
> >      const char *lrp_addr_s = NULL;
> > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > -              route_->is_discard_route, ofs, lflow_ref);
> > +              route_->is_src_route, route_->route_table_id,
> > +              bfd_connections, &route->header_, route_->is_discard_route,
> > +              ofs, lflow_ref);
> >
> >      free(prefix_s);
> >  }
> > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> >  static void
> >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> >                          const struct shash *meter_groups,
> > +                        const struct hmap *bfd_connections,
> >                          struct lflow_ref *lflow_ref)
> >  {
> > -    if (!op->has_bfd) {
> > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> >          return;
> >      }
> >
> > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> >   * LRP 'op'.
> >   */
> >  static void
> > -build_ip_routing_flows_for_lrp(
> > -        struct ovn_port *op, struct lflow_table *lflows,
> > -        struct lflow_ref *lflow_ref)
> > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > +                               const struct hmap *bfd_connections,
> > +                               struct lflow_table *lflows,
> > +                               struct lflow_ref *lflow_ref)
> >  {
> >      ovs_assert(op->nbrp);
> >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> >                    op->lrp_networks.ipv4_addrs[i].network_s,
> >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > -                  lflow_ref);
> > +                  bfd_connections, &op->nbrp->header_, false,
> > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> >      }
> >
> >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> >                    op->lrp_networks.ipv6_addrs[i].network_s,
> >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > -                  lflow_ref);
> > +                  bfd_connections, &op->nbrp->header_, false,
> > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> >      }
> >  }
> >
> >  static void
> >  build_static_route_flows_for_lrouter(
> >          struct ovn_datapath *od, struct lflow_table *lflows,
> > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > +        struct simap *route_tables, const struct hmap *bfd_connections,
> >          struct lflow_ref *lflow_ref)
> >  {
> >      ovs_assert(od->nbr);
> > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> >
> >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> >      struct ecmp_groups_node *group;
> >
> >      for (int i = 0; i < od->nbr->n_ports; i++) {
> >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > -                                &route_tables, lflow_ref);
> > +                                route_tables, lflow_ref);
> >      }
> >
> > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > -        struct parsed_route *route =
> > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > -                              od->nbr->static_routes[i], bfd_connections);
> > -        if (!route) {
> > -            continue;
> > -        }
> > +    struct parsed_route *route;
> > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > +                             parsed_routes) {
> >          group = ecmp_groups_find(&ecmp_groups, route);
> >          if (group) {
> >              ecmp_groups_add_route(group, route);
> > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> >      }
> >      const struct unique_routes_node *ur;
> >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > +                                bfd_connections, lflow_ref);
> >      }
> >      ecmp_groups_destroy(&ecmp_groups);
> >      unique_routes_destroy(&unique_routes);
> > -    parsed_routes_destroy(&parsed_routes);
> > -    simap_destroy(&route_tables);
> >  }
> >
> >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> >      }
> >  }
> >
> > +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->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;
> > +}
> > +
> > +void
> > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > +                     const struct hmap *bfd_connections,
> > +                     struct hmap *route_policies,
> > +                     struct hmap *bfd_active_connections)
> > +{
> > +    struct route_policy *rp;
> > +
> > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > +        if (rp->nbr == od->nbr) {
> > +            rp->stale = true;
> > +        }
> > +    }
> > +
> > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > +        size_t n_valid_nexthops = 0;
> > +        char **valid_nexthops = NULL;
> > +
> > +        if (!strcmp(rule->action, "reroute")) {
> > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > +
> > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > +            for (size_t j = 0; j < n_nexthops; j++) {
> > +                char *nexthop = rule->n_nexthops
> > +                    ? rule->nexthops[j] : rule->nexthop;
> > +                struct ovn_port *out_port =
> > +                    get_outport_for_routing_policy_nexthop(
> > +                            od, lr_ports, rule->priority, nexthop);
> > +                if (!out_port || !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->nbr = od->nbr;
> > +
> > +        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 {
> > +            rp->stale = false;
> > +            free(valid_nexthops);
> > +            free(new_rp);
> > +        }
> > +    }
> > +
> > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > +        if (!rp->stale) {
> > +            continue;
> > +        }
> > +
> > +        hmap_remove(route_policies, &rp->key_node);
> > +        free(rp->valid_nexthops);
> > +        free(rp);
> > +    }
> > +}
> > +
> >  /* Logical router ingress table POLICY: Policy.
> >   *
> >   * A packet that arrives at this table is an IP packet that should be
> > @@ -12862,7 +13104,7 @@ static void
> >  build_ingress_policy_flows_for_lrouter(
> >          struct ovn_datapath *od, struct lflow_table *lflows,
> >          const struct hmap *lr_ports,
> > -        const struct hmap *bfd_connections,
> > +        struct hmap *route_policies,
> >          struct lflow_ref *lflow_ref)
> >  {
> >      ovs_assert(od->nbr);
> > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> >
> >      /* Convert routing policies to flows. */
> >      uint16_t ecmp_group_id = 1;
> > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > -        const struct nbrec_logical_router_policy *rule
> > -            = od->nbr->policies[i];
> > +    struct route_policy *rp;
> > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > +                             route_policies) {
> > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> >          bool is_ecmp_reroute =
> >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> >
> >          if (is_ecmp_reroute) {
> > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > -                                            bfd_connections, ecmp_group_id,
> > -                                            lflow_ref);
> > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > +                                            ecmp_group_id, lflow_ref);
> >              ecmp_group_id++;
> >          } else {
> > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > -                                      bfd_connections, &rule->header_,
> > -                                      lflow_ref);
> > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > +                                      &rule->header_, lflow_ref);
> >          }
> >      }
> >  }
> > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> >                              struct lflow_table *lflows,
> >                              struct ds *match, struct ds *actions,
> >                              const struct shash *meter_groups,
> > +                            const struct hmap *bfd_connections,
> >                              struct lflow_ref *lflow_ref)
> >  {
> >      ovs_assert(op->nbrp);
> > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> >      }
> >
> >      /* BFD msg handling */
> > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > +                            lflow_ref);
> >
> >      /* ICMP time exceeded */
> >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> >  static void
> >  build_routable_flows_for_router_port(
> >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > +    const struct hmap *bfd_connections,
> >      struct lflow_table *lflows,
> >      struct ds *match,
> >      struct ds *actions)
> > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> >                                laddrs->ipv4_addrs[k].network_s,
> >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > -                              &router_port->nbrp->header_, false,
> > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > +                              bfd_connections, &router_port->nbrp->header_,
> > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> >                                lrp->stateful_lflow_ref);
> >                  }
> >              }
> > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> >  static void
> >  build_lbnat_lflows_iterate_by_lrp(
> >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > -    const struct shash *meter_groups, struct ds *match,
> > -    struct ds *actions, struct lflow_table *lflows)
> > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> >  {
> >      ovs_assert(op->nbrp);
> >
> > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> >                                  actions, lflows);
> >
> > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > -                                         actions);
> > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > +                                         lflows, match, actions);
> >  }
> >
> >  static void
> > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> >      struct ds actions;
> >      size_t thread_lflow_counter;
> >      const char *svc_monitor_mac;
> > +    struct hmap *parsed_routes;
> > +    struct hmap *route_policies;
> > +    struct simap *route_tables;
> >  };
> >
> >  /* Helper function to combine all lflow generation which is iterated by
> > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > -                                         lsi->bfd_connections,
> > -                                         NULL);
> > +                                         lsi->parsed_routes, lsi->route_tables,
> > +                                         lsi->bfd_connections, NULL);
> >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> >                                           &lsi->actions, NULL);
> >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > -                                           lsi->bfd_connections, NULL);
> > +                                           lsi->route_policies, NULL);
> >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> >                                            &lsi->match, &lsi->actions,
> > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> >                                            &lsi->actions, op->lflow_ref);
> >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> >                                                  &lsi->actions, op->lflow_ref);
> > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > +                                   lsi->lflows, op->lflow_ref);
> >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> >                                         &lsi->actions, lsi->meter_groups,
> >                                         op->lflow_ref);
> > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> >                                              lsi->meter_groups,
> >                                              op->lflow_ref);
> >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > -                                lsi->meter_groups, op->lflow_ref);
> > +                                lsi->meter_groups, lsi->bfd_connections,
> > +                                op->lflow_ref);
> >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> >                                                   &lsi->actions, op->lflow_ref);
> >  }
> > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> >                      build_lbnat_lflows_iterate_by_lrp(
> >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > +                        lsi->lflows);
> >                  }
> >              }
> >              for (bnum = control->id;
> > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> >      const struct hmap *svc_monitor_map,
> >      const struct hmap *bfd_connections,
> >      const struct chassis_features *features,
> > -    const char *svc_monitor_mac)
> > +    const char *svc_monitor_mac,
> > +    struct hmap *parsed_routes,
> > +    struct hmap *route_policies,
> > +    struct simap *route_tables)
> >  {
> >
> >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> >              lsiv[index].svc_check_match = svc_check_match;
> >              lsiv[index].thread_lflow_counter = 0;
> >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > +            lsiv[index].parsed_routes = parsed_routes;
> > +            lsiv[index].route_tables = route_tables;
> > +            lsiv[index].route_policies = route_policies;
> >              ds_init(&lsiv[index].match);
> >              ds_init(&lsiv[index].actions);
> >
> > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> >              .features = features,
> >              .svc_check_match = svc_check_match,
> >              .svc_monitor_mac = svc_monitor_mac,
> > +            .parsed_routes = parsed_routes,
> > +            .route_tables = route_tables,
> > +            .route_policies = route_policies,
> >              .match = DS_EMPTY_INITIALIZER,
> >              .actions = DS_EMPTY_INITIALIZER,
> >          };
> > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> >                                                lsi.meter_groups,
> > +                                              lsi.bfd_connections,
> >                                                &lsi.match,
> >                                                &lsi.actions,
> >                                                lsi.lflows);
> > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> >                                      input_data->svc_monitor_map,
> >                                      input_data->bfd_connections,
> >                                      input_data->features,
> > -                                    input_data->svc_monitor_mac);
> > +                                    input_data->svc_monitor_mac,
> > +                                    input_data->parsed_routes,
> > +                                    input_data->route_policies,
> > +                                    input_data->route_tables);
> >
> >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> >          parallelization_state = STATE_USE_PARALLELIZATION;
> > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> >              build_lbnat_lflows_iterate_by_lrp(op,
> >                                                lflow_input->lr_stateful_table,
> >                                                lflow_input->meter_groups,
> > +                                              lflow_input->bfd_connections,
> >                                                &match, &actions,
> >                                                lflows);
> >
> > @@ -17490,6 +17754,27 @@ 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);
> > +}
> > +
> > +void
> > +static_routes_init(struct static_routes_data *data)
> > +{
> > +    hmap_init(&data->parsed_routes);
> > +    simap_init(&data->route_tables);
> > +    hmap_init(&data->bfd_active_connections);
> > +}
> > +
> > +void
> > +bfd_init(struct bfd_data *data)
> > +{
> > +    hmap_init(&data->bfd_connections);
> > +}
> > +
> >  void
> >  northd_destroy(struct northd_data *data)
> >  {
> > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> >      destroy_northd_tracked_data(data);
> >  }
> >
> > +static void
> > +__bfd_destroy(struct hmap *bfd_connections)
> > +{
> > +    struct bfd_entry *bfd_e;
> > +
> > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > +        bfd_erase_entry(bfd_e);
> > +    }
> > +    hmap_destroy(bfd_connections);
> > +}
> > +
> > +void
> > +bfd_destroy(struct bfd_data *data)
> > +{
> > +    __bfd_destroy(&data->bfd_connections);
> > +}
> > +
> > +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);
> > +}
> > +
> > +void
> > +static_routes_destroy(struct static_routes_data *data)
> > +{
> > +    struct parsed_route *r;
> > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > +        free(r);
> > +    }
> > +    hmap_destroy(&data->parsed_routes);
> > +
> > +    simap_destroy(&data->route_tables);
> > +    __bfd_destroy(&data->bfd_active_connections);
> > +}
> > +
> >  void
> >  ovnnb_db_run(struct northd_input *input_data,
> >               struct northd_data *data,
> > diff --git a/northd/northd.h b/northd/northd.h
> > index d4a8d75ab..432072f9f 100644
> > --- a/northd/northd.h
> > +++ b/northd/northd.h
> > @@ -23,6 +23,7 @@
> >  #include "northd/en-port-group.h"
> >  #include "northd/ipam.h"
> >  #include "openvswitch/hmap.h"
> > +#include "simap.h"
> >  #include "ovs-thread.h"
> >
> >  struct northd_input {
> > @@ -160,14 +161,34 @@ 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;
> > +    const struct nbrec_logical_router *nbr;
> > +    bool stale;
> > +};
> > +
> > +struct static_routes_data {
> > +    struct hmap parsed_routes;
> > +    struct simap route_tables;
> > +    struct hmap bfd_active_connections;
> > +};
> > +
> > +struct route_policies_data {
> > +    struct hmap route_policies;
> > +    struct hmap bfd_active_connections;
> > +};
> > +
> > +struct bfd_data {
> > +    struct hmap bfd_connections;
> > +};
> > +
> >  struct lr_nat_table;
> >
> >  struct lflow_input {
> > -    /* Northbound table references */
> > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > -
> >      /* Southbound table references */
> > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > @@ -190,6 +211,9 @@ struct lflow_input {
> >      const struct hmap *svc_monitor_map;
> >      bool ovn_internal_version_changed;
> >      const char *svc_monitor_mac;
> > +    struct hmap *parsed_routes;
> > +    struct hmap *route_policies;
> > +    struct simap *route_tables;
> >  };
> >
> >  extern int parallelization_state;
> > @@ -593,8 +617,6 @@ struct ovn_port {
> >
> >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> >
> > -    bool has_bfd;
> > -
> >      /* The port's peer:
> >       *
> >       *     - A switch port S of type "router" has a router port R as a peer,
> > @@ -653,6 +675,20 @@ struct ovn_port {
> >      struct lflow_ref *stateful_lflow_ref;
> >  };
> >
> > +struct parsed_route {
> > +    struct hmap_node key_node;
> > +    struct in6_addr prefix;
> > +    unsigned int plen;
> > +    bool is_src_route;
> > +    uint32_t route_table_id;
> > +    uint32_t hash;
> > +    const struct nbrec_logical_router_static_route *route;
> > +    bool ecmp_symmetric_reply;
> > +    bool is_discard_route;
> > +    const struct nbrec_logical_router *nbr;
> > +    bool stale;
> > +};
> > +
> >  void ovnnb_db_run(struct northd_input *input_data,
> >                    struct northd_data *data,
> >                    struct ovsdb_idl_txn *ovnnb_txn,
> > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > +                         struct hmap *, struct hmap *, struct simap *,
> > +                         struct hmap *);
> > +uint32_t get_route_table_id(struct simap *, const char *);
> > +void static_routes_init(struct static_routes_data *);
> > +void static_routes_destroy(struct static_routes_data *);
> > +
> > +void bfd_init(struct bfd_data *);
> > +void bfd_destroy(struct bfd_data *);
> > +
> >  struct lflow_table;
> >  struct lr_stateful_tracked_data;
> >  struct ls_stateful_tracked_data;
> > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> >                                     struct hmap *lbgrp_datapaths_map,
> >                                     struct northd_tracked_data *);
> >
> > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > -                     const struct nbrec_bfd_table *,
> > -                     const struct sbrec_bfd_table *,
> > -                     const struct hmap *lr_ports,
> > -                     struct hmap *bfd_connections);
> > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > -                             struct hmap *bfd_map);
> > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > +                          const struct hmap *, struct hmap *, struct hmap *);
> > +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 *, struct hmap *);
> > +void build_bfd_map(const struct nbrec_bfd_table *,
> > +                   const struct sbrec_bfd_table *, struct hmap *);
> >  void run_update_worker_pool(int n_threads);
> >
> >  const struct ovn_datapath *northd_get_datapath_for_port(
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 199197f09..710500dce 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> >      ovn-sbctl list meter >> $1
> >      ovn-sbctl list meter_band >> $1
> >      ovn-sbctl list port_group >> $1
> > +    ovn-sbctl list bfd >> $1
> >      ovn-sbctl dump-flows > lflows_$1
> >  ])
> >
> > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> >  done
> >
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> >                       min_rx=1000 min_tx=1000 status=admin_down
> >
> > +check_engine_stats northd norecompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> >
> > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> >
> > +check_engine_stats northd norecompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> >  wait_column down bfd status logical_port=r0-sw1
> >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> >
> > +check_engine_stats northd recompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats static_routes recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> >  wait_column down bfd status logical_port=r0-sw2
> >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> >  wait_column down bfd status logical_port=r0-sw5
> >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> >
> > +check_engine_stats northd recompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats static_routes recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> >  wait_column down bfd status logical_port=r0-sw6
> >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> >
> > +check_engine_stats northd recompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats route_policies recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> >  wait_column down bfd status logical_port=r0-sw7
> >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> >
> > +check_engine_stats northd recompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats static_routes recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  check ovn-nbctl lr-policy-del r0
> >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> >
> > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> >  wait_column down bfd status dst_ip=192.168.9.3
> >  wait_column down bfd status dst_ip=192.168.9.4
> >
> > +check_engine_stats northd recompute nocompute
> > +check_engine_stats bfd recompute nocompute
> > +check_engine_stats route_policies recompute nocompute
> > +check_engine_stats lflow recompute nocompute
> > +check_engine_stats northd_output norecompute compute
> > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > +
> >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> >
> > --
> > 2.45.2
> >
>
Han Zhou Aug. 6, 2024, 6:36 p.m. UTC | #3
On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
<lorenzo.bianconi@redhat.com> wrote:
>
> [...]
> > > -void
> > > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > -                        struct hmap *bfd_map)
> > > +static bool
> > > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> > >  {
> > > -    const struct nbrec_bfd *nb_bt;
> > >      struct bfd_entry *bfd_e;
> > > -
> > > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > > -        if (!bfd_e) {
> > > -            continue;
> > > -        }
> > > -
> > > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > > -            /* no user for this bfd connection */
> > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> >
> > I like the idea of checking the existence of the bfd entry to decide
> > if a port has bfd instead of relying on the has_bfd status. However, I
> > have performance concerns for this loop. The bfd_port_lookup()
> > searches by hashing both logical_port and dst_ip, but here for
> > logical_port it just iterates all bfd entries, which looks
> > unreasonable. It may be better to add a hindex for logical_port.
>
> Hi Han,
>
> I did not get what you mean here, can you please provide more details?
> Do you mean another hmap to link the logical_port name to the bfd_entry?
>

The goal is to avoid this O(n) iteration looking for a given
logical_port. Since multiple bfd entries can have the same
logical_port name, it would be better to use the struct hindex instead
of struct hmap to link logical_port name to the bfd_entry.

> >
> > > +        if (!strcmp(bfd_e->logical_port, port)) {
> > > +            return true;
> > >          }
> > >      }
> > > -
> > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > -        free(bfd_e);
> > > -    }
> > > +    return false;
> > >  }
> > >
> > > +
> > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > >  #define BFD_DEF_MINRX       1000 /* 1s */
> > >  #define BFD_DEF_DETECT_MULT 5
> > > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> > >      return port + BFD_UDP_SRC_PORT_START;
> > >  }
> > >
> > > -void
> > > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > > +static char *
> > > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > > +                          const struct hmap *rp_bfd_connections,
> > > +                          const struct hmap *sr_bfd_connections)
> > >  {
> > > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > > -    const struct sbrec_bfd *sb_bt;
> > > -    unsigned long *bfd_src_ports;
> > > -    struct bfd_entry *bfd_e;
> > > -    uint32_t hash;
> > > +    struct bfd_entry *bfd_rp, *bfd_sr;
> > >
> > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > > +                             nb_bt->dst_ip);
> > > +    if (!bfd_rp) {
> > > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > > +                                 nb_bt->dst_ip);
> > > +        if (!bfd_sr) {
> > > +            return "admin_down";
> > > +        }
> > > +    }
> > >
> > > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > -        bfd_e = xmalloc(sizeof *bfd_e);
> > > -        bfd_e->sb_bt = sb_bt;
> > > -        hash = hash_string(sb_bt->dst_ip, 0);
> > > -        hash = hash_string(sb_bt->logical_port, hash);
> > > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > > +}
> > > +
> > > +void
> > > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > > +               const struct hmap *lr_ports,
> > > +               const struct hmap *bfd_connections,
> > > +               const struct hmap *rp_bfd_connections,
> > > +               const struct hmap *sr_bfd_connections,
> > > +               struct hmap *sync_bfd_connections)
> > > +{
> > > +    if (!ovnsb_txn) {
> > > +        return;
> > > +    }
> > > +
> > > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > +
> > > +    struct bfd_entry *bfd_e;
> > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > > +                                              bfd_e->logical_port,
> > > +                                              bfd_e->dst_ip, bfd_e->status);
> >
> > It looks like we are blindly allocating a new entry for each entry in
> > bfd_connections without checking if it already exists. Did I
> > misunderstand?
>
> So far sync_bfd_connections always start from scratch and it is 1:1 with
> bfd_connections, so I guess an entry can't already exist. But I prefer to be
> more generic and check it. I will fix it.
>

What I meant was that now the sync_bfd_connections table is persistent
across I-P engine runs (I didn't see it get destroyed across
iterations but correct me if I am wrong), the above code just
allocates entries again and again every iteration without removing the
old ones. For simplicity we may just destroy old entries at the
beginning of this function because now we always do recompute. In the
future it may be optimized to incrementally consolidate new/old
entries. Does this make sense?

Thanks,
Han

> Regards,
> Lorenzo
>
> >
> > Thanks,
> > Han
> >
> > > +        e->nb_bt = bfd_e->nb_bt;
> > > +        e->sb_bt = bfd_e->sb_bt;
> > > +        e->stale = true;
> > > +        /* we need to check if this entry is even in the BFD nb db table */
> > > +        if (bfd_e->sb_bt) {
> > > +            bitmap_set1(bfd_src_ports,
> > > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > +        }
> > >      }
> > >
> > >      const struct nbrec_bfd *nb_bt;
> > >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > -        if (!nb_bt->status) {
> > > -            /* default state is admin_down */
> > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > > +                                nb_bt->dst_ip);
> > > +        if (!bfd_e) {
> > > +            continue;
> > >          }
> > >
> > >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > > -        if (!bfd_e) {
> > > +        if (!op || !op->sb) {
> > > +            /* skip not bounded ports */
> > > +            continue;
> > > +        }
> > > +
> > > +        nbrec_bfd_set_status(nb_bt,
> > > +                             bfd_get_connection_status(nb_bt,
> > > +                                                       rp_bfd_connections,
> > > +                                                       sr_bfd_connections));
> > > +        if (!bfd_e->sb_bt) {
> > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > >              if (udp_src < 0) {
> > >                  continue;
> > >              }
> > >
> > > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > +            /* Add entry to bfd sb table. */
> > > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> > >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> > >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> > >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> > >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > > -            if (op && op->sb && op->sb->chassis) {
> > > +            if (op->sb->chassis) {
> > >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> > >              }
> > >
> > > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> > >                  }
> > >              }
> > > +
> > >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > > -            if (op && op->sb && op->sb->chassis &&
> > > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > > +                                           bfd_e->sb_bt->chassis_name)) {
> > >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> > >                                             op->sb->chassis->name);
> > >              }
> > > -
> > > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > > -            bfd_e->ref = false;
> > > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> > >          }
> > >
> > > -        if (op) {
> > > -            op->has_bfd = true;
> > > -        }
> > > +        bfd_e->stale = false;
> > >      }
> > >
> > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > > -                                            bfd_e->sb_bt->logical_port);
> > > -        if (op) {
> > > -            op->has_bfd = false;
> > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > > +        if (bfd_e->stale) {
> > > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > > +            bfd_erase_entry(bfd_e);
> > >          }
> > > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > > -        free(bfd_e);
> > >      }
> > > -    hmap_destroy(&sb_only);
> > >
> > >      bitmap_free(bfd_src_ports);
> > >  }
> > >
> > > +void
> > > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > > +              struct hmap *bfd_connections)
> > > +{
> > > +    struct bfd_entry *bfd_e;
> > > +
> > > +    /* align bfd_map to sb db */
> > > +    const struct sbrec_bfd *sb_bt;
> > > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > > +                                sb_bt->dst_ip);
> > > +        if (!bfd_e) {
> > > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > > +                                    sb_bt->dst_ip,  sb_bt->status);
> > > +        }
> > > +        bfd_e->sb_bt = sb_bt;
> > > +    }
> > > +
> > > +    const struct nbrec_bfd *nb_bt;
> > > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > +                                nb_bt->dst_ip);
> > > +        if (!bfd_e) {
> > > +            /* brand new entry. */
> > > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > > +                                    nb_bt->dst_ip, "admin_down");
> > > +        }
> > > +        bfd_e->nb_bt = nb_bt;
> > > +    }
> > > +}
> > > +
> > >  /* Returns a string of the IP address of the router port 'op' that
> > >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> > >   *
> > > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> > >      return NULL;
> > >  }
> > >
> > > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > > -
> > > -static bool check_bfd_state(
> > > -        const struct nbrec_logical_router_policy *rule,
> > > -        const struct hmap *bfd_connections,
> > > -        struct ovn_port *out_port,
> > > -        const char *nexthop)
> > > +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);
> > > -    bool ret = true;
> > >
> > >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> > >          /* Check if there is a BFD session associated to the reroute
> > > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> > >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > >                                                    nb_bt->logical_port,
> > >                                                    nb_bt->dst_ip);
> > > -        ovs_mutex_lock(&bfd_lock);
> > > -        if (bfd_e) {
> > > -            bfd_e->ref = true;
> > > +        if (!bfd_e) {
> > > +            continue;
> > >          }
> > >
> > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > +        /* 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);
> > >          }
> > >
> > > -        ret = strcmp(nb_bt->status, "down");
> > > -        ovs_mutex_unlock(&bfd_lock);
> > > -        break;
> > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > +            bfd_set_status(bfd_rp, "down");
> > > +        }
> > > +
> > > +        return strcmp(bfd_rp->status, "down");
> > >      }
> > >
> > > -    return ret;
> > > +    return true;
> > >  }
> > >
> > >  static void
> > >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > -                          const struct hmap *lr_ports,
> > > -                          const struct nbrec_logical_router_policy *rule,
> > > -                          const struct hmap *bfd_connections,
> > > +                          const struct hmap *lr_ports, struct route_policy *rp,
> > >                            const struct ovsdb_idl_row *stage_hint,
> > >                            struct lflow_ref *lflow_ref)
> > >  {
> > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > >      struct ds match = DS_EMPTY_INITIALIZER;
> > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > >
> > >      if (!strcmp(rule->action, "reroute")) {
> > >          ovs_assert(rule->n_nexthops <= 1);
> > >
> > > -        char *nexthop =
> > > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > > +        if (!rp->n_valid_nexthops) {
> > > +            return;
> > > +        }
> > > +
> > > +        char *nexthop = rp->valid_nexthops[0];
> > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > >               od, lr_ports, rule->priority, nexthop);
> > >          if (!out_port) {
> > > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > >              return;
> > >          }
> > >
> > > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > > -            return;
> > > -        }
> > > -
> > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > >          if (pkt_mark) {
> > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > @@ -10070,19 +10156,18 @@ static void
> > >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > >                                  struct ovn_datapath *od,
> > >                                  const struct hmap *lr_ports,
> > > -                                const struct nbrec_logical_router_policy *rule,
> > > -                                const struct hmap *bfd_connections,
> > > +                                struct route_policy *rp,
> > >                                  uint16_t ecmp_group_id,
> > >                                  struct lflow_ref *lflow_ref)
> > >  {
> > > -    ovs_assert(rule->n_nexthops > 1);
> > > -
> > >      bool nexthops_is_ipv4 = true;
> > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > +    ovs_assert(rule->n_nexthops > 1);
> > >
> > >      /* Check that all the nexthops belong to the same addr family before
> > >       * adding logical flows. */
> > > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > >
> > >          if (i == 0) {
> > >              nexthops_is_ipv4 = is_ipv4;
> > > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> > >                           "the match [%s] do not belong to the same address "
> > >                           "family as other next hops",
> > > -                         rule->nexthops[i], rule->match);
> > > +                         rp->valid_nexthops[i], rule->match);
> > >              return;
> > >          }
> > >      }
> > > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > >      struct ds match = DS_EMPTY_INITIALIZER;
> > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > >
> > > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > > -    size_t n_valid_nexthops = 0;
> > > -
> > > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> > >          if (!out_port) {
> > >              goto cleanup;
> > >          }
> > >
> > >          const char *lrp_addr_s =
> > > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> > >          if (!lrp_addr_s) {
> > >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> > >                              " priority %"PRId64" nexthop %s",
> > > -                            rule->priority, rule->nexthops[i]);
> > > +                            rule->priority, rp->valid_nexthops[i]);
> > >              goto cleanup;
> > >          }
> > >
> > > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > > -                             rule->nexthops[i])) {
> > > -            continue;
> > > -        }
> > > -
> > > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > > -
> > >          ds_clear(&actions);
> > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > >          if (pkt_mark) {
> > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > >          }
> > >
> > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > >
> > >          ds_put_format(&actions, "%s = %s; "
> > >                        "%s = %s; "
> > > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > >                        "flags.loopback = 1; "
> > >                        "next;",
> > >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > > -                      rule->nexthops[i],
> > > +                      rp->valid_nexthops[i],
> > >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> > >                        lrp_addr_s,
> > >                        out_port->lrp_networks.ea_s,
> > > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > >                                  lflow_ref);
> > >      }
> > >
> > > -    if (!n_valid_nexthops) {
> > > -        goto cleanup;
> > > -    }
> > > -
> > >      ds_clear(&actions);
> > > -    if (n_valid_nexthops > 1) {
> > > +    if (rp->n_valid_nexthops > 1) {
> > >          ds_put_format(&actions, "%s = %"PRIu16
> > >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> > >                        REG_ECMP_MEMBER_ID);
> > >
> > > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > >              if (i > 0) {
> > >                  ds_put_cstr(&actions, ", ");
> > >              }
> > >
> > > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> > >          }
> > >          ds_put_cstr(&actions, ");");
> > >      } else {
> > >          ds_put_format(&actions, "%s = %"PRIu16
> > > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > > -                      valid_nexthops[0]);
> > > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> > >      }
> > >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> > >                              rule->priority, rule->match,
> > >                              ds_cstr(&actions), &rule->header_,
> > >                              lflow_ref);
> > > -
> > >  cleanup:
> > > -    free(valid_nexthops);
> > >      ds_destroy(&match);
> > >      ds_destroy(&actions);
> > >  }
> > > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> > >      return rtb_id;
> > >  }
> > >
> > > -static uint32_t
> > > +uint32_t
> > >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> > >  {
> > >      if (!route_table_name || !route_table_name[0]) {
> > > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> > >      ds_destroy(&actions);
> > >  }
> > >
> > > -struct parsed_route {
> > > -    struct ovs_list list_node;
> > > -    struct in6_addr prefix;
> > > -    unsigned int plen;
> > > -    bool is_src_route;
> > > -    uint32_t route_table_id;
> > > -    uint32_t hash;
> > > -    const struct nbrec_logical_router_static_route *route;
> > > -    bool ecmp_symmetric_reply;
> > > -    bool is_discard_route;
> > > -};
> > > -
> > >  static uint32_t
> > >  route_hash(struct parsed_route *route)
> > >  {
> > > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >
> > >  /* Parse and validate the route. Return the parsed route if successful.
> > >   * Otherwise return NULL. */
> > > +
> > >  static struct parsed_route *
> > > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > > +                    struct parsed_route *new_pr)
> > > +{
> > > +    struct parsed_route *pr;
> > > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > > +        if (pr->plen != new_pr->plen) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (pr->is_src_route != new_pr->is_src_route) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (pr->route_table_id != new_pr->route_table_id) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (pr->route != new_pr->route) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > > +            continue;
> > > +        }
> > > +
> > > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > > +            continue;
> > > +        }
> > > +
> > > +        return pr;
> > > +    }
> > > +
> > > +    return NULL;
> > > +}
> > > +
> > > +static void
> > >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > -                  struct ovs_list *routes, struct simap *route_tables,
> > >                    const struct nbrec_logical_router_static_route *route,
> > > -                  const struct hmap *bfd_connections)
> > > +                  struct hmap *bfd_connections,
> > > +                  struct hmap *routes, struct simap *route_tables,
> > > +                  struct hmap *bfd_active_connections)
> > >  {
> > >      /* Verify that the next hop is an IP address with an all-ones mask. */
> > >      struct in6_addr nexthop;
> > > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> > >                           UUID_FMT, route->nexthop,
> > >                           UUID_ARGS(&route->header_.uuid));
> > > -            return NULL;
> > > +            return;
> > >          }
> > >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> > >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> > >                           UUID_FMT, route->nexthop,
> > >                           UUID_ARGS(&route->header_.uuid));
> > > -            return NULL;
> > > +            return;
> > >          }
> > >      }
> > >
> > > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> > >                       UUID_FMT, route->ip_prefix,
> > >                       UUID_ARGS(&route->header_.uuid));
> > > -        return NULL;
> > > +        return;
> > >      }
> > >
> > >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> > >                           route->ip_prefix, route->nexthop,
> > >                           UUID_ARGS(&route->header_.uuid));
> > > -            return NULL;
> > > +            return;
> > >          }
> > >      }
> > >
> > > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > >          !find_static_route_outport(od, lr_ports, route,
> > >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> > >                                     NULL, NULL)) {
> > > -        return NULL;
> > > +        return;
> > >      }
> > >
> > >      const struct nbrec_bfd *nb_bt = route->bfd;
> > >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > > -        struct bfd_entry *bfd_e;
> > > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > +                                                  nb_bt->logical_port,
> > > +                                                  nb_bt->dst_ip);
> > > +        if (!bfd_e) {
> > > +            return;
> > > +        }
> > >
> > > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > -                                nb_bt->dst_ip);
> > > -        ovs_mutex_lock(&bfd_lock);
> > > -        if (bfd_e) {
> > > -            bfd_e->ref = true;
> > > +        /* This static route is linked to an active bfd session. */
> > > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > > +                                                   nb_bt->logical_port,
> > > +                                                   nb_bt->dst_ip);
> > > +        if (!bfd_sr) {
> > > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > > +                                     bfd_e->status);
> > >          }
> > >
> > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > +            bfd_set_status(bfd_sr, "down");
> > >          }
> > >
> > > -        if (!strcmp(nb_bt->status, "down")) {
> > > -            ovs_mutex_unlock(&bfd_lock);
> > > -            return NULL;
> > > +
> > > +        if (!strcmp(bfd_sr->status, "down")) {
> > > +            return;
> > >          }
> > > -        ovs_mutex_unlock(&bfd_lock);
> > >      }
> > >
> > > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > > -    pr->prefix = prefix;
> > > -    pr->plen = plen;
> > > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > > -                                                 "src-ip"));
> > > -    pr->hash = route_hash(pr);
> > > -    pr->route = route;
> > > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > -                                             "ecmp_symmetric_reply", false);
> > > -    pr->is_discard_route = is_discard_route;
> > > -    ovs_list_insert(routes, &pr->list_node);
> > > -    return pr;
> > > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > > +    new_pr->prefix = prefix;
> > > +    new_pr->plen = plen;
> > > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > > +                                                route->route_table);
> > > +    new_pr->is_src_route = (route->policy &&
> > > +                            !strcmp(route->policy, "src-ip"));
> > > +    new_pr->hash = route_hash(new_pr);
> > > +    new_pr->route = route;
> > > +    new_pr->nbr = od->nbr;
> > > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > +                                                 "ecmp_symmetric_reply",
> > > +                                                 false);
> > > +    new_pr->is_discard_route = is_discard_route;
> > > +
> > > +    size_t hash = uuid_hash(&od->key);
> > > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > > +    if (!pr) {
> > > +        hmap_insert(routes, &new_pr->key_node, hash);
> > > +    } else {
> > > +        pr->stale = false;
> > > +        free(new_pr);
> > > +    }
> > >  }
> > >
> > > -static void
> > > -parsed_routes_destroy(struct ovs_list *routes)
> > > +void
> > > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > +                    struct hmap *bfd_connections, struct hmap *routes,
> > > +                    struct simap *route_tables,
> > > +                    struct hmap *bfd_active_connections)
> > >  {
> > >      struct parsed_route *pr;
> > > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > > -        ovs_list_remove(&pr->list_node);
> > > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > > +        if (pr->nbr == od->nbr) {
> > > +            pr->stale = true;
> > > +        }
> > > +    }
> > > +
> > > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > > +                          bfd_connections, routes, route_tables,
> > > +                          bfd_active_connections);
> > > +    }
> > > +
> > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > +        if (!pr->stale) {
> > > +            continue;
> > > +        }
> > > +
> > > +        hmap_remove(routes, &pr->key_node);
> > >          free(pr);
> > >      }
> > >  }
> > > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > >            const struct ovn_port *op, const char *lrp_addr_s,
> > >            const char *network_s, int plen, const char *gateway,
> > >            bool is_src_route, const uint32_t rtb_id,
> > > +          const struct hmap *bfd_connections,
> > >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> > >            int ofs, struct lflow_ref *lflow_ref)
> > >  {
> > > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > >                              priority, ds_cstr(&match),
> > >                              ds_cstr(&actions), stage_hint,
> > >                              lflow_ref);
> > > -    if (op && op->has_bfd) {
> > > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> > >          ds_put_format(&match, " && udp.dst == 3784");
> > >          ovn_lflow_add_with_hint(lflows, op->od,
> > >                                  S_ROUTER_IN_IP_ROUTING,
> > > @@ -10895,6 +11030,7 @@ static void
> > >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > >                          const struct hmap *lr_ports,
> > >                          const struct parsed_route *route_,
> > > +                        const struct hmap *bfd_connections,
> > >                          struct lflow_ref *lflow_ref)
> > >  {
> > >      const char *lrp_addr_s = NULL;
> > > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> > >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> > >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > > -              route_->is_discard_route, ofs, lflow_ref);
> > > +              route_->is_src_route, route_->route_table_id,
> > > +              bfd_connections, &route->header_, route_->is_discard_route,
> > > +              ofs, lflow_ref);
> > >
> > >      free(prefix_s);
> > >  }
> > > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> > >  static void
> > >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> > >                          const struct shash *meter_groups,
> > > +                        const struct hmap *bfd_connections,
> > >                          struct lflow_ref *lflow_ref)
> > >  {
> > > -    if (!op->has_bfd) {
> > > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> > >          return;
> > >      }
> > >
> > > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> > >   * LRP 'op'.
> > >   */
> > >  static void
> > > -build_ip_routing_flows_for_lrp(
> > > -        struct ovn_port *op, struct lflow_table *lflows,
> > > -        struct lflow_ref *lflow_ref)
> > > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > > +                               const struct hmap *bfd_connections,
> > > +                               struct lflow_table *lflows,
> > > +                               struct lflow_ref *lflow_ref)
> > >  {
> > >      ovs_assert(op->nbrp);
> > >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> > >                    op->lrp_networks.ipv4_addrs[i].network_s,
> > >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > -                  lflow_ref);
> > > +                  bfd_connections, &op->nbrp->header_, false,
> > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > >      }
> > >
> > >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> > >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> > >                    op->lrp_networks.ipv6_addrs[i].network_s,
> > >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > -                  lflow_ref);
> > > +                  bfd_connections, &op->nbrp->header_, false,
> > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > >      }
> > >  }
> > >
> > >  static void
> > >  build_static_route_flows_for_lrouter(
> > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > > +        struct simap *route_tables, const struct hmap *bfd_connections,
> > >          struct lflow_ref *lflow_ref)
> > >  {
> > >      ovs_assert(od->nbr);
> > > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> > >
> > >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> > >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> > >      struct ecmp_groups_node *group;
> > >
> > >      for (int i = 0; i < od->nbr->n_ports; i++) {
> > >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > > -                                &route_tables, lflow_ref);
> > > +                                route_tables, lflow_ref);
> > >      }
> > >
> > > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > -        struct parsed_route *route =
> > > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > > -                              od->nbr->static_routes[i], bfd_connections);
> > > -        if (!route) {
> > > -            continue;
> > > -        }
> > > +    struct parsed_route *route;
> > > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > > +                             parsed_routes) {
> > >          group = ecmp_groups_find(&ecmp_groups, route);
> > >          if (group) {
> > >              ecmp_groups_add_route(group, route);
> > > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> > >      }
> > >      const struct unique_routes_node *ur;
> > >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > > +                                bfd_connections, lflow_ref);
> > >      }
> > >      ecmp_groups_destroy(&ecmp_groups);
> > >      unique_routes_destroy(&unique_routes);
> > > -    parsed_routes_destroy(&parsed_routes);
> > > -    simap_destroy(&route_tables);
> > >  }
> > >
> > >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> > >      }
> > >  }
> > >
> > > +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->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;
> > > +}
> > > +
> > > +void
> > > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > +                     const struct hmap *bfd_connections,
> > > +                     struct hmap *route_policies,
> > > +                     struct hmap *bfd_active_connections)
> > > +{
> > > +    struct route_policy *rp;
> > > +
> > > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > > +        if (rp->nbr == od->nbr) {
> > > +            rp->stale = true;
> > > +        }
> > > +    }
> > > +
> > > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > > +        size_t n_valid_nexthops = 0;
> > > +        char **valid_nexthops = NULL;
> > > +
> > > +        if (!strcmp(rule->action, "reroute")) {
> > > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > > +
> > > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > > +            for (size_t j = 0; j < n_nexthops; j++) {
> > > +                char *nexthop = rule->n_nexthops
> > > +                    ? rule->nexthops[j] : rule->nexthop;
> > > +                struct ovn_port *out_port =
> > > +                    get_outport_for_routing_policy_nexthop(
> > > +                            od, lr_ports, rule->priority, nexthop);
> > > +                if (!out_port || !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->nbr = od->nbr;
> > > +
> > > +        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 {
> > > +            rp->stale = false;
> > > +            free(valid_nexthops);
> > > +            free(new_rp);
> > > +        }
> > > +    }
> > > +
> > > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > > +        if (!rp->stale) {
> > > +            continue;
> > > +        }
> > > +
> > > +        hmap_remove(route_policies, &rp->key_node);
> > > +        free(rp->valid_nexthops);
> > > +        free(rp);
> > > +    }
> > > +}
> > > +
> > >  /* Logical router ingress table POLICY: Policy.
> > >   *
> > >   * A packet that arrives at this table is an IP packet that should be
> > > @@ -12862,7 +13104,7 @@ static void
> > >  build_ingress_policy_flows_for_lrouter(
> > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > >          const struct hmap *lr_ports,
> > > -        const struct hmap *bfd_connections,
> > > +        struct hmap *route_policies,
> > >          struct lflow_ref *lflow_ref)
> > >  {
> > >      ovs_assert(od->nbr);
> > > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> > >
> > >      /* Convert routing policies to flows. */
> > >      uint16_t ecmp_group_id = 1;
> > > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > -        const struct nbrec_logical_router_policy *rule
> > > -            = od->nbr->policies[i];
> > > +    struct route_policy *rp;
> > > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > > +                             route_policies) {
> > > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> > >          bool is_ecmp_reroute =
> > >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> > >
> > >          if (is_ecmp_reroute) {
> > > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > > -                                            bfd_connections, ecmp_group_id,
> > > -                                            lflow_ref);
> > > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > > +                                            ecmp_group_id, lflow_ref);
> > >              ecmp_group_id++;
> > >          } else {
> > > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > > -                                      bfd_connections, &rule->header_,
> > > -                                      lflow_ref);
> > > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > > +                                      &rule->header_, lflow_ref);
> > >          }
> > >      }
> > >  }
> > > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > >                              struct lflow_table *lflows,
> > >                              struct ds *match, struct ds *actions,
> > >                              const struct shash *meter_groups,
> > > +                            const struct hmap *bfd_connections,
> > >                              struct lflow_ref *lflow_ref)
> > >  {
> > >      ovs_assert(op->nbrp);
> > > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > >      }
> > >
> > >      /* BFD msg handling */
> > > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > > +                            lflow_ref);
> > >
> > >      /* ICMP time exceeded */
> > >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> > >  static void
> > >  build_routable_flows_for_router_port(
> > >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > > +    const struct hmap *bfd_connections,
> > >      struct lflow_table *lflows,
> > >      struct ds *match,
> > >      struct ds *actions)
> > > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> > >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> > >                                laddrs->ipv4_addrs[k].network_s,
> > >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > > -                              &router_port->nbrp->header_, false,
> > > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > > +                              bfd_connections, &router_port->nbrp->header_,
> > > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> > >                                lrp->stateful_lflow_ref);
> > >                  }
> > >              }
> > > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> > >  static void
> > >  build_lbnat_lflows_iterate_by_lrp(
> > >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > > -    const struct shash *meter_groups, struct ds *match,
> > > -    struct ds *actions, struct lflow_table *lflows)
> > > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> > >  {
> > >      ovs_assert(op->nbrp);
> > >
> > > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> > >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> > >                                  actions, lflows);
> > >
> > > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > > -                                         actions);
> > > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > > +                                         lflows, match, actions);
> > >  }
> > >
> > >  static void
> > > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> > >      struct ds actions;
> > >      size_t thread_lflow_counter;
> > >      const char *svc_monitor_mac;
> > > +    struct hmap *parsed_routes;
> > > +    struct hmap *route_policies;
> > > +    struct simap *route_tables;
> > >  };
> > >
> > >  /* Helper function to combine all lflow generation which is iterated by
> > > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> > >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > -                                         lsi->bfd_connections,
> > > -                                         NULL);
> > > +                                         lsi->parsed_routes, lsi->route_tables,
> > > +                                         lsi->bfd_connections, NULL);
> > >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> > >                                           &lsi->actions, NULL);
> > >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > -                                           lsi->bfd_connections, NULL);
> > > +                                           lsi->route_policies, NULL);
> > >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> > >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > >                                            &lsi->match, &lsi->actions,
> > > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > >                                            &lsi->actions, op->lflow_ref);
> > >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > >                                                  &lsi->actions, op->lflow_ref);
> > > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > > +                                   lsi->lflows, op->lflow_ref);
> > >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > >                                         &lsi->actions, lsi->meter_groups,
> > >                                         op->lflow_ref);
> > > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > >                                              lsi->meter_groups,
> > >                                              op->lflow_ref);
> > >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > > -                                lsi->meter_groups, op->lflow_ref);
> > > +                                lsi->meter_groups, lsi->bfd_connections,
> > > +                                op->lflow_ref);
> > >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> > >                                                   &lsi->actions, op->lflow_ref);
> > >  }
> > > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> > >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> > >                      build_lbnat_lflows_iterate_by_lrp(
> > >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > > +                        lsi->lflows);
> > >                  }
> > >              }
> > >              for (bnum = control->id;
> > > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> > >      const struct hmap *svc_monitor_map,
> > >      const struct hmap *bfd_connections,
> > >      const struct chassis_features *features,
> > > -    const char *svc_monitor_mac)
> > > +    const char *svc_monitor_mac,
> > > +    struct hmap *parsed_routes,
> > > +    struct hmap *route_policies,
> > > +    struct simap *route_tables)
> > >  {
> > >
> > >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> > >              lsiv[index].svc_check_match = svc_check_match;
> > >              lsiv[index].thread_lflow_counter = 0;
> > >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > > +            lsiv[index].parsed_routes = parsed_routes;
> > > +            lsiv[index].route_tables = route_tables;
> > > +            lsiv[index].route_policies = route_policies;
> > >              ds_init(&lsiv[index].match);
> > >              ds_init(&lsiv[index].actions);
> > >
> > > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> > >              .features = features,
> > >              .svc_check_match = svc_check_match,
> > >              .svc_monitor_mac = svc_monitor_mac,
> > > +            .parsed_routes = parsed_routes,
> > > +            .route_tables = route_tables,
> > > +            .route_policies = route_policies,
> > >              .match = DS_EMPTY_INITIALIZER,
> > >              .actions = DS_EMPTY_INITIALIZER,
> > >          };
> > > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> > >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> > >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> > >                                                lsi.meter_groups,
> > > +                                              lsi.bfd_connections,
> > >                                                &lsi.match,
> > >                                                &lsi.actions,
> > >                                                lsi.lflows);
> > > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> > >                                      input_data->svc_monitor_map,
> > >                                      input_data->bfd_connections,
> > >                                      input_data->features,
> > > -                                    input_data->svc_monitor_mac);
> > > +                                    input_data->svc_monitor_mac,
> > > +                                    input_data->parsed_routes,
> > > +                                    input_data->route_policies,
> > > +                                    input_data->route_tables);
> > >
> > >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> > >          parallelization_state = STATE_USE_PARALLELIZATION;
> > > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> > >              build_lbnat_lflows_iterate_by_lrp(op,
> > >                                                lflow_input->lr_stateful_table,
> > >                                                lflow_input->meter_groups,
> > > +                                              lflow_input->bfd_connections,
> > >                                                &match, &actions,
> > >                                                lflows);
> > >
> > > @@ -17490,6 +17754,27 @@ 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);
> > > +}
> > > +
> > > +void
> > > +static_routes_init(struct static_routes_data *data)
> > > +{
> > > +    hmap_init(&data->parsed_routes);
> > > +    simap_init(&data->route_tables);
> > > +    hmap_init(&data->bfd_active_connections);
> > > +}
> > > +
> > > +void
> > > +bfd_init(struct bfd_data *data)
> > > +{
> > > +    hmap_init(&data->bfd_connections);
> > > +}
> > > +
> > >  void
> > >  northd_destroy(struct northd_data *data)
> > >  {
> > > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> > >      destroy_northd_tracked_data(data);
> > >  }
> > >
> > > +static void
> > > +__bfd_destroy(struct hmap *bfd_connections)
> > > +{
> > > +    struct bfd_entry *bfd_e;
> > > +
> > > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > > +        bfd_erase_entry(bfd_e);
> > > +    }
> > > +    hmap_destroy(bfd_connections);
> > > +}
> > > +
> > > +void
> > > +bfd_destroy(struct bfd_data *data)
> > > +{
> > > +    __bfd_destroy(&data->bfd_connections);
> > > +}
> > > +
> > > +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);
> > > +}
> > > +
> > > +void
> > > +static_routes_destroy(struct static_routes_data *data)
> > > +{
> > > +    struct parsed_route *r;
> > > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > > +        free(r);
> > > +    }
> > > +    hmap_destroy(&data->parsed_routes);
> > > +
> > > +    simap_destroy(&data->route_tables);
> > > +    __bfd_destroy(&data->bfd_active_connections);
> > > +}
> > > +
> > >  void
> > >  ovnnb_db_run(struct northd_input *input_data,
> > >               struct northd_data *data,
> > > diff --git a/northd/northd.h b/northd/northd.h
> > > index d4a8d75ab..432072f9f 100644
> > > --- a/northd/northd.h
> > > +++ b/northd/northd.h
> > > @@ -23,6 +23,7 @@
> > >  #include "northd/en-port-group.h"
> > >  #include "northd/ipam.h"
> > >  #include "openvswitch/hmap.h"
> > > +#include "simap.h"
> > >  #include "ovs-thread.h"
> > >
> > >  struct northd_input {
> > > @@ -160,14 +161,34 @@ 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;
> > > +    const struct nbrec_logical_router *nbr;
> > > +    bool stale;
> > > +};
> > > +
> > > +struct static_routes_data {
> > > +    struct hmap parsed_routes;
> > > +    struct simap route_tables;
> > > +    struct hmap bfd_active_connections;
> > > +};
> > > +
> > > +struct route_policies_data {
> > > +    struct hmap route_policies;
> > > +    struct hmap bfd_active_connections;
> > > +};
> > > +
> > > +struct bfd_data {
> > > +    struct hmap bfd_connections;
> > > +};
> > > +
> > >  struct lr_nat_table;
> > >
> > >  struct lflow_input {
> > > -    /* Northbound table references */
> > > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > > -
> > >      /* Southbound table references */
> > > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> > >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> > >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> > >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > > @@ -190,6 +211,9 @@ struct lflow_input {
> > >      const struct hmap *svc_monitor_map;
> > >      bool ovn_internal_version_changed;
> > >      const char *svc_monitor_mac;
> > > +    struct hmap *parsed_routes;
> > > +    struct hmap *route_policies;
> > > +    struct simap *route_tables;
> > >  };
> > >
> > >  extern int parallelization_state;
> > > @@ -593,8 +617,6 @@ struct ovn_port {
> > >
> > >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> > >
> > > -    bool has_bfd;
> > > -
> > >      /* The port's peer:
> > >       *
> > >       *     - A switch port S of type "router" has a router port R as a peer,
> > > @@ -653,6 +675,20 @@ struct ovn_port {
> > >      struct lflow_ref *stateful_lflow_ref;
> > >  };
> > >
> > > +struct parsed_route {
> > > +    struct hmap_node key_node;
> > > +    struct in6_addr prefix;
> > > +    unsigned int plen;
> > > +    bool is_src_route;
> > > +    uint32_t route_table_id;
> > > +    uint32_t hash;
> > > +    const struct nbrec_logical_router_static_route *route;
> > > +    bool ecmp_symmetric_reply;
> > > +    bool is_discard_route;
> > > +    const struct nbrec_logical_router *nbr;
> > > +    bool stale;
> > > +};
> > > +
> > >  void ovnnb_db_run(struct northd_input *input_data,
> > >                    struct northd_data *data,
> > >                    struct ovsdb_idl_txn *ovnnb_txn,
> > > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > > +                         struct hmap *, struct hmap *, struct simap *,
> > > +                         struct hmap *);
> > > +uint32_t get_route_table_id(struct simap *, const char *);
> > > +void static_routes_init(struct static_routes_data *);
> > > +void static_routes_destroy(struct static_routes_data *);
> > > +
> > > +void bfd_init(struct bfd_data *);
> > > +void bfd_destroy(struct bfd_data *);
> > > +
> > >  struct lflow_table;
> > >  struct lr_stateful_tracked_data;
> > >  struct ls_stateful_tracked_data;
> > > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> > >                                     struct hmap *lbgrp_datapaths_map,
> > >                                     struct northd_tracked_data *);
> > >
> > > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > -                     const struct nbrec_bfd_table *,
> > > -                     const struct sbrec_bfd_table *,
> > > -                     const struct hmap *lr_ports,
> > > -                     struct hmap *bfd_connections);
> > > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > > -                             struct hmap *bfd_map);
> > > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > > +                          const struct hmap *, struct hmap *, struct hmap *);
> > > +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 *, struct hmap *);
> > > +void build_bfd_map(const struct nbrec_bfd_table *,
> > > +                   const struct sbrec_bfd_table *, struct hmap *);
> > >  void run_update_worker_pool(int n_threads);
> > >
> > >  const struct ovn_datapath *northd_get_datapath_for_port(
> > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > index 199197f09..710500dce 100644
> > > --- a/tests/ovn-northd.at
> > > +++ b/tests/ovn-northd.at
> > > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> > >      ovn-sbctl list meter >> $1
> > >      ovn-sbctl list meter_band >> $1
> > >      ovn-sbctl list port_group >> $1
> > > +    ovn-sbctl list bfd >> $1
> > >      ovn-sbctl dump-flows > lflows_$1
> > >  ])
> > >
> > > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> > >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> > >  done
> > >
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> > >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> > >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> > >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> > >                       min_rx=1000 min_tx=1000 status=admin_down
> > >
> > > +check_engine_stats northd norecompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> > >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> > >
> > > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> > >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> > >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> > >
> > > +check_engine_stats northd norecompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> > >  wait_column down bfd status logical_port=r0-sw1
> > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> > >
> > > +check_engine_stats northd recompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats static_routes recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> > >  wait_column down bfd status logical_port=r0-sw2
> > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> > >  wait_column down bfd status logical_port=r0-sw5
> > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> > >
> > > +check_engine_stats northd recompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats static_routes recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> > >  wait_column down bfd status logical_port=r0-sw6
> > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> > >
> > > +check_engine_stats northd recompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats route_policies recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> > >  wait_column down bfd status logical_port=r0-sw7
> > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> > >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> > >
> > > +check_engine_stats northd recompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats static_routes recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  check ovn-nbctl lr-policy-del r0
> > >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> > >
> > > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> > >  wait_column down bfd status dst_ip=192.168.9.3
> > >  wait_column down bfd status dst_ip=192.168.9.4
> > >
> > > +check_engine_stats northd recompute nocompute
> > > +check_engine_stats bfd recompute nocompute
> > > +check_engine_stats route_policies recompute nocompute
> > > +check_engine_stats lflow recompute nocompute
> > > +check_engine_stats northd_output norecompute compute
> > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > +
> > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> > >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> > >
> > > --
> > > 2.45.2
> > >
> >
Lorenzo Bianconi Aug. 7, 2024, 5:29 p.m. UTC | #4
> On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
> <lorenzo.bianconi@redhat.com> wrote:
> >
> > [...]
> > > > -void
> > > > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > -                        struct hmap *bfd_map)
> > > > +static bool
> > > > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> > > >  {
> > > > -    const struct nbrec_bfd *nb_bt;
> > > >      struct bfd_entry *bfd_e;
> > > > -
> > > > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > > > -        if (!bfd_e) {
> > > > -            continue;
> > > > -        }
> > > > -
> > > > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > > > -            /* no user for this bfd connection */
> > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> > >
> > > I like the idea of checking the existence of the bfd entry to decide
> > > if a port has bfd instead of relying on the has_bfd status. However, I
> > > have performance concerns for this loop. The bfd_port_lookup()
> > > searches by hashing both logical_port and dst_ip, but here for
> > > logical_port it just iterates all bfd entries, which looks
> > > unreasonable. It may be better to add a hindex for logical_port.
> >
> > Hi Han,
> >
> > I did not get what you mean here, can you please provide more details?
> > Do you mean another hmap to link the logical_port name to the bfd_entry?
> >
> 
> The goal is to avoid this O(n) iteration looking for a given
> logical_port. Since multiple bfd entries can have the same
> logical_port name, it would be better to use the struct hindex instead
> of struct hmap to link logical_port name to the bfd_entry.

ack, fine. Since in this case we just need to know if the logical_port is used
in at least one bfd_entry, I guess a simap to count number of bfd_entry for a
given logical port is enough, what do you think?

> 
> > >
> > > > +        if (!strcmp(bfd_e->logical_port, port)) {
> > > > +            return true;
> > > >          }
> > > >      }
> > > > -
> > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > > -        free(bfd_e);
> > > > -    }
> > > > +    return false;
> > > >  }
> > > >
> > > > +
> > > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > > >  #define BFD_DEF_MINRX       1000 /* 1s */
> > > >  #define BFD_DEF_DETECT_MULT 5
> > > > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> > > >      return port + BFD_UDP_SRC_PORT_START;
> > > >  }
> > > >
> > > > -void
> > > > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > > > +static char *
> > > > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > > > +                          const struct hmap *rp_bfd_connections,
> > > > +                          const struct hmap *sr_bfd_connections)
> > > >  {
> > > > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > > > -    const struct sbrec_bfd *sb_bt;
> > > > -    unsigned long *bfd_src_ports;
> > > > -    struct bfd_entry *bfd_e;
> > > > -    uint32_t hash;
> > > > +    struct bfd_entry *bfd_rp, *bfd_sr;
> > > >
> > > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > > > +                             nb_bt->dst_ip);
> > > > +    if (!bfd_rp) {
> > > > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > > > +                                 nb_bt->dst_ip);
> > > > +        if (!bfd_sr) {
> > > > +            return "admin_down";
> > > > +        }
> > > > +    }
> > > >
> > > > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > -        bfd_e = xmalloc(sizeof *bfd_e);
> > > > -        bfd_e->sb_bt = sb_bt;
> > > > -        hash = hash_string(sb_bt->dst_ip, 0);
> > > > -        hash = hash_string(sb_bt->logical_port, hash);
> > > > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > > > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > > > +}
> > > > +
> > > > +void
> > > > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > > > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > +               const struct hmap *lr_ports,
> > > > +               const struct hmap *bfd_connections,
> > > > +               const struct hmap *rp_bfd_connections,
> > > > +               const struct hmap *sr_bfd_connections,
> > > > +               struct hmap *sync_bfd_connections)
> > > > +{
> > > > +    if (!ovnsb_txn) {
> > > > +        return;
> > > > +    }
> > > > +
> > > > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > +
> > > > +    struct bfd_entry *bfd_e;
> > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > > > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > > > +                                              bfd_e->logical_port,
> > > > +                                              bfd_e->dst_ip, bfd_e->status);
> > >
> > > It looks like we are blindly allocating a new entry for each entry in
> > > bfd_connections without checking if it already exists. Did I
> > > misunderstand?
> >
> > So far sync_bfd_connections always start from scratch and it is 1:1 with
> > bfd_connections, so I guess an entry can't already exist. But I prefer to be
> > more generic and check it. I will fix it.
> >
> 
> What I meant was that now the sync_bfd_connections table is persistent
> across I-P engine runs (I didn't see it get destroyed across
> iterations but correct me if I am wrong), the above code just
> allocates entries again and again every iteration without removing the
> old ones. For simplicity we may just destroy old entries at the
> beginning of this function because now we always do recompute. In the
> future it may be optimized to incrementally consolidate new/old
> entries. Does this make sense?


sync_bfd_connections is destroyed at the beginning of en_bfd_sync_run() so I
guess we already have what you are suggesting, right? Anyway we can still use
bfd_port_lookup() to check if an entry has been already allocated. What do you
prefer?

Regards,
Lorenzo

> 
> Thanks,
> Han
> 
> > Regards,
> > Lorenzo
> >
> > >
> > > Thanks,
> > > Han
> > >
> > > > +        e->nb_bt = bfd_e->nb_bt;
> > > > +        e->sb_bt = bfd_e->sb_bt;
> > > > +        e->stale = true;
> > > > +        /* we need to check if this entry is even in the BFD nb db table */
> > > > +        if (bfd_e->sb_bt) {
> > > > +            bitmap_set1(bfd_src_ports,
> > > > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > +        }
> > > >      }
> > > >
> > > >      const struct nbrec_bfd *nb_bt;
> > > >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > -        if (!nb_bt->status) {
> > > > -            /* default state is admin_down */
> > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > > > +                                nb_bt->dst_ip);
> > > > +        if (!bfd_e) {
> > > > +            continue;
> > > >          }
> > > >
> > > >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > > > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > > > -        if (!bfd_e) {
> > > > +        if (!op || !op->sb) {
> > > > +            /* skip not bounded ports */
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        nbrec_bfd_set_status(nb_bt,
> > > > +                             bfd_get_connection_status(nb_bt,
> > > > +                                                       rp_bfd_connections,
> > > > +                                                       sr_bfd_connections));
> > > > +        if (!bfd_e->sb_bt) {
> > > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > > >              if (udp_src < 0) {
> > > >                  continue;
> > > >              }
> > > >
> > > > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > +            /* Add entry to bfd sb table. */
> > > > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> > > >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> > > >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> > > >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> > > >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > > > -            if (op && op->sb && op->sb->chassis) {
> > > > +            if (op->sb->chassis) {
> > > >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> > > >              }
> > > >
> > > > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> > > >                  }
> > > >              }
> > > > +
> > > >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > > > -            if (op && op->sb && op->sb->chassis &&
> > > > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > > > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > > > +                                           bfd_e->sb_bt->chassis_name)) {
> > > >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> > > >                                             op->sb->chassis->name);
> > > >              }
> > > > -
> > > > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > > > -            bfd_e->ref = false;
> > > > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > > > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > > > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> > > >          }
> > > >
> > > > -        if (op) {
> > > > -            op->has_bfd = true;
> > > > -        }
> > > > +        bfd_e->stale = false;
> > > >      }
> > > >
> > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > > > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > > > -                                            bfd_e->sb_bt->logical_port);
> > > > -        if (op) {
> > > > -            op->has_bfd = false;
> > > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > > > +        if (bfd_e->stale) {
> > > > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > > > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > > > +            bfd_erase_entry(bfd_e);
> > > >          }
> > > > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > > > -        free(bfd_e);
> > > >      }
> > > > -    hmap_destroy(&sb_only);
> > > >
> > > >      bitmap_free(bfd_src_ports);
> > > >  }
> > > >
> > > > +void
> > > > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > +              struct hmap *bfd_connections)
> > > > +{
> > > > +    struct bfd_entry *bfd_e;
> > > > +
> > > > +    /* align bfd_map to sb db */
> > > > +    const struct sbrec_bfd *sb_bt;
> > > > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > > > +                                sb_bt->dst_ip);
> > > > +        if (!bfd_e) {
> > > > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > > > +                                    sb_bt->dst_ip,  sb_bt->status);
> > > > +        }
> > > > +        bfd_e->sb_bt = sb_bt;
> > > > +    }
> > > > +
> > > > +    const struct nbrec_bfd *nb_bt;
> > > > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > +                                nb_bt->dst_ip);
> > > > +        if (!bfd_e) {
> > > > +            /* brand new entry. */
> > > > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > > > +                                    nb_bt->dst_ip, "admin_down");
> > > > +        }
> > > > +        bfd_e->nb_bt = nb_bt;
> > > > +    }
> > > > +}
> > > > +
> > > >  /* Returns a string of the IP address of the router port 'op' that
> > > >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> > > >   *
> > > > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> > > >      return NULL;
> > > >  }
> > > >
> > > > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > > > -
> > > > -static bool check_bfd_state(
> > > > -        const struct nbrec_logical_router_policy *rule,
> > > > -        const struct hmap *bfd_connections,
> > > > -        struct ovn_port *out_port,
> > > > -        const char *nexthop)
> > > > +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);
> > > > -    bool ret = true;
> > > >
> > > >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> > > >          /* Check if there is a BFD session associated to the reroute
> > > > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> > > >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > >                                                    nb_bt->logical_port,
> > > >                                                    nb_bt->dst_ip);
> > > > -        ovs_mutex_lock(&bfd_lock);
> > > > -        if (bfd_e) {
> > > > -            bfd_e->ref = true;
> > > > +        if (!bfd_e) {
> > > > +            continue;
> > > >          }
> > > >
> > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > +        /* 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);
> > > >          }
> > > >
> > > > -        ret = strcmp(nb_bt->status, "down");
> > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > -        break;
> > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > +            bfd_set_status(bfd_rp, "down");
> > > > +        }
> > > > +
> > > > +        return strcmp(bfd_rp->status, "down");
> > > >      }
> > > >
> > > > -    return ret;
> > > > +    return true;
> > > >  }
> > > >
> > > >  static void
> > > >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > -                          const struct hmap *lr_ports,
> > > > -                          const struct nbrec_logical_router_policy *rule,
> > > > -                          const struct hmap *bfd_connections,
> > > > +                          const struct hmap *lr_ports, struct route_policy *rp,
> > > >                            const struct ovsdb_idl_row *stage_hint,
> > > >                            struct lflow_ref *lflow_ref)
> > > >  {
> > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > >
> > > >      if (!strcmp(rule->action, "reroute")) {
> > > >          ovs_assert(rule->n_nexthops <= 1);
> > > >
> > > > -        char *nexthop =
> > > > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > > > +        if (!rp->n_valid_nexthops) {
> > > > +            return;
> > > > +        }
> > > > +
> > > > +        char *nexthop = rp->valid_nexthops[0];
> > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > >               od, lr_ports, rule->priority, nexthop);
> > > >          if (!out_port) {
> > > > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > >              return;
> > > >          }
> > > >
> > > > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > > > -            return;
> > > > -        }
> > > > -
> > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > >          if (pkt_mark) {
> > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > @@ -10070,19 +10156,18 @@ static void
> > > >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > >                                  struct ovn_datapath *od,
> > > >                                  const struct hmap *lr_ports,
> > > > -                                const struct nbrec_logical_router_policy *rule,
> > > > -                                const struct hmap *bfd_connections,
> > > > +                                struct route_policy *rp,
> > > >                                  uint16_t ecmp_group_id,
> > > >                                  struct lflow_ref *lflow_ref)
> > > >  {
> > > > -    ovs_assert(rule->n_nexthops > 1);
> > > > -
> > > >      bool nexthops_is_ipv4 = true;
> > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > +    ovs_assert(rule->n_nexthops > 1);
> > > >
> > > >      /* Check that all the nexthops belong to the same addr family before
> > > >       * adding logical flows. */
> > > > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > >
> > > >          if (i == 0) {
> > > >              nexthops_is_ipv4 = is_ipv4;
> > > > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> > > >                           "the match [%s] do not belong to the same address "
> > > >                           "family as other next hops",
> > > > -                         rule->nexthops[i], rule->match);
> > > > +                         rp->valid_nexthops[i], rule->match);
> > > >              return;
> > > >          }
> > > >      }
> > > > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > >
> > > > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > > > -    size_t n_valid_nexthops = 0;
> > > > -
> > > > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > > > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > > > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> > > >          if (!out_port) {
> > > >              goto cleanup;
> > > >          }
> > > >
> > > >          const char *lrp_addr_s =
> > > > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > > > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> > > >          if (!lrp_addr_s) {
> > > >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > > >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> > > >                              " priority %"PRId64" nexthop %s",
> > > > -                            rule->priority, rule->nexthops[i]);
> > > > +                            rule->priority, rp->valid_nexthops[i]);
> > > >              goto cleanup;
> > > >          }
> > > >
> > > > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > > > -                             rule->nexthops[i])) {
> > > > -            continue;
> > > > -        }
> > > > -
> > > > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > > > -
> > > >          ds_clear(&actions);
> > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > >          if (pkt_mark) {
> > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > >          }
> > > >
> > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > >
> > > >          ds_put_format(&actions, "%s = %s; "
> > > >                        "%s = %s; "
> > > > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > >                        "flags.loopback = 1; "
> > > >                        "next;",
> > > >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > > > -                      rule->nexthops[i],
> > > > +                      rp->valid_nexthops[i],
> > > >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> > > >                        lrp_addr_s,
> > > >                        out_port->lrp_networks.ea_s,
> > > > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > >                                  lflow_ref);
> > > >      }
> > > >
> > > > -    if (!n_valid_nexthops) {
> > > > -        goto cleanup;
> > > > -    }
> > > > -
> > > >      ds_clear(&actions);
> > > > -    if (n_valid_nexthops > 1) {
> > > > +    if (rp->n_valid_nexthops > 1) {
> > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> > > >                        REG_ECMP_MEMBER_ID);
> > > >
> > > > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > > > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > >              if (i > 0) {
> > > >                  ds_put_cstr(&actions, ", ");
> > > >              }
> > > >
> > > > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > > > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> > > >          }
> > > >          ds_put_cstr(&actions, ");");
> > > >      } else {
> > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > > > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > > > -                      valid_nexthops[0]);
> > > > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > > > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> > > >      }
> > > >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> > > >                              rule->priority, rule->match,
> > > >                              ds_cstr(&actions), &rule->header_,
> > > >                              lflow_ref);
> > > > -
> > > >  cleanup:
> > > > -    free(valid_nexthops);
> > > >      ds_destroy(&match);
> > > >      ds_destroy(&actions);
> > > >  }
> > > > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> > > >      return rtb_id;
> > > >  }
> > > >
> > > > -static uint32_t
> > > > +uint32_t
> > > >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> > > >  {
> > > >      if (!route_table_name || !route_table_name[0]) {
> > > > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> > > >      ds_destroy(&actions);
> > > >  }
> > > >
> > > > -struct parsed_route {
> > > > -    struct ovs_list list_node;
> > > > -    struct in6_addr prefix;
> > > > -    unsigned int plen;
> > > > -    bool is_src_route;
> > > > -    uint32_t route_table_id;
> > > > -    uint32_t hash;
> > > > -    const struct nbrec_logical_router_static_route *route;
> > > > -    bool ecmp_symmetric_reply;
> > > > -    bool is_discard_route;
> > > > -};
> > > > -
> > > >  static uint32_t
> > > >  route_hash(struct parsed_route *route)
> > > >  {
> > > > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >
> > > >  /* Parse and validate the route. Return the parsed route if successful.
> > > >   * Otherwise return NULL. */
> > > > +
> > > >  static struct parsed_route *
> > > > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > > > +                    struct parsed_route *new_pr)
> > > > +{
> > > > +    struct parsed_route *pr;
> > > > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > > > +        if (pr->plen != new_pr->plen) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (pr->is_src_route != new_pr->is_src_route) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (pr->route_table_id != new_pr->route_table_id) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (pr->route != new_pr->route) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        return pr;
> > > > +    }
> > > > +
> > > > +    return NULL;
> > > > +}
> > > > +
> > > > +static void
> > > >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > -                  struct ovs_list *routes, struct simap *route_tables,
> > > >                    const struct nbrec_logical_router_static_route *route,
> > > > -                  const struct hmap *bfd_connections)
> > > > +                  struct hmap *bfd_connections,
> > > > +                  struct hmap *routes, struct simap *route_tables,
> > > > +                  struct hmap *bfd_active_connections)
> > > >  {
> > > >      /* Verify that the next hop is an IP address with an all-ones mask. */
> > > >      struct in6_addr nexthop;
> > > > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> > > >                           UUID_FMT, route->nexthop,
> > > >                           UUID_ARGS(&route->header_.uuid));
> > > > -            return NULL;
> > > > +            return;
> > > >          }
> > > >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> > > >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > > > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> > > >                           UUID_FMT, route->nexthop,
> > > >                           UUID_ARGS(&route->header_.uuid));
> > > > -            return NULL;
> > > > +            return;
> > > >          }
> > > >      }
> > > >
> > > > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> > > >                       UUID_FMT, route->ip_prefix,
> > > >                       UUID_ARGS(&route->header_.uuid));
> > > > -        return NULL;
> > > > +        return;
> > > >      }
> > > >
> > > >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > > > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> > > >                           route->ip_prefix, route->nexthop,
> > > >                           UUID_ARGS(&route->header_.uuid));
> > > > -            return NULL;
> > > > +            return;
> > > >          }
> > > >      }
> > > >
> > > > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > >          !find_static_route_outport(od, lr_ports, route,
> > > >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> > > >                                     NULL, NULL)) {
> > > > -        return NULL;
> > > > +        return;
> > > >      }
> > > >
> > > >      const struct nbrec_bfd *nb_bt = route->bfd;
> > > >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > > > -        struct bfd_entry *bfd_e;
> > > > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > +                                                  nb_bt->logical_port,
> > > > +                                                  nb_bt->dst_ip);
> > > > +        if (!bfd_e) {
> > > > +            return;
> > > > +        }
> > > >
> > > > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > -                                nb_bt->dst_ip);
> > > > -        ovs_mutex_lock(&bfd_lock);
> > > > -        if (bfd_e) {
> > > > -            bfd_e->ref = true;
> > > > +        /* This static route is linked to an active bfd session. */
> > > > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > > > +                                                   nb_bt->logical_port,
> > > > +                                                   nb_bt->dst_ip);
> > > > +        if (!bfd_sr) {
> > > > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > > > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > > > +                                     bfd_e->status);
> > > >          }
> > > >
> > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > +            bfd_set_status(bfd_sr, "down");
> > > >          }
> > > >
> > > > -        if (!strcmp(nb_bt->status, "down")) {
> > > > -            ovs_mutex_unlock(&bfd_lock);
> > > > -            return NULL;
> > > > +
> > > > +        if (!strcmp(bfd_sr->status, "down")) {
> > > > +            return;
> > > >          }
> > > > -        ovs_mutex_unlock(&bfd_lock);
> > > >      }
> > > >
> > > > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > > > -    pr->prefix = prefix;
> > > > -    pr->plen = plen;
> > > > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > > > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > > > -                                                 "src-ip"));
> > > > -    pr->hash = route_hash(pr);
> > > > -    pr->route = route;
> > > > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > -                                             "ecmp_symmetric_reply", false);
> > > > -    pr->is_discard_route = is_discard_route;
> > > > -    ovs_list_insert(routes, &pr->list_node);
> > > > -    return pr;
> > > > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > > > +    new_pr->prefix = prefix;
> > > > +    new_pr->plen = plen;
> > > > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > > > +                                                route->route_table);
> > > > +    new_pr->is_src_route = (route->policy &&
> > > > +                            !strcmp(route->policy, "src-ip"));
> > > > +    new_pr->hash = route_hash(new_pr);
> > > > +    new_pr->route = route;
> > > > +    new_pr->nbr = od->nbr;
> > > > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > +                                                 "ecmp_symmetric_reply",
> > > > +                                                 false);
> > > > +    new_pr->is_discard_route = is_discard_route;
> > > > +
> > > > +    size_t hash = uuid_hash(&od->key);
> > > > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > > > +    if (!pr) {
> > > > +        hmap_insert(routes, &new_pr->key_node, hash);
> > > > +    } else {
> > > > +        pr->stale = false;
> > > > +        free(new_pr);
> > > > +    }
> > > >  }
> > > >
> > > > -static void
> > > > -parsed_routes_destroy(struct ovs_list *routes)
> > > > +void
> > > > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > +                    struct hmap *bfd_connections, struct hmap *routes,
> > > > +                    struct simap *route_tables,
> > > > +                    struct hmap *bfd_active_connections)
> > > >  {
> > > >      struct parsed_route *pr;
> > > > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > > > -        ovs_list_remove(&pr->list_node);
> > > > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > > > +        if (pr->nbr == od->nbr) {
> > > > +            pr->stale = true;
> > > > +        }
> > > > +    }
> > > > +
> > > > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > > > +                          bfd_connections, routes, route_tables,
> > > > +                          bfd_active_connections);
> > > > +    }
> > > > +
> > > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > > +        if (!pr->stale) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        hmap_remove(routes, &pr->key_node);
> > > >          free(pr);
> > > >      }
> > > >  }
> > > > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > >            const struct ovn_port *op, const char *lrp_addr_s,
> > > >            const char *network_s, int plen, const char *gateway,
> > > >            bool is_src_route, const uint32_t rtb_id,
> > > > +          const struct hmap *bfd_connections,
> > > >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> > > >            int ofs, struct lflow_ref *lflow_ref)
> > > >  {
> > > > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > >                              priority, ds_cstr(&match),
> > > >                              ds_cstr(&actions), stage_hint,
> > > >                              lflow_ref);
> > > > -    if (op && op->has_bfd) {
> > > > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> > > >          ds_put_format(&match, " && udp.dst == 3784");
> > > >          ovn_lflow_add_with_hint(lflows, op->od,
> > > >                                  S_ROUTER_IN_IP_ROUTING,
> > > > @@ -10895,6 +11030,7 @@ static void
> > > >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > >                          const struct hmap *lr_ports,
> > > >                          const struct parsed_route *route_,
> > > > +                        const struct hmap *bfd_connections,
> > > >                          struct lflow_ref *lflow_ref)
> > > >  {
> > > >      const char *lrp_addr_s = NULL;
> > > > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> > > >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> > > >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > > > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > > > -              route_->is_discard_route, ofs, lflow_ref);
> > > > +              route_->is_src_route, route_->route_table_id,
> > > > +              bfd_connections, &route->header_, route_->is_discard_route,
> > > > +              ofs, lflow_ref);
> > > >
> > > >      free(prefix_s);
> > > >  }
> > > > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> > > >  static void
> > > >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> > > >                          const struct shash *meter_groups,
> > > > +                        const struct hmap *bfd_connections,
> > > >                          struct lflow_ref *lflow_ref)
> > > >  {
> > > > -    if (!op->has_bfd) {
> > > > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> > > >          return;
> > > >      }
> > > >
> > > > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> > > >   * LRP 'op'.
> > > >   */
> > > >  static void
> > > > -build_ip_routing_flows_for_lrp(
> > > > -        struct ovn_port *op, struct lflow_table *lflows,
> > > > -        struct lflow_ref *lflow_ref)
> > > > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > > > +                               const struct hmap *bfd_connections,
> > > > +                               struct lflow_table *lflows,
> > > > +                               struct lflow_ref *lflow_ref)
> > > >  {
> > > >      ovs_assert(op->nbrp);
> > > >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> > > >                    op->lrp_networks.ipv4_addrs[i].network_s,
> > > >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > -                  lflow_ref);
> > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > >      }
> > > >
> > > >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> > > >                    op->lrp_networks.ipv6_addrs[i].network_s,
> > > >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > -                  lflow_ref);
> > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > >      }
> > > >  }
> > > >
> > > >  static void
> > > >  build_static_route_flows_for_lrouter(
> > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > > > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > > > +        struct simap *route_tables, const struct hmap *bfd_connections,
> > > >          struct lflow_ref *lflow_ref)
> > > >  {
> > > >      ovs_assert(od->nbr);
> > > > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> > > >
> > > >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> > > >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > > > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > > > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> > > >      struct ecmp_groups_node *group;
> > > >
> > > >      for (int i = 0; i < od->nbr->n_ports; i++) {
> > > >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > > > -                                &route_tables, lflow_ref);
> > > > +                                route_tables, lflow_ref);
> > > >      }
> > > >
> > > > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > -        struct parsed_route *route =
> > > > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > > > -                              od->nbr->static_routes[i], bfd_connections);
> > > > -        if (!route) {
> > > > -            continue;
> > > > -        }
> > > > +    struct parsed_route *route;
> > > > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > > > +                             parsed_routes) {
> > > >          group = ecmp_groups_find(&ecmp_groups, route);
> > > >          if (group) {
> > > >              ecmp_groups_add_route(group, route);
> > > > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> > > >      }
> > > >      const struct unique_routes_node *ur;
> > > >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > > > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > > > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > > > +                                bfd_connections, lflow_ref);
> > > >      }
> > > >      ecmp_groups_destroy(&ecmp_groups);
> > > >      unique_routes_destroy(&unique_routes);
> > > > -    parsed_routes_destroy(&parsed_routes);
> > > > -    simap_destroy(&route_tables);
> > > >  }
> > > >
> > > >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > > > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> > > >      }
> > > >  }
> > > >
> > > > +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->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;
> > > > +}
> > > > +
> > > > +void
> > > > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > +                     const struct hmap *bfd_connections,
> > > > +                     struct hmap *route_policies,
> > > > +                     struct hmap *bfd_active_connections)
> > > > +{
> > > > +    struct route_policy *rp;
> > > > +
> > > > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > > > +        if (rp->nbr == od->nbr) {
> > > > +            rp->stale = true;
> > > > +        }
> > > > +    }
> > > > +
> > > > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > > > +        size_t n_valid_nexthops = 0;
> > > > +        char **valid_nexthops = NULL;
> > > > +
> > > > +        if (!strcmp(rule->action, "reroute")) {
> > > > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > > > +
> > > > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > > > +            for (size_t j = 0; j < n_nexthops; j++) {
> > > > +                char *nexthop = rule->n_nexthops
> > > > +                    ? rule->nexthops[j] : rule->nexthop;
> > > > +                struct ovn_port *out_port =
> > > > +                    get_outport_for_routing_policy_nexthop(
> > > > +                            od, lr_ports, rule->priority, nexthop);
> > > > +                if (!out_port || !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->nbr = od->nbr;
> > > > +
> > > > +        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 {
> > > > +            rp->stale = false;
> > > > +            free(valid_nexthops);
> > > > +            free(new_rp);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > > > +        if (!rp->stale) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        hmap_remove(route_policies, &rp->key_node);
> > > > +        free(rp->valid_nexthops);
> > > > +        free(rp);
> > > > +    }
> > > > +}
> > > > +
> > > >  /* Logical router ingress table POLICY: Policy.
> > > >   *
> > > >   * A packet that arrives at this table is an IP packet that should be
> > > > @@ -12862,7 +13104,7 @@ static void
> > > >  build_ingress_policy_flows_for_lrouter(
> > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > >          const struct hmap *lr_ports,
> > > > -        const struct hmap *bfd_connections,
> > > > +        struct hmap *route_policies,
> > > >          struct lflow_ref *lflow_ref)
> > > >  {
> > > >      ovs_assert(od->nbr);
> > > > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> > > >
> > > >      /* Convert routing policies to flows. */
> > > >      uint16_t ecmp_group_id = 1;
> > > > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > -        const struct nbrec_logical_router_policy *rule
> > > > -            = od->nbr->policies[i];
> > > > +    struct route_policy *rp;
> > > > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > > > +                             route_policies) {
> > > > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> > > >          bool is_ecmp_reroute =
> > > >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> > > >
> > > >          if (is_ecmp_reroute) {
> > > > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > > > -                                            bfd_connections, ecmp_group_id,
> > > > -                                            lflow_ref);
> > > > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > > > +                                            ecmp_group_id, lflow_ref);
> > > >              ecmp_group_id++;
> > > >          } else {
> > > > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > > > -                                      bfd_connections, &rule->header_,
> > > > -                                      lflow_ref);
> > > > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > > > +                                      &rule->header_, lflow_ref);
> > > >          }
> > > >      }
> > > >  }
> > > > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > >                              struct lflow_table *lflows,
> > > >                              struct ds *match, struct ds *actions,
> > > >                              const struct shash *meter_groups,
> > > > +                            const struct hmap *bfd_connections,
> > > >                              struct lflow_ref *lflow_ref)
> > > >  {
> > > >      ovs_assert(op->nbrp);
> > > > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > >      }
> > > >
> > > >      /* BFD msg handling */
> > > > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > > > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > > > +                            lflow_ref);
> > > >
> > > >      /* ICMP time exceeded */
> > > >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > > > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> > > >  static void
> > > >  build_routable_flows_for_router_port(
> > > >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > > > +    const struct hmap *bfd_connections,
> > > >      struct lflow_table *lflows,
> > > >      struct ds *match,
> > > >      struct ds *actions)
> > > > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> > > >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> > > >                                laddrs->ipv4_addrs[k].network_s,
> > > >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > > > -                              &router_port->nbrp->header_, false,
> > > > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > > > +                              bfd_connections, &router_port->nbrp->header_,
> > > > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > >                                lrp->stateful_lflow_ref);
> > > >                  }
> > > >              }
> > > > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> > > >  static void
> > > >  build_lbnat_lflows_iterate_by_lrp(
> > > >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > > > -    const struct shash *meter_groups, struct ds *match,
> > > > -    struct ds *actions, struct lflow_table *lflows)
> > > > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > > > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> > > >  {
> > > >      ovs_assert(op->nbrp);
> > > >
> > > > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> > > >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> > > >                                  actions, lflows);
> > > >
> > > > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > > > -                                         actions);
> > > > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > > > +                                         lflows, match, actions);
> > > >  }
> > > >
> > > >  static void
> > > > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> > > >      struct ds actions;
> > > >      size_t thread_lflow_counter;
> > > >      const char *svc_monitor_mac;
> > > > +    struct hmap *parsed_routes;
> > > > +    struct hmap *route_policies;
> > > > +    struct simap *route_tables;
> > > >  };
> > > >
> > > >  /* Helper function to combine all lflow generation which is iterated by
> > > > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > > >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> > > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> > > >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > -                                         lsi->bfd_connections,
> > > > -                                         NULL);
> > > > +                                         lsi->parsed_routes, lsi->route_tables,
> > > > +                                         lsi->bfd_connections, NULL);
> > > >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> > > >                                           &lsi->actions, NULL);
> > > >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > -                                           lsi->bfd_connections, NULL);
> > > > +                                           lsi->route_policies, NULL);
> > > >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> > > >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > >                                            &lsi->match, &lsi->actions,
> > > > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > >                                            &lsi->actions, op->lflow_ref);
> > > >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > >                                                  &lsi->actions, op->lflow_ref);
> > > > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > > > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > > > +                                   lsi->lflows, op->lflow_ref);
> > > >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > >                                         &lsi->actions, lsi->meter_groups,
> > > >                                         op->lflow_ref);
> > > > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > >                                              lsi->meter_groups,
> > > >                                              op->lflow_ref);
> > > >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > > > -                                lsi->meter_groups, op->lflow_ref);
> > > > +                                lsi->meter_groups, lsi->bfd_connections,
> > > > +                                op->lflow_ref);
> > > >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> > > >                                                   &lsi->actions, op->lflow_ref);
> > > >  }
> > > > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> > > >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> > > >                      build_lbnat_lflows_iterate_by_lrp(
> > > >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > > > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > > > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > > > +                        lsi->lflows);
> > > >                  }
> > > >              }
> > > >              for (bnum = control->id;
> > > > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> > > >      const struct hmap *svc_monitor_map,
> > > >      const struct hmap *bfd_connections,
> > > >      const struct chassis_features *features,
> > > > -    const char *svc_monitor_mac)
> > > > +    const char *svc_monitor_mac,
> > > > +    struct hmap *parsed_routes,
> > > > +    struct hmap *route_policies,
> > > > +    struct simap *route_tables)
> > > >  {
> > > >
> > > >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > > > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> > > >              lsiv[index].svc_check_match = svc_check_match;
> > > >              lsiv[index].thread_lflow_counter = 0;
> > > >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > > > +            lsiv[index].parsed_routes = parsed_routes;
> > > > +            lsiv[index].route_tables = route_tables;
> > > > +            lsiv[index].route_policies = route_policies;
> > > >              ds_init(&lsiv[index].match);
> > > >              ds_init(&lsiv[index].actions);
> > > >
> > > > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> > > >              .features = features,
> > > >              .svc_check_match = svc_check_match,
> > > >              .svc_monitor_mac = svc_monitor_mac,
> > > > +            .parsed_routes = parsed_routes,
> > > > +            .route_tables = route_tables,
> > > > +            .route_policies = route_policies,
> > > >              .match = DS_EMPTY_INITIALIZER,
> > > >              .actions = DS_EMPTY_INITIALIZER,
> > > >          };
> > > > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> > > >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> > > >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> > > >                                                lsi.meter_groups,
> > > > +                                              lsi.bfd_connections,
> > > >                                                &lsi.match,
> > > >                                                &lsi.actions,
> > > >                                                lsi.lflows);
> > > > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> > > >                                      input_data->svc_monitor_map,
> > > >                                      input_data->bfd_connections,
> > > >                                      input_data->features,
> > > > -                                    input_data->svc_monitor_mac);
> > > > +                                    input_data->svc_monitor_mac,
> > > > +                                    input_data->parsed_routes,
> > > > +                                    input_data->route_policies,
> > > > +                                    input_data->route_tables);
> > > >
> > > >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> > > >          parallelization_state = STATE_USE_PARALLELIZATION;
> > > > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> > > >              build_lbnat_lflows_iterate_by_lrp(op,
> > > >                                                lflow_input->lr_stateful_table,
> > > >                                                lflow_input->meter_groups,
> > > > +                                              lflow_input->bfd_connections,
> > > >                                                &match, &actions,
> > > >                                                lflows);
> > > >
> > > > @@ -17490,6 +17754,27 @@ 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);
> > > > +}
> > > > +
> > > > +void
> > > > +static_routes_init(struct static_routes_data *data)
> > > > +{
> > > > +    hmap_init(&data->parsed_routes);
> > > > +    simap_init(&data->route_tables);
> > > > +    hmap_init(&data->bfd_active_connections);
> > > > +}
> > > > +
> > > > +void
> > > > +bfd_init(struct bfd_data *data)
> > > > +{
> > > > +    hmap_init(&data->bfd_connections);
> > > > +}
> > > > +
> > > >  void
> > > >  northd_destroy(struct northd_data *data)
> > > >  {
> > > > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> > > >      destroy_northd_tracked_data(data);
> > > >  }
> > > >
> > > > +static void
> > > > +__bfd_destroy(struct hmap *bfd_connections)
> > > > +{
> > > > +    struct bfd_entry *bfd_e;
> > > > +
> > > > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > > > +        bfd_erase_entry(bfd_e);
> > > > +    }
> > > > +    hmap_destroy(bfd_connections);
> > > > +}
> > > > +
> > > > +void
> > > > +bfd_destroy(struct bfd_data *data)
> > > > +{
> > > > +    __bfd_destroy(&data->bfd_connections);
> > > > +}
> > > > +
> > > > +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);
> > > > +}
> > > > +
> > > > +void
> > > > +static_routes_destroy(struct static_routes_data *data)
> > > > +{
> > > > +    struct parsed_route *r;
> > > > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > > > +        free(r);
> > > > +    }
> > > > +    hmap_destroy(&data->parsed_routes);
> > > > +
> > > > +    simap_destroy(&data->route_tables);
> > > > +    __bfd_destroy(&data->bfd_active_connections);
> > > > +}
> > > > +
> > > >  void
> > > >  ovnnb_db_run(struct northd_input *input_data,
> > > >               struct northd_data *data,
> > > > diff --git a/northd/northd.h b/northd/northd.h
> > > > index d4a8d75ab..432072f9f 100644
> > > > --- a/northd/northd.h
> > > > +++ b/northd/northd.h
> > > > @@ -23,6 +23,7 @@
> > > >  #include "northd/en-port-group.h"
> > > >  #include "northd/ipam.h"
> > > >  #include "openvswitch/hmap.h"
> > > > +#include "simap.h"
> > > >  #include "ovs-thread.h"
> > > >
> > > >  struct northd_input {
> > > > @@ -160,14 +161,34 @@ 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;
> > > > +    const struct nbrec_logical_router *nbr;
> > > > +    bool stale;
> > > > +};
> > > > +
> > > > +struct static_routes_data {
> > > > +    struct hmap parsed_routes;
> > > > +    struct simap route_tables;
> > > > +    struct hmap bfd_active_connections;
> > > > +};
> > > > +
> > > > +struct route_policies_data {
> > > > +    struct hmap route_policies;
> > > > +    struct hmap bfd_active_connections;
> > > > +};
> > > > +
> > > > +struct bfd_data {
> > > > +    struct hmap bfd_connections;
> > > > +};
> > > > +
> > > >  struct lr_nat_table;
> > > >
> > > >  struct lflow_input {
> > > > -    /* Northbound table references */
> > > > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > > > -
> > > >      /* Southbound table references */
> > > > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> > > >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> > > >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> > > >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > > > @@ -190,6 +211,9 @@ struct lflow_input {
> > > >      const struct hmap *svc_monitor_map;
> > > >      bool ovn_internal_version_changed;
> > > >      const char *svc_monitor_mac;
> > > > +    struct hmap *parsed_routes;
> > > > +    struct hmap *route_policies;
> > > > +    struct simap *route_tables;
> > > >  };
> > > >
> > > >  extern int parallelization_state;
> > > > @@ -593,8 +617,6 @@ struct ovn_port {
> > > >
> > > >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> > > >
> > > > -    bool has_bfd;
> > > > -
> > > >      /* The port's peer:
> > > >       *
> > > >       *     - A switch port S of type "router" has a router port R as a peer,
> > > > @@ -653,6 +675,20 @@ struct ovn_port {
> > > >      struct lflow_ref *stateful_lflow_ref;
> > > >  };
> > > >
> > > > +struct parsed_route {
> > > > +    struct hmap_node key_node;
> > > > +    struct in6_addr prefix;
> > > > +    unsigned int plen;
> > > > +    bool is_src_route;
> > > > +    uint32_t route_table_id;
> > > > +    uint32_t hash;
> > > > +    const struct nbrec_logical_router_static_route *route;
> > > > +    bool ecmp_symmetric_reply;
> > > > +    bool is_discard_route;
> > > > +    const struct nbrec_logical_router *nbr;
> > > > +    bool stale;
> > > > +};
> > > > +
> > > >  void ovnnb_db_run(struct northd_input *input_data,
> > > >                    struct northd_data *data,
> > > >                    struct ovsdb_idl_txn *ovnnb_txn,
> > > > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > > > +                         struct hmap *, struct hmap *, struct simap *,
> > > > +                         struct hmap *);
> > > > +uint32_t get_route_table_id(struct simap *, const char *);
> > > > +void static_routes_init(struct static_routes_data *);
> > > > +void static_routes_destroy(struct static_routes_data *);
> > > > +
> > > > +void bfd_init(struct bfd_data *);
> > > > +void bfd_destroy(struct bfd_data *);
> > > > +
> > > >  struct lflow_table;
> > > >  struct lr_stateful_tracked_data;
> > > >  struct ls_stateful_tracked_data;
> > > > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> > > >                                     struct hmap *lbgrp_datapaths_map,
> > > >                                     struct northd_tracked_data *);
> > > >
> > > > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > -                     const struct nbrec_bfd_table *,
> > > > -                     const struct sbrec_bfd_table *,
> > > > -                     const struct hmap *lr_ports,
> > > > -                     struct hmap *bfd_connections);
> > > > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > > > -                             struct hmap *bfd_map);
> > > > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > > > +                          const struct hmap *, struct hmap *, struct hmap *);
> > > > +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 *, struct hmap *);
> > > > +void build_bfd_map(const struct nbrec_bfd_table *,
> > > > +                   const struct sbrec_bfd_table *, struct hmap *);
> > > >  void run_update_worker_pool(int n_threads);
> > > >
> > > >  const struct ovn_datapath *northd_get_datapath_for_port(
> > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > index 199197f09..710500dce 100644
> > > > --- a/tests/ovn-northd.at
> > > > +++ b/tests/ovn-northd.at
> > > > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> > > >      ovn-sbctl list meter >> $1
> > > >      ovn-sbctl list meter_band >> $1
> > > >      ovn-sbctl list port_group >> $1
> > > > +    ovn-sbctl list bfd >> $1
> > > >      ovn-sbctl dump-flows > lflows_$1
> > > >  ])
> > > >
> > > > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> > > >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> > > >  done
> > > >
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> > > >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> > > >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > > > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> > > >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> > > >                       min_rx=1000 min_tx=1000 status=admin_down
> > > >
> > > > +check_engine_stats northd norecompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> > > >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> > > >
> > > > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> > > >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> > > >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> > > >
> > > > +check_engine_stats northd norecompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> > > >  wait_column down bfd status logical_port=r0-sw1
> > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> > > >
> > > > +check_engine_stats northd recompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats static_routes recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> > > >  wait_column down bfd status logical_port=r0-sw2
> > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > > > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> > > >  wait_column down bfd status logical_port=r0-sw5
> > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> > > >
> > > > +check_engine_stats northd recompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats static_routes recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> > > >  wait_column down bfd status logical_port=r0-sw6
> > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> > > >
> > > > +check_engine_stats northd recompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats route_policies recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> > > >  wait_column down bfd status logical_port=r0-sw7
> > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > > > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> > > >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> > > >
> > > > +check_engine_stats northd recompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats static_routes recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  check ovn-nbctl lr-policy-del r0
> > > >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> > > >
> > > > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> > > >  wait_column down bfd status dst_ip=192.168.9.3
> > > >  wait_column down bfd status dst_ip=192.168.9.4
> > > >
> > > > +check_engine_stats northd recompute nocompute
> > > > +check_engine_stats bfd recompute nocompute
> > > > +check_engine_stats route_policies recompute nocompute
> > > > +check_engine_stats lflow recompute nocompute
> > > > +check_engine_stats northd_output norecompute compute
> > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > +
> > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> > > >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> > > >
> > > > --
> > > > 2.45.2
> > > >
> > >
>
Han Zhou Aug. 7, 2024, 9:36 p.m. UTC | #5
On Wed, Aug 7, 2024 at 10:29 AM Lorenzo Bianconi
<lorenzo.bianconi@redhat.com> wrote:
>
> > On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
> > <lorenzo.bianconi@redhat.com> wrote:
> > >
> > > [...]
> > > > > -void
> > > > > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > -                        struct hmap *bfd_map)
> > > > > +static bool
> > > > > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> > > > >  {
> > > > > -    const struct nbrec_bfd *nb_bt;
> > > > >      struct bfd_entry *bfd_e;
> > > > > -
> > > > > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > -        if (!bfd_e) {
> > > > > -            continue;
> > > > > -        }
> > > > > -
> > > > > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > > > > -            /* no user for this bfd connection */
> > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> > > >
> > > > I like the idea of checking the existence of the bfd entry to decide
> > > > if a port has bfd instead of relying on the has_bfd status. However, I
> > > > have performance concerns for this loop. The bfd_port_lookup()
> > > > searches by hashing both logical_port and dst_ip, but here for
> > > > logical_port it just iterates all bfd entries, which looks
> > > > unreasonable. It may be better to add a hindex for logical_port.
> > >
> > > Hi Han,
> > >
> > > I did not get what you mean here, can you please provide more details?
> > > Do you mean another hmap to link the logical_port name to the bfd_entry?
> > >
> >
> > The goal is to avoid this O(n) iteration looking for a given
> > logical_port. Since multiple bfd entries can have the same
> > logical_port name, it would be better to use the struct hindex instead
> > of struct hmap to link logical_port name to the bfd_entry.
>
> ack, fine. Since in this case we just need to know if the logical_port is used
> in at least one bfd_entry, I guess a simap to count number of bfd_entry for a
> given logical port is enough, what do you think?
>

Yes, or it can be just a sset, if we only care about the existence (0
or >0) but not the actual count.

> >
> > > >
> > > > > +        if (!strcmp(bfd_e->logical_port, port)) {
> > > > > +            return true;
> > > > >          }
> > > > >      }
> > > > > -
> > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > > > -        free(bfd_e);
> > > > > -    }
> > > > > +    return false;
> > > > >  }
> > > > >
> > > > > +
> > > > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > > > >  #define BFD_DEF_MINRX       1000 /* 1s */
> > > > >  #define BFD_DEF_DETECT_MULT 5
> > > > > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> > > > >      return port + BFD_UDP_SRC_PORT_START;
> > > > >  }
> > > > >
> > > > > -void
> > > > > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > > > > +static char *
> > > > > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > > > > +                          const struct hmap *rp_bfd_connections,
> > > > > +                          const struct hmap *sr_bfd_connections)
> > > > >  {
> > > > > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > > > > -    const struct sbrec_bfd *sb_bt;
> > > > > -    unsigned long *bfd_src_ports;
> > > > > -    struct bfd_entry *bfd_e;
> > > > > -    uint32_t hash;
> > > > > +    struct bfd_entry *bfd_rp, *bfd_sr;
> > > > >
> > > > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > > > > +                             nb_bt->dst_ip);
> > > > > +    if (!bfd_rp) {
> > > > > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > > > > +                                 nb_bt->dst_ip);
> > > > > +        if (!bfd_sr) {
> > > > > +            return "admin_down";
> > > > > +        }
> > > > > +    }
> > > > >
> > > > > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > -        bfd_e = xmalloc(sizeof *bfd_e);
> > > > > -        bfd_e->sb_bt = sb_bt;
> > > > > -        hash = hash_string(sb_bt->dst_ip, 0);
> > > > > -        hash = hash_string(sb_bt->logical_port, hash);
> > > > > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > > > > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > +               const struct hmap *lr_ports,
> > > > > +               const struct hmap *bfd_connections,
> > > > > +               const struct hmap *rp_bfd_connections,
> > > > > +               const struct hmap *sr_bfd_connections,
> > > > > +               struct hmap *sync_bfd_connections)
> > > > > +{
> > > > > +    if (!ovnsb_txn) {
> > > > > +        return;
> > > > > +    }
> > > > > +
> > > > > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > +
> > > > > +    struct bfd_entry *bfd_e;
> > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > > > > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > > > > +                                              bfd_e->logical_port,
> > > > > +                                              bfd_e->dst_ip, bfd_e->status);
> > > >
> > > > It looks like we are blindly allocating a new entry for each entry in
> > > > bfd_connections without checking if it already exists. Did I
> > > > misunderstand?
> > >
> > > So far sync_bfd_connections always start from scratch and it is 1:1 with
> > > bfd_connections, so I guess an entry can't already exist. But I prefer to be
> > > more generic and check it. I will fix it.
> > >
> >
> > What I meant was that now the sync_bfd_connections table is persistent
> > across I-P engine runs (I didn't see it get destroyed across
> > iterations but correct me if I am wrong), the above code just
> > allocates entries again and again every iteration without removing the
> > old ones. For simplicity we may just destroy old entries at the
> > beginning of this function because now we always do recompute. In the
> > future it may be optimized to incrementally consolidate new/old
> > entries. Does this make sense?
>
>
> sync_bfd_connections is destroyed at the beginning of en_bfd_sync_run() so I
> guess we already have what you are suggesting, right? Anyway we can still use
> bfd_port_lookup() to check if an entry has been already allocated. What do you
> prefer?
>

Oh, yes. Sorry I missed that. If it is recreated then there is no
point to check the existence again.

Thanks,
Han

> Regards,
> Lorenzo
>
> >
> > Thanks,
> > Han
> >
> > > Regards,
> > > Lorenzo
> > >
> > > >
> > > > Thanks,
> > > > Han
> > > >
> > > > > +        e->nb_bt = bfd_e->nb_bt;
> > > > > +        e->sb_bt = bfd_e->sb_bt;
> > > > > +        e->stale = true;
> > > > > +        /* we need to check if this entry is even in the BFD nb db table */
> > > > > +        if (bfd_e->sb_bt) {
> > > > > +            bitmap_set1(bfd_src_ports,
> > > > > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > +        }
> > > > >      }
> > > > >
> > > > >      const struct nbrec_bfd *nb_bt;
> > > > >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > -        if (!nb_bt->status) {
> > > > > -            /* default state is admin_down */
> > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > > > > +                                nb_bt->dst_ip);
> > > > > +        if (!bfd_e) {
> > > > > +            continue;
> > > > >          }
> > > > >
> > > > >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > > > > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > -        if (!bfd_e) {
> > > > > +        if (!op || !op->sb) {
> > > > > +            /* skip not bounded ports */
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        nbrec_bfd_set_status(nb_bt,
> > > > > +                             bfd_get_connection_status(nb_bt,
> > > > > +                                                       rp_bfd_connections,
> > > > > +                                                       sr_bfd_connections));
> > > > > +        if (!bfd_e->sb_bt) {
> > > > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > > > >              if (udp_src < 0) {
> > > > >                  continue;
> > > > >              }
> > > > >
> > > > > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > > +            /* Add entry to bfd sb table. */
> > > > > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> > > > >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> > > > >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> > > > >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> > > > >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > > > > -            if (op && op->sb && op->sb->chassis) {
> > > > > +            if (op->sb->chassis) {
> > > > >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> > > > >              }
> > > > >
> > > > > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> > > > >                  }
> > > > >              }
> > > > > +
> > > > >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > > > > -            if (op && op->sb && op->sb->chassis &&
> > > > > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > > > > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > > > > +                                           bfd_e->sb_bt->chassis_name)) {
> > > > >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> > > > >                                             op->sb->chassis->name);
> > > > >              }
> > > > > -
> > > > > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > > > > -            bfd_e->ref = false;
> > > > > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > > > > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > > > > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> > > > >          }
> > > > >
> > > > > -        if (op) {
> > > > > -            op->has_bfd = true;
> > > > > -        }
> > > > > +        bfd_e->stale = false;
> > > > >      }
> > > > >
> > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > > > > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > > > > -                                            bfd_e->sb_bt->logical_port);
> > > > > -        if (op) {
> > > > > -            op->has_bfd = false;
> > > > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > > > > +        if (bfd_e->stale) {
> > > > > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > > > > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > +            bfd_erase_entry(bfd_e);
> > > > >          }
> > > > > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > -        free(bfd_e);
> > > > >      }
> > > > > -    hmap_destroy(&sb_only);
> > > > >
> > > > >      bitmap_free(bfd_src_ports);
> > > > >  }
> > > > >
> > > > > +void
> > > > > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > +              struct hmap *bfd_connections)
> > > > > +{
> > > > > +    struct bfd_entry *bfd_e;
> > > > > +
> > > > > +    /* align bfd_map to sb db */
> > > > > +    const struct sbrec_bfd *sb_bt;
> > > > > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > > > > +                                sb_bt->dst_ip);
> > > > > +        if (!bfd_e) {
> > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > > > > +                                    sb_bt->dst_ip,  sb_bt->status);
> > > > > +        }
> > > > > +        bfd_e->sb_bt = sb_bt;
> > > > > +    }
> > > > > +
> > > > > +    const struct nbrec_bfd *nb_bt;
> > > > > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > +                                nb_bt->dst_ip);
> > > > > +        if (!bfd_e) {
> > > > > +            /* brand new entry. */
> > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > > > > +                                    nb_bt->dst_ip, "admin_down");
> > > > > +        }
> > > > > +        bfd_e->nb_bt = nb_bt;
> > > > > +    }
> > > > > +}
> > > > > +
> > > > >  /* Returns a string of the IP address of the router port 'op' that
> > > > >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> > > > >   *
> > > > > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> > > > >      return NULL;
> > > > >  }
> > > > >
> > > > > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > > > > -
> > > > > -static bool check_bfd_state(
> > > > > -        const struct nbrec_logical_router_policy *rule,
> > > > > -        const struct hmap *bfd_connections,
> > > > > -        struct ovn_port *out_port,
> > > > > -        const char *nexthop)
> > > > > +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);
> > > > > -    bool ret = true;
> > > > >
> > > > >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> > > > >          /* Check if there is a BFD session associated to the reroute
> > > > > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> > > > >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > >                                                    nb_bt->logical_port,
> > > > >                                                    nb_bt->dst_ip);
> > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > -        if (bfd_e) {
> > > > > -            bfd_e->ref = true;
> > > > > +        if (!bfd_e) {
> > > > > +            continue;
> > > > >          }
> > > > >
> > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > +        /* 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);
> > > > >          }
> > > > >
> > > > > -        ret = strcmp(nb_bt->status, "down");
> > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > -        break;
> > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > +            bfd_set_status(bfd_rp, "down");
> > > > > +        }
> > > > > +
> > > > > +        return strcmp(bfd_rp->status, "down");
> > > > >      }
> > > > >
> > > > > -    return ret;
> > > > > +    return true;
> > > > >  }
> > > > >
> > > > >  static void
> > > > >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > -                          const struct hmap *lr_ports,
> > > > > -                          const struct nbrec_logical_router_policy *rule,
> > > > > -                          const struct hmap *bfd_connections,
> > > > > +                          const struct hmap *lr_ports, struct route_policy *rp,
> > > > >                            const struct ovsdb_idl_row *stage_hint,
> > > > >                            struct lflow_ref *lflow_ref)
> > > > >  {
> > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > >
> > > > >      if (!strcmp(rule->action, "reroute")) {
> > > > >          ovs_assert(rule->n_nexthops <= 1);
> > > > >
> > > > > -        char *nexthop =
> > > > > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > > > > +        if (!rp->n_valid_nexthops) {
> > > > > +            return;
> > > > > +        }
> > > > > +
> > > > > +        char *nexthop = rp->valid_nexthops[0];
> > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > >               od, lr_ports, rule->priority, nexthop);
> > > > >          if (!out_port) {
> > > > > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > >              return;
> > > > >          }
> > > > >
> > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > > > > -            return;
> > > > > -        }
> > > > > -
> > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > >          if (pkt_mark) {
> > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > > @@ -10070,19 +10156,18 @@ static void
> > > > >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > >                                  struct ovn_datapath *od,
> > > > >                                  const struct hmap *lr_ports,
> > > > > -                                const struct nbrec_logical_router_policy *rule,
> > > > > -                                const struct hmap *bfd_connections,
> > > > > +                                struct route_policy *rp,
> > > > >                                  uint16_t ecmp_group_id,
> > > > >                                  struct lflow_ref *lflow_ref)
> > > > >  {
> > > > > -    ovs_assert(rule->n_nexthops > 1);
> > > > > -
> > > > >      bool nexthops_is_ipv4 = true;
> > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > +    ovs_assert(rule->n_nexthops > 1);
> > > > >
> > > > >      /* Check that all the nexthops belong to the same addr family before
> > > > >       * adding logical flows. */
> > > > > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > >
> > > > >          if (i == 0) {
> > > > >              nexthops_is_ipv4 = is_ipv4;
> > > > > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> > > > >                           "the match [%s] do not belong to the same address "
> > > > >                           "family as other next hops",
> > > > > -                         rule->nexthops[i], rule->match);
> > > > > +                         rp->valid_nexthops[i], rule->match);
> > > > >              return;
> > > > >          }
> > > > >      }
> > > > > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > >
> > > > > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > > > > -    size_t n_valid_nexthops = 0;
> > > > > -
> > > > > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > > > > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > > > > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> > > > >          if (!out_port) {
> > > > >              goto cleanup;
> > > > >          }
> > > > >
> > > > >          const char *lrp_addr_s =
> > > > > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > > > > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> > > > >          if (!lrp_addr_s) {
> > > > >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > > > >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> > > > >                              " priority %"PRId64" nexthop %s",
> > > > > -                            rule->priority, rule->nexthops[i]);
> > > > > +                            rule->priority, rp->valid_nexthops[i]);
> > > > >              goto cleanup;
> > > > >          }
> > > > >
> > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > > > > -                             rule->nexthops[i])) {
> > > > > -            continue;
> > > > > -        }
> > > > > -
> > > > > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > > > > -
> > > > >          ds_clear(&actions);
> > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > >          if (pkt_mark) {
> > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > >          }
> > > > >
> > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > >
> > > > >          ds_put_format(&actions, "%s = %s; "
> > > > >                        "%s = %s; "
> > > > > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > >                        "flags.loopback = 1; "
> > > > >                        "next;",
> > > > >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > > > > -                      rule->nexthops[i],
> > > > > +                      rp->valid_nexthops[i],
> > > > >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> > > > >                        lrp_addr_s,
> > > > >                        out_port->lrp_networks.ea_s,
> > > > > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > >                                  lflow_ref);
> > > > >      }
> > > > >
> > > > > -    if (!n_valid_nexthops) {
> > > > > -        goto cleanup;
> > > > > -    }
> > > > > -
> > > > >      ds_clear(&actions);
> > > > > -    if (n_valid_nexthops > 1) {
> > > > > +    if (rp->n_valid_nexthops > 1) {
> > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> > > > >                        REG_ECMP_MEMBER_ID);
> > > > >
> > > > > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > > > > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > >              if (i > 0) {
> > > > >                  ds_put_cstr(&actions, ", ");
> > > > >              }
> > > > >
> > > > > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > > > > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> > > > >          }
> > > > >          ds_put_cstr(&actions, ");");
> > > > >      } else {
> > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > > > > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > > > > -                      valid_nexthops[0]);
> > > > > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > > > > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> > > > >      }
> > > > >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> > > > >                              rule->priority, rule->match,
> > > > >                              ds_cstr(&actions), &rule->header_,
> > > > >                              lflow_ref);
> > > > > -
> > > > >  cleanup:
> > > > > -    free(valid_nexthops);
> > > > >      ds_destroy(&match);
> > > > >      ds_destroy(&actions);
> > > > >  }
> > > > > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> > > > >      return rtb_id;
> > > > >  }
> > > > >
> > > > > -static uint32_t
> > > > > +uint32_t
> > > > >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> > > > >  {
> > > > >      if (!route_table_name || !route_table_name[0]) {
> > > > > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> > > > >      ds_destroy(&actions);
> > > > >  }
> > > > >
> > > > > -struct parsed_route {
> > > > > -    struct ovs_list list_node;
> > > > > -    struct in6_addr prefix;
> > > > > -    unsigned int plen;
> > > > > -    bool is_src_route;
> > > > > -    uint32_t route_table_id;
> > > > > -    uint32_t hash;
> > > > > -    const struct nbrec_logical_router_static_route *route;
> > > > > -    bool ecmp_symmetric_reply;
> > > > > -    bool is_discard_route;
> > > > > -};
> > > > > -
> > > > >  static uint32_t
> > > > >  route_hash(struct parsed_route *route)
> > > > >  {
> > > > > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >
> > > > >  /* Parse and validate the route. Return the parsed route if successful.
> > > > >   * Otherwise return NULL. */
> > > > > +
> > > > >  static struct parsed_route *
> > > > > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > > > > +                    struct parsed_route *new_pr)
> > > > > +{
> > > > > +    struct parsed_route *pr;
> > > > > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > > > > +        if (pr->plen != new_pr->plen) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (pr->is_src_route != new_pr->is_src_route) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (pr->route_table_id != new_pr->route_table_id) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (pr->route != new_pr->route) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        return pr;
> > > > > +    }
> > > > > +
> > > > > +    return NULL;
> > > > > +}
> > > > > +
> > > > > +static void
> > > > >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > -                  struct ovs_list *routes, struct simap *route_tables,
> > > > >                    const struct nbrec_logical_router_static_route *route,
> > > > > -                  const struct hmap *bfd_connections)
> > > > > +                  struct hmap *bfd_connections,
> > > > > +                  struct hmap *routes, struct simap *route_tables,
> > > > > +                  struct hmap *bfd_active_connections)
> > > > >  {
> > > > >      /* Verify that the next hop is an IP address with an all-ones mask. */
> > > > >      struct in6_addr nexthop;
> > > > > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> > > > >                           UUID_FMT, route->nexthop,
> > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > -            return NULL;
> > > > > +            return;
> > > > >          }
> > > > >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> > > > >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > > > > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> > > > >                           UUID_FMT, route->nexthop,
> > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > -            return NULL;
> > > > > +            return;
> > > > >          }
> > > > >      }
> > > > >
> > > > > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> > > > >                       UUID_FMT, route->ip_prefix,
> > > > >                       UUID_ARGS(&route->header_.uuid));
> > > > > -        return NULL;
> > > > > +        return;
> > > > >      }
> > > > >
> > > > >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > > > > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> > > > >                           route->ip_prefix, route->nexthop,
> > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > -            return NULL;
> > > > > +            return;
> > > > >          }
> > > > >      }
> > > > >
> > > > > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > >          !find_static_route_outport(od, lr_ports, route,
> > > > >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> > > > >                                     NULL, NULL)) {
> > > > > -        return NULL;
> > > > > +        return;
> > > > >      }
> > > > >
> > > > >      const struct nbrec_bfd *nb_bt = route->bfd;
> > > > >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > > > > -        struct bfd_entry *bfd_e;
> > > > > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > > +                                                  nb_bt->logical_port,
> > > > > +                                                  nb_bt->dst_ip);
> > > > > +        if (!bfd_e) {
> > > > > +            return;
> > > > > +        }
> > > > >
> > > > > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > -                                nb_bt->dst_ip);
> > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > -        if (bfd_e) {
> > > > > -            bfd_e->ref = true;
> > > > > +        /* This static route is linked to an active bfd session. */
> > > > > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > > > > +                                                   nb_bt->logical_port,
> > > > > +                                                   nb_bt->dst_ip);
> > > > > +        if (!bfd_sr) {
> > > > > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > > > > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > > > > +                                     bfd_e->status);
> > > > >          }
> > > > >
> > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > +            bfd_set_status(bfd_sr, "down");
> > > > >          }
> > > > >
> > > > > -        if (!strcmp(nb_bt->status, "down")) {
> > > > > -            ovs_mutex_unlock(&bfd_lock);
> > > > > -            return NULL;
> > > > > +
> > > > > +        if (!strcmp(bfd_sr->status, "down")) {
> > > > > +            return;
> > > > >          }
> > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > >      }
> > > > >
> > > > > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > > > > -    pr->prefix = prefix;
> > > > > -    pr->plen = plen;
> > > > > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > > > > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > > > > -                                                 "src-ip"));
> > > > > -    pr->hash = route_hash(pr);
> > > > > -    pr->route = route;
> > > > > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > -                                             "ecmp_symmetric_reply", false);
> > > > > -    pr->is_discard_route = is_discard_route;
> > > > > -    ovs_list_insert(routes, &pr->list_node);
> > > > > -    return pr;
> > > > > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > > > > +    new_pr->prefix = prefix;
> > > > > +    new_pr->plen = plen;
> > > > > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > > > > +                                                route->route_table);
> > > > > +    new_pr->is_src_route = (route->policy &&
> > > > > +                            !strcmp(route->policy, "src-ip"));
> > > > > +    new_pr->hash = route_hash(new_pr);
> > > > > +    new_pr->route = route;
> > > > > +    new_pr->nbr = od->nbr;
> > > > > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > +                                                 "ecmp_symmetric_reply",
> > > > > +                                                 false);
> > > > > +    new_pr->is_discard_route = is_discard_route;
> > > > > +
> > > > > +    size_t hash = uuid_hash(&od->key);
> > > > > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > > > > +    if (!pr) {
> > > > > +        hmap_insert(routes, &new_pr->key_node, hash);
> > > > > +    } else {
> > > > > +        pr->stale = false;
> > > > > +        free(new_pr);
> > > > > +    }
> > > > >  }
> > > > >
> > > > > -static void
> > > > > -parsed_routes_destroy(struct ovs_list *routes)
> > > > > +void
> > > > > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > +                    struct hmap *bfd_connections, struct hmap *routes,
> > > > > +                    struct simap *route_tables,
> > > > > +                    struct hmap *bfd_active_connections)
> > > > >  {
> > > > >      struct parsed_route *pr;
> > > > > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > > > > -        ovs_list_remove(&pr->list_node);
> > > > > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > > > > +        if (pr->nbr == od->nbr) {
> > > > > +            pr->stale = true;
> > > > > +        }
> > > > > +    }
> > > > > +
> > > > > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > > > > +                          bfd_connections, routes, route_tables,
> > > > > +                          bfd_active_connections);
> > > > > +    }
> > > > > +
> > > > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > > > +        if (!pr->stale) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        hmap_remove(routes, &pr->key_node);
> > > > >          free(pr);
> > > > >      }
> > > > >  }
> > > > > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > >            const struct ovn_port *op, const char *lrp_addr_s,
> > > > >            const char *network_s, int plen, const char *gateway,
> > > > >            bool is_src_route, const uint32_t rtb_id,
> > > > > +          const struct hmap *bfd_connections,
> > > > >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> > > > >            int ofs, struct lflow_ref *lflow_ref)
> > > > >  {
> > > > > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > >                              priority, ds_cstr(&match),
> > > > >                              ds_cstr(&actions), stage_hint,
> > > > >                              lflow_ref);
> > > > > -    if (op && op->has_bfd) {
> > > > > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> > > > >          ds_put_format(&match, " && udp.dst == 3784");
> > > > >          ovn_lflow_add_with_hint(lflows, op->od,
> > > > >                                  S_ROUTER_IN_IP_ROUTING,
> > > > > @@ -10895,6 +11030,7 @@ static void
> > > > >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > >                          const struct hmap *lr_ports,
> > > > >                          const struct parsed_route *route_,
> > > > > +                        const struct hmap *bfd_connections,
> > > > >                          struct lflow_ref *lflow_ref)
> > > > >  {
> > > > >      const char *lrp_addr_s = NULL;
> > > > > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> > > > >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> > > > >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > > > > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > > > > -              route_->is_discard_route, ofs, lflow_ref);
> > > > > +              route_->is_src_route, route_->route_table_id,
> > > > > +              bfd_connections, &route->header_, route_->is_discard_route,
> > > > > +              ofs, lflow_ref);
> > > > >
> > > > >      free(prefix_s);
> > > > >  }
> > > > > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> > > > >  static void
> > > > >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> > > > >                          const struct shash *meter_groups,
> > > > > +                        const struct hmap *bfd_connections,
> > > > >                          struct lflow_ref *lflow_ref)
> > > > >  {
> > > > > -    if (!op->has_bfd) {
> > > > > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> > > > >          return;
> > > > >      }
> > > > >
> > > > > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> > > > >   * LRP 'op'.
> > > > >   */
> > > > >  static void
> > > > > -build_ip_routing_flows_for_lrp(
> > > > > -        struct ovn_port *op, struct lflow_table *lflows,
> > > > > -        struct lflow_ref *lflow_ref)
> > > > > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > > > > +                               const struct hmap *bfd_connections,
> > > > > +                               struct lflow_table *lflows,
> > > > > +                               struct lflow_ref *lflow_ref)
> > > > >  {
> > > > >      ovs_assert(op->nbrp);
> > > > >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> > > > >                    op->lrp_networks.ipv4_addrs[i].network_s,
> > > > >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > -                  lflow_ref);
> > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > >      }
> > > > >
> > > > >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> > > > >                    op->lrp_networks.ipv6_addrs[i].network_s,
> > > > >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > -                  lflow_ref);
> > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > >      }
> > > > >  }
> > > > >
> > > > >  static void
> > > > >  build_static_route_flows_for_lrouter(
> > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > > > > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > > > > +        struct simap *route_tables, const struct hmap *bfd_connections,
> > > > >          struct lflow_ref *lflow_ref)
> > > > >  {
> > > > >      ovs_assert(od->nbr);
> > > > > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> > > > >
> > > > >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> > > > >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > > > > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > > > > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> > > > >      struct ecmp_groups_node *group;
> > > > >
> > > > >      for (int i = 0; i < od->nbr->n_ports; i++) {
> > > > >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > > > > -                                &route_tables, lflow_ref);
> > > > > +                                route_tables, lflow_ref);
> > > > >      }
> > > > >
> > > > > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > -        struct parsed_route *route =
> > > > > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > > > > -                              od->nbr->static_routes[i], bfd_connections);
> > > > > -        if (!route) {
> > > > > -            continue;
> > > > > -        }
> > > > > +    struct parsed_route *route;
> > > > > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > > > > +                             parsed_routes) {
> > > > >          group = ecmp_groups_find(&ecmp_groups, route);
> > > > >          if (group) {
> > > > >              ecmp_groups_add_route(group, route);
> > > > > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> > > > >      }
> > > > >      const struct unique_routes_node *ur;
> > > > >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > > > > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > > > > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > > > > +                                bfd_connections, lflow_ref);
> > > > >      }
> > > > >      ecmp_groups_destroy(&ecmp_groups);
> > > > >      unique_routes_destroy(&unique_routes);
> > > > > -    parsed_routes_destroy(&parsed_routes);
> > > > > -    simap_destroy(&route_tables);
> > > > >  }
> > > > >
> > > > >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > > > > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> > > > >      }
> > > > >  }
> > > > >
> > > > > +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->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;
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > +                     const struct hmap *bfd_connections,
> > > > > +                     struct hmap *route_policies,
> > > > > +                     struct hmap *bfd_active_connections)
> > > > > +{
> > > > > +    struct route_policy *rp;
> > > > > +
> > > > > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > > > > +        if (rp->nbr == od->nbr) {
> > > > > +            rp->stale = true;
> > > > > +        }
> > > > > +    }
> > > > > +
> > > > > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > > > > +        size_t n_valid_nexthops = 0;
> > > > > +        char **valid_nexthops = NULL;
> > > > > +
> > > > > +        if (!strcmp(rule->action, "reroute")) {
> > > > > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > > > > +
> > > > > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > > > > +            for (size_t j = 0; j < n_nexthops; j++) {
> > > > > +                char *nexthop = rule->n_nexthops
> > > > > +                    ? rule->nexthops[j] : rule->nexthop;
> > > > > +                struct ovn_port *out_port =
> > > > > +                    get_outport_for_routing_policy_nexthop(
> > > > > +                            od, lr_ports, rule->priority, nexthop);
> > > > > +                if (!out_port || !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->nbr = od->nbr;
> > > > > +
> > > > > +        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 {
> > > > > +            rp->stale = false;
> > > > > +            free(valid_nexthops);
> > > > > +            free(new_rp);
> > > > > +        }
> > > > > +    }
> > > > > +
> > > > > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > > > > +        if (!rp->stale) {
> > > > > +            continue;
> > > > > +        }
> > > > > +
> > > > > +        hmap_remove(route_policies, &rp->key_node);
> > > > > +        free(rp->valid_nexthops);
> > > > > +        free(rp);
> > > > > +    }
> > > > > +}
> > > > > +
> > > > >  /* Logical router ingress table POLICY: Policy.
> > > > >   *
> > > > >   * A packet that arrives at this table is an IP packet that should be
> > > > > @@ -12862,7 +13104,7 @@ static void
> > > > >  build_ingress_policy_flows_for_lrouter(
> > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > >          const struct hmap *lr_ports,
> > > > > -        const struct hmap *bfd_connections,
> > > > > +        struct hmap *route_policies,
> > > > >          struct lflow_ref *lflow_ref)
> > > > >  {
> > > > >      ovs_assert(od->nbr);
> > > > > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> > > > >
> > > > >      /* Convert routing policies to flows. */
> > > > >      uint16_t ecmp_group_id = 1;
> > > > > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > -        const struct nbrec_logical_router_policy *rule
> > > > > -            = od->nbr->policies[i];
> > > > > +    struct route_policy *rp;
> > > > > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > > > > +                             route_policies) {
> > > > > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > >          bool is_ecmp_reroute =
> > > > >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> > > > >
> > > > >          if (is_ecmp_reroute) {
> > > > > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > > > > -                                            bfd_connections, ecmp_group_id,
> > > > > -                                            lflow_ref);
> > > > > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > > > > +                                            ecmp_group_id, lflow_ref);
> > > > >              ecmp_group_id++;
> > > > >          } else {
> > > > > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > > > > -                                      bfd_connections, &rule->header_,
> > > > > -                                      lflow_ref);
> > > > > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > > > > +                                      &rule->header_, lflow_ref);
> > > > >          }
> > > > >      }
> > > > >  }
> > > > > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > >                              struct lflow_table *lflows,
> > > > >                              struct ds *match, struct ds *actions,
> > > > >                              const struct shash *meter_groups,
> > > > > +                            const struct hmap *bfd_connections,
> > > > >                              struct lflow_ref *lflow_ref)
> > > > >  {
> > > > >      ovs_assert(op->nbrp);
> > > > > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > >      }
> > > > >
> > > > >      /* BFD msg handling */
> > > > > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > > > > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > > > > +                            lflow_ref);
> > > > >
> > > > >      /* ICMP time exceeded */
> > > > >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > > > > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> > > > >  static void
> > > > >  build_routable_flows_for_router_port(
> > > > >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > > > > +    const struct hmap *bfd_connections,
> > > > >      struct lflow_table *lflows,
> > > > >      struct ds *match,
> > > > >      struct ds *actions)
> > > > > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> > > > >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> > > > >                                laddrs->ipv4_addrs[k].network_s,
> > > > >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > > > > -                              &router_port->nbrp->header_, false,
> > > > > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > +                              bfd_connections, &router_port->nbrp->header_,
> > > > > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > >                                lrp->stateful_lflow_ref);
> > > > >                  }
> > > > >              }
> > > > > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> > > > >  static void
> > > > >  build_lbnat_lflows_iterate_by_lrp(
> > > > >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > > > > -    const struct shash *meter_groups, struct ds *match,
> > > > > -    struct ds *actions, struct lflow_table *lflows)
> > > > > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > > > > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> > > > >  {
> > > > >      ovs_assert(op->nbrp);
> > > > >
> > > > > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> > > > >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> > > > >                                  actions, lflows);
> > > > >
> > > > > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > > > > -                                         actions);
> > > > > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > > > > +                                         lflows, match, actions);
> > > > >  }
> > > > >
> > > > >  static void
> > > > > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> > > > >      struct ds actions;
> > > > >      size_t thread_lflow_counter;
> > > > >      const char *svc_monitor_mac;
> > > > > +    struct hmap *parsed_routes;
> > > > > +    struct hmap *route_policies;
> > > > > +    struct simap *route_tables;
> > > > >  };
> > > > >
> > > > >  /* Helper function to combine all lflow generation which is iterated by
> > > > > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > > > >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > -                                         lsi->bfd_connections,
> > > > > -                                         NULL);
> > > > > +                                         lsi->parsed_routes, lsi->route_tables,
> > > > > +                                         lsi->bfd_connections, NULL);
> > > > >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> > > > >                                           &lsi->actions, NULL);
> > > > >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > -                                           lsi->bfd_connections, NULL);
> > > > > +                                           lsi->route_policies, NULL);
> > > > >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > >                                            &lsi->match, &lsi->actions,
> > > > > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > >                                            &lsi->actions, op->lflow_ref);
> > > > >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > >                                                  &lsi->actions, op->lflow_ref);
> > > > > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > > > > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > > > > +                                   lsi->lflows, op->lflow_ref);
> > > > >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > >                                         &lsi->actions, lsi->meter_groups,
> > > > >                                         op->lflow_ref);
> > > > > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > >                                              lsi->meter_groups,
> > > > >                                              op->lflow_ref);
> > > > >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > > > > -                                lsi->meter_groups, op->lflow_ref);
> > > > > +                                lsi->meter_groups, lsi->bfd_connections,
> > > > > +                                op->lflow_ref);
> > > > >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> > > > >                                                   &lsi->actions, op->lflow_ref);
> > > > >  }
> > > > > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> > > > >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> > > > >                      build_lbnat_lflows_iterate_by_lrp(
> > > > >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > > > > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > > > > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > > > > +                        lsi->lflows);
> > > > >                  }
> > > > >              }
> > > > >              for (bnum = control->id;
> > > > > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> > > > >      const struct hmap *svc_monitor_map,
> > > > >      const struct hmap *bfd_connections,
> > > > >      const struct chassis_features *features,
> > > > > -    const char *svc_monitor_mac)
> > > > > +    const char *svc_monitor_mac,
> > > > > +    struct hmap *parsed_routes,
> > > > > +    struct hmap *route_policies,
> > > > > +    struct simap *route_tables)
> > > > >  {
> > > > >
> > > > >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > > > > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> > > > >              lsiv[index].svc_check_match = svc_check_match;
> > > > >              lsiv[index].thread_lflow_counter = 0;
> > > > >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > > > > +            lsiv[index].parsed_routes = parsed_routes;
> > > > > +            lsiv[index].route_tables = route_tables;
> > > > > +            lsiv[index].route_policies = route_policies;
> > > > >              ds_init(&lsiv[index].match);
> > > > >              ds_init(&lsiv[index].actions);
> > > > >
> > > > > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> > > > >              .features = features,
> > > > >              .svc_check_match = svc_check_match,
> > > > >              .svc_monitor_mac = svc_monitor_mac,
> > > > > +            .parsed_routes = parsed_routes,
> > > > > +            .route_tables = route_tables,
> > > > > +            .route_policies = route_policies,
> > > > >              .match = DS_EMPTY_INITIALIZER,
> > > > >              .actions = DS_EMPTY_INITIALIZER,
> > > > >          };
> > > > > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> > > > >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> > > > >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> > > > >                                                lsi.meter_groups,
> > > > > +                                              lsi.bfd_connections,
> > > > >                                                &lsi.match,
> > > > >                                                &lsi.actions,
> > > > >                                                lsi.lflows);
> > > > > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> > > > >                                      input_data->svc_monitor_map,
> > > > >                                      input_data->bfd_connections,
> > > > >                                      input_data->features,
> > > > > -                                    input_data->svc_monitor_mac);
> > > > > +                                    input_data->svc_monitor_mac,
> > > > > +                                    input_data->parsed_routes,
> > > > > +                                    input_data->route_policies,
> > > > > +                                    input_data->route_tables);
> > > > >
> > > > >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> > > > >          parallelization_state = STATE_USE_PARALLELIZATION;
> > > > > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> > > > >              build_lbnat_lflows_iterate_by_lrp(op,
> > > > >                                                lflow_input->lr_stateful_table,
> > > > >                                                lflow_input->meter_groups,
> > > > > +                                              lflow_input->bfd_connections,
> > > > >                                                &match, &actions,
> > > > >                                                lflows);
> > > > >
> > > > > @@ -17490,6 +17754,27 @@ 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);
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +static_routes_init(struct static_routes_data *data)
> > > > > +{
> > > > > +    hmap_init(&data->parsed_routes);
> > > > > +    simap_init(&data->route_tables);
> > > > > +    hmap_init(&data->bfd_active_connections);
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +bfd_init(struct bfd_data *data)
> > > > > +{
> > > > > +    hmap_init(&data->bfd_connections);
> > > > > +}
> > > > > +
> > > > >  void
> > > > >  northd_destroy(struct northd_data *data)
> > > > >  {
> > > > > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> > > > >      destroy_northd_tracked_data(data);
> > > > >  }
> > > > >
> > > > > +static void
> > > > > +__bfd_destroy(struct hmap *bfd_connections)
> > > > > +{
> > > > > +    struct bfd_entry *bfd_e;
> > > > > +
> > > > > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > > > > +        bfd_erase_entry(bfd_e);
> > > > > +    }
> > > > > +    hmap_destroy(bfd_connections);
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +bfd_destroy(struct bfd_data *data)
> > > > > +{
> > > > > +    __bfd_destroy(&data->bfd_connections);
> > > > > +}
> > > > > +
> > > > > +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);
> > > > > +}
> > > > > +
> > > > > +void
> > > > > +static_routes_destroy(struct static_routes_data *data)
> > > > > +{
> > > > > +    struct parsed_route *r;
> > > > > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > > > > +        free(r);
> > > > > +    }
> > > > > +    hmap_destroy(&data->parsed_routes);
> > > > > +
> > > > > +    simap_destroy(&data->route_tables);
> > > > > +    __bfd_destroy(&data->bfd_active_connections);
> > > > > +}
> > > > > +
> > > > >  void
> > > > >  ovnnb_db_run(struct northd_input *input_data,
> > > > >               struct northd_data *data,
> > > > > diff --git a/northd/northd.h b/northd/northd.h
> > > > > index d4a8d75ab..432072f9f 100644
> > > > > --- a/northd/northd.h
> > > > > +++ b/northd/northd.h
> > > > > @@ -23,6 +23,7 @@
> > > > >  #include "northd/en-port-group.h"
> > > > >  #include "northd/ipam.h"
> > > > >  #include "openvswitch/hmap.h"
> > > > > +#include "simap.h"
> > > > >  #include "ovs-thread.h"
> > > > >
> > > > >  struct northd_input {
> > > > > @@ -160,14 +161,34 @@ 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;
> > > > > +    const struct nbrec_logical_router *nbr;
> > > > > +    bool stale;
> > > > > +};
> > > > > +
> > > > > +struct static_routes_data {
> > > > > +    struct hmap parsed_routes;
> > > > > +    struct simap route_tables;
> > > > > +    struct hmap bfd_active_connections;
> > > > > +};
> > > > > +
> > > > > +struct route_policies_data {
> > > > > +    struct hmap route_policies;
> > > > > +    struct hmap bfd_active_connections;
> > > > > +};
> > > > > +
> > > > > +struct bfd_data {
> > > > > +    struct hmap bfd_connections;
> > > > > +};
> > > > > +
> > > > >  struct lr_nat_table;
> > > > >
> > > > >  struct lflow_input {
> > > > > -    /* Northbound table references */
> > > > > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > > > > -
> > > > >      /* Southbound table references */
> > > > > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> > > > >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> > > > >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> > > > >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > > > > @@ -190,6 +211,9 @@ struct lflow_input {
> > > > >      const struct hmap *svc_monitor_map;
> > > > >      bool ovn_internal_version_changed;
> > > > >      const char *svc_monitor_mac;
> > > > > +    struct hmap *parsed_routes;
> > > > > +    struct hmap *route_policies;
> > > > > +    struct simap *route_tables;
> > > > >  };
> > > > >
> > > > >  extern int parallelization_state;
> > > > > @@ -593,8 +617,6 @@ struct ovn_port {
> > > > >
> > > > >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> > > > >
> > > > > -    bool has_bfd;
> > > > > -
> > > > >      /* The port's peer:
> > > > >       *
> > > > >       *     - A switch port S of type "router" has a router port R as a peer,
> > > > > @@ -653,6 +675,20 @@ struct ovn_port {
> > > > >      struct lflow_ref *stateful_lflow_ref;
> > > > >  };
> > > > >
> > > > > +struct parsed_route {
> > > > > +    struct hmap_node key_node;
> > > > > +    struct in6_addr prefix;
> > > > > +    unsigned int plen;
> > > > > +    bool is_src_route;
> > > > > +    uint32_t route_table_id;
> > > > > +    uint32_t hash;
> > > > > +    const struct nbrec_logical_router_static_route *route;
> > > > > +    bool ecmp_symmetric_reply;
> > > > > +    bool is_discard_route;
> > > > > +    const struct nbrec_logical_router *nbr;
> > > > > +    bool stale;
> > > > > +};
> > > > > +
> > > > >  void ovnnb_db_run(struct northd_input *input_data,
> > > > >                    struct northd_data *data,
> > > > >                    struct ovsdb_idl_txn *ovnnb_txn,
> > > > > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > > > > +                         struct hmap *, struct hmap *, struct simap *,
> > > > > +                         struct hmap *);
> > > > > +uint32_t get_route_table_id(struct simap *, const char *);
> > > > > +void static_routes_init(struct static_routes_data *);
> > > > > +void static_routes_destroy(struct static_routes_data *);
> > > > > +
> > > > > +void bfd_init(struct bfd_data *);
> > > > > +void bfd_destroy(struct bfd_data *);
> > > > > +
> > > > >  struct lflow_table;
> > > > >  struct lr_stateful_tracked_data;
> > > > >  struct ls_stateful_tracked_data;
> > > > > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> > > > >                                     struct hmap *lbgrp_datapaths_map,
> > > > >                                     struct northd_tracked_data *);
> > > > >
> > > > > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > -                     const struct nbrec_bfd_table *,
> > > > > -                     const struct sbrec_bfd_table *,
> > > > > -                     const struct hmap *lr_ports,
> > > > > -                     struct hmap *bfd_connections);
> > > > > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > > > > -                             struct hmap *bfd_map);
> > > > > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > > > > +                          const struct hmap *, struct hmap *, struct hmap *);
> > > > > +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 *, struct hmap *);
> > > > > +void build_bfd_map(const struct nbrec_bfd_table *,
> > > > > +                   const struct sbrec_bfd_table *, struct hmap *);
> > > > >  void run_update_worker_pool(int n_threads);
> > > > >
> > > > >  const struct ovn_datapath *northd_get_datapath_for_port(
> > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > index 199197f09..710500dce 100644
> > > > > --- a/tests/ovn-northd.at
> > > > > +++ b/tests/ovn-northd.at
> > > > > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> > > > >      ovn-sbctl list meter >> $1
> > > > >      ovn-sbctl list meter_band >> $1
> > > > >      ovn-sbctl list port_group >> $1
> > > > > +    ovn-sbctl list bfd >> $1
> > > > >      ovn-sbctl dump-flows > lflows_$1
> > > > >  ])
> > > > >
> > > > > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> > > > >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> > > > >  done
> > > > >
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> > > > >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> > > > >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > > > > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> > > > >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> > > > >                       min_rx=1000 min_tx=1000 status=admin_down
> > > > >
> > > > > +check_engine_stats northd norecompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> > > > >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> > > > >
> > > > > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> > > > >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> > > > >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> > > > >
> > > > > +check_engine_stats northd norecompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> > > > >  wait_column down bfd status logical_port=r0-sw1
> > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> > > > >
> > > > > +check_engine_stats northd recompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats static_routes recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> > > > >  wait_column down bfd status logical_port=r0-sw2
> > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > > > > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> > > > >  wait_column down bfd status logical_port=r0-sw5
> > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> > > > >
> > > > > +check_engine_stats northd recompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats static_routes recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> > > > >  wait_column down bfd status logical_port=r0-sw6
> > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> > > > >
> > > > > +check_engine_stats northd recompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats route_policies recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> > > > >  wait_column down bfd status logical_port=r0-sw7
> > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > > > > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> > > > >
> > > > > +check_engine_stats northd recompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats static_routes recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  check ovn-nbctl lr-policy-del r0
> > > > >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> > > > >
> > > > > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> > > > >  wait_column down bfd status dst_ip=192.168.9.3
> > > > >  wait_column down bfd status dst_ip=192.168.9.4
> > > > >
> > > > > +check_engine_stats northd recompute nocompute
> > > > > +check_engine_stats bfd recompute nocompute
> > > > > +check_engine_stats route_policies recompute nocompute
> > > > > +check_engine_stats lflow recompute nocompute
> > > > > +check_engine_stats northd_output norecompute compute
> > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > +
> > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> > > > >
> > > > > --
> > > > > 2.45.2
> > > > >
> > > >
> >
Han Zhou Aug. 8, 2024, 4:22 p.m. UTC | #6
On Wed, Aug 7, 2024 at 2:36 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Wed, Aug 7, 2024 at 10:29 AM Lorenzo Bianconi
> <lorenzo.bianconi@redhat.com> wrote:
> >
> > > On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
> > > <lorenzo.bianconi@redhat.com> wrote:
> > > >
> > > > [...]
> > > > > > -void
> > > > > > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > -                        struct hmap *bfd_map)
> > > > > > +static bool
> > > > > > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> > > > > >  {
> > > > > > -    const struct nbrec_bfd *nb_bt;
> > > > > >      struct bfd_entry *bfd_e;
> > > > > > -
> > > > > > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > > -        if (!bfd_e) {
> > > > > > -            continue;
> > > > > > -        }
> > > > > > -
> > > > > > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > > > > > -            /* no user for this bfd connection */
> > > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> > > > >
> > > > > I like the idea of checking the existence of the bfd entry to decide
> > > > > if a port has bfd instead of relying on the has_bfd status. However, I
> > > > > have performance concerns for this loop. The bfd_port_lookup()
> > > > > searches by hashing both logical_port and dst_ip, but here for
> > > > > logical_port it just iterates all bfd entries, which looks
> > > > > unreasonable. It may be better to add a hindex for logical_port.
> > > >
> > > > Hi Han,
> > > >
> > > > I did not get what you mean here, can you please provide more details?
> > > > Do you mean another hmap to link the logical_port name to the bfd_entry?
> > > >
> > >
> > > The goal is to avoid this O(n) iteration looking for a given
> > > logical_port. Since multiple bfd entries can have the same
> > > logical_port name, it would be better to use the struct hindex instead
> > > of struct hmap to link logical_port name to the bfd_entry.
> >
> > ack, fine. Since in this case we just need to know if the logical_port is used
> > in at least one bfd_entry, I guess a simap to count number of bfd_entry for a
> > given logical port is enough, what do you think?
> >
>
> Yes, or it can be just a sset, if we only care about the existence (0
> or >0) but not the actual count.
>

To unblock the ECMP series, I am ok to merge it as is for now, and
optimize with sset later, preferably before the release.

Acked-by: Han Zhou <hzhou@ovn.org>

> > >
> > > > >
> > > > > > +        if (!strcmp(bfd_e->logical_port, port)) {
> > > > > > +            return true;
> > > > > >          }
> > > > > >      }
> > > > > > -
> > > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > > > > -        free(bfd_e);
> > > > > > -    }
> > > > > > +    return false;
> > > > > >  }
> > > > > >
> > > > > > +
> > > > > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > > > > >  #define BFD_DEF_MINRX       1000 /* 1s */
> > > > > >  #define BFD_DEF_DETECT_MULT 5
> > > > > > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> > > > > >      return port + BFD_UDP_SRC_PORT_START;
> > > > > >  }
> > > > > >
> > > > > > -void
> > > > > > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > > > > > +static char *
> > > > > > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > > > > > +                          const struct hmap *rp_bfd_connections,
> > > > > > +                          const struct hmap *sr_bfd_connections)
> > > > > >  {
> > > > > > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > > > > > -    const struct sbrec_bfd *sb_bt;
> > > > > > -    unsigned long *bfd_src_ports;
> > > > > > -    struct bfd_entry *bfd_e;
> > > > > > -    uint32_t hash;
> > > > > > +    struct bfd_entry *bfd_rp, *bfd_sr;
> > > > > >
> > > > > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > > > > > +                             nb_bt->dst_ip);
> > > > > > +    if (!bfd_rp) {
> > > > > > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > > > > > +                                 nb_bt->dst_ip);
> > > > > > +        if (!bfd_sr) {
> > > > > > +            return "admin_down";
> > > > > > +        }
> > > > > > +    }
> > > > > >
> > > > > > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > > -        bfd_e = xmalloc(sizeof *bfd_e);
> > > > > > -        bfd_e->sb_bt = sb_bt;
> > > > > > -        hash = hash_string(sb_bt->dst_ip, 0);
> > > > > > -        hash = hash_string(sb_bt->logical_port, hash);
> > > > > > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > > > > > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > +               const struct hmap *lr_ports,
> > > > > > +               const struct hmap *bfd_connections,
> > > > > > +               const struct hmap *rp_bfd_connections,
> > > > > > +               const struct hmap *sr_bfd_connections,
> > > > > > +               struct hmap *sync_bfd_connections)
> > > > > > +{
> > > > > > +    if (!ovnsb_txn) {
> > > > > > +        return;
> > > > > > +    }
> > > > > > +
> > > > > > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > +
> > > > > > +    struct bfd_entry *bfd_e;
> > > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > > > > > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > > > > > +                                              bfd_e->logical_port,
> > > > > > +                                              bfd_e->dst_ip, bfd_e->status);
> > > > >
> > > > > It looks like we are blindly allocating a new entry for each entry in
> > > > > bfd_connections without checking if it already exists. Did I
> > > > > misunderstand?
> > > >
> > > > So far sync_bfd_connections always start from scratch and it is 1:1 with
> > > > bfd_connections, so I guess an entry can't already exist. But I prefer to be
> > > > more generic and check it. I will fix it.
> > > >
> > >
> > > What I meant was that now the sync_bfd_connections table is persistent
> > > across I-P engine runs (I didn't see it get destroyed across
> > > iterations but correct me if I am wrong), the above code just
> > > allocates entries again and again every iteration without removing the
> > > old ones. For simplicity we may just destroy old entries at the
> > > beginning of this function because now we always do recompute. In the
> > > future it may be optimized to incrementally consolidate new/old
> > > entries. Does this make sense?
> >
> >
> > sync_bfd_connections is destroyed at the beginning of en_bfd_sync_run() so I
> > guess we already have what you are suggesting, right? Anyway we can still use
> > bfd_port_lookup() to check if an entry has been already allocated. What do you
> > prefer?
> >
>
> Oh, yes. Sorry I missed that. If it is recreated then there is no
> point to check the existence again.
>
> Thanks,
> Han
>
> > Regards,
> > Lorenzo
> >
> > >
> > > Thanks,
> > > Han
> > >
> > > > Regards,
> > > > Lorenzo
> > > >
> > > > >
> > > > > Thanks,
> > > > > Han
> > > > >
> > > > > > +        e->nb_bt = bfd_e->nb_bt;
> > > > > > +        e->sb_bt = bfd_e->sb_bt;
> > > > > > +        e->stale = true;
> > > > > > +        /* we need to check if this entry is even in the BFD nb db table */
> > > > > > +        if (bfd_e->sb_bt) {
> > > > > > +            bitmap_set1(bfd_src_ports,
> > > > > > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > > +        }
> > > > > >      }
> > > > > >
> > > > > >      const struct nbrec_bfd *nb_bt;
> > > > > >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > -        if (!nb_bt->status) {
> > > > > > -            /* default state is admin_down */
> > > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > > > > > +                                nb_bt->dst_ip);
> > > > > > +        if (!bfd_e) {
> > > > > > +            continue;
> > > > > >          }
> > > > > >
> > > > > >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > > > > > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > > -        if (!bfd_e) {
> > > > > > +        if (!op || !op->sb) {
> > > > > > +            /* skip not bounded ports */
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        nbrec_bfd_set_status(nb_bt,
> > > > > > +                             bfd_get_connection_status(nb_bt,
> > > > > > +                                                       rp_bfd_connections,
> > > > > > +                                                       sr_bfd_connections));
> > > > > > +        if (!bfd_e->sb_bt) {
> > > > > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > > > > >              if (udp_src < 0) {
> > > > > >                  continue;
> > > > > >              }
> > > > > >
> > > > > > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > > > +            /* Add entry to bfd sb table. */
> > > > > > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > > >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> > > > > >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> > > > > >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> > > > > >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> > > > > >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > > > > > -            if (op && op->sb && op->sb->chassis) {
> > > > > > +            if (op->sb->chassis) {
> > > > > >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> > > > > >              }
> > > > > >
> > > > > > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> > > > > >                  }
> > > > > >              }
> > > > > > +
> > > > > >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > > > > > -            if (op && op->sb && op->sb->chassis &&
> > > > > > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > > > > > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > > > > > +                                           bfd_e->sb_bt->chassis_name)) {
> > > > > >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> > > > > >                                             op->sb->chassis->name);
> > > > > >              }
> > > > > > -
> > > > > > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > > > > > -            bfd_e->ref = false;
> > > > > > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > > > > > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > > > > > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> > > > > >          }
> > > > > >
> > > > > > -        if (op) {
> > > > > > -            op->has_bfd = true;
> > > > > > -        }
> > > > > > +        bfd_e->stale = false;
> > > > > >      }
> > > > > >
> > > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > > > > > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > > > > > -                                            bfd_e->sb_bt->logical_port);
> > > > > > -        if (op) {
> > > > > > -            op->has_bfd = false;
> > > > > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > > > > > +        if (bfd_e->stale) {
> > > > > > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > > > > > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > > +            bfd_erase_entry(bfd_e);
> > > > > >          }
> > > > > > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > > -        free(bfd_e);
> > > > > >      }
> > > > > > -    hmap_destroy(&sb_only);
> > > > > >
> > > > > >      bitmap_free(bfd_src_ports);
> > > > > >  }
> > > > > >
> > > > > > +void
> > > > > > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > > +              struct hmap *bfd_connections)
> > > > > > +{
> > > > > > +    struct bfd_entry *bfd_e;
> > > > > > +
> > > > > > +    /* align bfd_map to sb db */
> > > > > > +    const struct sbrec_bfd *sb_bt;
> > > > > > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > > > > > +                                sb_bt->dst_ip);
> > > > > > +        if (!bfd_e) {
> > > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > > > > > +                                    sb_bt->dst_ip,  sb_bt->status);
> > > > > > +        }
> > > > > > +        bfd_e->sb_bt = sb_bt;
> > > > > > +    }
> > > > > > +
> > > > > > +    const struct nbrec_bfd *nb_bt;
> > > > > > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > > +                                nb_bt->dst_ip);
> > > > > > +        if (!bfd_e) {
> > > > > > +            /* brand new entry. */
> > > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > > > > > +                                    nb_bt->dst_ip, "admin_down");
> > > > > > +        }
> > > > > > +        bfd_e->nb_bt = nb_bt;
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > >  /* Returns a string of the IP address of the router port 'op' that
> > > > > >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> > > > > >   *
> > > > > > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> > > > > >      return NULL;
> > > > > >  }
> > > > > >
> > > > > > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > > > > > -
> > > > > > -static bool check_bfd_state(
> > > > > > -        const struct nbrec_logical_router_policy *rule,
> > > > > > -        const struct hmap *bfd_connections,
> > > > > > -        struct ovn_port *out_port,
> > > > > > -        const char *nexthop)
> > > > > > +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);
> > > > > > -    bool ret = true;
> > > > > >
> > > > > >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> > > > > >          /* Check if there is a BFD session associated to the reroute
> > > > > > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> > > > > >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > > >                                                    nb_bt->logical_port,
> > > > > >                                                    nb_bt->dst_ip);
> > > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > > -        if (bfd_e) {
> > > > > > -            bfd_e->ref = true;
> > > > > > +        if (!bfd_e) {
> > > > > > +            continue;
> > > > > >          }
> > > > > >
> > > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > > +        /* 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);
> > > > > >          }
> > > > > >
> > > > > > -        ret = strcmp(nb_bt->status, "down");
> > > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > > -        break;
> > > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > > +            bfd_set_status(bfd_rp, "down");
> > > > > > +        }
> > > > > > +
> > > > > > +        return strcmp(bfd_rp->status, "down");
> > > > > >      }
> > > > > >
> > > > > > -    return ret;
> > > > > > +    return true;
> > > > > >  }
> > > > > >
> > > > > >  static void
> > > > > >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > -                          const struct hmap *lr_ports,
> > > > > > -                          const struct nbrec_logical_router_policy *rule,
> > > > > > -                          const struct hmap *bfd_connections,
> > > > > > +                          const struct hmap *lr_ports, struct route_policy *rp,
> > > > > >                            const struct ovsdb_idl_row *stage_hint,
> > > > > >                            struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > >
> > > > > >      if (!strcmp(rule->action, "reroute")) {
> > > > > >          ovs_assert(rule->n_nexthops <= 1);
> > > > > >
> > > > > > -        char *nexthop =
> > > > > > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > > > > > +        if (!rp->n_valid_nexthops) {
> > > > > > +            return;
> > > > > > +        }
> > > > > > +
> > > > > > +        char *nexthop = rp->valid_nexthops[0];
> > > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > > >               od, lr_ports, rule->priority, nexthop);
> > > > > >          if (!out_port) {
> > > > > > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > >              return;
> > > > > >          }
> > > > > >
> > > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > > > > > -            return;
> > > > > > -        }
> > > > > > -
> > > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > > >          if (pkt_mark) {
> > > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > > > @@ -10070,19 +10156,18 @@ static void
> > > > > >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > >                                  struct ovn_datapath *od,
> > > > > >                                  const struct hmap *lr_ports,
> > > > > > -                                const struct nbrec_logical_router_policy *rule,
> > > > > > -                                const struct hmap *bfd_connections,
> > > > > > +                                struct route_policy *rp,
> > > > > >                                  uint16_t ecmp_group_id,
> > > > > >                                  struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > > -    ovs_assert(rule->n_nexthops > 1);
> > > > > > -
> > > > > >      bool nexthops_is_ipv4 = true;
> > > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > > +    ovs_assert(rule->n_nexthops > 1);
> > > > > >
> > > > > >      /* Check that all the nexthops belong to the same addr family before
> > > > > >       * adding logical flows. */
> > > > > > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > > >
> > > > > >          if (i == 0) {
> > > > > >              nexthops_is_ipv4 = is_ipv4;
> > > > > > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> > > > > >                           "the match [%s] do not belong to the same address "
> > > > > >                           "family as other next hops",
> > > > > > -                         rule->nexthops[i], rule->match);
> > > > > > +                         rp->valid_nexthops[i], rule->match);
> > > > > >              return;
> > > > > >          }
> > > > > >      }
> > > > > > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > >
> > > > > > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > > > > > -    size_t n_valid_nexthops = 0;
> > > > > > -
> > > > > > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > > > > > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > > > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > > > > > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> > > > > >          if (!out_port) {
> > > > > >              goto cleanup;
> > > > > >          }
> > > > > >
> > > > > >          const char *lrp_addr_s =
> > > > > > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > > > > > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> > > > > >          if (!lrp_addr_s) {
> > > > > >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > > > > >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> > > > > >                              " priority %"PRId64" nexthop %s",
> > > > > > -                            rule->priority, rule->nexthops[i]);
> > > > > > +                            rule->priority, rp->valid_nexthops[i]);
> > > > > >              goto cleanup;
> > > > > >          }
> > > > > >
> > > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > > > > > -                             rule->nexthops[i])) {
> > > > > > -            continue;
> > > > > > -        }
> > > > > > -
> > > > > > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > > > > > -
> > > > > >          ds_clear(&actions);
> > > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > > >          if (pkt_mark) {
> > > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > > >          }
> > > > > >
> > > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > > >
> > > > > >          ds_put_format(&actions, "%s = %s; "
> > > > > >                        "%s = %s; "
> > > > > > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > >                        "flags.loopback = 1; "
> > > > > >                        "next;",
> > > > > >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > > > > > -                      rule->nexthops[i],
> > > > > > +                      rp->valid_nexthops[i],
> > > > > >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> > > > > >                        lrp_addr_s,
> > > > > >                        out_port->lrp_networks.ea_s,
> > > > > > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > >                                  lflow_ref);
> > > > > >      }
> > > > > >
> > > > > > -    if (!n_valid_nexthops) {
> > > > > > -        goto cleanup;
> > > > > > -    }
> > > > > > -
> > > > > >      ds_clear(&actions);
> > > > > > -    if (n_valid_nexthops > 1) {
> > > > > > +    if (rp->n_valid_nexthops > 1) {
> > > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > > >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> > > > > >                        REG_ECMP_MEMBER_ID);
> > > > > >
> > > > > > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > > > > > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > >              if (i > 0) {
> > > > > >                  ds_put_cstr(&actions, ", ");
> > > > > >              }
> > > > > >
> > > > > > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > > > > > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> > > > > >          }
> > > > > >          ds_put_cstr(&actions, ");");
> > > > > >      } else {
> > > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > > > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > > > > > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > > > > > -                      valid_nexthops[0]);
> > > > > > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > > > > > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> > > > > >      }
> > > > > >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> > > > > >                              rule->priority, rule->match,
> > > > > >                              ds_cstr(&actions), &rule->header_,
> > > > > >                              lflow_ref);
> > > > > > -
> > > > > >  cleanup:
> > > > > > -    free(valid_nexthops);
> > > > > >      ds_destroy(&match);
> > > > > >      ds_destroy(&actions);
> > > > > >  }
> > > > > > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> > > > > >      return rtb_id;
> > > > > >  }
> > > > > >
> > > > > > -static uint32_t
> > > > > > +uint32_t
> > > > > >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> > > > > >  {
> > > > > >      if (!route_table_name || !route_table_name[0]) {
> > > > > > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > >      ds_destroy(&actions);
> > > > > >  }
> > > > > >
> > > > > > -struct parsed_route {
> > > > > > -    struct ovs_list list_node;
> > > > > > -    struct in6_addr prefix;
> > > > > > -    unsigned int plen;
> > > > > > -    bool is_src_route;
> > > > > > -    uint32_t route_table_id;
> > > > > > -    uint32_t hash;
> > > > > > -    const struct nbrec_logical_router_static_route *route;
> > > > > > -    bool ecmp_symmetric_reply;
> > > > > > -    bool is_discard_route;
> > > > > > -};
> > > > > > -
> > > > > >  static uint32_t
> > > > > >  route_hash(struct parsed_route *route)
> > > > > >  {
> > > > > > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >
> > > > > >  /* Parse and validate the route. Return the parsed route if successful.
> > > > > >   * Otherwise return NULL. */
> > > > > > +
> > > > > >  static struct parsed_route *
> > > > > > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > > > > > +                    struct parsed_route *new_pr)
> > > > > > +{
> > > > > > +    struct parsed_route *pr;
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > > > > > +        if (pr->plen != new_pr->plen) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (pr->is_src_route != new_pr->is_src_route) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (pr->route_table_id != new_pr->route_table_id) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (pr->route != new_pr->route) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        return pr;
> > > > > > +    }
> > > > > > +
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > -                  struct ovs_list *routes, struct simap *route_tables,
> > > > > >                    const struct nbrec_logical_router_static_route *route,
> > > > > > -                  const struct hmap *bfd_connections)
> > > > > > +                  struct hmap *bfd_connections,
> > > > > > +                  struct hmap *routes, struct simap *route_tables,
> > > > > > +                  struct hmap *bfd_active_connections)
> > > > > >  {
> > > > > >      /* Verify that the next hop is an IP address with an all-ones mask. */
> > > > > >      struct in6_addr nexthop;
> > > > > > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> > > > > >                           UUID_FMT, route->nexthop,
> > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > -            return NULL;
> > > > > > +            return;
> > > > > >          }
> > > > > >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> > > > > >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > > > > > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> > > > > >                           UUID_FMT, route->nexthop,
> > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > -            return NULL;
> > > > > > +            return;
> > > > > >          }
> > > > > >      }
> > > > > >
> > > > > > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> > > > > >                       UUID_FMT, route->ip_prefix,
> > > > > >                       UUID_ARGS(&route->header_.uuid));
> > > > > > -        return NULL;
> > > > > > +        return;
> > > > > >      }
> > > > > >
> > > > > >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > > > > > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> > > > > >                           route->ip_prefix, route->nexthop,
> > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > -            return NULL;
> > > > > > +            return;
> > > > > >          }
> > > > > >      }
> > > > > >
> > > > > > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > >          !find_static_route_outport(od, lr_ports, route,
> > > > > >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> > > > > >                                     NULL, NULL)) {
> > > > > > -        return NULL;
> > > > > > +        return;
> > > > > >      }
> > > > > >
> > > > > >      const struct nbrec_bfd *nb_bt = route->bfd;
> > > > > >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > > > > > -        struct bfd_entry *bfd_e;
> > > > > > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > > > +                                                  nb_bt->logical_port,
> > > > > > +                                                  nb_bt->dst_ip);
> > > > > > +        if (!bfd_e) {
> > > > > > +            return;
> > > > > > +        }
> > > > > >
> > > > > > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > > -                                nb_bt->dst_ip);
> > > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > > -        if (bfd_e) {
> > > > > > -            bfd_e->ref = true;
> > > > > > +        /* This static route is linked to an active bfd session. */
> > > > > > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > > > > > +                                                   nb_bt->logical_port,
> > > > > > +                                                   nb_bt->dst_ip);
> > > > > > +        if (!bfd_sr) {
> > > > > > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > > > > > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > > > > > +                                     bfd_e->status);
> > > > > >          }
> > > > > >
> > > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > > +            bfd_set_status(bfd_sr, "down");
> > > > > >          }
> > > > > >
> > > > > > -        if (!strcmp(nb_bt->status, "down")) {
> > > > > > -            ovs_mutex_unlock(&bfd_lock);
> > > > > > -            return NULL;
> > > > > > +
> > > > > > +        if (!strcmp(bfd_sr->status, "down")) {
> > > > > > +            return;
> > > > > >          }
> > > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > >      }
> > > > > >
> > > > > > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > > > > > -    pr->prefix = prefix;
> > > > > > -    pr->plen = plen;
> > > > > > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > > > > > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > > > > > -                                                 "src-ip"));
> > > > > > -    pr->hash = route_hash(pr);
> > > > > > -    pr->route = route;
> > > > > > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > > -                                             "ecmp_symmetric_reply", false);
> > > > > > -    pr->is_discard_route = is_discard_route;
> > > > > > -    ovs_list_insert(routes, &pr->list_node);
> > > > > > -    return pr;
> > > > > > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > > > > > +    new_pr->prefix = prefix;
> > > > > > +    new_pr->plen = plen;
> > > > > > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > > > > > +                                                route->route_table);
> > > > > > +    new_pr->is_src_route = (route->policy &&
> > > > > > +                            !strcmp(route->policy, "src-ip"));
> > > > > > +    new_pr->hash = route_hash(new_pr);
> > > > > > +    new_pr->route = route;
> > > > > > +    new_pr->nbr = od->nbr;
> > > > > > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > > +                                                 "ecmp_symmetric_reply",
> > > > > > +                                                 false);
> > > > > > +    new_pr->is_discard_route = is_discard_route;
> > > > > > +
> > > > > > +    size_t hash = uuid_hash(&od->key);
> > > > > > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > > > > > +    if (!pr) {
> > > > > > +        hmap_insert(routes, &new_pr->key_node, hash);
> > > > > > +    } else {
> > > > > > +        pr->stale = false;
> > > > > > +        free(new_pr);
> > > > > > +    }
> > > > > >  }
> > > > > >
> > > > > > -static void
> > > > > > -parsed_routes_destroy(struct ovs_list *routes)
> > > > > > +void
> > > > > > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > +                    struct hmap *bfd_connections, struct hmap *routes,
> > > > > > +                    struct simap *route_tables,
> > > > > > +                    struct hmap *bfd_active_connections)
> > > > > >  {
> > > > > >      struct parsed_route *pr;
> > > > > > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > > > > > -        ovs_list_remove(&pr->list_node);
> > > > > > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > > > > > +        if (pr->nbr == od->nbr) {
> > > > > > +            pr->stale = true;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > > > > > +                          bfd_connections, routes, route_tables,
> > > > > > +                          bfd_active_connections);
> > > > > > +    }
> > > > > > +
> > > > > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > > > > +        if (!pr->stale) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        hmap_remove(routes, &pr->key_node);
> > > > > >          free(pr);
> > > > > >      }
> > > > > >  }
> > > > > > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > >            const struct ovn_port *op, const char *lrp_addr_s,
> > > > > >            const char *network_s, int plen, const char *gateway,
> > > > > >            bool is_src_route, const uint32_t rtb_id,
> > > > > > +          const struct hmap *bfd_connections,
> > > > > >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> > > > > >            int ofs, struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > >                              priority, ds_cstr(&match),
> > > > > >                              ds_cstr(&actions), stage_hint,
> > > > > >                              lflow_ref);
> > > > > > -    if (op && op->has_bfd) {
> > > > > > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> > > > > >          ds_put_format(&match, " && udp.dst == 3784");
> > > > > >          ovn_lflow_add_with_hint(lflows, op->od,
> > > > > >                                  S_ROUTER_IN_IP_ROUTING,
> > > > > > @@ -10895,6 +11030,7 @@ static void
> > > > > >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > >                          const struct hmap *lr_ports,
> > > > > >                          const struct parsed_route *route_,
> > > > > > +                        const struct hmap *bfd_connections,
> > > > > >                          struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > >      const char *lrp_addr_s = NULL;
> > > > > > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> > > > > >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> > > > > >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > > > > > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > > > > > -              route_->is_discard_route, ofs, lflow_ref);
> > > > > > +              route_->is_src_route, route_->route_table_id,
> > > > > > +              bfd_connections, &route->header_, route_->is_discard_route,
> > > > > > +              ofs, lflow_ref);
> > > > > >
> > > > > >      free(prefix_s);
> > > > > >  }
> > > > > > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> > > > > >  static void
> > > > > >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> > > > > >                          const struct shash *meter_groups,
> > > > > > +                        const struct hmap *bfd_connections,
> > > > > >                          struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > > -    if (!op->has_bfd) {
> > > > > > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> > > > > >          return;
> > > > > >      }
> > > > > >
> > > > > > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> > > > > >   * LRP 'op'.
> > > > > >   */
> > > > > >  static void
> > > > > > -build_ip_routing_flows_for_lrp(
> > > > > > -        struct ovn_port *op, struct lflow_table *lflows,
> > > > > > -        struct lflow_ref *lflow_ref)
> > > > > > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > > > > > +                               const struct hmap *bfd_connections,
> > > > > > +                               struct lflow_table *lflows,
> > > > > > +                               struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > >      ovs_assert(op->nbrp);
> > > > > >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> > > > > >                    op->lrp_networks.ipv4_addrs[i].network_s,
> > > > > >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > -                  lflow_ref);
> > > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > > >      }
> > > > > >
> > > > > >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> > > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> > > > > >                    op->lrp_networks.ipv6_addrs[i].network_s,
> > > > > >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > -                  lflow_ref);
> > > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > > >      }
> > > > > >  }
> > > > > >
> > > > > >  static void
> > > > > >  build_static_route_flows_for_lrouter(
> > > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > > > > > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > > > > > +        struct simap *route_tables, const struct hmap *bfd_connections,
> > > > > >          struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > >      ovs_assert(od->nbr);
> > > > > > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> > > > > >
> > > > > >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> > > > > >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > > > > > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > > > > > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> > > > > >      struct ecmp_groups_node *group;
> > > > > >
> > > > > >      for (int i = 0; i < od->nbr->n_ports; i++) {
> > > > > >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > > > > > -                                &route_tables, lflow_ref);
> > > > > > +                                route_tables, lflow_ref);
> > > > > >      }
> > > > > >
> > > > > > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > > -        struct parsed_route *route =
> > > > > > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > > > > > -                              od->nbr->static_routes[i], bfd_connections);
> > > > > > -        if (!route) {
> > > > > > -            continue;
> > > > > > -        }
> > > > > > +    struct parsed_route *route;
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > > > > > +                             parsed_routes) {
> > > > > >          group = ecmp_groups_find(&ecmp_groups, route);
> > > > > >          if (group) {
> > > > > >              ecmp_groups_add_route(group, route);
> > > > > > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> > > > > >      }
> > > > > >      const struct unique_routes_node *ur;
> > > > > >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > > > > > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > > > > > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > > > > > +                                bfd_connections, lflow_ref);
> > > > > >      }
> > > > > >      ecmp_groups_destroy(&ecmp_groups);
> > > > > >      unique_routes_destroy(&unique_routes);
> > > > > > -    parsed_routes_destroy(&parsed_routes);
> > > > > > -    simap_destroy(&route_tables);
> > > > > >  }
> > > > > >
> > > > > >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > > > > > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> > > > > >      }
> > > > > >  }
> > > > > >
> > > > > > +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->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;
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > +                     const struct hmap *bfd_connections,
> > > > > > +                     struct hmap *route_policies,
> > > > > > +                     struct hmap *bfd_active_connections)
> > > > > > +{
> > > > > > +    struct route_policy *rp;
> > > > > > +
> > > > > > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > > > > > +        if (rp->nbr == od->nbr) {
> > > > > > +            rp->stale = true;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > > > > > +        size_t n_valid_nexthops = 0;
> > > > > > +        char **valid_nexthops = NULL;
> > > > > > +
> > > > > > +        if (!strcmp(rule->action, "reroute")) {
> > > > > > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > > > > > +
> > > > > > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > > > > > +            for (size_t j = 0; j < n_nexthops; j++) {
> > > > > > +                char *nexthop = rule->n_nexthops
> > > > > > +                    ? rule->nexthops[j] : rule->nexthop;
> > > > > > +                struct ovn_port *out_port =
> > > > > > +                    get_outport_for_routing_policy_nexthop(
> > > > > > +                            od, lr_ports, rule->priority, nexthop);
> > > > > > +                if (!out_port || !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->nbr = od->nbr;
> > > > > > +
> > > > > > +        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 {
> > > > > > +            rp->stale = false;
> > > > > > +            free(valid_nexthops);
> > > > > > +            free(new_rp);
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > > > > > +        if (!rp->stale) {
> > > > > > +            continue;
> > > > > > +        }
> > > > > > +
> > > > > > +        hmap_remove(route_policies, &rp->key_node);
> > > > > > +        free(rp->valid_nexthops);
> > > > > > +        free(rp);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > >  /* Logical router ingress table POLICY: Policy.
> > > > > >   *
> > > > > >   * A packet that arrives at this table is an IP packet that should be
> > > > > > @@ -12862,7 +13104,7 @@ static void
> > > > > >  build_ingress_policy_flows_for_lrouter(
> > > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > >          const struct hmap *lr_ports,
> > > > > > -        const struct hmap *bfd_connections,
> > > > > > +        struct hmap *route_policies,
> > > > > >          struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > >      ovs_assert(od->nbr);
> > > > > > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> > > > > >
> > > > > >      /* Convert routing policies to flows. */
> > > > > >      uint16_t ecmp_group_id = 1;
> > > > > > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > > -        const struct nbrec_logical_router_policy *rule
> > > > > > -            = od->nbr->policies[i];
> > > > > > +    struct route_policy *rp;
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > > > > > +                             route_policies) {
> > > > > > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > >          bool is_ecmp_reroute =
> > > > > >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> > > > > >
> > > > > >          if (is_ecmp_reroute) {
> > > > > > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > > > > > -                                            bfd_connections, ecmp_group_id,
> > > > > > -                                            lflow_ref);
> > > > > > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > > > > > +                                            ecmp_group_id, lflow_ref);
> > > > > >              ecmp_group_id++;
> > > > > >          } else {
> > > > > > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > > > > > -                                      bfd_connections, &rule->header_,
> > > > > > -                                      lflow_ref);
> > > > > > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > > > > > +                                      &rule->header_, lflow_ref);
> > > > > >          }
> > > > > >      }
> > > > > >  }
> > > > > > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > > >                              struct lflow_table *lflows,
> > > > > >                              struct ds *match, struct ds *actions,
> > > > > >                              const struct shash *meter_groups,
> > > > > > +                            const struct hmap *bfd_connections,
> > > > > >                              struct lflow_ref *lflow_ref)
> > > > > >  {
> > > > > >      ovs_assert(op->nbrp);
> > > > > > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > > >      }
> > > > > >
> > > > > >      /* BFD msg handling */
> > > > > > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > > > > > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > > > > > +                            lflow_ref);
> > > > > >
> > > > > >      /* ICMP time exceeded */
> > > > > >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > > > > > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> > > > > >  static void
> > > > > >  build_routable_flows_for_router_port(
> > > > > >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > > > > > +    const struct hmap *bfd_connections,
> > > > > >      struct lflow_table *lflows,
> > > > > >      struct ds *match,
> > > > > >      struct ds *actions)
> > > > > > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> > > > > >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> > > > > >                                laddrs->ipv4_addrs[k].network_s,
> > > > > >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > > > > > -                              &router_port->nbrp->header_, false,
> > > > > > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > +                              bfd_connections, &router_port->nbrp->header_,
> > > > > > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > >                                lrp->stateful_lflow_ref);
> > > > > >                  }
> > > > > >              }
> > > > > > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> > > > > >  static void
> > > > > >  build_lbnat_lflows_iterate_by_lrp(
> > > > > >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > > > > > -    const struct shash *meter_groups, struct ds *match,
> > > > > > -    struct ds *actions, struct lflow_table *lflows)
> > > > > > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > > > > > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> > > > > >  {
> > > > > >      ovs_assert(op->nbrp);
> > > > > >
> > > > > > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> > > > > >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> > > > > >                                  actions, lflows);
> > > > > >
> > > > > > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > > > > > -                                         actions);
> > > > > > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > > > > > +                                         lflows, match, actions);
> > > > > >  }
> > > > > >
> > > > > >  static void
> > > > > > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> > > > > >      struct ds actions;
> > > > > >      size_t thread_lflow_counter;
> > > > > >      const char *svc_monitor_mac;
> > > > > > +    struct hmap *parsed_routes;
> > > > > > +    struct hmap *route_policies;
> > > > > > +    struct simap *route_tables;
> > > > > >  };
> > > > > >
> > > > > >  /* Helper function to combine all lflow generation which is iterated by
> > > > > > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > > > > >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > > -                                         lsi->bfd_connections,
> > > > > > -                                         NULL);
> > > > > > +                                         lsi->parsed_routes, lsi->route_tables,
> > > > > > +                                         lsi->bfd_connections, NULL);
> > > > > >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> > > > > >                                           &lsi->actions, NULL);
> > > > > >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > > -                                           lsi->bfd_connections, NULL);
> > > > > > +                                           lsi->route_policies, NULL);
> > > > > >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > >                                            &lsi->match, &lsi->actions,
> > > > > > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > > >                                            &lsi->actions, op->lflow_ref);
> > > > > >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > > >                                                  &lsi->actions, op->lflow_ref);
> > > > > > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > > > > > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > > > > > +                                   lsi->lflows, op->lflow_ref);
> > > > > >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > > >                                         &lsi->actions, lsi->meter_groups,
> > > > > >                                         op->lflow_ref);
> > > > > > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > > >                                              lsi->meter_groups,
> > > > > >                                              op->lflow_ref);
> > > > > >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > > > > > -                                lsi->meter_groups, op->lflow_ref);
> > > > > > +                                lsi->meter_groups, lsi->bfd_connections,
> > > > > > +                                op->lflow_ref);
> > > > > >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> > > > > >                                                   &lsi->actions, op->lflow_ref);
> > > > > >  }
> > > > > > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> > > > > >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> > > > > >                      build_lbnat_lflows_iterate_by_lrp(
> > > > > >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > > > > > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > > > > > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > > > > > +                        lsi->lflows);
> > > > > >                  }
> > > > > >              }
> > > > > >              for (bnum = control->id;
> > > > > > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> > > > > >      const struct hmap *svc_monitor_map,
> > > > > >      const struct hmap *bfd_connections,
> > > > > >      const struct chassis_features *features,
> > > > > > -    const char *svc_monitor_mac)
> > > > > > +    const char *svc_monitor_mac,
> > > > > > +    struct hmap *parsed_routes,
> > > > > > +    struct hmap *route_policies,
> > > > > > +    struct simap *route_tables)
> > > > > >  {
> > > > > >
> > > > > >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > > > > > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> > > > > >              lsiv[index].svc_check_match = svc_check_match;
> > > > > >              lsiv[index].thread_lflow_counter = 0;
> > > > > >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > > > > > +            lsiv[index].parsed_routes = parsed_routes;
> > > > > > +            lsiv[index].route_tables = route_tables;
> > > > > > +            lsiv[index].route_policies = route_policies;
> > > > > >              ds_init(&lsiv[index].match);
> > > > > >              ds_init(&lsiv[index].actions);
> > > > > >
> > > > > > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> > > > > >              .features = features,
> > > > > >              .svc_check_match = svc_check_match,
> > > > > >              .svc_monitor_mac = svc_monitor_mac,
> > > > > > +            .parsed_routes = parsed_routes,
> > > > > > +            .route_tables = route_tables,
> > > > > > +            .route_policies = route_policies,
> > > > > >              .match = DS_EMPTY_INITIALIZER,
> > > > > >              .actions = DS_EMPTY_INITIALIZER,
> > > > > >          };
> > > > > > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> > > > > >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> > > > > >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> > > > > >                                                lsi.meter_groups,
> > > > > > +                                              lsi.bfd_connections,
> > > > > >                                                &lsi.match,
> > > > > >                                                &lsi.actions,
> > > > > >                                                lsi.lflows);
> > > > > > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > >                                      input_data->svc_monitor_map,
> > > > > >                                      input_data->bfd_connections,
> > > > > >                                      input_data->features,
> > > > > > -                                    input_data->svc_monitor_mac);
> > > > > > +                                    input_data->svc_monitor_mac,
> > > > > > +                                    input_data->parsed_routes,
> > > > > > +                                    input_data->route_policies,
> > > > > > +                                    input_data->route_tables);
> > > > > >
> > > > > >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> > > > > >          parallelization_state = STATE_USE_PARALLELIZATION;
> > > > > > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > >              build_lbnat_lflows_iterate_by_lrp(op,
> > > > > >                                                lflow_input->lr_stateful_table,
> > > > > >                                                lflow_input->meter_groups,
> > > > > > +                                              lflow_input->bfd_connections,
> > > > > >                                                &match, &actions,
> > > > > >                                                lflows);
> > > > > >
> > > > > > @@ -17490,6 +17754,27 @@ 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);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +static_routes_init(struct static_routes_data *data)
> > > > > > +{
> > > > > > +    hmap_init(&data->parsed_routes);
> > > > > > +    simap_init(&data->route_tables);
> > > > > > +    hmap_init(&data->bfd_active_connections);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +bfd_init(struct bfd_data *data)
> > > > > > +{
> > > > > > +    hmap_init(&data->bfd_connections);
> > > > > > +}
> > > > > > +
> > > > > >  void
> > > > > >  northd_destroy(struct northd_data *data)
> > > > > >  {
> > > > > > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> > > > > >      destroy_northd_tracked_data(data);
> > > > > >  }
> > > > > >
> > > > > > +static void
> > > > > > +__bfd_destroy(struct hmap *bfd_connections)
> > > > > > +{
> > > > > > +    struct bfd_entry *bfd_e;
> > > > > > +
> > > > > > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > > > > > +        bfd_erase_entry(bfd_e);
> > > > > > +    }
> > > > > > +    hmap_destroy(bfd_connections);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +bfd_destroy(struct bfd_data *data)
> > > > > > +{
> > > > > > +    __bfd_destroy(&data->bfd_connections);
> > > > > > +}
> > > > > > +
> > > > > > +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);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +static_routes_destroy(struct static_routes_data *data)
> > > > > > +{
> > > > > > +    struct parsed_route *r;
> > > > > > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > > > > > +        free(r);
> > > > > > +    }
> > > > > > +    hmap_destroy(&data->parsed_routes);
> > > > > > +
> > > > > > +    simap_destroy(&data->route_tables);
> > > > > > +    __bfd_destroy(&data->bfd_active_connections);
> > > > > > +}
> > > > > > +
> > > > > >  void
> > > > > >  ovnnb_db_run(struct northd_input *input_data,
> > > > > >               struct northd_data *data,
> > > > > > diff --git a/northd/northd.h b/northd/northd.h
> > > > > > index d4a8d75ab..432072f9f 100644
> > > > > > --- a/northd/northd.h
> > > > > > +++ b/northd/northd.h
> > > > > > @@ -23,6 +23,7 @@
> > > > > >  #include "northd/en-port-group.h"
> > > > > >  #include "northd/ipam.h"
> > > > > >  #include "openvswitch/hmap.h"
> > > > > > +#include "simap.h"
> > > > > >  #include "ovs-thread.h"
> > > > > >
> > > > > >  struct northd_input {
> > > > > > @@ -160,14 +161,34 @@ 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;
> > > > > > +    const struct nbrec_logical_router *nbr;
> > > > > > +    bool stale;
> > > > > > +};
> > > > > > +
> > > > > > +struct static_routes_data {
> > > > > > +    struct hmap parsed_routes;
> > > > > > +    struct simap route_tables;
> > > > > > +    struct hmap bfd_active_connections;
> > > > > > +};
> > > > > > +
> > > > > > +struct route_policies_data {
> > > > > > +    struct hmap route_policies;
> > > > > > +    struct hmap bfd_active_connections;
> > > > > > +};
> > > > > > +
> > > > > > +struct bfd_data {
> > > > > > +    struct hmap bfd_connections;
> > > > > > +};
> > > > > > +
> > > > > >  struct lr_nat_table;
> > > > > >
> > > > > >  struct lflow_input {
> > > > > > -    /* Northbound table references */
> > > > > > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > > > > > -
> > > > > >      /* Southbound table references */
> > > > > > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> > > > > >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> > > > > >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> > > > > >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > > > > > @@ -190,6 +211,9 @@ struct lflow_input {
> > > > > >      const struct hmap *svc_monitor_map;
> > > > > >      bool ovn_internal_version_changed;
> > > > > >      const char *svc_monitor_mac;
> > > > > > +    struct hmap *parsed_routes;
> > > > > > +    struct hmap *route_policies;
> > > > > > +    struct simap *route_tables;
> > > > > >  };
> > > > > >
> > > > > >  extern int parallelization_state;
> > > > > > @@ -593,8 +617,6 @@ struct ovn_port {
> > > > > >
> > > > > >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> > > > > >
> > > > > > -    bool has_bfd;
> > > > > > -
> > > > > >      /* The port's peer:
> > > > > >       *
> > > > > >       *     - A switch port S of type "router" has a router port R as a peer,
> > > > > > @@ -653,6 +675,20 @@ struct ovn_port {
> > > > > >      struct lflow_ref *stateful_lflow_ref;
> > > > > >  };
> > > > > >
> > > > > > +struct parsed_route {
> > > > > > +    struct hmap_node key_node;
> > > > > > +    struct in6_addr prefix;
> > > > > > +    unsigned int plen;
> > > > > > +    bool is_src_route;
> > > > > > +    uint32_t route_table_id;
> > > > > > +    uint32_t hash;
> > > > > > +    const struct nbrec_logical_router_static_route *route;
> > > > > > +    bool ecmp_symmetric_reply;
> > > > > > +    bool is_discard_route;
> > > > > > +    const struct nbrec_logical_router *nbr;
> > > > > > +    bool stale;
> > > > > > +};
> > > > > > +
> > > > > >  void ovnnb_db_run(struct northd_input *input_data,
> > > > > >                    struct northd_data *data,
> > > > > >                    struct ovsdb_idl_txn *ovnnb_txn,
> > > > > > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > > > > > +                         struct hmap *, struct hmap *, struct simap *,
> > > > > > +                         struct hmap *);
> > > > > > +uint32_t get_route_table_id(struct simap *, const char *);
> > > > > > +void static_routes_init(struct static_routes_data *);
> > > > > > +void static_routes_destroy(struct static_routes_data *);
> > > > > > +
> > > > > > +void bfd_init(struct bfd_data *);
> > > > > > +void bfd_destroy(struct bfd_data *);
> > > > > > +
> > > > > >  struct lflow_table;
> > > > > >  struct lr_stateful_tracked_data;
> > > > > >  struct ls_stateful_tracked_data;
> > > > > > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> > > > > >                                     struct hmap *lbgrp_datapaths_map,
> > > > > >                                     struct northd_tracked_data *);
> > > > > >
> > > > > > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > -                     const struct nbrec_bfd_table *,
> > > > > > -                     const struct sbrec_bfd_table *,
> > > > > > -                     const struct hmap *lr_ports,
> > > > > > -                     struct hmap *bfd_connections);
> > > > > > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > > > > > -                             struct hmap *bfd_map);
> > > > > > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > > > > > +                          const struct hmap *, struct hmap *, struct hmap *);
> > > > > > +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 *, struct hmap *);
> > > > > > +void build_bfd_map(const struct nbrec_bfd_table *,
> > > > > > +                   const struct sbrec_bfd_table *, struct hmap *);
> > > > > >  void run_update_worker_pool(int n_threads);
> > > > > >
> > > > > >  const struct ovn_datapath *northd_get_datapath_for_port(
> > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > > index 199197f09..710500dce 100644
> > > > > > --- a/tests/ovn-northd.at
> > > > > > +++ b/tests/ovn-northd.at
> > > > > > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> > > > > >      ovn-sbctl list meter >> $1
> > > > > >      ovn-sbctl list meter_band >> $1
> > > > > >      ovn-sbctl list port_group >> $1
> > > > > > +    ovn-sbctl list bfd >> $1
> > > > > >      ovn-sbctl dump-flows > lflows_$1
> > > > > >  ])
> > > > > >
> > > > > > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> > > > > >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> > > > > >  done
> > > > > >
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> > > > > >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> > > > > >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > > > > > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> > > > > >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> > > > > >                       min_rx=1000 min_tx=1000 status=admin_down
> > > > > >
> > > > > > +check_engine_stats northd norecompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> > > > > >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> > > > > >
> > > > > > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> > > > > >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> > > > > >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> > > > > >
> > > > > > +check_engine_stats northd norecompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> > > > > >  wait_column down bfd status logical_port=r0-sw1
> > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> > > > > >
> > > > > > +check_engine_stats northd recompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> > > > > >  wait_column down bfd status logical_port=r0-sw2
> > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > > > > > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> > > > > >  wait_column down bfd status logical_port=r0-sw5
> > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> > > > > >
> > > > > > +check_engine_stats northd recompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> > > > > >  wait_column down bfd status logical_port=r0-sw6
> > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> > > > > >
> > > > > > +check_engine_stats northd recompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats route_policies recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> > > > > >  wait_column down bfd status logical_port=r0-sw7
> > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > > > > > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> > > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> > > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> > > > > >
> > > > > > +check_engine_stats northd recompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  check ovn-nbctl lr-policy-del r0
> > > > > >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> > > > > >
> > > > > > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> > > > > >  wait_column down bfd status dst_ip=192.168.9.3
> > > > > >  wait_column down bfd status dst_ip=192.168.9.4
> > > > > >
> > > > > > +check_engine_stats northd recompute nocompute
> > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > +check_engine_stats route_policies recompute nocompute
> > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > +
> > > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> > > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> > > > > >
> > > > > > --
> > > > > > 2.45.2
> > > > > >
> > > > >
> > >
Lorenzo Bianconi Aug. 8, 2024, 5:30 p.m. UTC | #7
> On Wed, Aug 7, 2024 at 2:36 PM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Wed, Aug 7, 2024 at 10:29 AM Lorenzo Bianconi
> > <lorenzo.bianconi@redhat.com> wrote:
> > >
> > > > On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
> > > > <lorenzo.bianconi@redhat.com> wrote:
> > > > >
> > > > > [...]
> > > > > > > -void
> > > > > > > -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > > -                        struct hmap *bfd_map)
> > > > > > > +static bool
> > > > > > > +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
> > > > > > >  {
> > > > > > > -    const struct nbrec_bfd *nb_bt;
> > > > > > >      struct bfd_entry *bfd_e;
> > > > > > > -
> > > > > > > -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > > -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > > > -        if (!bfd_e) {
> > > > > > > -            continue;
> > > > > > > -        }
> > > > > > > -
> > > > > > > -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
> > > > > > > -            /* no user for this bfd connection */
> > > > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
> > > > > >
> > > > > > I like the idea of checking the existence of the bfd entry to decide
> > > > > > if a port has bfd instead of relying on the has_bfd status. However, I
> > > > > > have performance concerns for this loop. The bfd_port_lookup()
> > > > > > searches by hashing both logical_port and dst_ip, but here for
> > > > > > logical_port it just iterates all bfd entries, which looks
> > > > > > unreasonable. It may be better to add a hindex for logical_port.
> > > > >
> > > > > Hi Han,
> > > > >
> > > > > I did not get what you mean here, can you please provide more details?
> > > > > Do you mean another hmap to link the logical_port name to the bfd_entry?
> > > > >
> > > >
> > > > The goal is to avoid this O(n) iteration looking for a given
> > > > logical_port. Since multiple bfd entries can have the same
> > > > logical_port name, it would be better to use the struct hindex instead
> > > > of struct hmap to link logical_port name to the bfd_entry.
> > >
> > > ack, fine. Since in this case we just need to know if the logical_port is used
> > > in at least one bfd_entry, I guess a simap to count number of bfd_entry for a
> > > given logical port is enough, what do you think?
> > >
> >
> > Yes, or it can be just a sset, if we only care about the existence (0
> > or >0) but not the actual count.
> >
> 
> To unblock the ECMP series, I am ok to merge it as is for now, and
> optimize with sset later, preferably before the release.
> 
> Acked-by: Han Zhou <hzhou@ovn.org>

I worked on the requested changes, you can find the patch here [0], but if you
prefer we can apply the v11 and I can post just a follow-up patch. Up to you.

Regards,
Lorenzo

[0] https://github.com/LorenzoBianconi/ovn/commit/27d160598b4180427c26a0a25768c125b1801392

> 
> > > >
> > > > > >
> > > > > > > +        if (!strcmp(bfd_e->logical_port, port)) {
> > > > > > > +            return true;
> > > > > > >          }
> > > > > > >      }
> > > > > > > -
> > > > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > > > > > -        free(bfd_e);
> > > > > > > -    }
> > > > > > > +    return false;
> > > > > > >  }
> > > > > > >
> > > > > > > +
> > > > > > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > > > > > >  #define BFD_DEF_MINRX       1000 /* 1s */
> > > > > > >  #define BFD_DEF_DETECT_MULT 5
> > > > > > > @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
> > > > > > >      return port + BFD_UDP_SRC_PORT_START;
> > > > > > >  }
> > > > > > >
> > > > > > > -void
> > > > > > > -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > > -                const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > > -                const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > > > -                const struct hmap *lr_ports, struct hmap *bfd_connections)
> > > > > > > +static char *
> > > > > > > +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
> > > > > > > +                          const struct hmap *rp_bfd_connections,
> > > > > > > +                          const struct hmap *sr_bfd_connections)
> > > > > > >  {
> > > > > > > -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
> > > > > > > -    const struct sbrec_bfd *sb_bt;
> > > > > > > -    unsigned long *bfd_src_ports;
> > > > > > > -    struct bfd_entry *bfd_e;
> > > > > > > -    uint32_t hash;
> > > > > > > +    struct bfd_entry *bfd_rp, *bfd_sr;
> > > > > > >
> > > > > > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > > +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
> > > > > > > +                             nb_bt->dst_ip);
> > > > > > > +    if (!bfd_rp) {
> > > > > > > +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
> > > > > > > +                                 nb_bt->dst_ip);
> > > > > > > +        if (!bfd_sr) {
> > > > > > > +            return "admin_down";
> > > > > > > +        }
> > > > > > > +    }
> > > > > > >
> > > > > > > -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > > > -        bfd_e = xmalloc(sizeof *bfd_e);
> > > > > > > -        bfd_e->sb_bt = sb_bt;
> > > > > > > -        hash = hash_string(sb_bt->dst_ip, 0);
> > > > > > > -        hash = hash_string(sb_bt->logical_port, hash);
> > > > > > > -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
> > > > > > > -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > > > +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > > +               const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > > +               const struct hmap *lr_ports,
> > > > > > > +               const struct hmap *bfd_connections,
> > > > > > > +               const struct hmap *rp_bfd_connections,
> > > > > > > +               const struct hmap *sr_bfd_connections,
> > > > > > > +               struct hmap *sync_bfd_connections)
> > > > > > > +{
> > > > > > > +    if (!ovnsb_txn) {
> > > > > > > +        return;
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > > +
> > > > > > > +    struct bfd_entry *bfd_e;
> > > > > > > +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
> > > > > > > +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
> > > > > > > +                                              bfd_e->logical_port,
> > > > > > > +                                              bfd_e->dst_ip, bfd_e->status);
> > > > > >
> > > > > > It looks like we are blindly allocating a new entry for each entry in
> > > > > > bfd_connections without checking if it already exists. Did I
> > > > > > misunderstand?
> > > > >
> > > > > So far sync_bfd_connections always start from scratch and it is 1:1 with
> > > > > bfd_connections, so I guess an entry can't already exist. But I prefer to be
> > > > > more generic and check it. I will fix it.
> > > > >
> > > >
> > > > What I meant was that now the sync_bfd_connections table is persistent
> > > > across I-P engine runs (I didn't see it get destroyed across
> > > > iterations but correct me if I am wrong), the above code just
> > > > allocates entries again and again every iteration without removing the
> > > > old ones. For simplicity we may just destroy old entries at the
> > > > beginning of this function because now we always do recompute. In the
> > > > future it may be optimized to incrementally consolidate new/old
> > > > entries. Does this make sense?
> > >
> > >
> > > sync_bfd_connections is destroyed at the beginning of en_bfd_sync_run() so I
> > > guess we already have what you are suggesting, right? Anyway we can still use
> > > bfd_port_lookup() to check if an entry has been already allocated. What do you
> > > prefer?
> > >
> >
> > Oh, yes. Sorry I missed that. If it is recreated then there is no
> > point to check the existence again.
> >
> > Thanks,
> > Han
> >
> > > Regards,
> > > Lorenzo
> > >
> > > >
> > > > Thanks,
> > > > Han
> > > >
> > > > > Regards,
> > > > > Lorenzo
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Han
> > > > > >
> > > > > > > +        e->nb_bt = bfd_e->nb_bt;
> > > > > > > +        e->sb_bt = bfd_e->sb_bt;
> > > > > > > +        e->stale = true;
> > > > > > > +        /* we need to check if this entry is even in the BFD nb db table */
> > > > > > > +        if (bfd_e->sb_bt) {
> > > > > > > +            bitmap_set1(bfd_src_ports,
> > > > > > > +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
> > > > > > > +        }
> > > > > > >      }
> > > > > > >
> > > > > > >      const struct nbrec_bfd *nb_bt;
> > > > > > >      NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > > -        if (!nb_bt->status) {
> > > > > > > -            /* default state is admin_down */
> > > > > > > -            nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > > > +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
> > > > > > > +                                nb_bt->dst_ip);
> > > > > > > +        if (!bfd_e) {
> > > > > > > +            continue;
> > > > > > >          }
> > > > > > >
> > > > > > >          struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
> > > > > > > -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
> > > > > > > -        if (!bfd_e) {
> > > > > > > +        if (!op || !op->sb) {
> > > > > > > +            /* skip not bounded ports */
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        nbrec_bfd_set_status(nb_bt,
> > > > > > > +                             bfd_get_connection_status(nb_bt,
> > > > > > > +                                                       rp_bfd_connections,
> > > > > > > +                                                       sr_bfd_connections));
> > > > > > > +        if (!bfd_e->sb_bt) {
> > > > > > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > > > > > >              if (udp_src < 0) {
> > > > > > >                  continue;
> > > > > > >              }
> > > > > > >
> > > > > > > -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > > > > +            /* Add entry to bfd sb table. */
> > > > > > > +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
> > > > > > >              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
> > > > > > >              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
> > > > > > >              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> > > > > > >              sbrec_bfd_set_src_port(sb_bt, udp_src);
> > > > > > >              sbrec_bfd_set_status(sb_bt, nb_bt->status);
> > > > > > > -            if (op && op->sb && op->sb->chassis) {
> > > > > > > +            if (op->sb->chassis) {
> > > > > > >                  sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
> > > > > > >              }
> > > > > > >
> > > > > > > @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > >                      nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
> > > > > > >                  }
> > > > > > >              }
> > > > > > > +
> > > > > > >              build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
> > > > > > > -            if (op && op->sb && op->sb->chassis &&
> > > > > > > -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
> > > > > > > +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
> > > > > > > +                                           bfd_e->sb_bt->chassis_name)) {
> > > > > > >                  sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
> > > > > > >                                             op->sb->chassis->name);
> > > > > > >              }
> > > > > > > -
> > > > > > > -            hmap_remove(&sb_only, &bfd_e->hmap_node);
> > > > > > > -            bfd_e->ref = false;
> > > > > > > -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
> > > > > > > -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
> > > > > > > -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (op) {
> > > > > > > -            op->has_bfd = true;
> > > > > > > -        }
> > > > > > > +        bfd_e->stale = false;
> > > > > > >      }
> > > > > > >
> > > > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
> > > > > > > -        struct ovn_port *op = ovn_port_find(lr_ports,
> > > > > > > -                                            bfd_e->sb_bt->logical_port);
> > > > > > > -        if (op) {
> > > > > > > -            op->has_bfd = false;
> > > > > > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
> > > > > > > +        if (bfd_e->stale) {
> > > > > > > +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
> > > > > > > +            sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > > > +            bfd_erase_entry(bfd_e);
> > > > > > >          }
> > > > > > > -        sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > > > -        free(bfd_e);
> > > > > > >      }
> > > > > > > -    hmap_destroy(&sb_only);
> > > > > > >
> > > > > > >      bitmap_free(bfd_src_ports);
> > > > > > >  }
> > > > > > >
> > > > > > > +void
> > > > > > > +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
> > > > > > > +              const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > > > +              struct hmap *bfd_connections)
> > > > > > > +{
> > > > > > > +    struct bfd_entry *bfd_e;
> > > > > > > +
> > > > > > > +    /* align bfd_map to sb db */
> > > > > > > +    const struct sbrec_bfd *sb_bt;
> > > > > > > +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
> > > > > > > +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
> > > > > > > +                                sb_bt->dst_ip);
> > > > > > > +        if (!bfd_e) {
> > > > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
> > > > > > > +                                    sb_bt->dst_ip,  sb_bt->status);
> > > > > > > +        }
> > > > > > > +        bfd_e->sb_bt = sb_bt;
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    const struct nbrec_bfd *nb_bt;
> > > > > > > +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
> > > > > > > +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > > > +                                nb_bt->dst_ip);
> > > > > > > +        if (!bfd_e) {
> > > > > > > +            /* brand new entry. */
> > > > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
> > > > > > > +                                    nb_bt->dst_ip, "admin_down");
> > > > > > > +        }
> > > > > > > +        bfd_e->nb_bt = nb_bt;
> > > > > > > +    }
> > > > > > > +}
> > > > > > > +
> > > > > > >  /* Returns a string of the IP address of the router port 'op' that
> > > > > > >   * overlaps with 'ip_s".  If one is not found, returns NULL.
> > > > > > >   *
> > > > > > > @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
> > > > > > >      return NULL;
> > > > > > >  }
> > > > > > >
> > > > > > > -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
> > > > > > > -
> > > > > > > -static bool check_bfd_state(
> > > > > > > -        const struct nbrec_logical_router_policy *rule,
> > > > > > > -        const struct hmap *bfd_connections,
> > > > > > > -        struct ovn_port *out_port,
> > > > > > > -        const char *nexthop)
> > > > > > > +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);
> > > > > > > -    bool ret = true;
> > > > > > >
> > > > > > >      for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
> > > > > > >          /* Check if there is a BFD session associated to the reroute
> > > > > > > @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
> > > > > > >          struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > > > >                                                    nb_bt->logical_port,
> > > > > > >                                                    nb_bt->dst_ip);
> > > > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > > > -        if (bfd_e) {
> > > > > > > -            bfd_e->ref = true;
> > > > > > > +        if (!bfd_e) {
> > > > > > > +            continue;
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > > > +        /* 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);
> > > > > > >          }
> > > > > > >
> > > > > > > -        ret = strcmp(nb_bt->status, "down");
> > > > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > > > -        break;
> > > > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > > > +            bfd_set_status(bfd_rp, "down");
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        return strcmp(bfd_rp->status, "down");
> > > > > > >      }
> > > > > > >
> > > > > > > -    return ret;
> > > > > > > +    return true;
> > > > > > >  }
> > > > > > >
> > > > > > >  static void
> > > > > > >  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > > -                          const struct hmap *lr_ports,
> > > > > > > -                          const struct nbrec_logical_router_policy *rule,
> > > > > > > -                          const struct hmap *bfd_connections,
> > > > > > > +                          const struct hmap *lr_ports, struct route_policy *rp,
> > > > > > >                            const struct ovsdb_idl_row *stage_hint,
> > > > > > >                            struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > > >
> > > > > > >      if (!strcmp(rule->action, "reroute")) {
> > > > > > >          ovs_assert(rule->n_nexthops <= 1);
> > > > > > >
> > > > > > > -        char *nexthop =
> > > > > > > -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
> > > > > > > +        if (!rp->n_valid_nexthops) {
> > > > > > > +            return;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        char *nexthop = rp->valid_nexthops[0];
> > > > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > > > >               od, lr_ports, rule->priority, nexthop);
> > > > > > >          if (!out_port) {
> > > > > > > @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > >              return;
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
> > > > > > > -            return;
> > > > > > > -        }
> > > > > > > -
> > > > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > > > >          if (pkt_mark) {
> > > > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > > > > @@ -10070,19 +10156,18 @@ static void
> > > > > > >  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > > >                                  struct ovn_datapath *od,
> > > > > > >                                  const struct hmap *lr_ports,
> > > > > > > -                                const struct nbrec_logical_router_policy *rule,
> > > > > > > -                                const struct hmap *bfd_connections,
> > > > > > > +                                struct route_policy *rp,
> > > > > > >                                  uint16_t ecmp_group_id,
> > > > > > >                                  struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > > -    ovs_assert(rule->n_nexthops > 1);
> > > > > > > -
> > > > > > >      bool nexthops_is_ipv4 = true;
> > > > > > > +    const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > > > +    ovs_assert(rule->n_nexthops > 1);
> > > > > > >
> > > > > > >      /* Check that all the nexthops belong to the same addr family before
> > > > > > >       * adding logical flows. */
> > > > > > > -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
> > > > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > > > +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > > > >
> > > > > > >          if (i == 0) {
> > > > > > >              nexthops_is_ipv4 = is_ipv4;
> > > > > > > @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > > >              VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
> > > > > > >                           "the match [%s] do not belong to the same address "
> > > > > > >                           "family as other next hops",
> > > > > > > -                         rule->nexthops[i], rule->match);
> > > > > > > +                         rp->valid_nexthops[i], rule->match);
> > > > > > >              return;
> > > > > > >          }
> > > > > > >      }
> > > > > > > @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > > >      struct ds match = DS_EMPTY_INITIALIZER;
> > > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > > >
> > > > > > > -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
> > > > > > > -    size_t n_valid_nexthops = 0;
> > > > > > > -
> > > > > > > -    for (size_t i = 0; i < rule->n_nexthops; i++) {
> > > > > > > +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > > >          struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
> > > > > > > -             od, lr_ports, rule->priority, rule->nexthops[i]);
> > > > > > > +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
> > > > > > >          if (!out_port) {
> > > > > > >              goto cleanup;
> > > > > > >          }
> > > > > > >
> > > > > > >          const char *lrp_addr_s =
> > > > > > > -            find_lrp_member_ip(out_port, rule->nexthops[i]);
> > > > > > > +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
> > > > > > >          if (!lrp_addr_s) {
> > > > > > >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > > > > > >              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
> > > > > > >                              " priority %"PRId64" nexthop %s",
> > > > > > > -                            rule->priority, rule->nexthops[i]);
> > > > > > > +                            rule->priority, rp->valid_nexthops[i]);
> > > > > > >              goto cleanup;
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (!check_bfd_state(rule, bfd_connections, out_port,
> > > > > > > -                             rule->nexthops[i])) {
> > > > > > > -            continue;
> > > > > > > -        }
> > > > > > > -
> > > > > > > -        valid_nexthops[n_valid_nexthops++] = i + 1;
> > > > > > > -
> > > > > > >          ds_clear(&actions);
> > > > > > >          uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
> > > > > > >          if (pkt_mark) {
> > > > > > >              ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
> > > > > > >          }
> > > > > > >
> > > > > > > -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
> > > > > > > +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
> > > > > > >
> > > > > > >          ds_put_format(&actions, "%s = %s; "
> > > > > > >                        "%s = %s; "
> > > > > > > @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > > >                        "flags.loopback = 1; "
> > > > > > >                        "next;",
> > > > > > >                        is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
> > > > > > > -                      rule->nexthops[i],
> > > > > > > +                      rp->valid_nexthops[i],
> > > > > > >                        is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
> > > > > > >                        lrp_addr_s,
> > > > > > >                        out_port->lrp_networks.ea_s,
> > > > > > > @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
> > > > > > >                                  lflow_ref);
> > > > > > >      }
> > > > > > >
> > > > > > > -    if (!n_valid_nexthops) {
> > > > > > > -        goto cleanup;
> > > > > > > -    }
> > > > > > > -
> > > > > > >      ds_clear(&actions);
> > > > > > > -    if (n_valid_nexthops > 1) {
> > > > > > > +    if (rp->n_valid_nexthops > 1) {
> > > > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > > > >                        "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
> > > > > > >                        REG_ECMP_MEMBER_ID);
> > > > > > >
> > > > > > > -        for (size_t i = 0; i < n_valid_nexthops; i++) {
> > > > > > > +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
> > > > > > >              if (i > 0) {
> > > > > > >                  ds_put_cstr(&actions, ", ");
> > > > > > >              }
> > > > > > >
> > > > > > > -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
> > > > > > > +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
> > > > > > >          }
> > > > > > >          ds_put_cstr(&actions, ");");
> > > > > > >      } else {
> > > > > > >          ds_put_format(&actions, "%s = %"PRIu16
> > > > > > > -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
> > > > > > > -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
> > > > > > > -                      valid_nexthops[0]);
> > > > > > > +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
> > > > > > > +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
> > > > > > >      }
> > > > > > >      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
> > > > > > >                              rule->priority, rule->match,
> > > > > > >                              ds_cstr(&actions), &rule->header_,
> > > > > > >                              lflow_ref);
> > > > > > > -
> > > > > > >  cleanup:
> > > > > > > -    free(valid_nexthops);
> > > > > > >      ds_destroy(&match);
> > > > > > >      ds_destroy(&actions);
> > > > > > >  }
> > > > > > > @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
> > > > > > >      return rtb_id;
> > > > > > >  }
> > > > > > >
> > > > > > > -static uint32_t
> > > > > > > +uint32_t
> > > > > > >  get_route_table_id(struct simap *route_tables, const char *route_table_name)
> > > > > > >  {
> > > > > > >      if (!route_table_name || !route_table_name[0]) {
> > > > > > > @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > > >      ds_destroy(&actions);
> > > > > > >  }
> > > > > > >
> > > > > > > -struct parsed_route {
> > > > > > > -    struct ovs_list list_node;
> > > > > > > -    struct in6_addr prefix;
> > > > > > > -    unsigned int plen;
> > > > > > > -    bool is_src_route;
> > > > > > > -    uint32_t route_table_id;
> > > > > > > -    uint32_t hash;
> > > > > > > -    const struct nbrec_logical_router_static_route *route;
> > > > > > > -    bool ecmp_symmetric_reply;
> > > > > > > -    bool is_discard_route;
> > > > > > > -};
> > > > > > > -
> > > > > > >  static uint32_t
> > > > > > >  route_hash(struct parsed_route *route)
> > > > > > >  {
> > > > > > > @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >
> > > > > > >  /* Parse and validate the route. Return the parsed route if successful.
> > > > > > >   * Otherwise return NULL. */
> > > > > > > +
> > > > > > >  static struct parsed_route *
> > > > > > > +parsed_route_lookup(struct hmap *routes, size_t hash,
> > > > > > > +                    struct parsed_route *new_pr)
> > > > > > > +{
> > > > > > > +    struct parsed_route *pr;
> > > > > > > +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
> > > > > > > +        if (pr->plen != new_pr->plen) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (pr->is_src_route != new_pr->is_src_route) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (pr->route_table_id != new_pr->route_table_id) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (pr->route != new_pr->route) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (pr->is_discard_route != new_pr->is_discard_route) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        return pr;
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    return NULL;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void
> > > > > > >  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > > -                  struct ovs_list *routes, struct simap *route_tables,
> > > > > > >                    const struct nbrec_logical_router_static_route *route,
> > > > > > > -                  const struct hmap *bfd_connections)
> > > > > > > +                  struct hmap *bfd_connections,
> > > > > > > +                  struct hmap *routes, struct simap *route_tables,
> > > > > > > +                  struct hmap *bfd_active_connections)
> > > > > > >  {
> > > > > > >      /* Verify that the next hop is an IP address with an all-ones mask. */
> > > > > > >      struct in6_addr nexthop;
> > > > > > > @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >              VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
> > > > > > >                           UUID_FMT, route->nexthop,
> > > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > > -            return NULL;
> > > > > > > +            return;
> > > > > > >          }
> > > > > > >          if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
> > > > > > >              (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
> > > > > > > @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >              VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
> > > > > > >                           UUID_FMT, route->nexthop,
> > > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > > -            return NULL;
> > > > > > > +            return;
> > > > > > >          }
> > > > > > >      }
> > > > > > >
> > > > > > > @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >          VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
> > > > > > >                       UUID_FMT, route->ip_prefix,
> > > > > > >                       UUID_ARGS(&route->header_.uuid));
> > > > > > > -        return NULL;
> > > > > > > +        return;
> > > > > > >      }
> > > > > > >
> > > > > > >      /* Verify that ip_prefix and nexthop have same address familiy. */
> > > > > > > @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >                           " %s and 'nexthop' %s in static route "UUID_FMT,
> > > > > > >                           route->ip_prefix, route->nexthop,
> > > > > > >                           UUID_ARGS(&route->header_.uuid));
> > > > > > > -            return NULL;
> > > > > > > +            return;
> > > > > > >          }
> > > > > > >      }
> > > > > > >
> > > > > > > @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > >          !find_static_route_outport(od, lr_ports, route,
> > > > > > >                                     IN6_IS_ADDR_V4MAPPED(&prefix),
> > > > > > >                                     NULL, NULL)) {
> > > > > > > -        return NULL;
> > > > > > > +        return;
> > > > > > >      }
> > > > > > >
> > > > > > >      const struct nbrec_bfd *nb_bt = route->bfd;
> > > > > > >      if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
> > > > > > > -        struct bfd_entry *bfd_e;
> > > > > > > +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
> > > > > > > +                                                  nb_bt->logical_port,
> > > > > > > +                                                  nb_bt->dst_ip);
> > > > > > > +        if (!bfd_e) {
> > > > > > > +            return;
> > > > > > > +        }
> > > > > > >
> > > > > > > -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
> > > > > > > -                                nb_bt->dst_ip);
> > > > > > > -        ovs_mutex_lock(&bfd_lock);
> > > > > > > -        if (bfd_e) {
> > > > > > > -            bfd_e->ref = true;
> > > > > > > +        /* This static route is linked to an active bfd session. */
> > > > > > > +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
> > > > > > > +                                                   nb_bt->logical_port,
> > > > > > > +                                                   nb_bt->dst_ip);
> > > > > > > +        if (!bfd_sr) {
> > > > > > > +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
> > > > > > > +                                     nb_bt->logical_port, nb_bt->dst_ip,
> > > > > > > +                                     bfd_e->status);
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > > -            nbrec_bfd_set_status(nb_bt, "down");
> > > > > > > +        if (!strcmp(bfd_e->status, "admin_down")) {
> > > > > > > +            bfd_set_status(bfd_sr, "down");
> > > > > > >          }
> > > > > > >
> > > > > > > -        if (!strcmp(nb_bt->status, "down")) {
> > > > > > > -            ovs_mutex_unlock(&bfd_lock);
> > > > > > > -            return NULL;
> > > > > > > +
> > > > > > > +        if (!strcmp(bfd_sr->status, "down")) {
> > > > > > > +            return;
> > > > > > >          }
> > > > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > > >      }
> > > > > > >
> > > > > > > -    struct parsed_route *pr = xzalloc(sizeof *pr);
> > > > > > > -    pr->prefix = prefix;
> > > > > > > -    pr->plen = plen;
> > > > > > > -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
> > > > > > > -    pr->is_src_route = (route->policy && !strcmp(route->policy,
> > > > > > > -                                                 "src-ip"));
> > > > > > > -    pr->hash = route_hash(pr);
> > > > > > > -    pr->route = route;
> > > > > > > -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > > > -                                             "ecmp_symmetric_reply", false);
> > > > > > > -    pr->is_discard_route = is_discard_route;
> > > > > > > -    ovs_list_insert(routes, &pr->list_node);
> > > > > > > -    return pr;
> > > > > > > +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
> > > > > > > +    new_pr->prefix = prefix;
> > > > > > > +    new_pr->plen = plen;
> > > > > > > +    new_pr->route_table_id = get_route_table_id(route_tables,
> > > > > > > +                                                route->route_table);
> > > > > > > +    new_pr->is_src_route = (route->policy &&
> > > > > > > +                            !strcmp(route->policy, "src-ip"));
> > > > > > > +    new_pr->hash = route_hash(new_pr);
> > > > > > > +    new_pr->route = route;
> > > > > > > +    new_pr->nbr = od->nbr;
> > > > > > > +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
> > > > > > > +                                                 "ecmp_symmetric_reply",
> > > > > > > +                                                 false);
> > > > > > > +    new_pr->is_discard_route = is_discard_route;
> > > > > > > +
> > > > > > > +    size_t hash = uuid_hash(&od->key);
> > > > > > > +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
> > > > > > > +    if (!pr) {
> > > > > > > +        hmap_insert(routes, &new_pr->key_node, hash);
> > > > > > > +    } else {
> > > > > > > +        pr->stale = false;
> > > > > > > +        free(new_pr);
> > > > > > > +    }
> > > > > > >  }
> > > > > > >
> > > > > > > -static void
> > > > > > > -parsed_routes_destroy(struct ovs_list *routes)
> > > > > > > +void
> > > > > > > +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > > +                    struct hmap *bfd_connections, struct hmap *routes,
> > > > > > > +                    struct simap *route_tables,
> > > > > > > +                    struct hmap *bfd_active_connections)
> > > > > > >  {
> > > > > > >      struct parsed_route *pr;
> > > > > > > -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
> > > > > > > -        ovs_list_remove(&pr->list_node);
> > > > > > > +    HMAP_FOR_EACH (pr, key_node, routes) {
> > > > > > > +        if (pr->nbr == od->nbr) {
> > > > > > > +            pr->stale = true;
> > > > > > > +        }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > > > +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
> > > > > > > +                          bfd_connections, routes, route_tables,
> > > > > > > +                          bfd_active_connections);
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > > > > > +        if (!pr->stale) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        hmap_remove(routes, &pr->key_node);
> > > > > > >          free(pr);
> > > > > > >      }
> > > > > > >  }
> > > > > > > @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > >            const struct ovn_port *op, const char *lrp_addr_s,
> > > > > > >            const char *network_s, int plen, const char *gateway,
> > > > > > >            bool is_src_route, const uint32_t rtb_id,
> > > > > > > +          const struct hmap *bfd_connections,
> > > > > > >            const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
> > > > > > >            int ofs, struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > > @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > >                              priority, ds_cstr(&match),
> > > > > > >                              ds_cstr(&actions), stage_hint,
> > > > > > >                              lflow_ref);
> > > > > > > -    if (op && op->has_bfd) {
> > > > > > > +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
> > > > > > >          ds_put_format(&match, " && udp.dst == 3784");
> > > > > > >          ovn_lflow_add_with_hint(lflows, op->od,
> > > > > > >                                  S_ROUTER_IN_IP_ROUTING,
> > > > > > > @@ -10895,6 +11030,7 @@ static void
> > > > > > >  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > >                          const struct hmap *lr_ports,
> > > > > > >                          const struct parsed_route *route_,
> > > > > > > +                        const struct hmap *bfd_connections,
> > > > > > >                          struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > >      const char *lrp_addr_s = NULL;
> > > > > > > @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
> > > > > > >      char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
> > > > > > >      add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
> > > > > > >                lrp_addr_s, prefix_s, route_->plen, route->nexthop,
> > > > > > > -              route_->is_src_route, route_->route_table_id, &route->header_,
> > > > > > > -              route_->is_discard_route, ofs, lflow_ref);
> > > > > > > +              route_->is_src_route, route_->route_table_id,
> > > > > > > +              bfd_connections, &route->header_, route_->is_discard_route,
> > > > > > > +              ofs, lflow_ref);
> > > > > > >
> > > > > > >      free(prefix_s);
> > > > > > >  }
> > > > > > > @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
> > > > > > >  static void
> > > > > > >  build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
> > > > > > >                          const struct shash *meter_groups,
> > > > > > > +                        const struct hmap *bfd_connections,
> > > > > > >                          struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > > -    if (!op->has_bfd) {
> > > > > > > +    if (!bfd_is_port_running(bfd_connections, op->key)) {
> > > > > > >          return;
> > > > > > >      }
> > > > > > >
> > > > > > > @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> > > > > > >   * LRP 'op'.
> > > > > > >   */
> > > > > > >  static void
> > > > > > > -build_ip_routing_flows_for_lrp(
> > > > > > > -        struct ovn_port *op, struct lflow_table *lflows,
> > > > > > > -        struct lflow_ref *lflow_ref)
> > > > > > > +build_ip_routing_flows_for_lrp(struct ovn_port *op,
> > > > > > > +                               const struct hmap *bfd_connections,
> > > > > > > +                               struct lflow_table *lflows,
> > > > > > > +                               struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > >      ovs_assert(op->nbrp);
> > > > > > >      for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > > > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
> > > > > > >                    op->lrp_networks.ipv4_addrs[i].network_s,
> > > > > > >                    op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
> > > > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > > -                  lflow_ref);
> > > > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > > > >      }
> > > > > > >
> > > > > > >      for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> > > > > > >          add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
> > > > > > >                    op->lrp_networks.ipv6_addrs[i].network_s,
> > > > > > >                    op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
> > > > > > > -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > > -                  lflow_ref);
> > > > > > > +                  bfd_connections, &op->nbrp->header_, false,
> > > > > > > +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
> > > > > > >      }
> > > > > > >  }
> > > > > > >
> > > > > > >  static void
> > > > > > >  build_static_route_flows_for_lrouter(
> > > > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > > > -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
> > > > > > > +        const struct hmap *lr_ports, struct hmap *parsed_routes,
> > > > > > > +        struct simap *route_tables, const struct hmap *bfd_connections,
> > > > > > >          struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > >      ovs_assert(od->nbr);
> > > > > > > @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
> > > > > > >
> > > > > > >      struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
> > > > > > >      struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
> > > > > > > -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
> > > > > > > -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
> > > > > > >      struct ecmp_groups_node *group;
> > > > > > >
> > > > > > >      for (int i = 0; i < od->nbr->n_ports; i++) {
> > > > > > >          build_route_table_lflow(od, lflows, od->nbr->ports[i],
> > > > > > > -                                &route_tables, lflow_ref);
> > > > > > > +                                route_tables, lflow_ref);
> > > > > > >      }
> > > > > > >
> > > > > > > -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
> > > > > > > -        struct parsed_route *route =
> > > > > > > -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
> > > > > > > -                              od->nbr->static_routes[i], bfd_connections);
> > > > > > > -        if (!route) {
> > > > > > > -            continue;
> > > > > > > -        }
> > > > > > > +    struct parsed_route *route;
> > > > > > > +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
> > > > > > > +                             parsed_routes) {
> > > > > > >          group = ecmp_groups_find(&ecmp_groups, route);
> > > > > > >          if (group) {
> > > > > > >              ecmp_groups_add_route(group, route);
> > > > > > > @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
> > > > > > >      }
> > > > > > >      const struct unique_routes_node *ur;
> > > > > > >      HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
> > > > > > > -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
> > > > > > > +        build_static_route_flow(lflows, od, lr_ports, ur->route,
> > > > > > > +                                bfd_connections, lflow_ref);
> > > > > > >      }
> > > > > > >      ecmp_groups_destroy(&ecmp_groups);
> > > > > > >      unique_routes_destroy(&unique_routes);
> > > > > > > -    parsed_routes_destroy(&parsed_routes);
> > > > > > > -    simap_destroy(&route_tables);
> > > > > > >  }
> > > > > > >
> > > > > > >  /* IP Multicast lookup. Here we set the output port, adjust TTL and
> > > > > > > @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
> > > > > > >      }
> > > > > > >  }
> > > > > > >
> > > > > > > +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->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;
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
> > > > > > > +                     const struct hmap *bfd_connections,
> > > > > > > +                     struct hmap *route_policies,
> > > > > > > +                     struct hmap *bfd_active_connections)
> > > > > > > +{
> > > > > > > +    struct route_policy *rp;
> > > > > > > +
> > > > > > > +    HMAP_FOR_EACH (rp, key_node, route_policies) {
> > > > > > > +        if (rp->nbr == od->nbr) {
> > > > > > > +            rp->stale = true;
> > > > > > > +        }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > > > +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
> > > > > > > +        size_t n_valid_nexthops = 0;
> > > > > > > +        char **valid_nexthops = NULL;
> > > > > > > +
> > > > > > > +        if (!strcmp(rule->action, "reroute")) {
> > > > > > > +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
> > > > > > > +
> > > > > > > +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
> > > > > > > +            for (size_t j = 0; j < n_nexthops; j++) {
> > > > > > > +                char *nexthop = rule->n_nexthops
> > > > > > > +                    ? rule->nexthops[j] : rule->nexthop;
> > > > > > > +                struct ovn_port *out_port =
> > > > > > > +                    get_outport_for_routing_policy_nexthop(
> > > > > > > +                            od, lr_ports, rule->priority, nexthop);
> > > > > > > +                if (!out_port || !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->nbr = od->nbr;
> > > > > > > +
> > > > > > > +        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 {
> > > > > > > +            rp->stale = false;
> > > > > > > +            free(valid_nexthops);
> > > > > > > +            free(new_rp);
> > > > > > > +        }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
> > > > > > > +        if (!rp->stale) {
> > > > > > > +            continue;
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        hmap_remove(route_policies, &rp->key_node);
> > > > > > > +        free(rp->valid_nexthops);
> > > > > > > +        free(rp);
> > > > > > > +    }
> > > > > > > +}
> > > > > > > +
> > > > > > >  /* Logical router ingress table POLICY: Policy.
> > > > > > >   *
> > > > > > >   * A packet that arrives at this table is an IP packet that should be
> > > > > > > @@ -12862,7 +13104,7 @@ static void
> > > > > > >  build_ingress_policy_flows_for_lrouter(
> > > > > > >          struct ovn_datapath *od, struct lflow_table *lflows,
> > > > > > >          const struct hmap *lr_ports,
> > > > > > > -        const struct hmap *bfd_connections,
> > > > > > > +        struct hmap *route_policies,
> > > > > > >          struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > >      ovs_assert(od->nbr);
> > > > > > > @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
> > > > > > >
> > > > > > >      /* Convert routing policies to flows. */
> > > > > > >      uint16_t ecmp_group_id = 1;
> > > > > > > -    for (int i = 0; i < od->nbr->n_policies; i++) {
> > > > > > > -        const struct nbrec_logical_router_policy *rule
> > > > > > > -            = od->nbr->policies[i];
> > > > > > > +    struct route_policy *rp;
> > > > > > > +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
> > > > > > > +                             route_policies) {
> > > > > > > +        const struct nbrec_logical_router_policy *rule = rp->rule;
> > > > > > >          bool is_ecmp_reroute =
> > > > > > >              (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
> > > > > > >
> > > > > > >          if (is_ecmp_reroute) {
> > > > > > > -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
> > > > > > > -                                            bfd_connections, ecmp_group_id,
> > > > > > > -                                            lflow_ref);
> > > > > > > +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
> > > > > > > +                                            ecmp_group_id, lflow_ref);
> > > > > > >              ecmp_group_id++;
> > > > > > >          } else {
> > > > > > > -            build_routing_policy_flow(lflows, od, lr_ports, rule,
> > > > > > > -                                      bfd_connections, &rule->header_,
> > > > > > > -                                      lflow_ref);
> > > > > > > +            build_routing_policy_flow(lflows, od, lr_ports, rp,
> > > > > > > +                                      &rule->header_, lflow_ref);
> > > > > > >          }
> > > > > > >      }
> > > > > > >  }
> > > > > > > @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > > > >                              struct lflow_table *lflows,
> > > > > > >                              struct ds *match, struct ds *actions,
> > > > > > >                              const struct shash *meter_groups,
> > > > > > > +                            const struct hmap *bfd_connections,
> > > > > > >                              struct lflow_ref *lflow_ref)
> > > > > > >  {
> > > > > > >      ovs_assert(op->nbrp);
> > > > > > > @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> > > > > > >      }
> > > > > > >
> > > > > > >      /* BFD msg handling */
> > > > > > > -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
> > > > > > > +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
> > > > > > > +                            lflow_ref);
> > > > > > >
> > > > > > >      /* ICMP time exceeded */
> > > > > > >      struct ds ip_ds = DS_EMPTY_INITIALIZER;
> > > > > > > @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> > > > > > >  static void
> > > > > > >  build_routable_flows_for_router_port(
> > > > > > >      struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
> > > > > > > +    const struct hmap *bfd_connections,
> > > > > > >      struct lflow_table *lflows,
> > > > > > >      struct ds *match,
> > > > > > >      struct ds *actions)
> > > > > > > @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
> > > > > > >                                router_port->lrp_networks.ipv4_addrs[0].addr_s,
> > > > > > >                                laddrs->ipv4_addrs[k].network_s,
> > > > > > >                                laddrs->ipv4_addrs[k].plen, NULL, false, 0,
> > > > > > > -                              &router_port->nbrp->header_, false,
> > > > > > > -                              ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > > +                              bfd_connections, &router_port->nbrp->header_,
> > > > > > > +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
> > > > > > >                                lrp->stateful_lflow_ref);
> > > > > > >                  }
> > > > > > >              }
> > > > > > > @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
> > > > > > >  static void
> > > > > > >  build_lbnat_lflows_iterate_by_lrp(
> > > > > > >      struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> > > > > > > -    const struct shash *meter_groups, struct ds *match,
> > > > > > > -    struct ds *actions, struct lflow_table *lflows)
> > > > > > > +    const struct shash *meter_groups, const struct hmap *bfd_connections,
> > > > > > > +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
> > > > > > >  {
> > > > > > >      ovs_assert(op->nbrp);
> > > > > > >
> > > > > > > @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
> > > > > > >      build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> > > > > > >                                  actions, lflows);
> > > > > > >
> > > > > > > -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
> > > > > > > -                                         actions);
> > > > > > > +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
> > > > > > > +                                         lflows, match, actions);
> > > > > > >  }
> > > > > > >
> > > > > > >  static void
> > > > > > > @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
> > > > > > >      struct ds actions;
> > > > > > >      size_t thread_lflow_counter;
> > > > > > >      const char *svc_monitor_mac;
> > > > > > > +    struct hmap *parsed_routes;
> > > > > > > +    struct hmap *route_policies;
> > > > > > > +    struct simap *route_tables;
> > > > > > >  };
> > > > > > >
> > > > > > >  /* Helper function to combine all lflow generation which is iterated by
> > > > > > > @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > > > > > >      build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > > >      build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > > > -                                         lsi->bfd_connections,
> > > > > > > -                                         NULL);
> > > > > > > +                                         lsi->parsed_routes, lsi->route_tables,
> > > > > > > +                                         lsi->bfd_connections, NULL);
> > > > > > >      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
> > > > > > >                                           &lsi->actions, NULL);
> > > > > > >      build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > > > -                                           lsi->bfd_connections, NULL);
> > > > > > > +                                           lsi->route_policies, NULL);
> > > > > > >      build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
> > > > > > >      build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> > > > > > >                                            &lsi->match, &lsi->actions,
> > > > > > > @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > > > >                                            &lsi->actions, op->lflow_ref);
> > > > > > >      build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > > > >                                                  &lsi->actions, op->lflow_ref);
> > > > > > > -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
> > > > > > > +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
> > > > > > > +                                   lsi->lflows, op->lflow_ref);
> > > > > > >      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> > > > > > >                                         &lsi->actions, lsi->meter_groups,
> > > > > > >                                         op->lflow_ref);
> > > > > > > @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
> > > > > > >                                              lsi->meter_groups,
> > > > > > >                                              op->lflow_ref);
> > > > > > >      build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> > > > > > > -                                lsi->meter_groups, op->lflow_ref);
> > > > > > > +                                lsi->meter_groups, lsi->bfd_connections,
> > > > > > > +                                op->lflow_ref);
> > > > > > >      build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
> > > > > > >                                                   &lsi->actions, op->lflow_ref);
> > > > > > >  }
> > > > > > > @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
> > > > > > >                      build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> > > > > > >                      build_lbnat_lflows_iterate_by_lrp(
> > > > > > >                          op, lsi->lr_stateful_table, lsi->meter_groups,
> > > > > > > -                        &lsi->match, &lsi->actions, lsi->lflows);
> > > > > > > +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
> > > > > > > +                        lsi->lflows);
> > > > > > >                  }
> > > > > > >              }
> > > > > > >              for (bnum = control->id;
> > > > > > > @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
> > > > > > >      const struct hmap *svc_monitor_map,
> > > > > > >      const struct hmap *bfd_connections,
> > > > > > >      const struct chassis_features *features,
> > > > > > > -    const char *svc_monitor_mac)
> > > > > > > +    const char *svc_monitor_mac,
> > > > > > > +    struct hmap *parsed_routes,
> > > > > > > +    struct hmap *route_policies,
> > > > > > > +    struct simap *route_tables)
> > > > > > >  {
> > > > > > >
> > > > > > >      char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
> > > > > > > @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
> > > > > > >              lsiv[index].svc_check_match = svc_check_match;
> > > > > > >              lsiv[index].thread_lflow_counter = 0;
> > > > > > >              lsiv[index].svc_monitor_mac = svc_monitor_mac;
> > > > > > > +            lsiv[index].parsed_routes = parsed_routes;
> > > > > > > +            lsiv[index].route_tables = route_tables;
> > > > > > > +            lsiv[index].route_policies = route_policies;
> > > > > > >              ds_init(&lsiv[index].match);
> > > > > > >              ds_init(&lsiv[index].actions);
> > > > > > >
> > > > > > > @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
> > > > > > >              .features = features,
> > > > > > >              .svc_check_match = svc_check_match,
> > > > > > >              .svc_monitor_mac = svc_monitor_mac,
> > > > > > > +            .parsed_routes = parsed_routes,
> > > > > > > +            .route_tables = route_tables,
> > > > > > > +            .route_policies = route_policies,
> > > > > > >              .match = DS_EMPTY_INITIALIZER,
> > > > > > >              .actions = DS_EMPTY_INITIALIZER,
> > > > > > >          };
> > > > > > > @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
> > > > > > >              build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> > > > > > >              build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> > > > > > >                                                lsi.meter_groups,
> > > > > > > +                                              lsi.bfd_connections,
> > > > > > >                                                &lsi.match,
> > > > > > >                                                &lsi.actions,
> > > > > > >                                                lsi.lflows);
> > > > > > > @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > >                                      input_data->svc_monitor_map,
> > > > > > >                                      input_data->bfd_connections,
> > > > > > >                                      input_data->features,
> > > > > > > -                                    input_data->svc_monitor_mac);
> > > > > > > +                                    input_data->svc_monitor_mac,
> > > > > > > +                                    input_data->parsed_routes,
> > > > > > > +                                    input_data->route_policies,
> > > > > > > +                                    input_data->route_tables);
> > > > > > >
> > > > > > >      if (parallelization_state == STATE_INIT_HASH_SIZES) {
> > > > > > >          parallelization_state = STATE_USE_PARALLELIZATION;
> > > > > > > @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > >              build_lbnat_lflows_iterate_by_lrp(op,
> > > > > > >                                                lflow_input->lr_stateful_table,
> > > > > > >                                                lflow_input->meter_groups,
> > > > > > > +                                              lflow_input->bfd_connections,
> > > > > > >                                                &match, &actions,
> > > > > > >                                                lflows);
> > > > > > >
> > > > > > > @@ -17490,6 +17754,27 @@ 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);
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +static_routes_init(struct static_routes_data *data)
> > > > > > > +{
> > > > > > > +    hmap_init(&data->parsed_routes);
> > > > > > > +    simap_init(&data->route_tables);
> > > > > > > +    hmap_init(&data->bfd_active_connections);
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +bfd_init(struct bfd_data *data)
> > > > > > > +{
> > > > > > > +    hmap_init(&data->bfd_connections);
> > > > > > > +}
> > > > > > > +
> > > > > > >  void
> > > > > > >  northd_destroy(struct northd_data *data)
> > > > > > >  {
> > > > > > > @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
> > > > > > >      destroy_northd_tracked_data(data);
> > > > > > >  }
> > > > > > >
> > > > > > > +static void
> > > > > > > +__bfd_destroy(struct hmap *bfd_connections)
> > > > > > > +{
> > > > > > > +    struct bfd_entry *bfd_e;
> > > > > > > +
> > > > > > > +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
> > > > > > > +        bfd_erase_entry(bfd_e);
> > > > > > > +    }
> > > > > > > +    hmap_destroy(bfd_connections);
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +bfd_destroy(struct bfd_data *data)
> > > > > > > +{
> > > > > > > +    __bfd_destroy(&data->bfd_connections);
> > > > > > > +}
> > > > > > > +
> > > > > > > +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);
> > > > > > > +}
> > > > > > > +
> > > > > > > +void
> > > > > > > +static_routes_destroy(struct static_routes_data *data)
> > > > > > > +{
> > > > > > > +    struct parsed_route *r;
> > > > > > > +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
> > > > > > > +        free(r);
> > > > > > > +    }
> > > > > > > +    hmap_destroy(&data->parsed_routes);
> > > > > > > +
> > > > > > > +    simap_destroy(&data->route_tables);
> > > > > > > +    __bfd_destroy(&data->bfd_active_connections);
> > > > > > > +}
> > > > > > > +
> > > > > > >  void
> > > > > > >  ovnnb_db_run(struct northd_input *input_data,
> > > > > > >               struct northd_data *data,
> > > > > > > diff --git a/northd/northd.h b/northd/northd.h
> > > > > > > index d4a8d75ab..432072f9f 100644
> > > > > > > --- a/northd/northd.h
> > > > > > > +++ b/northd/northd.h
> > > > > > > @@ -23,6 +23,7 @@
> > > > > > >  #include "northd/en-port-group.h"
> > > > > > >  #include "northd/ipam.h"
> > > > > > >  #include "openvswitch/hmap.h"
> > > > > > > +#include "simap.h"
> > > > > > >  #include "ovs-thread.h"
> > > > > > >
> > > > > > >  struct northd_input {
> > > > > > > @@ -160,14 +161,34 @@ 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;
> > > > > > > +    const struct nbrec_logical_router *nbr;
> > > > > > > +    bool stale;
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct static_routes_data {
> > > > > > > +    struct hmap parsed_routes;
> > > > > > > +    struct simap route_tables;
> > > > > > > +    struct hmap bfd_active_connections;
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct route_policies_data {
> > > > > > > +    struct hmap route_policies;
> > > > > > > +    struct hmap bfd_active_connections;
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct bfd_data {
> > > > > > > +    struct hmap bfd_connections;
> > > > > > > +};
> > > > > > > +
> > > > > > >  struct lr_nat_table;
> > > > > > >
> > > > > > >  struct lflow_input {
> > > > > > > -    /* Northbound table references */
> > > > > > > -    const struct nbrec_bfd_table *nbrec_bfd_table;
> > > > > > > -
> > > > > > >      /* Southbound table references */
> > > > > > > -    const struct sbrec_bfd_table *sbrec_bfd_table;
> > > > > > >      const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> > > > > > >      const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
> > > > > > >      const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> > > > > > > @@ -190,6 +211,9 @@ struct lflow_input {
> > > > > > >      const struct hmap *svc_monitor_map;
> > > > > > >      bool ovn_internal_version_changed;
> > > > > > >      const char *svc_monitor_mac;
> > > > > > > +    struct hmap *parsed_routes;
> > > > > > > +    struct hmap *route_policies;
> > > > > > > +    struct simap *route_tables;
> > > > > > >  };
> > > > > > >
> > > > > > >  extern int parallelization_state;
> > > > > > > @@ -593,8 +617,6 @@ struct ovn_port {
> > > > > > >
> > > > > > >      bool has_unknown; /* If the addresses have 'unknown' defined. */
> > > > > > >
> > > > > > > -    bool has_bfd;
> > > > > > > -
> > > > > > >      /* The port's peer:
> > > > > > >       *
> > > > > > >       *     - A switch port S of type "router" has a router port R as a peer,
> > > > > > > @@ -653,6 +675,20 @@ struct ovn_port {
> > > > > > >      struct lflow_ref *stateful_lflow_ref;
> > > > > > >  };
> > > > > > >
> > > > > > > +struct parsed_route {
> > > > > > > +    struct hmap_node key_node;
> > > > > > > +    struct in6_addr prefix;
> > > > > > > +    unsigned int plen;
> > > > > > > +    bool is_src_route;
> > > > > > > +    uint32_t route_table_id;
> > > > > > > +    uint32_t hash;
> > > > > > > +    const struct nbrec_logical_router_static_route *route;
> > > > > > > +    bool ecmp_symmetric_reply;
> > > > > > > +    bool is_discard_route;
> > > > > > > +    const struct nbrec_logical_router *nbr;
> > > > > > > +    bool stale;
> > > > > > > +};
> > > > > > > +
> > > > > > >  void ovnnb_db_run(struct northd_input *input_data,
> > > > > > >                    struct northd_data *data,
> > > > > > >                    struct ovsdb_idl_txn *ovnnb_txn,
> > > > > > > @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
> > > > > > > +                         struct hmap *, struct hmap *, struct simap *,
> > > > > > > +                         struct hmap *);
> > > > > > > +uint32_t get_route_table_id(struct simap *, const char *);
> > > > > > > +void static_routes_init(struct static_routes_data *);
> > > > > > > +void static_routes_destroy(struct static_routes_data *);
> > > > > > > +
> > > > > > > +void bfd_init(struct bfd_data *);
> > > > > > > +void bfd_destroy(struct bfd_data *);
> > > > > > > +
> > > > > > >  struct lflow_table;
> > > > > > >  struct lr_stateful_tracked_data;
> > > > > > >  struct ls_stateful_tracked_data;
> > > > > > > @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> > > > > > >                                     struct hmap *lbgrp_datapaths_map,
> > > > > > >                                     struct northd_tracked_data *);
> > > > > > >
> > > > > > > -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
> > > > > > > -                     const struct nbrec_bfd_table *,
> > > > > > > -                     const struct sbrec_bfd_table *,
> > > > > > > -                     const struct hmap *lr_ports,
> > > > > > > -                     struct hmap *bfd_connections);
> > > > > > > -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
> > > > > > > -                             struct hmap *bfd_map);
> > > > > > > +void build_route_policies(struct ovn_datapath *, const struct hmap *,
> > > > > > > +                          const struct hmap *, struct hmap *, struct hmap *);
> > > > > > > +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 *, struct hmap *);
> > > > > > > +void build_bfd_map(const struct nbrec_bfd_table *,
> > > > > > > +                   const struct sbrec_bfd_table *, struct hmap *);
> > > > > > >  void run_update_worker_pool(int n_threads);
> > > > > > >
> > > > > > >  const struct ovn_datapath *northd_get_datapath_for_port(
> > > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > > > index 199197f09..710500dce 100644
> > > > > > > --- a/tests/ovn-northd.at
> > > > > > > +++ b/tests/ovn-northd.at
> > > > > > > @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
> > > > > > >      ovn-sbctl list meter >> $1
> > > > > > >      ovn-sbctl list meter_band >> $1
> > > > > > >      ovn-sbctl list port_group >> $1
> > > > > > > +    ovn-sbctl list bfd >> $1
> > > > > > >      ovn-sbctl dump-flows > lflows_$1
> > > > > > >  ])
> > > > > > >
> > > > > > > @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
> > > > > > >      check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
> > > > > > >  done
> > > > > > >
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > >  uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
> > > > > > >  ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
> > > > > > >  ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
> > > > > > > @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
> > > > > > >  wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
> > > > > > >                       min_rx=1000 min_tx=1000 status=admin_down
> > > > > > >
> > > > > > > +check_engine_stats northd norecompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
> > > > > > >  check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
> > > > > > >
> > > > > > > @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
> > > > > > >  wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
> > > > > > >  wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
> > > > > > >
> > > > > > > +check_engine_stats northd norecompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
> > > > > > >  wait_column down bfd status logical_port=r0-sw1
> > > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
> > > > > > >
> > > > > > > +check_engine_stats northd recompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
> > > > > > >  wait_column down bfd status logical_port=r0-sw2
> > > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
> > > > > > > @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
> > > > > > >  wait_column down bfd status logical_port=r0-sw5
> > > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
> > > > > > >
> > > > > > > +check_engine_stats northd recompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
> > > > > > >  wait_column down bfd status logical_port=r0-sw6
> > > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
> > > > > > >
> > > > > > > +check_engine_stats northd recompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats route_policies recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
> > > > > > >  wait_column down bfd status logical_port=r0-sw7
> > > > > > >  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
> > > > > > > @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
> > > > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
> > > > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
> > > > > > >
> > > > > > > +check_engine_stats northd recompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats static_routes recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  check ovn-nbctl lr-policy-del r0
> > > > > > >  check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
> > > > > > >
> > > > > > > @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
> > > > > > >  wait_column down bfd status dst_ip=192.168.9.3
> > > > > > >  wait_column down bfd status dst_ip=192.168.9.4
> > > > > > >
> > > > > > > +check_engine_stats northd recompute nocompute
> > > > > > > +check_engine_stats bfd recompute nocompute
> > > > > > > +check_engine_stats route_policies recompute nocompute
> > > > > > > +check_engine_stats lflow recompute nocompute
> > > > > > > +check_engine_stats northd_output norecompute compute
> > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> > > > > > > +
> > > > > > >  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
> > > > > > >  AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
> > > > > > >
> > > > > > > --
> > > > > > > 2.45.2
> > > > > > >
> > > > > >
> > > >
>
Mark Michelson Aug. 8, 2024, 8:50 p.m. UTC | #8
On 8/8/24 12:22, Han Zhou wrote:
> On Wed, Aug 7, 2024 at 2:36 PM Han Zhou <hzhou@ovn.org> wrote:
>>
>> On Wed, Aug 7, 2024 at 10:29 AM Lorenzo Bianconi
>> <lorenzo.bianconi@redhat.com> wrote:
>>>
>>>> On Tue, Aug 6, 2024 at 9:53 AM Lorenzo Bianconi
>>>> <lorenzo.bianconi@redhat.com> wrote:
>>>>>
>>>>> [...]
>>>>>>> -void
>>>>>>> -bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
>>>>>>> -                        struct hmap *bfd_map)
>>>>>>> +static bool
>>>>>>> +bfd_is_port_running(const struct hmap *bfd_map, const char *port)
>>>>>>>   {
>>>>>>> -    const struct nbrec_bfd *nb_bt;
>>>>>>>       struct bfd_entry *bfd_e;
>>>>>>> -
>>>>>>> -    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
>>>>>>> -        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
>>>>>>> -        if (!bfd_e) {
>>>>>>> -            continue;
>>>>>>> -        }
>>>>>>> -
>>>>>>> -        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
>>>>>>> -            /* no user for this bfd connection */
>>>>>>> -            nbrec_bfd_set_status(nb_bt, "admin_down");
>>>>>>> +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
>>>>>>
>>>>>> I like the idea of checking the existence of the bfd entry to decide
>>>>>> if a port has bfd instead of relying on the has_bfd status. However, I
>>>>>> have performance concerns for this loop. The bfd_port_lookup()
>>>>>> searches by hashing both logical_port and dst_ip, but here for
>>>>>> logical_port it just iterates all bfd entries, which looks
>>>>>> unreasonable. It may be better to add a hindex for logical_port.
>>>>>
>>>>> Hi Han,
>>>>>
>>>>> I did not get what you mean here, can you please provide more details?
>>>>> Do you mean another hmap to link the logical_port name to the bfd_entry?
>>>>>
>>>>
>>>> The goal is to avoid this O(n) iteration looking for a given
>>>> logical_port. Since multiple bfd entries can have the same
>>>> logical_port name, it would be better to use the struct hindex instead
>>>> of struct hmap to link logical_port name to the bfd_entry.
>>>
>>> ack, fine. Since in this case we just need to know if the logical_port is used
>>> in at least one bfd_entry, I guess a simap to count number of bfd_entry for a
>>> given logical port is enough, what do you think?
>>>
>>
>> Yes, or it can be just a sset, if we only care about the existence (0
>> or >0) but not the actual count.
>>
> 
> To unblock the ECMP series, I am ok to merge it as is for now, and
> optimize with sset later, preferably before the release.
> 
> Acked-by: Han Zhou <hzhou@ovn.org>
> 

Thanks Han. I merged this to main. We'll get the optimization done 
before release. It looks like Lorenzo replied with a WIP for this.

>>>>
>>>>>>
>>>>>>> +        if (!strcmp(bfd_e->logical_port, port)) {
>>>>>>> +            return true;
>>>>>>>           }
>>>>>>>       }
>>>>>>> -
>>>>>>> -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
>>>>>>> -        free(bfd_e);
>>>>>>> -    }
>>>>>>> +    return false;
>>>>>>>   }
>>>>>>>
>>>>>>> +
>>>>>>>   #define BFD_DEF_MINTX       1000 /* 1s */
>>>>>>>   #define BFD_DEF_MINRX       1000 /* 1s */
>>>>>>>   #define BFD_DEF_DETECT_MULT 5
>>>>>>> @@ -9809,51 +9835,88 @@ static int bfd_get_unused_port(unsigned long *bfd_src_ports)
>>>>>>>       return port + BFD_UDP_SRC_PORT_START;
>>>>>>>   }
>>>>>>>
>>>>>>> -void
>>>>>>> -build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>> -                const struct nbrec_bfd_table *nbrec_bfd_table,
>>>>>>> -                const struct sbrec_bfd_table *sbrec_bfd_table,
>>>>>>> -                const struct hmap *lr_ports, struct hmap *bfd_connections)
>>>>>>> +static char *
>>>>>>> +bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
>>>>>>> +                          const struct hmap *rp_bfd_connections,
>>>>>>> +                          const struct hmap *sr_bfd_connections)
>>>>>>>   {
>>>>>>> -    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
>>>>>>> -    const struct sbrec_bfd *sb_bt;
>>>>>>> -    unsigned long *bfd_src_ports;
>>>>>>> -    struct bfd_entry *bfd_e;
>>>>>>> -    uint32_t hash;
>>>>>>> +    struct bfd_entry *bfd_rp, *bfd_sr;
>>>>>>>
>>>>>>> -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
>>>>>>> +    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
>>>>>>> +                             nb_bt->dst_ip);
>>>>>>> +    if (!bfd_rp) {
>>>>>>> +        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
>>>>>>> +                                 nb_bt->dst_ip);
>>>>>>> +        if (!bfd_sr) {
>>>>>>> +            return "admin_down";
>>>>>>> +        }
>>>>>>> +    }
>>>>>>>
>>>>>>> -    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
>>>>>>> -        bfd_e = xmalloc(sizeof *bfd_e);
>>>>>>> -        bfd_e->sb_bt = sb_bt;
>>>>>>> -        hash = hash_string(sb_bt->dst_ip, 0);
>>>>>>> -        hash = hash_string(sb_bt->logical_port, hash);
>>>>>>> -        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
>>>>>>> -        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
>>>>>>> +    return bfd_rp ? bfd_rp->status : bfd_sr->status;
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>> +               const struct nbrec_bfd_table *nbrec_bfd_table,
>>>>>>> +               const struct hmap *lr_ports,
>>>>>>> +               const struct hmap *bfd_connections,
>>>>>>> +               const struct hmap *rp_bfd_connections,
>>>>>>> +               const struct hmap *sr_bfd_connections,
>>>>>>> +               struct hmap *sync_bfd_connections)
>>>>>>> +{
>>>>>>> +    if (!ovnsb_txn) {
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
>>>>>>> +
>>>>>>> +    struct bfd_entry *bfd_e;
>>>>>>> +    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
>>>>>>> +        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
>>>>>>> +                                              bfd_e->logical_port,
>>>>>>> +                                              bfd_e->dst_ip, bfd_e->status);
>>>>>>
>>>>>> It looks like we are blindly allocating a new entry for each entry in
>>>>>> bfd_connections without checking if it already exists. Did I
>>>>>> misunderstand?
>>>>>
>>>>> So far sync_bfd_connections always start from scratch and it is 1:1 with
>>>>> bfd_connections, so I guess an entry can't already exist. But I prefer to be
>>>>> more generic and check it. I will fix it.
>>>>>
>>>>
>>>> What I meant was that now the sync_bfd_connections table is persistent
>>>> across I-P engine runs (I didn't see it get destroyed across
>>>> iterations but correct me if I am wrong), the above code just
>>>> allocates entries again and again every iteration without removing the
>>>> old ones. For simplicity we may just destroy old entries at the
>>>> beginning of this function because now we always do recompute. In the
>>>> future it may be optimized to incrementally consolidate new/old
>>>> entries. Does this make sense?
>>>
>>>
>>> sync_bfd_connections is destroyed at the beginning of en_bfd_sync_run() so I
>>> guess we already have what you are suggesting, right? Anyway we can still use
>>> bfd_port_lookup() to check if an entry has been already allocated. What do you
>>> prefer?
>>>
>>
>> Oh, yes. Sorry I missed that. If it is recreated then there is no
>> point to check the existence again.
>>
>> Thanks,
>> Han
>>
>>> Regards,
>>> Lorenzo
>>>
>>>>
>>>> Thanks,
>>>> Han
>>>>
>>>>> Regards,
>>>>> Lorenzo
>>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> Han
>>>>>>
>>>>>>> +        e->nb_bt = bfd_e->nb_bt;
>>>>>>> +        e->sb_bt = bfd_e->sb_bt;
>>>>>>> +        e->stale = true;
>>>>>>> +        /* we need to check if this entry is even in the BFD nb db table */
>>>>>>> +        if (bfd_e->sb_bt) {
>>>>>>> +            bitmap_set1(bfd_src_ports,
>>>>>>> +                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
>>>>>>> +        }
>>>>>>>       }
>>>>>>>
>>>>>>>       const struct nbrec_bfd *nb_bt;
>>>>>>>       NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
>>>>>>> -        if (!nb_bt->status) {
>>>>>>> -            /* default state is admin_down */
>>>>>>> -            nbrec_bfd_set_status(nb_bt, "admin_down");
>>>>>>> +        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
>>>>>>> +                                nb_bt->dst_ip);
>>>>>>> +        if (!bfd_e) {
>>>>>>> +            continue;
>>>>>>>           }
>>>>>>>
>>>>>>>           struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
>>>>>>> -        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
>>>>>>> -        if (!bfd_e) {
>>>>>>> +        if (!op || !op->sb) {
>>>>>>> +            /* skip not bounded ports */
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        nbrec_bfd_set_status(nb_bt,
>>>>>>> +                             bfd_get_connection_status(nb_bt,
>>>>>>> +                                                       rp_bfd_connections,
>>>>>>> +                                                       sr_bfd_connections));
>>>>>>> +        if (!bfd_e->sb_bt) {
>>>>>>>               int udp_src = bfd_get_unused_port(bfd_src_ports);
>>>>>>>               if (udp_src < 0) {
>>>>>>>                   continue;
>>>>>>>               }
>>>>>>>
>>>>>>> -            sb_bt = sbrec_bfd_insert(ovnsb_txn);
>>>>>>> +            /* Add entry to bfd sb table. */
>>>>>>> +            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
>>>>>>>               sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
>>>>>>>               sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
>>>>>>>               sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
>>>>>>>               sbrec_bfd_set_src_port(sb_bt, udp_src);
>>>>>>>               sbrec_bfd_set_status(sb_bt, nb_bt->status);
>>>>>>> -            if (op && op->sb && op->sb->chassis) {
>>>>>>> +            if (op->sb->chassis) {
>>>>>>>                   sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
>>>>>>>               }
>>>>>>>
>>>>>>> @@ -9873,39 +9936,61 @@ build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>>                       nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
>>>>>>>                   }
>>>>>>>               }
>>>>>>> +
>>>>>>>               build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
>>>>>>> -            if (op && op->sb && op->sb->chassis &&
>>>>>>> -                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
>>>>>>> +            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
>>>>>>> +                                           bfd_e->sb_bt->chassis_name)) {
>>>>>>>                   sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
>>>>>>>                                              op->sb->chassis->name);
>>>>>>>               }
>>>>>>> -
>>>>>>> -            hmap_remove(&sb_only, &bfd_e->hmap_node);
>>>>>>> -            bfd_e->ref = false;
>>>>>>> -            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
>>>>>>> -            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
>>>>>>> -            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (op) {
>>>>>>> -            op->has_bfd = true;
>>>>>>> -        }
>>>>>>> +        bfd_e->stale = false;
>>>>>>>       }
>>>>>>>
>>>>>>> -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
>>>>>>> -        struct ovn_port *op = ovn_port_find(lr_ports,
>>>>>>> -                                            bfd_e->sb_bt->logical_port);
>>>>>>> -        if (op) {
>>>>>>> -            op->has_bfd = false;
>>>>>>> +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
>>>>>>> +        if (bfd_e->stale) {
>>>>>>> +            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
>>>>>>> +            sbrec_bfd_delete(bfd_e->sb_bt);
>>>>>>> +            bfd_erase_entry(bfd_e);
>>>>>>>           }
>>>>>>> -        sbrec_bfd_delete(bfd_e->sb_bt);
>>>>>>> -        free(bfd_e);
>>>>>>>       }
>>>>>>> -    hmap_destroy(&sb_only);
>>>>>>>
>>>>>>>       bitmap_free(bfd_src_ports);
>>>>>>>   }
>>>>>>>
>>>>>>> +void
>>>>>>> +build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
>>>>>>> +              const struct sbrec_bfd_table *sbrec_bfd_table,
>>>>>>> +              struct hmap *bfd_connections)
>>>>>>> +{
>>>>>>> +    struct bfd_entry *bfd_e;
>>>>>>> +
>>>>>>> +    /* align bfd_map to sb db */
>>>>>>> +    const struct sbrec_bfd *sb_bt;
>>>>>>> +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
>>>>>>> +        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
>>>>>>> +                                sb_bt->dst_ip);
>>>>>>> +        if (!bfd_e) {
>>>>>>> +            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
>>>>>>> +                                    sb_bt->dst_ip,  sb_bt->status);
>>>>>>> +        }
>>>>>>> +        bfd_e->sb_bt = sb_bt;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    const struct nbrec_bfd *nb_bt;
>>>>>>> +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
>>>>>>> +        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
>>>>>>> +                                nb_bt->dst_ip);
>>>>>>> +        if (!bfd_e) {
>>>>>>> +            /* brand new entry. */
>>>>>>> +            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
>>>>>>> +                                    nb_bt->dst_ip, "admin_down");
>>>>>>> +        }
>>>>>>> +        bfd_e->nb_bt = nb_bt;
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>   /* Returns a string of the IP address of the router port 'op' that
>>>>>>>    * overlaps with 'ip_s".  If one is not found, returns NULL.
>>>>>>>    *
>>>>>>> @@ -9941,17 +10026,13 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
>>>>>>>       return NULL;
>>>>>>>   }
>>>>>>>
>>>>>>> -static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
>>>>>>> -
>>>>>>> -static bool check_bfd_state(
>>>>>>> -        const struct nbrec_logical_router_policy *rule,
>>>>>>> -        const struct hmap *bfd_connections,
>>>>>>> -        struct ovn_port *out_port,
>>>>>>> -        const char *nexthop)
>>>>>>> +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);
>>>>>>> -    bool ret = true;
>>>>>>>
>>>>>>>       for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
>>>>>>>           /* Check if there is a BFD session associated to the reroute
>>>>>>> @@ -9976,39 +10057,48 @@ static bool check_bfd_state(
>>>>>>>           struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
>>>>>>>                                                     nb_bt->logical_port,
>>>>>>>                                                     nb_bt->dst_ip);
>>>>>>> -        ovs_mutex_lock(&bfd_lock);
>>>>>>> -        if (bfd_e) {
>>>>>>> -            bfd_e->ref = true;
>>>>>>> +        if (!bfd_e) {
>>>>>>> +            continue;
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (!strcmp(nb_bt->status, "admin_down")) {
>>>>>>> -            nbrec_bfd_set_status(nb_bt, "down");
>>>>>>> +        /* 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);
>>>>>>>           }
>>>>>>>
>>>>>>> -        ret = strcmp(nb_bt->status, "down");
>>>>>>> -        ovs_mutex_unlock(&bfd_lock);
>>>>>>> -        break;
>>>>>>> +        if (!strcmp(bfd_e->status, "admin_down")) {
>>>>>>> +            bfd_set_status(bfd_rp, "down");
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        return strcmp(bfd_rp->status, "down");
>>>>>>>       }
>>>>>>>
>>>>>>> -    return ret;
>>>>>>> +    return true;
>>>>>>>   }
>>>>>>>
>>>>>>>   static void
>>>>>>>   build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>> -                          const struct hmap *lr_ports,
>>>>>>> -                          const struct nbrec_logical_router_policy *rule,
>>>>>>> -                          const struct hmap *bfd_connections,
>>>>>>> +                          const struct hmap *lr_ports, struct route_policy *rp,
>>>>>>>                             const struct ovsdb_idl_row *stage_hint,
>>>>>>>                             struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>> +    const struct nbrec_logical_router_policy *rule = rp->rule;
>>>>>>>       struct ds match = DS_EMPTY_INITIALIZER;
>>>>>>>       struct ds actions = DS_EMPTY_INITIALIZER;
>>>>>>>
>>>>>>>       if (!strcmp(rule->action, "reroute")) {
>>>>>>>           ovs_assert(rule->n_nexthops <= 1);
>>>>>>>
>>>>>>> -        char *nexthop =
>>>>>>> -            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
>>>>>>> +        if (!rp->n_valid_nexthops) {
>>>>>>> +            return;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        char *nexthop = rp->valid_nexthops[0];
>>>>>>>           struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
>>>>>>>                od, lr_ports, rule->priority, nexthop);
>>>>>>>           if (!out_port) {
>>>>>>> @@ -10024,10 +10114,6 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>>               return;
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
>>>>>>> -            return;
>>>>>>> -        }
>>>>>>> -
>>>>>>>           uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
>>>>>>>           if (pkt_mark) {
>>>>>>>               ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
>>>>>>> @@ -10070,19 +10156,18 @@ static void
>>>>>>>   build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>>>>>>>                                   struct ovn_datapath *od,
>>>>>>>                                   const struct hmap *lr_ports,
>>>>>>> -                                const struct nbrec_logical_router_policy *rule,
>>>>>>> -                                const struct hmap *bfd_connections,
>>>>>>> +                                struct route_policy *rp,
>>>>>>>                                   uint16_t ecmp_group_id,
>>>>>>>                                   struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>> -    ovs_assert(rule->n_nexthops > 1);
>>>>>>> -
>>>>>>>       bool nexthops_is_ipv4 = true;
>>>>>>> +    const struct nbrec_logical_router_policy *rule = rp->rule;
>>>>>>> +    ovs_assert(rule->n_nexthops > 1);
>>>>>>>
>>>>>>>       /* Check that all the nexthops belong to the same addr family before
>>>>>>>        * adding logical flows. */
>>>>>>> -    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
>>>>>>> -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
>>>>>>> +    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
>>>>>>> +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
>>>>>>>
>>>>>>>           if (i == 0) {
>>>>>>>               nexthops_is_ipv4 = is_ipv4;
>>>>>>> @@ -10093,7 +10178,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>>>>>>>               VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
>>>>>>>                            "the match [%s] do not belong to the same address "
>>>>>>>                            "family as other next hops",
>>>>>>> -                         rule->nexthops[i], rule->match);
>>>>>>> +                         rp->valid_nexthops[i], rule->match);
>>>>>>>               return;
>>>>>>>           }
>>>>>>>       }
>>>>>>> @@ -10101,40 +10186,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>>>>>>>       struct ds match = DS_EMPTY_INITIALIZER;
>>>>>>>       struct ds actions = DS_EMPTY_INITIALIZER;
>>>>>>>
>>>>>>> -    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
>>>>>>> -    size_t n_valid_nexthops = 0;
>>>>>>> -
>>>>>>> -    for (size_t i = 0; i < rule->n_nexthops; i++) {
>>>>>>> +    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
>>>>>>>           struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
>>>>>>> -             od, lr_ports, rule->priority, rule->nexthops[i]);
>>>>>>> +             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
>>>>>>>           if (!out_port) {
>>>>>>>               goto cleanup;
>>>>>>>           }
>>>>>>>
>>>>>>>           const char *lrp_addr_s =
>>>>>>> -            find_lrp_member_ip(out_port, rule->nexthops[i]);
>>>>>>> +            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
>>>>>>>           if (!lrp_addr_s) {
>>>>>>>               static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>>>>>>>               VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
>>>>>>>                               " priority %"PRId64" nexthop %s",
>>>>>>> -                            rule->priority, rule->nexthops[i]);
>>>>>>> +                            rule->priority, rp->valid_nexthops[i]);
>>>>>>>               goto cleanup;
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (!check_bfd_state(rule, bfd_connections, out_port,
>>>>>>> -                             rule->nexthops[i])) {
>>>>>>> -            continue;
>>>>>>> -        }
>>>>>>> -
>>>>>>> -        valid_nexthops[n_valid_nexthops++] = i + 1;
>>>>>>> -
>>>>>>>           ds_clear(&actions);
>>>>>>>           uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
>>>>>>>           if (pkt_mark) {
>>>>>>>               ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
>>>>>>>           }
>>>>>>>
>>>>>>> -        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
>>>>>>> +        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
>>>>>>>
>>>>>>>           ds_put_format(&actions, "%s = %s; "
>>>>>>>                         "%s = %s; "
>>>>>>> @@ -10143,7 +10218,7 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>>>>>>>                         "flags.loopback = 1; "
>>>>>>>                         "next;",
>>>>>>>                         is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
>>>>>>> -                      rule->nexthops[i],
>>>>>>> +                      rp->valid_nexthops[i],
>>>>>>>                         is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
>>>>>>>                         lrp_addr_s,
>>>>>>>                         out_port->lrp_networks.ea_s,
>>>>>>> @@ -10159,37 +10234,30 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows,
>>>>>>>                                   lflow_ref);
>>>>>>>       }
>>>>>>>
>>>>>>> -    if (!n_valid_nexthops) {
>>>>>>> -        goto cleanup;
>>>>>>> -    }
>>>>>>> -
>>>>>>>       ds_clear(&actions);
>>>>>>> -    if (n_valid_nexthops > 1) {
>>>>>>> +    if (rp->n_valid_nexthops > 1) {
>>>>>>>           ds_put_format(&actions, "%s = %"PRIu16
>>>>>>>                         "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
>>>>>>>                         REG_ECMP_MEMBER_ID);
>>>>>>>
>>>>>>> -        for (size_t i = 0; i < n_valid_nexthops; i++) {
>>>>>>> +        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
>>>>>>>               if (i > 0) {
>>>>>>>                   ds_put_cstr(&actions, ", ");
>>>>>>>               }
>>>>>>>
>>>>>>> -            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
>>>>>>> +            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
>>>>>>>           }
>>>>>>>           ds_put_cstr(&actions, ");");
>>>>>>>       } else {
>>>>>>>           ds_put_format(&actions, "%s = %"PRIu16
>>>>>>> -                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
>>>>>>> -                      ecmp_group_id, REG_ECMP_MEMBER_ID,
>>>>>>> -                      valid_nexthops[0]);
>>>>>>> +                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
>>>>>>> +                      ecmp_group_id, REG_ECMP_MEMBER_ID);
>>>>>>>       }
>>>>>>>       ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
>>>>>>>                               rule->priority, rule->match,
>>>>>>>                               ds_cstr(&actions), &rule->header_,
>>>>>>>                               lflow_ref);
>>>>>>> -
>>>>>>>   cleanup:
>>>>>>> -    free(valid_nexthops);
>>>>>>>       ds_destroy(&match);
>>>>>>>       ds_destroy(&actions);
>>>>>>>   }
>>>>>>> @@ -10214,7 +10282,7 @@ route_table_add(struct simap *route_tables, const char *route_table_name)
>>>>>>>       return rtb_id;
>>>>>>>   }
>>>>>>>
>>>>>>> -static uint32_t
>>>>>>> +uint32_t
>>>>>>>   get_route_table_id(struct simap *route_tables, const char *route_table_name)
>>>>>>>   {
>>>>>>>       if (!route_table_name || !route_table_name[0]) {
>>>>>>> @@ -10255,18 +10323,6 @@ build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
>>>>>>>       ds_destroy(&actions);
>>>>>>>   }
>>>>>>>
>>>>>>> -struct parsed_route {
>>>>>>> -    struct ovs_list list_node;
>>>>>>> -    struct in6_addr prefix;
>>>>>>> -    unsigned int plen;
>>>>>>> -    bool is_src_route;
>>>>>>> -    uint32_t route_table_id;
>>>>>>> -    uint32_t hash;
>>>>>>> -    const struct nbrec_logical_router_static_route *route;
>>>>>>> -    bool ecmp_symmetric_reply;
>>>>>>> -    bool is_discard_route;
>>>>>>> -};
>>>>>>> -
>>>>>>>   static uint32_t
>>>>>>>   route_hash(struct parsed_route *route)
>>>>>>>   {
>>>>>>> @@ -10281,11 +10337,53 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>
>>>>>>>   /* Parse and validate the route. Return the parsed route if successful.
>>>>>>>    * Otherwise return NULL. */
>>>>>>> +
>>>>>>>   static struct parsed_route *
>>>>>>> +parsed_route_lookup(struct hmap *routes, size_t hash,
>>>>>>> +                    struct parsed_route *new_pr)
>>>>>>> +{
>>>>>>> +    struct parsed_route *pr;
>>>>>>> +    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
>>>>>>> +        if (pr->plen != new_pr->plen) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (pr->is_src_route != new_pr->is_src_route) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (pr->route_table_id != new_pr->route_table_id) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (pr->route != new_pr->route) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (pr->is_discard_route != new_pr->is_discard_route) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        return pr;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    return NULL;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>>   parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>> -                  struct ovs_list *routes, struct simap *route_tables,
>>>>>>>                     const struct nbrec_logical_router_static_route *route,
>>>>>>> -                  const struct hmap *bfd_connections)
>>>>>>> +                  struct hmap *bfd_connections,
>>>>>>> +                  struct hmap *routes, struct simap *route_tables,
>>>>>>> +                  struct hmap *bfd_active_connections)
>>>>>>>   {
>>>>>>>       /* Verify that the next hop is an IP address with an all-ones mask. */
>>>>>>>       struct in6_addr nexthop;
>>>>>>> @@ -10298,7 +10396,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>               VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
>>>>>>>                            UUID_FMT, route->nexthop,
>>>>>>>                            UUID_ARGS(&route->header_.uuid));
>>>>>>> -            return NULL;
>>>>>>> +            return;
>>>>>>>           }
>>>>>>>           if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
>>>>>>>               (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
>>>>>>> @@ -10306,7 +10404,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>               VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
>>>>>>>                            UUID_FMT, route->nexthop,
>>>>>>>                            UUID_ARGS(&route->header_.uuid));
>>>>>>> -            return NULL;
>>>>>>> +            return;
>>>>>>>           }
>>>>>>>       }
>>>>>>>
>>>>>>> @@ -10317,7 +10415,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>           VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
>>>>>>>                        UUID_FMT, route->ip_prefix,
>>>>>>>                        UUID_ARGS(&route->header_.uuid));
>>>>>>> -        return NULL;
>>>>>>> +        return;
>>>>>>>       }
>>>>>>>
>>>>>>>       /* Verify that ip_prefix and nexthop have same address familiy. */
>>>>>>> @@ -10328,7 +10426,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>                            " %s and 'nexthop' %s in static route "UUID_FMT,
>>>>>>>                            route->ip_prefix, route->nexthop,
>>>>>>>                            UUID_ARGS(&route->header_.uuid));
>>>>>>> -            return NULL;
>>>>>>> +            return;
>>>>>>>           }
>>>>>>>       }
>>>>>>>
>>>>>>> @@ -10337,52 +10435,88 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>           !find_static_route_outport(od, lr_ports, route,
>>>>>>>                                      IN6_IS_ADDR_V4MAPPED(&prefix),
>>>>>>>                                      NULL, NULL)) {
>>>>>>> -        return NULL;
>>>>>>> +        return;
>>>>>>>       }
>>>>>>>
>>>>>>>       const struct nbrec_bfd *nb_bt = route->bfd;
>>>>>>>       if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
>>>>>>> -        struct bfd_entry *bfd_e;
>>>>>>> +        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
>>>>>>> +                                                  nb_bt->logical_port,
>>>>>>> +                                                  nb_bt->dst_ip);
>>>>>>> +        if (!bfd_e) {
>>>>>>> +            return;
>>>>>>> +        }
>>>>>>>
>>>>>>> -        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
>>>>>>> -                                nb_bt->dst_ip);
>>>>>>> -        ovs_mutex_lock(&bfd_lock);
>>>>>>> -        if (bfd_e) {
>>>>>>> -            bfd_e->ref = true;
>>>>>>> +        /* This static route is linked to an active bfd session. */
>>>>>>> +        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
>>>>>>> +                                                   nb_bt->logical_port,
>>>>>>> +                                                   nb_bt->dst_ip);
>>>>>>> +        if (!bfd_sr) {
>>>>>>> +            bfd_sr = bfd_alloc_entry(bfd_active_connections,
>>>>>>> +                                     nb_bt->logical_port, nb_bt->dst_ip,
>>>>>>> +                                     bfd_e->status);
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (!strcmp(nb_bt->status, "admin_down")) {
>>>>>>> -            nbrec_bfd_set_status(nb_bt, "down");
>>>>>>> +        if (!strcmp(bfd_e->status, "admin_down")) {
>>>>>>> +            bfd_set_status(bfd_sr, "down");
>>>>>>>           }
>>>>>>>
>>>>>>> -        if (!strcmp(nb_bt->status, "down")) {
>>>>>>> -            ovs_mutex_unlock(&bfd_lock);
>>>>>>> -            return NULL;
>>>>>>> +
>>>>>>> +        if (!strcmp(bfd_sr->status, "down")) {
>>>>>>> +            return;
>>>>>>>           }
>>>>>>> -        ovs_mutex_unlock(&bfd_lock);
>>>>>>>       }
>>>>>>>
>>>>>>> -    struct parsed_route *pr = xzalloc(sizeof *pr);
>>>>>>> -    pr->prefix = prefix;
>>>>>>> -    pr->plen = plen;
>>>>>>> -    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
>>>>>>> -    pr->is_src_route = (route->policy && !strcmp(route->policy,
>>>>>>> -                                                 "src-ip"));
>>>>>>> -    pr->hash = route_hash(pr);
>>>>>>> -    pr->route = route;
>>>>>>> -    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
>>>>>>> -                                             "ecmp_symmetric_reply", false);
>>>>>>> -    pr->is_discard_route = is_discard_route;
>>>>>>> -    ovs_list_insert(routes, &pr->list_node);
>>>>>>> -    return pr;
>>>>>>> +    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
>>>>>>> +    new_pr->prefix = prefix;
>>>>>>> +    new_pr->plen = plen;
>>>>>>> +    new_pr->route_table_id = get_route_table_id(route_tables,
>>>>>>> +                                                route->route_table);
>>>>>>> +    new_pr->is_src_route = (route->policy &&
>>>>>>> +                            !strcmp(route->policy, "src-ip"));
>>>>>>> +    new_pr->hash = route_hash(new_pr);
>>>>>>> +    new_pr->route = route;
>>>>>>> +    new_pr->nbr = od->nbr;
>>>>>>> +    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
>>>>>>> +                                                 "ecmp_symmetric_reply",
>>>>>>> +                                                 false);
>>>>>>> +    new_pr->is_discard_route = is_discard_route;
>>>>>>> +
>>>>>>> +    size_t hash = uuid_hash(&od->key);
>>>>>>> +    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
>>>>>>> +    if (!pr) {
>>>>>>> +        hmap_insert(routes, &new_pr->key_node, hash);
>>>>>>> +    } else {
>>>>>>> +        pr->stale = false;
>>>>>>> +        free(new_pr);
>>>>>>> +    }
>>>>>>>   }
>>>>>>>
>>>>>>> -static void
>>>>>>> -parsed_routes_destroy(struct ovs_list *routes)
>>>>>>> +void
>>>>>>> +build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>> +                    struct hmap *bfd_connections, struct hmap *routes,
>>>>>>> +                    struct simap *route_tables,
>>>>>>> +                    struct hmap *bfd_active_connections)
>>>>>>>   {
>>>>>>>       struct parsed_route *pr;
>>>>>>> -    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
>>>>>>> -        ovs_list_remove(&pr->list_node);
>>>>>>> +    HMAP_FOR_EACH (pr, key_node, routes) {
>>>>>>> +        if (pr->nbr == od->nbr) {
>>>>>>> +            pr->stale = true;
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    for (int i = 0; i < od->nbr->n_static_routes; i++) {
>>>>>>> +        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
>>>>>>> +                          bfd_connections, routes, route_tables,
>>>>>>> +                          bfd_active_connections);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
>>>>>>> +        if (!pr->stale) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        hmap_remove(routes, &pr->key_node);
>>>>>>>           free(pr);
>>>>>>>       }
>>>>>>>   }
>>>>>>> @@ -10830,6 +10964,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>>             const struct ovn_port *op, const char *lrp_addr_s,
>>>>>>>             const char *network_s, int plen, const char *gateway,
>>>>>>>             bool is_src_route, const uint32_t rtb_id,
>>>>>>> +          const struct hmap *bfd_connections,
>>>>>>>             const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
>>>>>>>             int ofs, struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>> @@ -10878,7 +11013,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>>                               priority, ds_cstr(&match),
>>>>>>>                               ds_cstr(&actions), stage_hint,
>>>>>>>                               lflow_ref);
>>>>>>> -    if (op && op->has_bfd) {
>>>>>>> +    if (op && bfd_is_port_running(bfd_connections, op->key)) {
>>>>>>>           ds_put_format(&match, " && udp.dst == 3784");
>>>>>>>           ovn_lflow_add_with_hint(lflows, op->od,
>>>>>>>                                   S_ROUTER_IN_IP_ROUTING,
>>>>>>> @@ -10895,6 +11030,7 @@ static void
>>>>>>>   build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>>                           const struct hmap *lr_ports,
>>>>>>>                           const struct parsed_route *route_,
>>>>>>> +                        const struct hmap *bfd_connections,
>>>>>>>                           struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>>       const char *lrp_addr_s = NULL;
>>>>>>> @@ -10918,8 +11054,9 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
>>>>>>>       char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
>>>>>>>       add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
>>>>>>>                 lrp_addr_s, prefix_s, route_->plen, route->nexthop,
>>>>>>> -              route_->is_src_route, route_->route_table_id, &route->header_,
>>>>>>> -              route_->is_discard_route, ofs, lflow_ref);
>>>>>>> +              route_->is_src_route, route_->route_table_id,
>>>>>>> +              bfd_connections, &route->header_, route_->is_discard_route,
>>>>>>> +              ofs, lflow_ref);
>>>>>>>
>>>>>>>       free(prefix_s);
>>>>>>>   }
>>>>>>> @@ -12062,9 +12199,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
>>>>>>>   static void
>>>>>>>   build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
>>>>>>>                           const struct shash *meter_groups,
>>>>>>> +                        const struct hmap *bfd_connections,
>>>>>>>                           struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>> -    if (!op->has_bfd) {
>>>>>>> +    if (!bfd_is_port_running(bfd_connections, op->key)) {
>>>>>>>           return;
>>>>>>>       }
>>>>>>>
>>>>>>> @@ -12659,32 +12797,34 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
>>>>>>>    * LRP 'op'.
>>>>>>>    */
>>>>>>>   static void
>>>>>>> -build_ip_routing_flows_for_lrp(
>>>>>>> -        struct ovn_port *op, struct lflow_table *lflows,
>>>>>>> -        struct lflow_ref *lflow_ref)
>>>>>>> +build_ip_routing_flows_for_lrp(struct ovn_port *op,
>>>>>>> +                               const struct hmap *bfd_connections,
>>>>>>> +                               struct lflow_table *lflows,
>>>>>>> +                               struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>>       ovs_assert(op->nbrp);
>>>>>>>       for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>>>>>>>           add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
>>>>>>>                     op->lrp_networks.ipv4_addrs[i].network_s,
>>>>>>>                     op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
>>>>>>> -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
>>>>>>> -                  lflow_ref);
>>>>>>> +                  bfd_connections, &op->nbrp->header_, false,
>>>>>>> +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
>>>>>>>       }
>>>>>>>
>>>>>>>       for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
>>>>>>>           add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
>>>>>>>                     op->lrp_networks.ipv6_addrs[i].network_s,
>>>>>>>                     op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
>>>>>>> -                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
>>>>>>> -                  lflow_ref);
>>>>>>> +                  bfd_connections, &op->nbrp->header_, false,
>>>>>>> +                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
>>>>>>>       }
>>>>>>>   }
>>>>>>>
>>>>>>>   static void
>>>>>>>   build_static_route_flows_for_lrouter(
>>>>>>>           struct ovn_datapath *od, struct lflow_table *lflows,
>>>>>>> -        const struct hmap *lr_ports, const struct hmap *bfd_connections,
>>>>>>> +        const struct hmap *lr_ports, struct hmap *parsed_routes,
>>>>>>> +        struct simap *route_tables, const struct hmap *bfd_connections,
>>>>>>>           struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>>       ovs_assert(od->nbr);
>>>>>>> @@ -12698,22 +12838,16 @@ build_static_route_flows_for_lrouter(
>>>>>>>
>>>>>>>       struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
>>>>>>>       struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
>>>>>>> -    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
>>>>>>> -    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
>>>>>>>       struct ecmp_groups_node *group;
>>>>>>>
>>>>>>>       for (int i = 0; i < od->nbr->n_ports; i++) {
>>>>>>>           build_route_table_lflow(od, lflows, od->nbr->ports[i],
>>>>>>> -                                &route_tables, lflow_ref);
>>>>>>> +                                route_tables, lflow_ref);
>>>>>>>       }
>>>>>>>
>>>>>>> -    for (int i = 0; i < od->nbr->n_static_routes; i++) {
>>>>>>> -        struct parsed_route *route =
>>>>>>> -            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
>>>>>>> -                              od->nbr->static_routes[i], bfd_connections);
>>>>>>> -        if (!route) {
>>>>>>> -            continue;
>>>>>>> -        }
>>>>>>> +    struct parsed_route *route;
>>>>>>> +    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
>>>>>>> +                             parsed_routes) {
>>>>>>>           group = ecmp_groups_find(&ecmp_groups, route);
>>>>>>>           if (group) {
>>>>>>>               ecmp_groups_add_route(group, route);
>>>>>>> @@ -12737,12 +12871,11 @@ build_static_route_flows_for_lrouter(
>>>>>>>       }
>>>>>>>       const struct unique_routes_node *ur;
>>>>>>>       HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
>>>>>>> -        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
>>>>>>> +        build_static_route_flow(lflows, od, lr_ports, ur->route,
>>>>>>> +                                bfd_connections, lflow_ref);
>>>>>>>       }
>>>>>>>       ecmp_groups_destroy(&ecmp_groups);
>>>>>>>       unique_routes_destroy(&unique_routes);
>>>>>>> -    parsed_routes_destroy(&parsed_routes);
>>>>>>> -    simap_destroy(&route_tables);
>>>>>>>   }
>>>>>>>
>>>>>>>   /* IP Multicast lookup. Here we set the output port, adjust TTL and
>>>>>>> @@ -12849,6 +12982,115 @@ build_mcast_lookup_flows_for_lrouter(
>>>>>>>       }
>>>>>>>   }
>>>>>>>
>>>>>>> +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->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;
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>> +                     const struct hmap *bfd_connections,
>>>>>>> +                     struct hmap *route_policies,
>>>>>>> +                     struct hmap *bfd_active_connections)
>>>>>>> +{
>>>>>>> +    struct route_policy *rp;
>>>>>>> +
>>>>>>> +    HMAP_FOR_EACH (rp, key_node, route_policies) {
>>>>>>> +        if (rp->nbr == od->nbr) {
>>>>>>> +            rp->stale = true;
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    for (int i = 0; i < od->nbr->n_policies; i++) {
>>>>>>> +        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
>>>>>>> +        size_t n_valid_nexthops = 0;
>>>>>>> +        char **valid_nexthops = NULL;
>>>>>>> +
>>>>>>> +        if (!strcmp(rule->action, "reroute")) {
>>>>>>> +            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
>>>>>>> +
>>>>>>> +            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
>>>>>>> +            for (size_t j = 0; j < n_nexthops; j++) {
>>>>>>> +                char *nexthop = rule->n_nexthops
>>>>>>> +                    ? rule->nexthops[j] : rule->nexthop;
>>>>>>> +                struct ovn_port *out_port =
>>>>>>> +                    get_outport_for_routing_policy_nexthop(
>>>>>>> +                            od, lr_ports, rule->priority, nexthop);
>>>>>>> +                if (!out_port || !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->nbr = od->nbr;
>>>>>>> +
>>>>>>> +        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 {
>>>>>>> +            rp->stale = false;
>>>>>>> +            free(valid_nexthops);
>>>>>>> +            free(new_rp);
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
>>>>>>> +        if (!rp->stale) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        hmap_remove(route_policies, &rp->key_node);
>>>>>>> +        free(rp->valid_nexthops);
>>>>>>> +        free(rp);
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>   /* Logical router ingress table POLICY: Policy.
>>>>>>>    *
>>>>>>>    * A packet that arrives at this table is an IP packet that should be
>>>>>>> @@ -12862,7 +13104,7 @@ static void
>>>>>>>   build_ingress_policy_flows_for_lrouter(
>>>>>>>           struct ovn_datapath *od, struct lflow_table *lflows,
>>>>>>>           const struct hmap *lr_ports,
>>>>>>> -        const struct hmap *bfd_connections,
>>>>>>> +        struct hmap *route_policies,
>>>>>>>           struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>>       ovs_assert(od->nbr);
>>>>>>> @@ -12879,21 +13121,20 @@ build_ingress_policy_flows_for_lrouter(
>>>>>>>
>>>>>>>       /* Convert routing policies to flows. */
>>>>>>>       uint16_t ecmp_group_id = 1;
>>>>>>> -    for (int i = 0; i < od->nbr->n_policies; i++) {
>>>>>>> -        const struct nbrec_logical_router_policy *rule
>>>>>>> -            = od->nbr->policies[i];
>>>>>>> +    struct route_policy *rp;
>>>>>>> +    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
>>>>>>> +                             route_policies) {
>>>>>>> +        const struct nbrec_logical_router_policy *rule = rp->rule;
>>>>>>>           bool is_ecmp_reroute =
>>>>>>>               (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
>>>>>>>
>>>>>>>           if (is_ecmp_reroute) {
>>>>>>> -            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
>>>>>>> -                                            bfd_connections, ecmp_group_id,
>>>>>>> -                                            lflow_ref);
>>>>>>> +            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
>>>>>>> +                                            ecmp_group_id, lflow_ref);
>>>>>>>               ecmp_group_id++;
>>>>>>>           } else {
>>>>>>> -            build_routing_policy_flow(lflows, od, lr_ports, rule,
>>>>>>> -                                      bfd_connections, &rule->header_,
>>>>>>> -                                      lflow_ref);
>>>>>>> +            build_routing_policy_flow(lflows, od, lr_ports, rp,
>>>>>>> +                                      &rule->header_, lflow_ref);
>>>>>>>           }
>>>>>>>       }
>>>>>>>   }
>>>>>>> @@ -14105,6 +14346,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>>>>>>>                               struct lflow_table *lflows,
>>>>>>>                               struct ds *match, struct ds *actions,
>>>>>>>                               const struct shash *meter_groups,
>>>>>>> +                            const struct hmap *bfd_connections,
>>>>>>>                               struct lflow_ref *lflow_ref)
>>>>>>>   {
>>>>>>>       ovs_assert(op->nbrp);
>>>>>>> @@ -14146,7 +14388,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>>>>>>>       }
>>>>>>>
>>>>>>>       /* BFD msg handling */
>>>>>>> -    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
>>>>>>> +    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
>>>>>>> +                            lflow_ref);
>>>>>>>
>>>>>>>       /* ICMP time exceeded */
>>>>>>>       struct ds ip_ds = DS_EMPTY_INITIALIZER;
>>>>>>> @@ -15589,6 +15832,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
>>>>>>>   static void
>>>>>>>   build_routable_flows_for_router_port(
>>>>>>>       struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
>>>>>>> +    const struct hmap *bfd_connections,
>>>>>>>       struct lflow_table *lflows,
>>>>>>>       struct ds *match,
>>>>>>>       struct ds *actions)
>>>>>>> @@ -15625,8 +15869,8 @@ build_routable_flows_for_router_port(
>>>>>>>                                 router_port->lrp_networks.ipv4_addrs[0].addr_s,
>>>>>>>                                 laddrs->ipv4_addrs[k].network_s,
>>>>>>>                                 laddrs->ipv4_addrs[k].plen, NULL, false, 0,
>>>>>>> -                              &router_port->nbrp->header_, false,
>>>>>>> -                              ROUTE_PRIO_OFFSET_CONNECTED,
>>>>>>> +                              bfd_connections, &router_port->nbrp->header_,
>>>>>>> +                              false, ROUTE_PRIO_OFFSET_CONNECTED,
>>>>>>>                                 lrp->stateful_lflow_ref);
>>>>>>>                   }
>>>>>>>               }
>>>>>>> @@ -15736,8 +15980,8 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
>>>>>>>   static void
>>>>>>>   build_lbnat_lflows_iterate_by_lrp(
>>>>>>>       struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
>>>>>>> -    const struct shash *meter_groups, struct ds *match,
>>>>>>> -    struct ds *actions, struct lflow_table *lflows)
>>>>>>> +    const struct shash *meter_groups, const struct hmap *bfd_connections,
>>>>>>> +    struct ds *match, struct ds *actions, struct lflow_table *lflows)
>>>>>>>   {
>>>>>>>       ovs_assert(op->nbrp);
>>>>>>>
>>>>>>> @@ -15749,8 +15993,8 @@ build_lbnat_lflows_iterate_by_lrp(
>>>>>>>       build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
>>>>>>>                                   actions, lflows);
>>>>>>>
>>>>>>> -    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
>>>>>>> -                                         actions);
>>>>>>> +    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
>>>>>>> +                                         lflows, match, actions);
>>>>>>>   }
>>>>>>>
>>>>>>>   static void
>>>>>>> @@ -15818,6 +16062,9 @@ struct lswitch_flow_build_info {
>>>>>>>       struct ds actions;
>>>>>>>       size_t thread_lflow_counter;
>>>>>>>       const char *svc_monitor_mac;
>>>>>>> +    struct hmap *parsed_routes;
>>>>>>> +    struct hmap *route_policies;
>>>>>>> +    struct simap *route_tables;
>>>>>>>   };
>>>>>>>
>>>>>>>   /* Helper function to combine all lflow generation which is iterated by
>>>>>>> @@ -15863,12 +16110,12 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
>>>>>>>       build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
>>>>>>>       build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
>>>>>>>       build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
>>>>>>> -                                         lsi->bfd_connections,
>>>>>>> -                                         NULL);
>>>>>>> +                                         lsi->parsed_routes, lsi->route_tables,
>>>>>>> +                                         lsi->bfd_connections, NULL);
>>>>>>>       build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
>>>>>>>                                            &lsi->actions, NULL);
>>>>>>>       build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
>>>>>>> -                                           lsi->bfd_connections, NULL);
>>>>>>> +                                           lsi->route_policies, NULL);
>>>>>>>       build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
>>>>>>>       build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
>>>>>>>                                             &lsi->match, &lsi->actions,
>>>>>>> @@ -15935,7 +16182,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
>>>>>>>                                             &lsi->actions, op->lflow_ref);
>>>>>>>       build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
>>>>>>>                                                   &lsi->actions, op->lflow_ref);
>>>>>>> -    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
>>>>>>> +    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
>>>>>>> +                                   lsi->lflows, op->lflow_ref);
>>>>>>>       build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
>>>>>>>                                          &lsi->actions, lsi->meter_groups,
>>>>>>>                                          op->lflow_ref);
>>>>>>> @@ -15953,7 +16201,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
>>>>>>>                                               lsi->meter_groups,
>>>>>>>                                               op->lflow_ref);
>>>>>>>       build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
>>>>>>> -                                lsi->meter_groups, op->lflow_ref);
>>>>>>> +                                lsi->meter_groups, lsi->bfd_connections,
>>>>>>> +                                op->lflow_ref);
>>>>>>>       build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
>>>>>>>                                                    &lsi->actions, op->lflow_ref);
>>>>>>>   }
>>>>>>> @@ -16042,7 +16291,8 @@ build_lflows_thread(void *arg)
>>>>>>>                       build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
>>>>>>>                       build_lbnat_lflows_iterate_by_lrp(
>>>>>>>                           op, lsi->lr_stateful_table, lsi->meter_groups,
>>>>>>> -                        &lsi->match, &lsi->actions, lsi->lflows);
>>>>>>> +                        lsi->bfd_connections, &lsi->match, &lsi->actions,
>>>>>>> +                        lsi->lflows);
>>>>>>>                   }
>>>>>>>               }
>>>>>>>               for (bnum = control->id;
>>>>>>> @@ -16182,7 +16432,10 @@ build_lswitch_and_lrouter_flows(
>>>>>>>       const struct hmap *svc_monitor_map,
>>>>>>>       const struct hmap *bfd_connections,
>>>>>>>       const struct chassis_features *features,
>>>>>>> -    const char *svc_monitor_mac)
>>>>>>> +    const char *svc_monitor_mac,
>>>>>>> +    struct hmap *parsed_routes,
>>>>>>> +    struct hmap *route_policies,
>>>>>>> +    struct simap *route_tables)
>>>>>>>   {
>>>>>>>
>>>>>>>       char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
>>>>>>> @@ -16216,6 +16469,9 @@ build_lswitch_and_lrouter_flows(
>>>>>>>               lsiv[index].svc_check_match = svc_check_match;
>>>>>>>               lsiv[index].thread_lflow_counter = 0;
>>>>>>>               lsiv[index].svc_monitor_mac = svc_monitor_mac;
>>>>>>> +            lsiv[index].parsed_routes = parsed_routes;
>>>>>>> +            lsiv[index].route_tables = route_tables;
>>>>>>> +            lsiv[index].route_policies = route_policies;
>>>>>>>               ds_init(&lsiv[index].match);
>>>>>>>               ds_init(&lsiv[index].actions);
>>>>>>>
>>>>>>> @@ -16256,6 +16512,9 @@ build_lswitch_and_lrouter_flows(
>>>>>>>               .features = features,
>>>>>>>               .svc_check_match = svc_check_match,
>>>>>>>               .svc_monitor_mac = svc_monitor_mac,
>>>>>>> +            .parsed_routes = parsed_routes,
>>>>>>> +            .route_tables = route_tables,
>>>>>>> +            .route_policies = route_policies,
>>>>>>>               .match = DS_EMPTY_INITIALIZER,
>>>>>>>               .actions = DS_EMPTY_INITIALIZER,
>>>>>>>           };
>>>>>>> @@ -16287,6 +16546,7 @@ build_lswitch_and_lrouter_flows(
>>>>>>>               build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
>>>>>>>               build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
>>>>>>>                                                 lsi.meter_groups,
>>>>>>> +                                              lsi.bfd_connections,
>>>>>>>                                                 &lsi.match,
>>>>>>>                                                 &lsi.actions,
>>>>>>>                                                 lsi.lflows);
>>>>>>> @@ -16417,7 +16677,10 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>>                                       input_data->svc_monitor_map,
>>>>>>>                                       input_data->bfd_connections,
>>>>>>>                                       input_data->features,
>>>>>>> -                                    input_data->svc_monitor_mac);
>>>>>>> +                                    input_data->svc_monitor_mac,
>>>>>>> +                                    input_data->parsed_routes,
>>>>>>> +                                    input_data->route_policies,
>>>>>>> +                                    input_data->route_tables);
>>>>>>>
>>>>>>>       if (parallelization_state == STATE_INIT_HASH_SIZES) {
>>>>>>>           parallelization_state = STATE_USE_PARALLELIZATION;
>>>>>>> @@ -16779,6 +17042,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>>               build_lbnat_lflows_iterate_by_lrp(op,
>>>>>>>                                                 lflow_input->lr_stateful_table,
>>>>>>>                                                 lflow_input->meter_groups,
>>>>>>> +                                              lflow_input->bfd_connections,
>>>>>>>                                                 &match, &actions,
>>>>>>>                                                 lflows);
>>>>>>>
>>>>>>> @@ -17490,6 +17754,27 @@ 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);
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +static_routes_init(struct static_routes_data *data)
>>>>>>> +{
>>>>>>> +    hmap_init(&data->parsed_routes);
>>>>>>> +    simap_init(&data->route_tables);
>>>>>>> +    hmap_init(&data->bfd_active_connections);
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +bfd_init(struct bfd_data *data)
>>>>>>> +{
>>>>>>> +    hmap_init(&data->bfd_connections);
>>>>>>> +}
>>>>>>> +
>>>>>>>   void
>>>>>>>   northd_destroy(struct northd_data *data)
>>>>>>>   {
>>>>>>> @@ -17529,6 +17814,48 @@ northd_destroy(struct northd_data *data)
>>>>>>>       destroy_northd_tracked_data(data);
>>>>>>>   }
>>>>>>>
>>>>>>> +static void
>>>>>>> +__bfd_destroy(struct hmap *bfd_connections)
>>>>>>> +{
>>>>>>> +    struct bfd_entry *bfd_e;
>>>>>>> +
>>>>>>> +    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
>>>>>>> +        bfd_erase_entry(bfd_e);
>>>>>>> +    }
>>>>>>> +    hmap_destroy(bfd_connections);
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +bfd_destroy(struct bfd_data *data)
>>>>>>> +{
>>>>>>> +    __bfd_destroy(&data->bfd_connections);
>>>>>>> +}
>>>>>>> +
>>>>>>> +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);
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +static_routes_destroy(struct static_routes_data *data)
>>>>>>> +{
>>>>>>> +    struct parsed_route *r;
>>>>>>> +    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
>>>>>>> +        free(r);
>>>>>>> +    }
>>>>>>> +    hmap_destroy(&data->parsed_routes);
>>>>>>> +
>>>>>>> +    simap_destroy(&data->route_tables);
>>>>>>> +    __bfd_destroy(&data->bfd_active_connections);
>>>>>>> +}
>>>>>>> +
>>>>>>>   void
>>>>>>>   ovnnb_db_run(struct northd_input *input_data,
>>>>>>>                struct northd_data *data,
>>>>>>> diff --git a/northd/northd.h b/northd/northd.h
>>>>>>> index d4a8d75ab..432072f9f 100644
>>>>>>> --- a/northd/northd.h
>>>>>>> +++ b/northd/northd.h
>>>>>>> @@ -23,6 +23,7 @@
>>>>>>>   #include "northd/en-port-group.h"
>>>>>>>   #include "northd/ipam.h"
>>>>>>>   #include "openvswitch/hmap.h"
>>>>>>> +#include "simap.h"
>>>>>>>   #include "ovs-thread.h"
>>>>>>>
>>>>>>>   struct northd_input {
>>>>>>> @@ -160,14 +161,34 @@ 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;
>>>>>>> +    const struct nbrec_logical_router *nbr;
>>>>>>> +    bool stale;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct static_routes_data {
>>>>>>> +    struct hmap parsed_routes;
>>>>>>> +    struct simap route_tables;
>>>>>>> +    struct hmap bfd_active_connections;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct route_policies_data {
>>>>>>> +    struct hmap route_policies;
>>>>>>> +    struct hmap bfd_active_connections;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct bfd_data {
>>>>>>> +    struct hmap bfd_connections;
>>>>>>> +};
>>>>>>> +
>>>>>>>   struct lr_nat_table;
>>>>>>>
>>>>>>>   struct lflow_input {
>>>>>>> -    /* Northbound table references */
>>>>>>> -    const struct nbrec_bfd_table *nbrec_bfd_table;
>>>>>>> -
>>>>>>>       /* Southbound table references */
>>>>>>> -    const struct sbrec_bfd_table *sbrec_bfd_table;
>>>>>>>       const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
>>>>>>>       const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
>>>>>>>       const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
>>>>>>> @@ -190,6 +211,9 @@ struct lflow_input {
>>>>>>>       const struct hmap *svc_monitor_map;
>>>>>>>       bool ovn_internal_version_changed;
>>>>>>>       const char *svc_monitor_mac;
>>>>>>> +    struct hmap *parsed_routes;
>>>>>>> +    struct hmap *route_policies;
>>>>>>> +    struct simap *route_tables;
>>>>>>>   };
>>>>>>>
>>>>>>>   extern int parallelization_state;
>>>>>>> @@ -593,8 +617,6 @@ struct ovn_port {
>>>>>>>
>>>>>>>       bool has_unknown; /* If the addresses have 'unknown' defined. */
>>>>>>>
>>>>>>> -    bool has_bfd;
>>>>>>> -
>>>>>>>       /* The port's peer:
>>>>>>>        *
>>>>>>>        *     - A switch port S of type "router" has a router port R as a peer,
>>>>>>> @@ -653,6 +675,20 @@ struct ovn_port {
>>>>>>>       struct lflow_ref *stateful_lflow_ref;
>>>>>>>   };
>>>>>>>
>>>>>>> +struct parsed_route {
>>>>>>> +    struct hmap_node key_node;
>>>>>>> +    struct in6_addr prefix;
>>>>>>> +    unsigned int plen;
>>>>>>> +    bool is_src_route;
>>>>>>> +    uint32_t route_table_id;
>>>>>>> +    uint32_t hash;
>>>>>>> +    const struct nbrec_logical_router_static_route *route;
>>>>>>> +    bool ecmp_symmetric_reply;
>>>>>>> +    bool is_discard_route;
>>>>>>> +    const struct nbrec_logical_router *nbr;
>>>>>>> +    bool stale;
>>>>>>> +};
>>>>>>> +
>>>>>>>   void ovnnb_db_run(struct northd_input *input_data,
>>>>>>>                     struct northd_data *data,
>>>>>>>                     struct ovsdb_idl_txn *ovnnb_txn,
>>>>>>> @@ -674,6 +710,18 @@ 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(struct ovn_datapath *, const struct hmap *,
>>>>>>> +                         struct hmap *, struct hmap *, struct simap *,
>>>>>>> +                         struct hmap *);
>>>>>>> +uint32_t get_route_table_id(struct simap *, const char *);
>>>>>>> +void static_routes_init(struct static_routes_data *);
>>>>>>> +void static_routes_destroy(struct static_routes_data *);
>>>>>>> +
>>>>>>> +void bfd_init(struct bfd_data *);
>>>>>>> +void bfd_destroy(struct bfd_data *);
>>>>>>> +
>>>>>>>   struct lflow_table;
>>>>>>>   struct lr_stateful_tracked_data;
>>>>>>>   struct ls_stateful_tracked_data;
>>>>>>> @@ -711,13 +759,13 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data *,
>>>>>>>                                      struct hmap *lbgrp_datapaths_map,
>>>>>>>                                      struct northd_tracked_data *);
>>>>>>>
>>>>>>> -void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>> -                     const struct nbrec_bfd_table *,
>>>>>>> -                     const struct sbrec_bfd_table *,
>>>>>>> -                     const struct hmap *lr_ports,
>>>>>>> -                     struct hmap *bfd_connections);
>>>>>>> -void bfd_cleanup_connections(const struct nbrec_bfd_table *,
>>>>>>> -                             struct hmap *bfd_map);
>>>>>>> +void build_route_policies(struct ovn_datapath *, const struct hmap *,
>>>>>>> +                          const struct hmap *, struct hmap *, struct hmap *);
>>>>>>> +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 *, struct hmap *);
>>>>>>> +void build_bfd_map(const struct nbrec_bfd_table *,
>>>>>>> +                   const struct sbrec_bfd_table *, struct hmap *);
>>>>>>>   void run_update_worker_pool(int n_threads);
>>>>>>>
>>>>>>>   const struct ovn_datapath *northd_get_datapath_for_port(
>>>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>>>>> index 199197f09..710500dce 100644
>>>>>>> --- a/tests/ovn-northd.at
>>>>>>> +++ b/tests/ovn-northd.at
>>>>>>> @@ -12,6 +12,7 @@ m4_define([_DUMP_DB_TABLES], [
>>>>>>>       ovn-sbctl list meter >> $1
>>>>>>>       ovn-sbctl list meter_band >> $1
>>>>>>>       ovn-sbctl list port_group >> $1
>>>>>>> +    ovn-sbctl list bfd >> $1
>>>>>>>       ovn-sbctl dump-flows > lflows_$1
>>>>>>>   ])
>>>>>>>
>>>>>>> @@ -3861,6 +3862,7 @@ for i in $(seq 1 9); do
>>>>>>>       check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
>>>>>>>   done
>>>>>>>
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>>   uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
>>>>>>>   ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
>>>>>>>   ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
>>>>>>> @@ -3873,6 +3875,13 @@ wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
>>>>>>>   wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
>>>>>>>                        min_rx=1000 min_tx=1000 status=admin_down
>>>>>>>
>>>>>>> +check_engine_stats northd norecompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
>>>>>>>   check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
>>>>>>>
>>>>>>> @@ -3881,10 +3890,25 @@ check ovn-nbctl clear bfd $uuid_2 min_rx
>>>>>>>   wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
>>>>>>>   wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
>>>>>>>
>>>>>>> +check_engine_stats northd norecompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
>>>>>>>   wait_column down bfd status logical_port=r0-sw1
>>>>>>>   AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
>>>>>>>
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats static_routes recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
>>>>>>>   wait_column down bfd status logical_port=r0-sw2
>>>>>>>   AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
>>>>>>> @@ -3893,10 +3917,26 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
>>>>>>>   wait_column down bfd status logical_port=r0-sw5
>>>>>>>   AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
>>>>>>>
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats static_routes recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
>>>>>>>   wait_column down bfd status logical_port=r0-sw6
>>>>>>>   AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
>>>>>>>
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats route_policies recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
>>>>>>>   wait_column down bfd status logical_port=r0-sw7
>>>>>>>   AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
>>>>>>> @@ -3924,6 +3964,14 @@ wait_column down bfd status logical_port=r0-sw8
>>>>>>>   bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
>>>>>>>   AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
>>>>>>>
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats static_routes recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   check ovn-nbctl lr-policy-del r0
>>>>>>>   check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
>>>>>>>
>>>>>>> @@ -3931,6 +3979,14 @@ wait_column down bfd status dst_ip=192.168.9.2
>>>>>>>   wait_column down bfd status dst_ip=192.168.9.3
>>>>>>>   wait_column down bfd status dst_ip=192.168.9.4
>>>>>>>
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats bfd recompute nocompute
>>>>>>> +check_engine_stats route_policies recompute nocompute
>>>>>>> +check_engine_stats lflow recompute nocompute
>>>>>>> +check_engine_stats northd_output norecompute compute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>>>>> +
>>>>>>>   bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
>>>>>>>   AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])
>>>>>>>
>>>>>>> --
>>>>>>> 2.45.2
>>>>>>>
>>>>>>
>>>>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
diff mbox series

Patch

diff --git a/northd/en-lflow.c b/northd/en-lflow.c
index c4b927fb8..467a6314b 100644
--- a/northd/en-lflow.c
+++ b/northd/en-lflow.c
@@ -41,6 +41,11 @@  lflow_get_input_data(struct engine_node *node,
                      struct lflow_input *lflow_input)
 {
     struct northd_data *northd_data = engine_get_input_data("northd", node);
+    struct bfd_data *bfd_data = engine_get_input_data("bfd_sync", node);
+    struct static_routes_data *static_routes_data =
+        engine_get_input_data("static_routes", node);
+    struct route_policies_data *route_policies_data =
+        engine_get_input_data("route_policies", node);
     struct port_group_data *pg_data =
         engine_get_input_data("port_group", node);
     struct sync_meters_data *sync_meters_data =
@@ -50,10 +55,6 @@  lflow_get_input_data(struct engine_node *node,
     struct ed_type_ls_stateful *ls_stateful_data =
         engine_get_input_data("ls_stateful", node);
 
-    lflow_input->nbrec_bfd_table =
-        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
-    lflow_input->sbrec_bfd_table =
-        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
     lflow_input->sbrec_logical_flow_table =
         EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
     lflow_input->sbrec_multicast_group_table =
@@ -78,7 +79,10 @@  lflow_get_input_data(struct engine_node *node,
     lflow_input->meter_groups = &sync_meters_data->meter_groups;
     lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
     lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
-    lflow_input->bfd_connections = NULL;
+    lflow_input->bfd_connections = &bfd_data->bfd_connections;
+    lflow_input->parsed_routes = &static_routes_data->parsed_routes;
+    lflow_input->route_tables = &static_routes_data->route_tables;
+    lflow_input->route_policies = &route_policies_data->route_policies;
 
     struct ed_type_global_config *global_config =
         engine_get_input_data("global_config", node);
@@ -95,25 +99,14 @@  void en_lflow_run(struct engine_node *node, void *data)
     struct lflow_input lflow_input;
     lflow_get_input_data(node, &lflow_input);
 
-    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
-    lflow_input.bfd_connections = &bfd_connections;
-
     stopwatch_start(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
 
     struct lflow_data *lflow_data = data;
     lflow_table_clear(lflow_data->lflow_table);
     lflow_reset_northd_refs(&lflow_input);
 
-    build_bfd_table(eng_ctx->ovnsb_idl_txn,
-                    lflow_input.nbrec_bfd_table,
-                    lflow_input.sbrec_bfd_table,
-                    lflow_input.lr_ports,
-                    &bfd_connections);
     build_lflows(eng_ctx->ovnsb_idl_txn, &lflow_input,
                  lflow_data->lflow_table);
-    bfd_cleanup_connections(lflow_input.nbrec_bfd_table,
-                            &bfd_connections);
-    hmap_destroy(&bfd_connections);
     stopwatch_stop(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
 
     engine_set_node_state(node, EN_UPDATED);
diff --git a/northd/en-northd.c b/northd/en-northd.c
index 4479b4aff..dd2b4353d 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -236,6 +236,174 @@  northd_global_config_handler(struct engine_node *node, void *data OVS_UNUSED)
     return true;
 }
 
+bool
+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 false;
+    }
+
+    /* This node uses the below data from the en_northd engine node.
+     * See (lr_stateful_get_input_data())
+     *   1. northd_data->lr_datapaths
+     *   2. northd_data->lr_ports
+     *      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.
+     *      Note: When we add I-P to the created/deleted logical routers or
+     *      logical router ports, we need to revisit this handler.
+     *
+     *      This node also accesses the route policies of the logical router.
+     *      When these route policies get updated, en_northd engine recomputes
+     *      and so does this node.
+     *      Note: When we add I-P to handle route policies changes, we need
+     *      to revisit this handler.
+     */
+    return true;
+}
+
+void
+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, &northd_data->lr_ports,
+                             &bfd_data->bfd_connections,
+                             &route_policies_data->route_policies,
+                             &route_policies_data->bfd_active_connections);
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+bool
+static_routes_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 false;
+    }
+
+    /* This node uses the below data from the en_northd engine node.
+     * See (lr_stateful_get_input_data())
+     *   1. northd_data->lr_datapaths
+     *   2. northd_data->lr_ports
+     *      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.
+     *      Note: When we add I-P to the created/deleted logical routers or
+     *      logical router ports, we need to revisit this handler.
+     *
+     *      This node also accesses the static routes of the logical router.
+     *      When these static routes gets updated, en_northd engine recomputes
+     *      and so does this node.
+     *      Note: When we add I-P to handle static routes changes, we need
+     *      to revisit this handler.
+     */
+    return true;
+}
+
+void
+en_static_routes_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 static_routes_data *static_routes_data = data;
+
+    static_routes_destroy(data);
+    static_routes_init(data);
+
+    struct ovn_datapath *od;
+    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
+        for (int i = 0; i < od->nbr->n_ports; i++) {
+            const char *route_table_name =
+                smap_get(&od->nbr->ports[i]->options, "route_table");
+            get_route_table_id(&static_routes_data->route_tables,
+                               route_table_name);
+        }
+
+        build_parsed_routes(od, &northd_data->lr_ports,
+                            &bfd_data->bfd_connections,
+                            &static_routes_data->parsed_routes,
+                            &static_routes_data->route_tables,
+                            &static_routes_data->bfd_active_connections);
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+void
+en_bfd_run(struct engine_node *node, void *data)
+{
+    struct bfd_data *bfd_data = data;
+    const struct nbrec_bfd_table *nbrec_bfd_table =
+        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
+    const struct sbrec_bfd_table *sbrec_bfd_table =
+        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
+
+    bfd_destroy(data);
+    bfd_init(data);
+    build_bfd_map(nbrec_bfd_table, sbrec_bfd_table,
+                  &bfd_data->bfd_connections);
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+bool
+bfd_sync_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 false;
+    }
+
+    /* This node uses the below data from the en_northd engine node.
+     * See (lr_stateful_get_input_data())
+     *   1. northd_data->lr_ports
+     *      This data gets updated when a logical router port is created or
+     *      deleted. Northd engine node presently falls back to full recompute
+     *      when this happens and so does this node.
+     *      Note: When we add I-P to the created/deleted logical router ports,
+     *      we need to revisit this handler.
+     */
+    return true;
+}
+
+void
+en_bfd_sync_run(struct engine_node *node, void *data)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    const struct engine_context *eng_ctx = engine_get_context();
+    struct bfd_data *bfd_data = engine_get_input_data("bfd", node);
+    struct route_policies_data *route_policies_data
+        = engine_get_input_data("route_policies", node);
+    struct static_routes_data *static_routes_data
+        = engine_get_input_data("static_routes", node);
+    const struct nbrec_bfd_table *nbrec_bfd_table =
+        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
+    struct bfd_data *bfd_sync_data = data;
+
+    bfd_destroy(data);
+    bfd_init(data);
+    bfd_table_sync(eng_ctx->ovnsb_idl_txn, nbrec_bfd_table,
+                   &northd_data->lr_ports, &bfd_data->bfd_connections,
+                   &route_policies_data->bfd_active_connections,
+                   &static_routes_data->bfd_active_connections,
+                   &bfd_sync_data->bfd_connections);
+    engine_set_node_state(node, EN_UPDATED);
+}
+
 void
 *en_northd_init(struct engine_node *node OVS_UNUSED,
                 struct engine_arg *arg OVS_UNUSED)
@@ -247,6 +415,45 @@  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_static_routes_init(struct engine_node *node OVS_UNUSED,
+                      struct engine_arg *arg OVS_UNUSED)
+{
+    struct static_routes_data *data = xzalloc(sizeof *data);
+
+    static_routes_init(data);
+    return data;
+}
+
+void
+*en_bfd_init(struct engine_node *node OVS_UNUSED,
+             struct engine_arg *arg OVS_UNUSED)
+{
+    struct bfd_data *data = xzalloc(sizeof *data);
+
+    bfd_init(data);
+    return data;
+}
+
+void
+*en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
+                  struct engine_arg *arg OVS_UNUSED)
+{
+    struct bfd_data *data = xzalloc(sizeof *data);
+    bfd_init(data);
+    return data;
+}
+
 void
 en_northd_cleanup(void *data)
 {
@@ -259,3 +466,27 @@  en_northd_clear_tracked_data(void *data_)
     struct northd_data *data = data_;
     destroy_northd_data_tracked_changes(data);
 }
+
+void
+en_route_policies_cleanup(void *data)
+{
+    route_policies_destroy(data);
+}
+
+void
+en_static_routes_cleanup(void *data)
+{
+    static_routes_destroy(data);
+}
+
+void
+en_bfd_cleanup(void *data)
+{
+    bfd_destroy(data);
+}
+
+void
+en_bfd_sync_cleanup(void *data)
+{
+    bfd_destroy(data);
+}
diff --git a/northd/en-northd.h b/northd/en-northd.h
index 9b7bda32a..f2d0ce8ba 100644
--- a/northd/en-northd.h
+++ b/northd/en-northd.h
@@ -19,5 +19,27 @@  bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
 bool northd_nb_logical_router_handler(struct engine_node *, void *data);
 bool northd_sb_port_binding_handler(struct engine_node *, void *data);
 bool northd_lb_data_handler(struct engine_node *, void *data);
+void *en_static_routes_init(struct engine_node *node OVS_UNUSED,
+                            struct engine_arg *arg OVS_UNUSED);
+void en_route_policies_cleanup(void *data);
+bool route_policies_northd_change_handler(struct engine_node *node,
+                                          void *data OVS_UNUSED);
+void 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_static_routes_cleanup(void *data);
+bool static_routes_northd_change_handler(struct engine_node *node,
+                                         void *data OVS_UNUSED);
+void en_static_routes_run(struct engine_node *node, void *data);
+void *en_bfd_init(struct engine_node *node OVS_UNUSED,
+                  struct engine_arg *arg OVS_UNUSED);
+void en_bfd_cleanup(void *data);
+void en_bfd_run(struct engine_node *node, void *data);
+void *en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
+                       struct engine_arg *arg OVS_UNUSED);
+bool bfd_sync_northd_change_handler(struct engine_node *node,
+                                    void *data OVS_UNUSED);
+void en_bfd_sync_run(struct engine_node *node, void *data);
+void en_bfd_sync_cleanup(void *data OVS_UNUSED);
 
 #endif /* EN_NORTHD_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 522236ad2..7d4c28b13 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -155,6 +155,10 @@  static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_nat, "lr_nat");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_stateful, "lr_stateful");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(ls_stateful, "ls_stateful");
+static ENGINE_NODE(route_policies, "route_policies");
+static ENGINE_NODE(static_routes, "static_routes");
+static ENGINE_NODE(bfd, "bfd");
+static ENGINE_NODE(bfd_sync, "bfd_sync");
 
 void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                           struct ovsdb_idl_loop *sb)
@@ -237,18 +241,36 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_fdb_aging, &en_global_config,
                      node_global_config_handler);
 
+    engine_add_input(&en_bfd, &en_nb_bfd, NULL);
+    engine_add_input(&en_bfd, &en_sb_bfd, NULL);
+
+    engine_add_input(&en_route_policies, &en_bfd, NULL);
+    engine_add_input(&en_route_policies, &en_northd,
+                     route_policies_northd_change_handler);
+
+    engine_add_input(&en_static_routes, &en_bfd, NULL);
+    engine_add_input(&en_static_routes, &en_northd,
+                     static_routes_northd_change_handler);
+
+    engine_add_input(&en_bfd_sync, &en_bfd, NULL);
+    engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);
+    engine_add_input(&en_bfd_sync, &en_static_routes, NULL);
+    engine_add_input(&en_bfd_sync, &en_route_policies, NULL);
+    engine_add_input(&en_bfd_sync, &en_northd, bfd_sync_northd_change_handler);
+
     engine_add_input(&en_sync_meters, &en_nb_acl, NULL);
     engine_add_input(&en_sync_meters, &en_nb_meter, NULL);
     engine_add_input(&en_sync_meters, &en_sb_meter, NULL);
 
-    engine_add_input(&en_lflow, &en_nb_bfd, NULL);
     engine_add_input(&en_lflow, &en_nb_acl, NULL);
     engine_add_input(&en_lflow, &en_sync_meters, NULL);
-    engine_add_input(&en_lflow, &en_sb_bfd, NULL);
     engine_add_input(&en_lflow, &en_sb_logical_flow, NULL);
     engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
     engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
     engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);
+    engine_add_input(&en_lflow, &en_bfd_sync, NULL);
+    engine_add_input(&en_lflow, &en_route_policies, NULL);
+    engine_add_input(&en_lflow, &en_static_routes, NULL);
     engine_add_input(&en_lflow, &en_global_config,
                      node_global_config_handler);
     engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
diff --git a/northd/northd.c b/northd/northd.c
index a8a0b6f94..e0b06ff9a 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -9702,11 +9702,47 @@  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;
 
-    bool ref;
+    char *logical_port;
+    char *dst_ip;
+    char *status;
+    bool stale;
 };
 
+static struct bfd_entry *
+bfd_alloc_entry(struct hmap *bfd_connections,
+                const char *logical_port, const char *dst_ip,
+                const char *status)
+{
+    struct bfd_entry *bfd_e = xzalloc(sizeof *bfd_e);
+    bfd_e->logical_port = xstrdup(logical_port);
+    bfd_e->dst_ip = xstrdup(dst_ip);
+    bfd_e->status = xstrdup(status);
+    uint32_t hash = hash_string(dst_ip, 0);
+    hash = hash_string(logical_port, hash);
+    hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
+
+    return bfd_e;
+}
+
+static void
+bfd_erase_entry(struct bfd_entry *bfd_e)
+{
+    free(bfd_e->logical_port);
+    free(bfd_e->dst_ip);
+    free(bfd_e->status);
+    free(bfd_e);
+}
+
+static 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 *
 bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
                 const char *dst_ip)
@@ -9717,38 +9753,28 @@  bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
     hash = hash_string(dst_ip, 0);
     hash = hash_string(logical_port, hash);
     HMAP_FOR_EACH_WITH_HASH (bfd_e, hmap_node, hash, bfd_map) {
-        if (!strcmp(bfd_e->sb_bt->logical_port, logical_port) &&
-            !strcmp(bfd_e->sb_bt->dst_ip, dst_ip)) {
+        if (!strcmp(bfd_e->logical_port, logical_port) &&
+            !strcmp(bfd_e->dst_ip, dst_ip)) {
             return bfd_e;
         }
     }
+
     return NULL;
 }
 
-void
-bfd_cleanup_connections(const struct nbrec_bfd_table *nbrec_bfd_table,
-                        struct hmap *bfd_map)
+static bool
+bfd_is_port_running(const struct hmap *bfd_map, const char *port)
 {
-    const struct nbrec_bfd *nb_bt;
     struct bfd_entry *bfd_e;
-
-    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
-        bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
-        if (!bfd_e) {
-            continue;
-        }
-
-        if (!bfd_e->ref && strcmp(nb_bt->status, "admin_down")) {
-            /* no user for this bfd connection */
-            nbrec_bfd_set_status(nb_bt, "admin_down");
+    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
+        if (!strcmp(bfd_e->logical_port, port)) {
+            return true;
         }
     }
-
-    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
-        free(bfd_e);
-    }
+    return false;
 }
 
+
 #define BFD_DEF_MINTX       1000 /* 1s */
 #define BFD_DEF_MINRX       1000 /* 1s */
 #define BFD_DEF_DETECT_MULT 5
@@ -9809,51 +9835,88 @@  static int bfd_get_unused_port(unsigned long *bfd_src_ports)
     return port + BFD_UDP_SRC_PORT_START;
 }
 
-void
-build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
-                const struct nbrec_bfd_table *nbrec_bfd_table,
-                const struct sbrec_bfd_table *sbrec_bfd_table,
-                const struct hmap *lr_ports, struct hmap *bfd_connections)
+static char *
+bfd_get_connection_status(const struct nbrec_bfd *nb_bt,
+                          const struct hmap *rp_bfd_connections,
+                          const struct hmap *sr_bfd_connections)
 {
-    struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
-    const struct sbrec_bfd *sb_bt;
-    unsigned long *bfd_src_ports;
-    struct bfd_entry *bfd_e;
-    uint32_t hash;
+    struct bfd_entry *bfd_rp, *bfd_sr;
 
-    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
+    bfd_rp = bfd_port_lookup(rp_bfd_connections, nb_bt->logical_port,
+                             nb_bt->dst_ip);
+    if (!bfd_rp) {
+        bfd_sr = bfd_port_lookup(sr_bfd_connections, nb_bt->logical_port,
+                                 nb_bt->dst_ip);
+        if (!bfd_sr) {
+            return "admin_down";
+        }
+    }
 
-    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
-        bfd_e = xmalloc(sizeof *bfd_e);
-        bfd_e->sb_bt = sb_bt;
-        hash = hash_string(sb_bt->dst_ip, 0);
-        hash = hash_string(sb_bt->logical_port, hash);
-        hmap_insert(&sb_only, &bfd_e->hmap_node, hash);
-        bitmap_set1(bfd_src_ports, sb_bt->src_port - BFD_UDP_SRC_PORT_START);
+    return bfd_rp ? bfd_rp->status : bfd_sr->status;
+}
+
+void
+bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
+               const struct nbrec_bfd_table *nbrec_bfd_table,
+               const struct hmap *lr_ports,
+               const struct hmap *bfd_connections,
+               const struct hmap *rp_bfd_connections,
+               const struct hmap *sr_bfd_connections,
+               struct hmap *sync_bfd_connections)
+{
+    if (!ovnsb_txn) {
+        return;
+    }
+
+    unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
+
+    struct bfd_entry *bfd_e;
+    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
+        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
+                                              bfd_e->logical_port,
+                                              bfd_e->dst_ip, bfd_e->status);
+        e->nb_bt = bfd_e->nb_bt;
+        e->sb_bt = bfd_e->sb_bt;
+        e->stale = true;
+        /* we need to check if this entry is even in the BFD nb db table */
+        if (bfd_e->sb_bt) {
+            bitmap_set1(bfd_src_ports,
+                        bfd_e->sb_bt->src_port - BFD_UDP_SRC_PORT_START);
+        }
     }
 
     const struct nbrec_bfd *nb_bt;
     NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
-        if (!nb_bt->status) {
-            /* default state is admin_down */
-            nbrec_bfd_set_status(nb_bt, "admin_down");
+        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
+                                nb_bt->dst_ip);
+        if (!bfd_e) {
+            continue;
         }
 
         struct ovn_port *op = ovn_port_find(lr_ports, nb_bt->logical_port);
-        bfd_e = bfd_port_lookup(&sb_only, nb_bt->logical_port, nb_bt->dst_ip);
-        if (!bfd_e) {
+        if (!op || !op->sb) {
+            /* skip not bounded ports */
+            continue;
+        }
+
+        nbrec_bfd_set_status(nb_bt,
+                             bfd_get_connection_status(nb_bt,
+                                                       rp_bfd_connections,
+                                                       sr_bfd_connections));
+        if (!bfd_e->sb_bt) {
             int udp_src = bfd_get_unused_port(bfd_src_ports);
             if (udp_src < 0) {
                 continue;
             }
 
-            sb_bt = sbrec_bfd_insert(ovnsb_txn);
+            /* Add entry to bfd sb table. */
+            const struct sbrec_bfd *sb_bt = sbrec_bfd_insert(ovnsb_txn);
             sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
             sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
             sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
             sbrec_bfd_set_src_port(sb_bt, udp_src);
             sbrec_bfd_set_status(sb_bt, nb_bt->status);
-            if (op && op->sb && op->sb->chassis) {
+            if (op->sb->chassis) {
                 sbrec_bfd_set_chassis_name(sb_bt, op->sb->chassis->name);
             }
 
@@ -9873,39 +9936,61 @@  build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
                     nbrec_bfd_set_status(nb_bt, bfd_e->sb_bt->status);
                 }
             }
+
             build_bfd_update_sb_conf(nb_bt, bfd_e->sb_bt);
-            if (op && op->sb && op->sb->chassis &&
-                strcmp(op->sb->chassis->name, bfd_e->sb_bt->chassis_name)) {
+            if (op->sb->chassis && !strcmp(op->sb->chassis->name,
+                                           bfd_e->sb_bt->chassis_name)) {
                 sbrec_bfd_set_chassis_name(bfd_e->sb_bt,
                                            op->sb->chassis->name);
             }
-
-            hmap_remove(&sb_only, &bfd_e->hmap_node);
-            bfd_e->ref = false;
-            hash = hash_string(bfd_e->sb_bt->dst_ip, 0);
-            hash = hash_string(bfd_e->sb_bt->logical_port, hash);
-            hmap_insert(bfd_connections, &bfd_e->hmap_node, hash);
         }
 
-        if (op) {
-            op->has_bfd = true;
-        }
+        bfd_e->stale = false;
     }
 
-    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sb_only) {
-        struct ovn_port *op = ovn_port_find(lr_ports,
-                                            bfd_e->sb_bt->logical_port);
-        if (op) {
-            op->has_bfd = false;
+    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
+        if (bfd_e->stale) {
+            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
+            sbrec_bfd_delete(bfd_e->sb_bt);
+            bfd_erase_entry(bfd_e);
         }
-        sbrec_bfd_delete(bfd_e->sb_bt);
-        free(bfd_e);
     }
-    hmap_destroy(&sb_only);
 
     bitmap_free(bfd_src_ports);
 }
 
+void
+build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
+              const struct sbrec_bfd_table *sbrec_bfd_table,
+              struct hmap *bfd_connections)
+{
+    struct bfd_entry *bfd_e;
+
+    /* align bfd_map to sb db */
+    const struct sbrec_bfd *sb_bt;
+    SBREC_BFD_TABLE_FOR_EACH (sb_bt, sbrec_bfd_table) {
+        bfd_e = bfd_port_lookup(bfd_connections, sb_bt->logical_port,
+                                sb_bt->dst_ip);
+        if (!bfd_e) {
+            bfd_e = bfd_alloc_entry(bfd_connections, sb_bt->logical_port,
+                                    sb_bt->dst_ip,  sb_bt->status);
+        }
+        bfd_e->sb_bt = sb_bt;
+    }
+
+    const struct nbrec_bfd *nb_bt;
+    NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
+        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
+                                nb_bt->dst_ip);
+        if (!bfd_e) {
+            /* brand new entry. */
+            bfd_e = bfd_alloc_entry(bfd_connections, nb_bt->logical_port,
+                                    nb_bt->dst_ip, "admin_down");
+        }
+        bfd_e->nb_bt = nb_bt;
+    }
+}
+
 /* Returns a string of the IP address of the router port 'op' that
  * overlaps with 'ip_s".  If one is not found, returns NULL.
  *
@@ -9941,17 +10026,13 @@  get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
     return NULL;
 }
 
-static struct ovs_mutex bfd_lock = OVS_MUTEX_INITIALIZER;
-
-static bool check_bfd_state(
-        const struct nbrec_logical_router_policy *rule,
-        const struct hmap *bfd_connections,
-        struct ovn_port *out_port,
-        const char *nexthop)
+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);
-    bool ret = true;
 
     for (size_t i = 0; i < rule->n_bfd_sessions; i++) {
         /* Check if there is a BFD session associated to the reroute
@@ -9976,39 +10057,48 @@  static bool check_bfd_state(
         struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
                                                   nb_bt->logical_port,
                                                   nb_bt->dst_ip);
-        ovs_mutex_lock(&bfd_lock);
-        if (bfd_e) {
-            bfd_e->ref = true;
+        if (!bfd_e) {
+            continue;
         }
 
-        if (!strcmp(nb_bt->status, "admin_down")) {
-            nbrec_bfd_set_status(nb_bt, "down");
+        /* 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);
         }
 
-        ret = strcmp(nb_bt->status, "down");
-        ovs_mutex_unlock(&bfd_lock);
-        break;
+        if (!strcmp(bfd_e->status, "admin_down")) {
+            bfd_set_status(bfd_rp, "down");
+        }
+
+        return strcmp(bfd_rp->status, "down");
     }
 
-    return ret;
+    return true;
 }
 
 static void
 build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
-                          const struct hmap *lr_ports,
-                          const struct nbrec_logical_router_policy *rule,
-                          const struct hmap *bfd_connections,
+                          const struct hmap *lr_ports, struct route_policy *rp,
                           const struct ovsdb_idl_row *stage_hint,
                           struct lflow_ref *lflow_ref)
 {
+    const struct nbrec_logical_router_policy *rule = rp->rule;
     struct ds match = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
 
     if (!strcmp(rule->action, "reroute")) {
         ovs_assert(rule->n_nexthops <= 1);
 
-        char *nexthop =
-            (rule->n_nexthops == 1 ? rule->nexthops[0] : rule->nexthop);
+        if (!rp->n_valid_nexthops) {
+            return;
+        }
+
+        char *nexthop = rp->valid_nexthops[0];
         struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
              od, lr_ports, rule->priority, nexthop);
         if (!out_port) {
@@ -10024,10 +10114,6 @@  build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od,
             return;
         }
 
-        if (!check_bfd_state(rule, bfd_connections, out_port, nexthop)) {
-            return;
-        }
-
         uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
         if (pkt_mark) {
             ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
@@ -10070,19 +10156,18 @@  static void
 build_ecmp_routing_policy_flows(struct lflow_table *lflows,
                                 struct ovn_datapath *od,
                                 const struct hmap *lr_ports,
-                                const struct nbrec_logical_router_policy *rule,
-                                const struct hmap *bfd_connections,
+                                struct route_policy *rp,
                                 uint16_t ecmp_group_id,
                                 struct lflow_ref *lflow_ref)
 {
-    ovs_assert(rule->n_nexthops > 1);
-
     bool nexthops_is_ipv4 = true;
+    const struct nbrec_logical_router_policy *rule = rp->rule;
+    ovs_assert(rule->n_nexthops > 1);
 
     /* Check that all the nexthops belong to the same addr family before
      * adding logical flows. */
-    for (uint16_t i = 0; i < rule->n_nexthops; i++) {
-        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
+    for (uint16_t i = 0; i < rp->n_valid_nexthops; i++) {
+        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
 
         if (i == 0) {
             nexthops_is_ipv4 = is_ipv4;
@@ -10093,7 +10178,7 @@  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
             VLOG_WARN_RL(&rl, "nexthop [%s] of the router policy with "
                          "the match [%s] do not belong to the same address "
                          "family as other next hops",
-                         rule->nexthops[i], rule->match);
+                         rp->valid_nexthops[i], rule->match);
             return;
         }
     }
@@ -10101,40 +10186,30 @@  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
     struct ds match = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
 
-    size_t *valid_nexthops = xcalloc(rule->n_nexthops, sizeof *valid_nexthops);
-    size_t n_valid_nexthops = 0;
-
-    for (size_t i = 0; i < rule->n_nexthops; i++) {
+    for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
         struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
-             od, lr_ports, rule->priority, rule->nexthops[i]);
+             od, lr_ports, rule->priority, rp->valid_nexthops[i]);
         if (!out_port) {
             goto cleanup;
         }
 
         const char *lrp_addr_s =
-            find_lrp_member_ip(out_port, rule->nexthops[i]);
+            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
         if (!lrp_addr_s) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
             VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
                             " priority %"PRId64" nexthop %s",
-                            rule->priority, rule->nexthops[i]);
+                            rule->priority, rp->valid_nexthops[i]);
             goto cleanup;
         }
 
-        if (!check_bfd_state(rule, bfd_connections, out_port,
-                             rule->nexthops[i])) {
-            continue;
-        }
-
-        valid_nexthops[n_valid_nexthops++] = i + 1;
-
         ds_clear(&actions);
         uint32_t pkt_mark = smap_get_uint(&rule->options, "pkt_mark", 0);
         if (pkt_mark) {
             ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
         }
 
-        bool is_ipv4 = strchr(rule->nexthops[i], '.') ? true : false;
+        bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false;
 
         ds_put_format(&actions, "%s = %s; "
                       "%s = %s; "
@@ -10143,7 +10218,7 @@  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
                       "flags.loopback = 1; "
                       "next;",
                       is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6,
-                      rule->nexthops[i],
+                      rp->valid_nexthops[i],
                       is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6,
                       lrp_addr_s,
                       out_port->lrp_networks.ea_s,
@@ -10159,37 +10234,30 @@  build_ecmp_routing_policy_flows(struct lflow_table *lflows,
                                 lflow_ref);
     }
 
-    if (!n_valid_nexthops) {
-        goto cleanup;
-    }
-
     ds_clear(&actions);
-    if (n_valid_nexthops > 1) {
+    if (rp->n_valid_nexthops > 1) {
         ds_put_format(&actions, "%s = %"PRIu16
                       "; %s = select(", REG_ECMP_GROUP_ID, ecmp_group_id,
                       REG_ECMP_MEMBER_ID);
 
-        for (size_t i = 0; i < n_valid_nexthops; i++) {
+        for (size_t i = 0; i < rp->n_valid_nexthops; i++) {
             if (i > 0) {
                 ds_put_cstr(&actions, ", ");
             }
 
-            ds_put_format(&actions, "%"PRIuSIZE, valid_nexthops[i]);
+            ds_put_format(&actions, "%"PRIuSIZE, i + 1);
         }
         ds_put_cstr(&actions, ");");
     } else {
         ds_put_format(&actions, "%s = %"PRIu16
-                      "; %s = %"PRIuSIZE"; next;", REG_ECMP_GROUP_ID,
-                      ecmp_group_id, REG_ECMP_MEMBER_ID,
-                      valid_nexthops[0]);
+                      "; %s = 1; next;", REG_ECMP_GROUP_ID,
+                      ecmp_group_id, REG_ECMP_MEMBER_ID);
     }
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
                             rule->priority, rule->match,
                             ds_cstr(&actions), &rule->header_,
                             lflow_ref);
-
 cleanup:
-    free(valid_nexthops);
     ds_destroy(&match);
     ds_destroy(&actions);
 }
@@ -10214,7 +10282,7 @@  route_table_add(struct simap *route_tables, const char *route_table_name)
     return rtb_id;
 }
 
-static uint32_t
+uint32_t
 get_route_table_id(struct simap *route_tables, const char *route_table_name)
 {
     if (!route_table_name || !route_table_name[0]) {
@@ -10255,18 +10323,6 @@  build_route_table_lflow(struct ovn_datapath *od, struct lflow_table *lflows,
     ds_destroy(&actions);
 }
 
-struct parsed_route {
-    struct ovs_list list_node;
-    struct in6_addr prefix;
-    unsigned int plen;
-    bool is_src_route;
-    uint32_t route_table_id;
-    uint32_t hash;
-    const struct nbrec_logical_router_static_route *route;
-    bool ecmp_symmetric_reply;
-    bool is_discard_route;
-};
-
 static uint32_t
 route_hash(struct parsed_route *route)
 {
@@ -10281,11 +10337,53 @@  find_static_route_outport(struct ovn_datapath *od, const struct hmap *lr_ports,
 
 /* Parse and validate the route. Return the parsed route if successful.
  * Otherwise return NULL. */
+
 static struct parsed_route *
+parsed_route_lookup(struct hmap *routes, size_t hash,
+                    struct parsed_route *new_pr)
+{
+    struct parsed_route *pr;
+    HMAP_FOR_EACH_WITH_HASH (pr, key_node, hash, routes) {
+        if (pr->plen != new_pr->plen) {
+            continue;
+        }
+
+        if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) {
+            continue;
+        }
+
+        if (pr->is_src_route != new_pr->is_src_route) {
+            continue;
+        }
+
+        if (pr->route_table_id != new_pr->route_table_id) {
+            continue;
+        }
+
+        if (pr->route != new_pr->route) {
+            continue;
+        }
+
+        if (pr->ecmp_symmetric_reply != new_pr->ecmp_symmetric_reply) {
+            continue;
+        }
+
+        if (pr->is_discard_route != new_pr->is_discard_route) {
+            continue;
+        }
+
+        return pr;
+    }
+
+    return NULL;
+}
+
+static void
 parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
-                  struct ovs_list *routes, struct simap *route_tables,
                   const struct nbrec_logical_router_static_route *route,
-                  const struct hmap *bfd_connections)
+                  struct hmap *bfd_connections,
+                  struct hmap *routes, struct simap *route_tables,
+                  struct hmap *bfd_active_connections)
 {
     /* Verify that the next hop is an IP address with an all-ones mask. */
     struct in6_addr nexthop;
@@ -10298,7 +10396,7 @@  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
             VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route "
                          UUID_FMT, route->nexthop,
                          UUID_ARGS(&route->header_.uuid));
-            return NULL;
+            return;
         }
         if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) ||
             (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) {
@@ -10306,7 +10404,7 @@  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
             VLOG_WARN_RL(&rl, "bad next hop mask %s in static route "
                          UUID_FMT, route->nexthop,
                          UUID_ARGS(&route->header_.uuid));
-            return NULL;
+            return;
         }
     }
 
@@ -10317,7 +10415,7 @@  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
         VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route "
                      UUID_FMT, route->ip_prefix,
                      UUID_ARGS(&route->header_.uuid));
-        return NULL;
+        return;
     }
 
     /* Verify that ip_prefix and nexthop have same address familiy. */
@@ -10328,7 +10426,7 @@  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
                          " %s and 'nexthop' %s in static route "UUID_FMT,
                          route->ip_prefix, route->nexthop,
                          UUID_ARGS(&route->header_.uuid));
-            return NULL;
+            return;
         }
     }
 
@@ -10337,52 +10435,88 @@  parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
         !find_static_route_outport(od, lr_ports, route,
                                    IN6_IS_ADDR_V4MAPPED(&prefix),
                                    NULL, NULL)) {
-        return NULL;
+        return;
     }
 
     const struct nbrec_bfd *nb_bt = route->bfd;
     if (nb_bt && !strcmp(nb_bt->dst_ip, route->nexthop)) {
-        struct bfd_entry *bfd_e;
+        struct bfd_entry *bfd_e = bfd_port_lookup(bfd_connections,
+                                                  nb_bt->logical_port,
+                                                  nb_bt->dst_ip);
+        if (!bfd_e) {
+            return;
+        }
 
-        bfd_e = bfd_port_lookup(bfd_connections, nb_bt->logical_port,
-                                nb_bt->dst_ip);
-        ovs_mutex_lock(&bfd_lock);
-        if (bfd_e) {
-            bfd_e->ref = true;
+        /* This static route is linked to an active bfd session. */
+        struct bfd_entry *bfd_sr = bfd_port_lookup(bfd_active_connections,
+                                                   nb_bt->logical_port,
+                                                   nb_bt->dst_ip);
+        if (!bfd_sr) {
+            bfd_sr = bfd_alloc_entry(bfd_active_connections,
+                                     nb_bt->logical_port, nb_bt->dst_ip,
+                                     bfd_e->status);
         }
 
-        if (!strcmp(nb_bt->status, "admin_down")) {
-            nbrec_bfd_set_status(nb_bt, "down");
+        if (!strcmp(bfd_e->status, "admin_down")) {
+            bfd_set_status(bfd_sr, "down");
         }
 
-        if (!strcmp(nb_bt->status, "down")) {
-            ovs_mutex_unlock(&bfd_lock);
-            return NULL;
+
+        if (!strcmp(bfd_sr->status, "down")) {
+            return;
         }
-        ovs_mutex_unlock(&bfd_lock);
     }
 
-    struct parsed_route *pr = xzalloc(sizeof *pr);
-    pr->prefix = prefix;
-    pr->plen = plen;
-    pr->route_table_id = get_route_table_id(route_tables, route->route_table);
-    pr->is_src_route = (route->policy && !strcmp(route->policy,
-                                                 "src-ip"));
-    pr->hash = route_hash(pr);
-    pr->route = route;
-    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
-                                             "ecmp_symmetric_reply", false);
-    pr->is_discard_route = is_discard_route;
-    ovs_list_insert(routes, &pr->list_node);
-    return pr;
+    struct parsed_route *new_pr = xzalloc(sizeof *new_pr);
+    new_pr->prefix = prefix;
+    new_pr->plen = plen;
+    new_pr->route_table_id = get_route_table_id(route_tables,
+                                                route->route_table);
+    new_pr->is_src_route = (route->policy &&
+                            !strcmp(route->policy, "src-ip"));
+    new_pr->hash = route_hash(new_pr);
+    new_pr->route = route;
+    new_pr->nbr = od->nbr;
+    new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
+                                                 "ecmp_symmetric_reply",
+                                                 false);
+    new_pr->is_discard_route = is_discard_route;
+
+    size_t hash = uuid_hash(&od->key);
+    struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);
+    if (!pr) {
+        hmap_insert(routes, &new_pr->key_node, hash);
+    } else {
+        pr->stale = false;
+        free(new_pr);
+    }
 }
 
-static void
-parsed_routes_destroy(struct ovs_list *routes)
+void
+build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
+                    struct hmap *bfd_connections, struct hmap *routes,
+                    struct simap *route_tables,
+                    struct hmap *bfd_active_connections)
 {
     struct parsed_route *pr;
-    LIST_FOR_EACH_SAFE (pr, list_node, routes) {
-        ovs_list_remove(&pr->list_node);
+    HMAP_FOR_EACH (pr, key_node, routes) {
+        if (pr->nbr == od->nbr) {
+            pr->stale = true;
+        }
+    }
+
+    for (int i = 0; i < od->nbr->n_static_routes; i++) {
+        parsed_routes_add(od, lr_ports, od->nbr->static_routes[i],
+                          bfd_connections, routes, route_tables,
+                          bfd_active_connections);
+    }
+
+    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
+        if (!pr->stale) {
+            continue;
+        }
+
+        hmap_remove(routes, &pr->key_node);
         free(pr);
     }
 }
@@ -10830,6 +10964,7 @@  add_route(struct lflow_table *lflows, struct ovn_datapath *od,
           const struct ovn_port *op, const char *lrp_addr_s,
           const char *network_s, int plen, const char *gateway,
           bool is_src_route, const uint32_t rtb_id,
+          const struct hmap *bfd_connections,
           const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
           int ofs, struct lflow_ref *lflow_ref)
 {
@@ -10878,7 +11013,7 @@  add_route(struct lflow_table *lflows, struct ovn_datapath *od,
                             priority, ds_cstr(&match),
                             ds_cstr(&actions), stage_hint,
                             lflow_ref);
-    if (op && op->has_bfd) {
+    if (op && bfd_is_port_running(bfd_connections, op->key)) {
         ds_put_format(&match, " && udp.dst == 3784");
         ovn_lflow_add_with_hint(lflows, op->od,
                                 S_ROUTER_IN_IP_ROUTING,
@@ -10895,6 +11030,7 @@  static void
 build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
                         const struct hmap *lr_ports,
                         const struct parsed_route *route_,
+                        const struct hmap *bfd_connections,
                         struct lflow_ref *lflow_ref)
 {
     const char *lrp_addr_s = NULL;
@@ -10918,8 +11054,9 @@  build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
     char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
     add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
               lrp_addr_s, prefix_s, route_->plen, route->nexthop,
-              route_->is_src_route, route_->route_table_id, &route->header_,
-              route_->is_discard_route, ofs, lflow_ref);
+              route_->is_src_route, route_->route_table_id,
+              bfd_connections, &route->header_, route_->is_discard_route,
+              ofs, lflow_ref);
 
     free(prefix_s);
 }
@@ -12062,9 +12199,10 @@  build_lrouter_force_snat_flows_op(struct ovn_port *op,
 static void
 build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
                         const struct shash *meter_groups,
+                        const struct hmap *bfd_connections,
                         struct lflow_ref *lflow_ref)
 {
-    if (!op->has_bfd) {
+    if (!bfd_is_port_running(bfd_connections, op->key)) {
         return;
     }
 
@@ -12659,32 +12797,34 @@  build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
  * LRP 'op'.
  */
 static void
-build_ip_routing_flows_for_lrp(
-        struct ovn_port *op, struct lflow_table *lflows,
-        struct lflow_ref *lflow_ref)
+build_ip_routing_flows_for_lrp(struct ovn_port *op,
+                               const struct hmap *bfd_connections,
+                               struct lflow_table *lflows,
+                               struct lflow_ref *lflow_ref)
 {
     ovs_assert(op->nbrp);
     for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
         add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
                   op->lrp_networks.ipv4_addrs[i].network_s,
                   op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
-                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
-                  lflow_ref);
+                  bfd_connections, &op->nbrp->header_, false,
+                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
     }
 
     for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
         add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
                   op->lrp_networks.ipv6_addrs[i].network_s,
                   op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
-                  &op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED,
-                  lflow_ref);
+                  bfd_connections, &op->nbrp->header_, false,
+                  ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
     }
 }
 
 static void
 build_static_route_flows_for_lrouter(
         struct ovn_datapath *od, struct lflow_table *lflows,
-        const struct hmap *lr_ports, const struct hmap *bfd_connections,
+        const struct hmap *lr_ports, struct hmap *parsed_routes,
+        struct simap *route_tables, const struct hmap *bfd_connections,
         struct lflow_ref *lflow_ref)
 {
     ovs_assert(od->nbr);
@@ -12698,22 +12838,16 @@  build_static_route_flows_for_lrouter(
 
     struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
     struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
-    struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
-    struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
     struct ecmp_groups_node *group;
 
     for (int i = 0; i < od->nbr->n_ports; i++) {
         build_route_table_lflow(od, lflows, od->nbr->ports[i],
-                                &route_tables, lflow_ref);
+                                route_tables, lflow_ref);
     }
 
-    for (int i = 0; i < od->nbr->n_static_routes; i++) {
-        struct parsed_route *route =
-            parsed_routes_add(od, lr_ports, &parsed_routes, &route_tables,
-                              od->nbr->static_routes[i], bfd_connections);
-        if (!route) {
-            continue;
-        }
+    struct parsed_route *route;
+    HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key),
+                             parsed_routes) {
         group = ecmp_groups_find(&ecmp_groups, route);
         if (group) {
             ecmp_groups_add_route(group, route);
@@ -12737,12 +12871,11 @@  build_static_route_flows_for_lrouter(
     }
     const struct unique_routes_node *ur;
     HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
-        build_static_route_flow(lflows, od, lr_ports, ur->route, lflow_ref);
+        build_static_route_flow(lflows, od, lr_ports, ur->route,
+                                bfd_connections, lflow_ref);
     }
     ecmp_groups_destroy(&ecmp_groups);
     unique_routes_destroy(&unique_routes);
-    parsed_routes_destroy(&parsed_routes);
-    simap_destroy(&route_tables);
 }
 
 /* IP Multicast lookup. Here we set the output port, adjust TTL and
@@ -12849,6 +12982,115 @@  build_mcast_lookup_flows_for_lrouter(
     }
 }
 
+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->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;
+}
+
+void
+build_route_policies(struct ovn_datapath *od, const struct hmap *lr_ports,
+                     const struct hmap *bfd_connections,
+                     struct hmap *route_policies,
+                     struct hmap *bfd_active_connections)
+{
+    struct route_policy *rp;
+
+    HMAP_FOR_EACH (rp, key_node, route_policies) {
+        if (rp->nbr == od->nbr) {
+            rp->stale = true;
+        }
+    }
+
+    for (int i = 0; i < od->nbr->n_policies; i++) {
+        const struct nbrec_logical_router_policy *rule = od->nbr->policies[i];
+        size_t n_valid_nexthops = 0;
+        char **valid_nexthops = NULL;
+
+        if (!strcmp(rule->action, "reroute")) {
+            size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1;
+
+            valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops);
+            for (size_t j = 0; j < n_nexthops; j++) {
+                char *nexthop = rule->n_nexthops
+                    ? rule->nexthops[j] : rule->nexthop;
+                struct ovn_port *out_port =
+                    get_outport_for_routing_policy_nexthop(
+                            od, lr_ports, rule->priority, nexthop);
+                if (!out_port || !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->nbr = od->nbr;
+
+        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 {
+            rp->stale = false;
+            free(valid_nexthops);
+            free(new_rp);
+        }
+    }
+
+    HMAP_FOR_EACH_SAFE (rp, key_node, route_policies) {
+        if (!rp->stale) {
+            continue;
+        }
+
+        hmap_remove(route_policies, &rp->key_node);
+        free(rp->valid_nexthops);
+        free(rp);
+    }
+}
+
 /* Logical router ingress table POLICY: Policy.
  *
  * A packet that arrives at this table is an IP packet that should be
@@ -12862,7 +13104,7 @@  static void
 build_ingress_policy_flows_for_lrouter(
         struct ovn_datapath *od, struct lflow_table *lflows,
         const struct hmap *lr_ports,
-        const struct hmap *bfd_connections,
+        struct hmap *route_policies,
         struct lflow_ref *lflow_ref)
 {
     ovs_assert(od->nbr);
@@ -12879,21 +13121,20 @@  build_ingress_policy_flows_for_lrouter(
 
     /* Convert routing policies to flows. */
     uint16_t ecmp_group_id = 1;
-    for (int i = 0; i < od->nbr->n_policies; i++) {
-        const struct nbrec_logical_router_policy *rule
-            = od->nbr->policies[i];
+    struct route_policy *rp;
+    HMAP_FOR_EACH_WITH_HASH (rp, key_node, uuid_hash(&od->key),
+                             route_policies) {
+        const struct nbrec_logical_router_policy *rule = rp->rule;
         bool is_ecmp_reroute =
             (!strcmp(rule->action, "reroute") && rule->n_nexthops > 1);
 
         if (is_ecmp_reroute) {
-            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rule,
-                                            bfd_connections, ecmp_group_id,
-                                            lflow_ref);
+            build_ecmp_routing_policy_flows(lflows, od, lr_ports, rp,
+                                            ecmp_group_id, lflow_ref);
             ecmp_group_id++;
         } else {
-            build_routing_policy_flow(lflows, od, lr_ports, rule,
-                                      bfd_connections, &rule->header_,
-                                      lflow_ref);
+            build_routing_policy_flow(lflows, od, lr_ports, rp,
+                                      &rule->header_, lflow_ref);
         }
     }
 }
@@ -14105,6 +14346,7 @@  build_lrouter_ipv4_ip_input(struct ovn_port *op,
                             struct lflow_table *lflows,
                             struct ds *match, struct ds *actions,
                             const struct shash *meter_groups,
+                            const struct hmap *bfd_connections,
                             struct lflow_ref *lflow_ref)
 {
     ovs_assert(op->nbrp);
@@ -14146,7 +14388,8 @@  build_lrouter_ipv4_ip_input(struct ovn_port *op,
     }
 
     /* BFD msg handling */
-    build_lrouter_bfd_flows(lflows, op, meter_groups, lflow_ref);
+    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
+                            lflow_ref);
 
     /* ICMP time exceeded */
     struct ds ip_ds = DS_EMPTY_INITIALIZER;
@@ -15589,6 +15832,7 @@  build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
 static void
 build_routable_flows_for_router_port(
     struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
+    const struct hmap *bfd_connections,
     struct lflow_table *lflows,
     struct ds *match,
     struct ds *actions)
@@ -15625,8 +15869,8 @@  build_routable_flows_for_router_port(
                               router_port->lrp_networks.ipv4_addrs[0].addr_s,
                               laddrs->ipv4_addrs[k].network_s,
                               laddrs->ipv4_addrs[k].plen, NULL, false, 0,
-                              &router_port->nbrp->header_, false,
-                              ROUTE_PRIO_OFFSET_CONNECTED,
+                              bfd_connections, &router_port->nbrp->header_,
+                              false, ROUTE_PRIO_OFFSET_CONNECTED,
                               lrp->stateful_lflow_ref);
                 }
             }
@@ -15736,8 +15980,8 @@  build_lrp_lflows_for_lbnats(struct ovn_port *op,
 static void
 build_lbnat_lflows_iterate_by_lrp(
     struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
-    const struct shash *meter_groups, struct ds *match,
-    struct ds *actions, struct lflow_table *lflows)
+    const struct shash *meter_groups, const struct hmap *bfd_connections,
+    struct ds *match, struct ds *actions, struct lflow_table *lflows)
 {
     ovs_assert(op->nbrp);
 
@@ -15749,8 +15993,8 @@  build_lbnat_lflows_iterate_by_lrp(
     build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
                                 actions, lflows);
 
-    build_routable_flows_for_router_port(op, lr_stateful_rec, lflows, match,
-                                         actions);
+    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
+                                         lflows, match, actions);
 }
 
 static void
@@ -15818,6 +16062,9 @@  struct lswitch_flow_build_info {
     struct ds actions;
     size_t thread_lflow_counter;
     const char *svc_monitor_mac;
+    struct hmap *parsed_routes;
+    struct hmap *route_policies;
+    struct simap *route_tables;
 };
 
 /* Helper function to combine all lflow generation which is iterated by
@@ -15863,12 +16110,12 @@  build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
     build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL);
     build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
     build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
-                                         lsi->bfd_connections,
-                                         NULL);
+                                         lsi->parsed_routes, lsi->route_tables,
+                                         lsi->bfd_connections, NULL);
     build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
                                          &lsi->actions, NULL);
     build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
-                                           lsi->bfd_connections, NULL);
+                                           lsi->route_policies, NULL);
     build_arp_resolve_flows_for_lrouter(od, lsi->lflows, NULL);
     build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
                                           &lsi->match, &lsi->actions,
@@ -15935,7 +16182,8 @@  build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
                                           &lsi->actions, op->lflow_ref);
     build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
                                                 &lsi->actions, op->lflow_ref);
-    build_ip_routing_flows_for_lrp(op, lsi->lflows, op->lflow_ref);
+    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
+                                   lsi->lflows, op->lflow_ref);
     build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
                                        &lsi->actions, lsi->meter_groups,
                                        op->lflow_ref);
@@ -15953,7 +16201,8 @@  build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
                                             lsi->meter_groups,
                                             op->lflow_ref);
     build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
-                                lsi->meter_groups, op->lflow_ref);
+                                lsi->meter_groups, lsi->bfd_connections,
+                                op->lflow_ref);
     build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
                                                  &lsi->actions, op->lflow_ref);
 }
@@ -16042,7 +16291,8 @@  build_lflows_thread(void *arg)
                     build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
                     build_lbnat_lflows_iterate_by_lrp(
                         op, lsi->lr_stateful_table, lsi->meter_groups,
-                        &lsi->match, &lsi->actions, lsi->lflows);
+                        lsi->bfd_connections, &lsi->match, &lsi->actions,
+                        lsi->lflows);
                 }
             }
             for (bnum = control->id;
@@ -16182,7 +16432,10 @@  build_lswitch_and_lrouter_flows(
     const struct hmap *svc_monitor_map,
     const struct hmap *bfd_connections,
     const struct chassis_features *features,
-    const char *svc_monitor_mac)
+    const char *svc_monitor_mac,
+    struct hmap *parsed_routes,
+    struct hmap *route_policies,
+    struct simap *route_tables)
 {
 
     char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
@@ -16216,6 +16469,9 @@  build_lswitch_and_lrouter_flows(
             lsiv[index].svc_check_match = svc_check_match;
             lsiv[index].thread_lflow_counter = 0;
             lsiv[index].svc_monitor_mac = svc_monitor_mac;
+            lsiv[index].parsed_routes = parsed_routes;
+            lsiv[index].route_tables = route_tables;
+            lsiv[index].route_policies = route_policies;
             ds_init(&lsiv[index].match);
             ds_init(&lsiv[index].actions);
 
@@ -16256,6 +16512,9 @@  build_lswitch_and_lrouter_flows(
             .features = features,
             .svc_check_match = svc_check_match,
             .svc_monitor_mac = svc_monitor_mac,
+            .parsed_routes = parsed_routes,
+            .route_tables = route_tables,
+            .route_policies = route_policies,
             .match = DS_EMPTY_INITIALIZER,
             .actions = DS_EMPTY_INITIALIZER,
         };
@@ -16287,6 +16546,7 @@  build_lswitch_and_lrouter_flows(
             build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
             build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
                                               lsi.meter_groups,
+                                              lsi.bfd_connections,
                                               &lsi.match,
                                               &lsi.actions,
                                               lsi.lflows);
@@ -16417,7 +16677,10 @@  void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
                                     input_data->svc_monitor_map,
                                     input_data->bfd_connections,
                                     input_data->features,
-                                    input_data->svc_monitor_mac);
+                                    input_data->svc_monitor_mac,
+                                    input_data->parsed_routes,
+                                    input_data->route_policies,
+                                    input_data->route_tables);
 
     if (parallelization_state == STATE_INIT_HASH_SIZES) {
         parallelization_state = STATE_USE_PARALLELIZATION;
@@ -16779,6 +17042,7 @@  lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
             build_lbnat_lflows_iterate_by_lrp(op,
                                               lflow_input->lr_stateful_table,
                                               lflow_input->meter_groups,
+                                              lflow_input->bfd_connections,
                                               &match, &actions,
                                               lflows);
 
@@ -17490,6 +17754,27 @@  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);
+}
+
+void
+static_routes_init(struct static_routes_data *data)
+{
+    hmap_init(&data->parsed_routes);
+    simap_init(&data->route_tables);
+    hmap_init(&data->bfd_active_connections);
+}
+
+void
+bfd_init(struct bfd_data *data)
+{
+    hmap_init(&data->bfd_connections);
+}
+
 void
 northd_destroy(struct northd_data *data)
 {
@@ -17529,6 +17814,48 @@  northd_destroy(struct northd_data *data)
     destroy_northd_tracked_data(data);
 }
 
+static void
+__bfd_destroy(struct hmap *bfd_connections)
+{
+    struct bfd_entry *bfd_e;
+
+    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_connections) {
+        bfd_erase_entry(bfd_e);
+    }
+    hmap_destroy(bfd_connections);
+}
+
+void
+bfd_destroy(struct bfd_data *data)
+{
+    __bfd_destroy(&data->bfd_connections);
+}
+
+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);
+}
+
+void
+static_routes_destroy(struct static_routes_data *data)
+{
+    struct parsed_route *r;
+    HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
+        free(r);
+    }
+    hmap_destroy(&data->parsed_routes);
+
+    simap_destroy(&data->route_tables);
+    __bfd_destroy(&data->bfd_active_connections);
+}
+
 void
 ovnnb_db_run(struct northd_input *input_data,
              struct northd_data *data,
diff --git a/northd/northd.h b/northd/northd.h
index d4a8d75ab..432072f9f 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -23,6 +23,7 @@ 
 #include "northd/en-port-group.h"
 #include "northd/ipam.h"
 #include "openvswitch/hmap.h"
+#include "simap.h"
 #include "ovs-thread.h"
 
 struct northd_input {
@@ -160,14 +161,34 @@  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;
+    const struct nbrec_logical_router *nbr;
+    bool stale;
+};
+
+struct static_routes_data {
+    struct hmap parsed_routes;
+    struct simap route_tables;
+    struct hmap bfd_active_connections;
+};
+
+struct route_policies_data {
+    struct hmap route_policies;
+    struct hmap bfd_active_connections;
+};
+
+struct bfd_data {
+    struct hmap bfd_connections;
+};
+
 struct lr_nat_table;
 
 struct lflow_input {
-    /* Northbound table references */
-    const struct nbrec_bfd_table *nbrec_bfd_table;
-
     /* Southbound table references */
-    const struct sbrec_bfd_table *sbrec_bfd_table;
     const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
     const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
     const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
@@ -190,6 +211,9 @@  struct lflow_input {
     const struct hmap *svc_monitor_map;
     bool ovn_internal_version_changed;
     const char *svc_monitor_mac;
+    struct hmap *parsed_routes;
+    struct hmap *route_policies;
+    struct simap *route_tables;
 };
 
 extern int parallelization_state;
@@ -593,8 +617,6 @@  struct ovn_port {
 
     bool has_unknown; /* If the addresses have 'unknown' defined. */
 
-    bool has_bfd;
-
     /* The port's peer:
      *
      *     - A switch port S of type "router" has a router port R as a peer,
@@ -653,6 +675,20 @@  struct ovn_port {
     struct lflow_ref *stateful_lflow_ref;
 };
 
+struct parsed_route {
+    struct hmap_node key_node;
+    struct in6_addr prefix;
+    unsigned int plen;
+    bool is_src_route;
+    uint32_t route_table_id;
+    uint32_t hash;
+    const struct nbrec_logical_router_static_route *route;
+    bool ecmp_symmetric_reply;
+    bool is_discard_route;
+    const struct nbrec_logical_router *nbr;
+    bool stale;
+};
+
 void ovnnb_db_run(struct northd_input *input_data,
                   struct northd_data *data,
                   struct ovsdb_idl_txn *ovnnb_txn,
@@ -674,6 +710,18 @@  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(struct ovn_datapath *, const struct hmap *,
+                         struct hmap *, struct hmap *, struct simap *,
+                         struct hmap *);
+uint32_t get_route_table_id(struct simap *, const char *);
+void static_routes_init(struct static_routes_data *);
+void static_routes_destroy(struct static_routes_data *);
+
+void bfd_init(struct bfd_data *);
+void bfd_destroy(struct bfd_data *);
+
 struct lflow_table;
 struct lr_stateful_tracked_data;
 struct ls_stateful_tracked_data;
@@ -711,13 +759,13 @@  bool northd_handle_lb_data_changes(struct tracked_lb_data *,
                                    struct hmap *lbgrp_datapaths_map,
                                    struct northd_tracked_data *);
 
-void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
-                     const struct nbrec_bfd_table *,
-                     const struct sbrec_bfd_table *,
-                     const struct hmap *lr_ports,
-                     struct hmap *bfd_connections);
-void bfd_cleanup_connections(const struct nbrec_bfd_table *,
-                             struct hmap *bfd_map);
+void build_route_policies(struct ovn_datapath *, const struct hmap *,
+                          const struct hmap *, struct hmap *, struct hmap *);
+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 *, struct hmap *);
+void build_bfd_map(const struct nbrec_bfd_table *,
+                   const struct sbrec_bfd_table *, struct hmap *);
 void run_update_worker_pool(int n_threads);
 
 const struct ovn_datapath *northd_get_datapath_for_port(
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 199197f09..710500dce 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -12,6 +12,7 @@  m4_define([_DUMP_DB_TABLES], [
     ovn-sbctl list meter >> $1
     ovn-sbctl list meter_band >> $1
     ovn-sbctl list port_group >> $1
+    ovn-sbctl list bfd >> $1
     ovn-sbctl dump-flows > lflows_$1
 ])
 
@@ -3861,6 +3862,7 @@  for i in $(seq 1 9); do
     check ovn-nbctl --wait=sb lsp-set-addresses sw$i-r0 00:00:00:00:00:0$i
 done
 
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 uuid=$(ovn-nbctl create bfd logical_port=r0-sw1 dst_ip=192.168.1.2 status=down min_tx=250 min_rx=250 detect_mult=10)
 ovn-nbctl create bfd logical_port=r0-sw2 dst_ip=192.168.2.2 status=down min_tx=500 min_rx=500 detect_mult=20
 ovn-nbctl create bfd logical_port=r0-sw3 dst_ip=192.168.3.2 status=down
@@ -3873,6 +3875,13 @@  wait_row_count bfd 1 logical_port=r0-sw2 detect_mult=20 dst_ip=192.168.2.2 \
 wait_row_count bfd 1 logical_port=r0-sw3 detect_mult=5 dst_ip=192.168.3.2 \
                      min_rx=1000 min_tx=1000 status=admin_down
 
+check_engine_stats northd norecompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw1)
 check ovn-nbctl set bfd $uuid min_tx=1000 min_rx=1000 detect_mult=100
 
@@ -3881,10 +3890,25 @@  check ovn-nbctl clear bfd $uuid_2 min_rx
 wait_row_count bfd 1 logical_port=r0-sw2 min_rx=1000
 wait_row_count bfd 1 logical_port=r0-sw1 min_rx=1000 min_tx=1000 detect_mult=100
 
+check_engine_stats northd norecompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 192.168.1.2
 wait_column down bfd status logical_port=r0-sw1
 AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],[0])
 
+check_engine_stats northd recompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats static_routes recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 check ovn-nbctl --bfd lr-route-add r0 200.0.0.0/8 192.168.2.2
 wait_column down bfd status logical_port=r0-sw2
 AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.2.2 | grep -q bfd],[0])
@@ -3893,10 +3917,26 @@  check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.5.2 r0-sw5
 wait_column down bfd status logical_port=r0-sw5
 AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],[0])
 
+check_engine_stats northd recompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats static_routes recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.6.1/32 192.168.10.10 r0-sw6
 wait_column down bfd status logical_port=r0-sw6
 AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],[0])
 
+check_engine_stats northd recompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats route_policies recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 192.168.7.1/32 192.168.10.10 r0-sw7
 wait_column down bfd status logical_port=r0-sw7
 AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.7.1 | grep -q bfd],[0])
@@ -3924,6 +3964,14 @@  wait_column down bfd status logical_port=r0-sw8
 bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
 AT_CHECK([ovn-nbctl list logical_router_policy | grep -q $bfd_route_policy_uuid])
 
+check_engine_stats northd recompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats static_routes recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 check ovn-nbctl lr-policy-del r0
 check ovn-nbctl --bfd lr-policy-add r0 100 "ip4.src == 2.3.4.0/24" reroute 192.168.9.2,192.168.9.3,192.168.9.4
 
@@ -3931,6 +3979,14 @@  wait_column down bfd status dst_ip=192.168.9.2
 wait_column down bfd status dst_ip=192.168.9.3
 wait_column down bfd status dst_ip=192.168.9.4
 
+check_engine_stats northd recompute nocompute
+check_engine_stats bfd recompute nocompute
+check_engine_stats route_policies recompute nocompute
+check_engine_stats lflow recompute nocompute
+check_engine_stats northd_output norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+
 bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw9)
 AT_CHECK([ovn-nbctl list logical_router_policy | sed s/,//g | grep -q "$bfd_route_policy_uuid"])