> On Sat, Jul 20, 2024 at 8:22 AM Lorenzo Bianconi
> <[email protected]> wrote:
> >
> > > On Fri, Jul 19, 2024 at 9:20 AM Lorenzo Bianconi
> > > <[email protected]> wrote:
> > > >
> > > > [...]
> > > > >
> > > > > Hi Han,
> > > > >
> > > > > I had suggested Lorenzo to add a new node to do the sync for the
> > > > > reasons Lorenzo mentioned.
> > > > > I'm fine with your suggestion or any other optimization or re-ordering
> > > > > as long as we can
> > > > > avoid looping every static route and route policy for each bfd entry
> > > > > to check if it is being
> > > > > used actively before putting its state to admin_down.
> > > >
> > >
> > > Thanks Lore and Numan for the details. Now I understand the context.
> > >
> > > > Hi Han and Numan,
> > > >
> > > > I do not think we can move en_bfd node after en_route_policies or
> > > > en_static_routes ones since the en_bfd computation results are used by
> > > > en_route_policies and en_static_routes.
> > >
> > > I didn't see how en_bfd computation results are used by
> > > en_route_policies and en_static_routes. In your patch,
> > > en_route_policies and en_static_routes don't have en_bfd as input. Is
> > > there any hidden dependency I didn't see?
> >
> > Hi Han,
> >
> > I guess en_route_policies and en_static_routes nodes depend on the result 
> > from
> > en_bfd one since we check nb_bt->status value in check_bfd_state() and
> > parsed_route_lookup() to see if route_policy/static_route is usable or not.
> > Please take a look to [0] and [1].
> 
> I see. These are indeed hidden dependencies. Originally all these
> logic belong to a single node en_lflow, so it wasn't a problem, but
> now since they are split into different nodes, the dependencies need
> to be properly tracked.
> 
> In the current patch the en_bfd not only generates output data in the
> bfd_connections hmap, but also updates NB-DB's bfd status, and
> en_route_policies and en_static_routes nodes depend on the NB-DB bfd
> status. The I-P engine doesn't detect OVSDB changes within an
> iteration, which makes the situation very tricky.
> 
> > Moreover please consider build_bfd_table() can modify nb_bt->status 
> > according
> > to the sb_bt->status value since ovn-controller could update it (e.g. if the
> > bfd link goes down). Please take a look to [2].
> > Thinking again about it, I guess we need to keep en_bfd node as input of
> > en_route_policies/en_static_routes.
> 
> Yes we can add en_bfd as the input of both en_route_policies and
> en_static_routes, even though there is no direct use of en_bfd's data
> from these nodes, just because the dependency is through the NB-DB IDL
> which is not reflected by the I-P engine. This is a workaround and we
> need to carefully add comments to describe the reason for this special
> case.
> 
> Alternatively, I think a better approach is that in en_bfd's data
> (bfd_connections) we keep track of the desired state of each entry but
> don't directly update the NB-DB. In en_route_policies and
> en_static_routes nodes we don't check NB-DB status but check the
> states stored in bfd_connections, and they shouldn't make any change
> to the NB-DB state either (I see there are NB-DB updates from
> parsed_routes_add but I wonder if the state change can be moved to the
> en_bfd node). Finally in the en_bfd_nb_sync it can take care of all
> the NB-DB bfd state updates as needed, according to the data of
> en_bfd, en_route_policies and en_static_routes. And these NB-DB update
> notifications will later trigger another I-P run anyway, thus the
> en_bfd node run will be triggered and the states in bfd_connections
> would eventually reflect the NB-DB bfd states. This avoids the hidden
> dependency, and makes NB bfd status sync in one place, which is more
> clear, but I understand it requires more changes. Please give it some
> thoughts.

Thanks for pointing this out Han :).
Ack, I agree this approach would be more clean. I will address it in v8.

Regards,
Lorenzo

> 
> Thanks,
> Han
> 
> > What do you think? Am I missing something?
> >
> > Regards,
> > Lorenzo
> >
> > [0] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L10012
> > [1] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L10382
> > [2] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L9897
> >
> > >
> > > If the dependency is correct, I think the en_bfd_nb_sync is
> > > unnecessary because you can add en_route_policies and en_static_routes
> > > as inputs for en_bfd, and move just the same logic of en_bfd_nb_sync
> > > to en_bfd. This shouldn't require looping every static route and route
> > > policy for each bfd entry. What do you think?
> > >
> > > Thanks,
> > > Han
> > >
> > > > Let's consider the following example:
> > > > we have an ecmp route configured with two different next-hops. Both 
> > > > next-hops
> > > > are linked to two different bfd sessions. If the active next-hop goes 
> > > > down,
> > > > en_bfd node will mark the connection status to down and will trigger
> > > > en_static_routes recompute to switch to the other next-hop.
> > > > At the same time we need the en_static_routes and en_route_policies 
> > > > computation
> > > > results in order to understand if a bfd session is really used or not 
> > > > (so to
> > > > understand if we can move the state from "down" to "admin_down" as 
> > > > described in
> > > > the previous email).
> > > > The only way I can find to not split the bfd logic in two different 
> > > > nodes (en_bfd
> > > > and en_bfd_nb_sync) is to loop over all static_routes and 
> > > > route_policies in
> > > > en_bfd node as proposed in v4.
> > > >
> > > > Regards,
> > > > Lorenzo
> > > >
> > > > >
> > > > > Numan
> > > > >
> > > > >
> > > > > >
> > > > > > [0] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L9765
> > > > > > [1] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L10005
> > > > > > [2] https://github.com/ovn-org/ovn/blob/main/northd/northd.c#L10375
> > > > > > [3] 
> > > > > > https://patchwork.ozlabs.org/project/ovn/patch/99f193dd93762bc90b86ba29684e04c11737e06a.1719750478.git.lorenzo.bianc...@redhat.com/
> > > > > >
> > > > > > >
> > > > > > > > +    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 6898daa00..be6cee5bf 100644
> > > > > > > > --- a/northd/northd.c
> > > > > > > > +++ b/northd/northd.c
> > > > > > > > @@ -9728,9 +9728,33 @@ struct bfd_entry {
> > > > > > > >
> > > > > > > >      const struct sbrec_bfd *sb_bt;
> > > > > > > >
> > > > > > > > -    bool ref;
> > > > > > > > +    char *logical_port;
> > > > > > > > +    char *dst_ip;
> > > > > > > > +    bool stale;
> > > > > > > >  };
> > > > > > > >
> > > > > > > > +static struct bfd_entry *
> > > > > > > > +bfd_alloc_entry(struct hmap *bfd_connections,
> > > > > > > > +                const char *logical_port, const char *dst_ip)
> > > > > > > > +{
> > > > > > > > +    struct bfd_entry *bfd_e = xzalloc(sizeof *bfd_e);
> > > > > > > > +    bfd_e->logical_port = xstrdup(logical_port);
> > > > > > > > +    bfd_e->dst_ip = xstrdup(dst_ip);
> > > > > > > > +    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);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > >  static struct bfd_entry *
> > > > > > > >  bfd_port_lookup(const struct hmap *bfd_map, const char 
> > > > > > > > *logical_port,
> > > > > > > >                  const char *dst_ip)
> > > > > > > > @@ -9741,36 +9765,31 @@ 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)
> > > > > > > > +bfd_connections_cleanup(const struct nbrec_bfd_table 
> > > > > > > > *nbrec_bfd_table,
> > > > > > >
> > > > > > > This function is now updating the BFD status and has nothing to do
> > > > > > > with BFD connections cleanup, so it is better to be renamed as
> > > > > > > something like bfd_nb_status_update.
> > > > > >
> > > > > > ack, I have kept the original routine name, but I do not have 
> > > > > > strong opinion
> > > > > > about it. I will fix it in v8.
> > > > > >
> > > > > > Regards,
> > > > > > Lorenzo
> > > > > >
> > > > > > >
> > > > > > > Best regards,
> > > > > > > Han
> > > > > > >
> > > > > > > > +                        struct hmap 
> > > > > > > > *static_route_bfd_connections,
> > > > > > > > +                        struct hmap 
> > > > > > > > *route_policy_bfd_connections)
> > > > > > > >  {
> > > > > > > >      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")) {
> > > > > > > > +        if (strcmp(nb_bt->status, "admin_down") &&
> > > > > > > > +            !bfd_port_lookup(static_route_bfd_connections,
> > > > > > > > +                             nb_bt->logical_port, 
> > > > > > > > nb_bt->dst_ip) &&
> > > > > > > > +            !bfd_port_lookup(route_policy_bfd_connections,
> > > > > > > > +                             nb_bt->logical_port, 
> > > > > > > > nb_bt->dst_ip)) {
> > > > > > > >              /* no user for this bfd connection */
> > > > > > > >              nbrec_bfd_set_status(nb_bt, "admin_down");
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > > -
> > > > > > > > -    HMAP_FOR_EACH_POP (bfd_e, hmap_node, bfd_map) {
> > > > > > > > -        free(bfd_e);
> > > > > > > > -    }
> > > > > > > >  }
> > > > > > > >
> > > > > > > >  #define BFD_DEF_MINTX       1000 /* 1s */
> > > > > > > > @@ -9839,20 +9858,25 @@ build_bfd_table(struct ovsdb_idl_txn 
> > > > > > > > *ovnsb_txn,
> > > > > > > >                  const struct sbrec_bfd_table *sbrec_bfd_table,
> > > > > > > >                  const struct hmap *lr_ports, struct hmap 
> > > > > > > > *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;
> > > > > > > > +    if (!ovnsb_txn) {
> > > > > > > > +        return;
> > > > > > > > +    }
> > > > > > > >
> > > > > > > > -    bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > > > +    unsigned long *bfd_src_ports = 
> > > > > > > > bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
> > > > > > > > +    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 = xmalloc(sizeof *bfd_e);
> > > > > > > > +        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);
> > > > > > > > +        }
> > > > > > > >          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);
> > > > > > > > +        bfd_e->stale = true;
> > > > > > > > +        /* we need to check if this entry is even in the BFD 
> > > > > > > > nb db table */
> > > > > > > >          bitmap_set1(bfd_src_ports, sb_bt->src_port - 
> > > > > > > > BFD_UDP_SRC_PORT_START);
> > > > > > > >      }
> > > > > > > >
> > > > > > > > @@ -9864,7 +9888,13 @@ build_bfd_table(struct ovsdb_idl_txn 
> > > > > > > > *ovnsb_txn,
> > > > > > > >          }
> > > > > > > >
> > > > > > > >          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 (!op || !op->sb) {
> > > > > > > > +            /* skip not bounded ports */
> > > > > > > > +            continue;
> > > > > > > > +        }
> > > > > > > > +
> > > > > > > > +        bfd_e = bfd_port_lookup(bfd_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                                nb_bt->dst_ip);
> > > > > > > >          if (!bfd_e) {
> > > > > > > >              int udp_src = bfd_get_unused_port(bfd_src_ports);
> > > > > > > >              if (udp_src < 0) {
> > > > > > > > @@ -9877,7 +9907,7 @@ build_bfd_table(struct ovsdb_idl_txn 
> > > > > > > > *ovnsb_txn,
> > > > > > > >              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);
> > > > > > > >              }
> > > > > > > >
> > > > > > > > @@ -9888,7 +9918,12 @@ build_bfd_table(struct ovsdb_idl_txn 
> > > > > > > > *ovnsb_txn,
> > > > > > > >              int d_mult = nb_bt->n_detect_mult ? 
> > > > > > > > nb_bt->detect_mult[0]
> > > > > > > >                                                : 
> > > > > > > > BFD_DEF_DETECT_MULT;
> > > > > > > >              sbrec_bfd_set_detect_mult(sb_bt, d_mult);
> > > > > > > > +
> > > > > > > > +            bfd_e = bfd_alloc_entry(bfd_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                                    nb_bt->dst_ip);
> > > > > > > > +            bfd_e->sb_bt = sb_bt;
> > > > > > > >          } else {
> > > > > > > > +            bfd_e->stale = false;
> > > > > > > >              if (strcmp(bfd_e->sb_bt->status, nb_bt->status)) {
> > > > > > > >                  if (!strcmp(nb_bt->status, "admin_down") ||
> > > > > > > >                      !strcmp(bfd_e->sb_bt->status, 
> > > > > > > > "admin_down")) {
> > > > > > > > @@ -9897,18 +9932,13 @@ 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) {
> > > > > > > > @@ -9916,16 +9946,20 @@ build_bfd_table(struct ovsdb_idl_txn 
> > > > > > > > *ovnsb_txn,
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > >
> > > > > > > > -    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);
> > > > > > > > +    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, bfd_connections) {
> > > > > > > > +        if (!bfd_e->stale) {
> > > > > > > > +            continue;
> > > > > > > > +        }
> > > > > > > > +
> > > > > > > > +        struct ovn_port *op = ovn_port_find(lr_ports, 
> > > > > > > > bfd_e->logical_port);
> > > > > > > >          if (op) {
> > > > > > > >              op->has_bfd = false;
> > > > > > > >          }
> > > > > > > > +
> > > > > > > > +        hmap_remove(bfd_connections, &bfd_e->hmap_node);
> > > > > > > >          sbrec_bfd_delete(bfd_e->sb_bt);
> > > > > > > > -        free(bfd_e);
> > > > > > > > +        bfd_erase_entry(bfd_e);
> > > > > > > >      }
> > > > > > > > -    hmap_destroy(&sb_only);
> > > > > > > >
> > > > > > > >      bitmap_free(bfd_src_ports);
> > > > > > > >  }
> > > > > > > > @@ -9965,13 +9999,9 @@ 
> > > > > > > > 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,
> > > > > > > > +                            struct hmap 
> > > > > > > > *bfd_active_connections)
> > > > > > > >  {
> > > > > > > >      struct in6_addr nexthop_v6;
> > > > > > > >      bool is_nexthop_v6 = ipv6_parse(nexthop, &nexthop_v6);
> > > > > > > > @@ -9997,20 +10027,16 @@ static bool check_bfd_state(
> > > > > > > >              continue;
> > > > > > > >          }
> > > > > > > >
> > > > > > > > -        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 (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > > >              nbrec_bfd_set_status(nb_bt, "down");
> > > > > > > >          }
> > > > > > > >
> > > > > > > > +        if (!bfd_port_lookup(bfd_active_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                             nb_bt->dst_ip)) {
> > > > > > > > +            bfd_alloc_entry(bfd_active_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                            nb_bt->dst_ip);
> > > > > > > > +        }
> > > > > > > >          ret = strcmp(nb_bt->status, "down");
> > > > > > > > -        ovs_mutex_unlock(&bfd_lock);
> > > > > > > >          break;
> > > > > > > >      }
> > > > > > > >
> > > > > > > > @@ -10019,20 +10045,22 @@ static bool check_bfd_state(
> > > > > > > >
> > > > > > > >  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) {
> > > > > > > > @@ -10048,10 +10076,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);
> > > > > > > > @@ -10094,19 +10118,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;
> > > > > > > > @@ -10117,7 +10140,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;
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > > @@ -10125,40 +10148,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; "
> > > > > > > > @@ -10167,7 +10180,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,
> > > > > > > > @@ -10183,37 +10196,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);
> > > > > > > >  }
> > > > > > > > @@ -10238,7 +10244,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]) {
> > > > > > > > @@ -10279,18 +10285,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)
> > > > > > > >  {
> > > > > > > > @@ -10305,11 +10299,52 @@ 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,
> > > > > > > > +                  struct hmap *routes, struct simap 
> > > > > > > > *route_tables,
> > > > > > > >                    const struct 
> > > > > > > > nbrec_logical_router_static_route *route,
> > > > > > > > -                  const struct hmap *bfd_connections)
> > > > > > > > +                  struct hmap *bfd_active_connections)
> > > > > > > >  {
> > > > > > > >      /* Verify that the next hop is an IP address with an 
> > > > > > > > all-ones mask. */
> > > > > > > >      struct in6_addr nexthop;
> > > > > > > > @@ -10322,7 +10357,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)) {
> > > > > > > > @@ -10330,7 +10365,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;
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > >
> > > > > > > > @@ -10341,7 +10376,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. */
> > > > > > > > @@ -10352,7 +10387,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;
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > >
> > > > > > > > @@ -10361,52 +10396,75 @@ 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;
> > > > > > > > -
> > > > > > > > -        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 (!strcmp(nb_bt->status, "admin_down")) {
> > > > > > > >              nbrec_bfd_set_status(nb_bt, "down");
> > > > > > > >          }
> > > > > > > >
> > > > > > > > +        if (!bfd_port_lookup(bfd_active_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                             nb_bt->dst_ip)) {
> > > > > > > > +            bfd_alloc_entry(bfd_active_connections, 
> > > > > > > > nb_bt->logical_port,
> > > > > > > > +                            nb_bt->dst_ip);
> > > > > > > > +        }
> > > > > > > > +
> > > > > > > >          if (!strcmp(nb_bt->status, "down")) {
> > > > > > > > -            ovs_mutex_unlock(&bfd_lock);
> > > > > > > > -            return NULL;
> > > > > > > > +            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 *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, routes, route_tables,
> > > > > > > > +                          od->nbr->static_routes[i],
> > > > > > > > +                          bfd_active_connections);
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    HMAP_FOR_EACH_SAFE (pr, key_node, routes) {
> > > > > > > > +        if (!pr->stale) {
> > > > > > > > +            continue;
> > > > > > > > +        }
> > > > > > > > +
> > > > > > > > +        hmap_remove(routes, &pr->key_node);
> > > > > > > >          free(pr);
> > > > > > > >      }
> > > > > > > >  }
> > > > > > > > @@ -12719,7 +12777,8 @@ static void
> > > > > > > >  build_static_route_flows_for_lrouter(
> > > > > > > >          struct ovn_datapath *od, const struct chassis_features 
> > > > > > > > *features,
> > > > > > > >          struct lflow_table *lflows, const struct hmap 
> > > > > > > > *lr_ports,
> > > > > > > > -        const struct hmap *bfd_connections,
> > > > > > > > +        struct hmap *parsed_routes,
> > > > > > > > +        struct simap *route_tables,
> > > > > > > >          struct lflow_ref *lflow_ref)
> > > > > > > >  {
> > > > > > > >      ovs_assert(od->nbr);
> > > > > > > > @@ -12733,22 +12792,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);
> > > > > > > > @@ -12777,8 +12830,6 @@ build_static_route_flows_for_lrouter(
> > > > > > > >      }
> > > > > > > >      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
> > > > > > > > @@ -12885,6 +12936,113 @@ 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, struct hmap 
> > > > > > > > *lr_ports,
> > > > > > > > +                     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_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
> > > > > > > > @@ -12898,7 +13056,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);
> > > > > > > > @@ -12915,21 +13073,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);
> > > > > > > >          }
> > > > > > > >      }
> > > > > > > >  }
> > > > > > > > @@ -15864,6 +16021,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
> > > > > > > > @@ -15910,12 +16070,13 @@ 
> > > > > > > > build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > > > > > > >      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, 
> > > > > > > > NULL);
> > > > > > > >      build_static_route_flows_for_lrouter(od, lsi->features,
> > > > > > > >                                           lsi->lflows, 
> > > > > > > > lsi->lr_ports,
> > > > > > > > -                                         lsi->bfd_connections,
> > > > > > > > +                                         lsi->parsed_routes,
> > > > > > > > +                                         lsi->route_tables,
> > > > > > > >                                           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,
> > > > > > > > @@ -16231,7 +16392,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);
> > > > > > > > @@ -16265,6 +16429,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);
> > > > > > > >
> > > > > > > > @@ -16305,6 +16472,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,
> > > > > > > >          };
> > > > > > > > @@ -16466,7 +16636,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;
> > > > > > > > @@ -17542,6 +17715,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)
> > > > > > > >  {
> > > > > > > > @@ -17581,6 +17775,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..6eb529589 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;
> > > > > > > > @@ -653,6 +677,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 +712,17 @@ 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 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 +760,15 @@ bool northd_handle_lb_data_changes(struct 
> > > > > > > > tracked_lb_data *,
> > > > > > > >                                     struct hmap 
> > > > > > > > *lbgrp_datapaths_map,
> > > > > > > >                                     struct northd_tracked_data 
> > > > > > > > *);
> > > > > > > >
> > > > > > > > +void build_route_policies(struct ovn_datapath *, struct hmap 
> > > > > > > > *, struct hmap *,
> > > > > > > > +                          struct hmap *);
> > > > > > > > +void bfd_connections_cleanup(const struct nbrec_bfd_table *, 
> > > > > > > > struct hmap *,
> > > > > > > > +                             struct hmap *);
> > > > > > > >  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 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 a389d1988..a84fb6fab 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
> > > > > > > >  ])
> > > > > > > >
> > > > > > > > @@ -3858,6 +3859,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
> > > > > > > > @@ -3870,6 +3872,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
> > > > > > > >
> > > > > > > > @@ -3878,10 +3887,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])
> > > > > > > > @@ -3890,10 +3914,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])
> > > > > > > > @@ -3921,6 +3961,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
> > > > > > > >
> > > > > > > > @@ -3928,6 +3976,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
> > > > > > [email protected]
> > > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > >
> > >
> 
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to