On Thu, 2025-02-06 at 15:08 +0100, Felix Huettner wrote: > On Thu, Feb 06, 2025 at 02:59:21PM +0100, > [email protected] wrote: > > On Thu, 2025-02-06 at 11:52 +0100, Dumitru Ceara wrote: > > > On 2/6/25 11:46 AM, Felix Huettner via dev wrote: > > > > On Thu, Feb 06, 2025 at 10:45:15AM +0100, > > > > [email protected] wrote: > > > > > He Felix, I have one comment regarding the publishing of > > > > > SNATs > > > > > that was > > > > > discussed in my patch series as well. > > > > > > > > > > On Tue, 2025-02-04 at 14:59 +0100, Felix Huettner via dev > > > > > wrote: > > > > > > Sometimes we want to use individual host routes instead of > > > > > > the > > > > > > connected > > > > > > routes of LRPs. > > > > > > This allows the network fabric to know which addresses are > > > > > > actually > > > > > > in > > > > > > use and e.g. drop traffic to addresses that are not used > > > > > > anyway. > > > > > > > > > > > > Signed-off-by: Felix Huettner > > > > > > <[email protected]> > > > > > > --- > > > > > > v5->v6: > > > > > > * addressed review comments > > > > > > v4->v5: skipped > > > > > > v3->v4: > > > > > > * fix a memory leak > > > > > > v2->v3: > > > > > > * A lot of minor review comments. > > > > > > > > > > > > NEWS | 3 + > > > > > > northd/en-advertised-route-sync.c | 227 > > > > > > ++++++++++++++++++++++++++++-- > > > > > > northd/en-advertised-route-sync.h | 11 ++ > > > > > > northd/inc-proc-northd.c | 4 + > > > > > > northd/northd.c | 35 +++-- > > > > > > northd/northd.h | 22 +++ > > > > > > ovn-nb.xml | 27 ++++ > > > > > > tests/ovn-northd.at | 113 ++++++++++++++- > > > > > > 8 files changed, 407 insertions(+), 35 deletions(-) > > > > > > > > > > > > diff --git a/NEWS b/NEWS > > > > > > index b0ca1992e..3547f659f 100644 > > > > > > --- a/NEWS > > > > > > +++ b/NEWS > > > > > > @@ -50,6 +50,9 @@ Post v24.09.0 > > > > > > fabric. Routes entered into the "Learned_Route" > > > > > > table > > > > > > in the > > > > > > southbound > > > > > > database will be learned by the respective LR. They > > > > > > are > > > > > > included in the > > > > > > route table with a lower priority than static > > > > > > routes. > > > > > > + * Add the option "dynamic-routing-connected-as-host- > > > > > > routes" to > > > > > > LRPs. If > > > > > > + set to true then connected routes are announced as > > > > > > individual > > > > > > host > > > > > > + routes. > > > > > > > > > > > > OVN v24.09.0 - 13 Sep 2024 > > > > > > -------------------------- > > > > > > diff --git a/northd/en-advertised-route-sync.c b/northd/en- > > > > > > advertised-route-sync.c > > > > > > index d4360763f..a0087c71a 100644 > > > > > > --- a/northd/en-advertised-route-sync.c > > > > > > +++ b/northd/en-advertised-route-sync.c > > > > > > @@ -20,6 +20,7 @@ > > > > > > #include "northd.h" > > > > > > > > > > > > #include "en-advertised-route-sync.h" > > > > > > +#include "en-lr-stateful.h" > > > > > > #include "lib/stopwatch-names.h" > > > > > > #include "openvswitch/hmap.h" > > > > > > #include "ovn-util.h" > > > > > > @@ -28,34 +29,131 @@ static void > > > > > > advertised_route_table_sync( > > > > > > struct ovsdb_idl_txn *ovnsb_txn, > > > > > > const struct sbrec_advertised_route_table > > > > > > *sbrec_advertised_route_table, > > > > > > - const struct hmap *parsed_routes); > > > > > > + const struct lr_stateful_table *lr_stateful_table, > > > > > > + const struct hmap *parsed_routes, > > > > > > + struct advertised_route_sync_data *data); > > > > > > + > > > > > > +bool > > > > > > +advertised_route_sync_lr_stateful_change_handler(struct > > > > > > engine_node > > > > > > *node, > > > > > > + void > > > > > > *data_) > > > > > > +{ > > > > > > + /* We only actually use lr_stateful data if we expose > > > > > > individual > > > > > > host > > > > > > + * routes. In this case we for now just recompute. > > > > > > + * */ > > > > > > + struct ed_type_lr_stateful *lr_stateful_data = > > > > > > + engine_get_input_data("lr_stateful", node); > > > > > > + struct advertised_route_sync_data *data = data_; > > > > > > + > > > > > > + struct hmapx_node *hmapx_node; > > > > > > + const struct lr_stateful_record *lr_stateful_rec; > > > > > > + HMAPX_FOR_EACH (hmapx_node, &lr_stateful_data- > > > > > > > trk_data.crupdated) { > > > > > > + lr_stateful_rec = hmapx_node->data; > > > > > > + if (uuidset_contains(&data->nb_lr, > > > > > > + &lr_stateful_rec->nbr_uuid)) > > > > > > { > > > > > > + return false; > > > > > > + } > > > > > > + } > > > > > > + > > > > > > + return true; > > > > > > +} > > > > > > + > > > > > > +bool > > > > > > +advertised_route_sync_northd_change_handler(struct > > > > > > engine_node > > > > > > *node, > > > > > > + void *data_) > > > > > > +{ > > > > > > + struct advertised_route_sync_data *data = data_; > > > > > > + struct northd_data *northd_data = > > > > > > engine_get_input_data("northd", node); > > > > > > + if (!northd_has_tracked_data(&northd_data->trk_data)) > > > > > > { > > > > > > + return false; > > > > > > + } > > > > > > + > > > > > > + /* We indirectly use northd_data->ls_ports if we > > > > > > announce > > > > > > host > > > > > > routes. > > > > > > + * For now we just recompute on any change to lsps > > > > > > that > > > > > > are > > > > > > relevant to us. > > > > > > + */ > > > > > > + struct hmapx_node *hmapx_node; > > > > > > + const struct ovn_port *op; > > > > > > + HMAPX_FOR_EACH (hmapx_node, &northd_data- > > > > > > > trk_data.trk_lsps.created) { > > > > > > + op = hmapx_node->data; > > > > > > + if (uuidset_contains(&data->nb_ls, > > > > > > + &op->od->nbs->header_.uuid)) > > > > > > { > > > > > > + return false; > > > > > > + } > > > > > > + } > > > > > > + HMAPX_FOR_EACH (hmapx_node, &northd_data- > > > > > > > trk_data.trk_lsps.updated) { > > > > > > + op = hmapx_node->data; > > > > > > + if (uuidset_contains(&data->nb_ls, > > > > > > + &op->od->nbs->header_.uuid)) > > > > > > { > > > > > > + return false; > > > > > > + } > > > > > > + } > > > > > > + HMAPX_FOR_EACH (hmapx_node, &northd_data- > > > > > > > trk_data.trk_lsps.deleted) { > > > > > > + op = hmapx_node->data; > > > > > > + if (uuidset_contains(&data->nb_ls, > > > > > > + &op->od->nbs->header_.uuid)) > > > > > > { > > > > > > + return false; > > > > > > + } > > > > > > + } > > > > > > + > > > > > > + return true; > > > > > > +} > > > > > > + > > > > > > +static void > > > > > > +routes_sync_init(struct advertised_route_sync_data *data) > > > > > > +{ > > > > > > + uuidset_init(&data->nb_lr); > > > > > > + uuidset_init(&data->nb_ls); > > > > > > +} > > > > > > + > > > > > > +static void > > > > > > +routes_sync_clear(struct advertised_route_sync_data *data) > > > > > > +{ > > > > > > + uuidset_clear(&data->nb_lr); > > > > > > + uuidset_clear(&data->nb_ls); > > > > > > +} > > > > > > + > > > > > > +static void > > > > > > +routes_sync_destroy(struct advertised_route_sync_data > > > > > > *data) > > > > > > +{ > > > > > > + uuidset_destroy(&data->nb_lr); > > > > > > + uuidset_destroy(&data->nb_ls); > > > > > > +} > > > > > > > > > > > > void > > > > > > *en_advertised_route_sync_init(struct engine_node *node > > > > > > OVS_UNUSED, > > > > > > struct engine_arg *arg OVS_UNUSED) > > > > > > { > > > > > > - return NULL; > > > > > > + struct advertised_route_sync_data *data = > > > > > > xzalloc(sizeof > > > > > > *data); > > > > > > + routes_sync_init(data); > > > > > > + return data; > > > > > > } > > > > > > > > > > > > void > > > > > > en_advertised_route_sync_cleanup(void *data OVS_UNUSED) > > > > > > { > > > > > > + routes_sync_destroy(data); > > > > > > } > > > > > > > > > > > > void > > > > > > en_advertised_route_sync_run(struct engine_node *node, > > > > > > void > > > > > > *data > > > > > > OVS_UNUSED) > > > > > > { > > > > > > + routes_sync_clear(data); > > > > > > + > > > > > > + struct advertised_route_sync_data *routes_sync_data = > > > > > > data; > > > > > > struct routes_data *routes_data > > > > > > = engine_get_input_data("routes", node); > > > > > > const struct engine_context *eng_ctx = > > > > > > engine_get_context(); > > > > > > const struct sbrec_advertised_route_table > > > > > > *sbrec_advertised_route_table = > > > > > > > > > > > > EN_OVSDB_GET(engine_get_input("SB_advertised_route", > > > > > > node)); > > > > > > + struct ed_type_lr_stateful *lr_stateful_data = > > > > > > + engine_get_input_data("lr_stateful", node); > > > > > > > > > > > > > > > > > > stopwatch_start(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, > > > > > > time_msec()); > > > > > > > > > > > > advertised_route_table_sync(eng_ctx->ovnsb_idl_txn, > > > > > > > > > > > > sbrec_advertised_route_table, > > > > > > - &routes_data- > > > > > > >parsed_routes); > > > > > > + &lr_stateful_data->table, > > > > > > + &routes_data- > > > > > > >parsed_routes, > > > > > > + routes_sync_data); > > > > > > > > > > > > > > > > > > stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, > > > > > > time_msec()); > > > > > > engine_set_node_state(node, EN_UPDATED); > > > > > > @@ -68,19 +166,24 @@ struct ar_entry { > > > > > > > > > > > > const struct sbrec_port_binding *logical_port; > > > > > > char *ip_prefix; > > > > > > + const struct sbrec_port_binding *tracked_port; > > > > > > }; > > > > > > > > > > > > /* Add a new entries to the to-be-advertised routes. > > > > > > * Takes ownership of ip_prefix. */ > > > > > > static struct ar_entry * > > > > > > ar_add_entry(struct hmap *routes, const struct > > > > > > sbrec_datapath_binding *sb_db, > > > > > > - const struct sbrec_port_binding > > > > > > *logical_port, > > > > > > char > > > > > > *ip_prefix) > > > > > > + const struct sbrec_port_binding > > > > > > *logical_port, > > > > > > char > > > > > > *ip_prefix, > > > > > > + const struct sbrec_port_binding > > > > > > *tracked_port) > > > > > > { > > > > > > struct ar_entry *route_e = xzalloc(sizeof *route_e); > > > > > > > > > > > > route_e->sb_db = sb_db; > > > > > > route_e->logical_port = logical_port; > > > > > > route_e->ip_prefix = ip_prefix; > > > > > > + if (tracked_port) { > > > > > > + route_e->tracked_port = tracked_port; > > > > > > + } > > > > > > uint32_t hash = uuid_hash(&sb_db->header_.uuid); > > > > > > hash = hash_string(logical_port->logical_port, hash); > > > > > > hash = hash_string(ip_prefix, hash); > > > > > > @@ -91,7 +194,8 @@ ar_add_entry(struct hmap *routes, const > > > > > > struct > > > > > > sbrec_datapath_binding *sb_db, > > > > > > > > > > > > static struct ar_entry * > > > > > > ar_find(struct hmap *route_map, const struct > > > > > > sbrec_datapath_binding > > > > > > *sb_db, > > > > > > - const struct sbrec_port_binding *logical_port, > > > > > > const > > > > > > char > > > > > > *ip_prefix) > > > > > > + const struct sbrec_port_binding *logical_port, > > > > > > const > > > > > > char > > > > > > *ip_prefix, > > > > > > + const struct sbrec_port_binding *tracked_port) > > > > > > { > > > > > > struct ar_entry *route_e; > > > > > > uint32_t hash; > > > > > > @@ -114,6 +218,10 @@ ar_find(struct hmap *route_map, const > > > > > > struct > > > > > > sbrec_datapath_binding *sb_db, > > > > > > continue; > > > > > > } > > > > > > > > > > > > + if (tracked_port != route_e->tracked_port) { > > > > > > + continue; > > > > > > + } > > > > > > + > > > > > > return route_e; > > > > > > } > > > > > > > > > > > > @@ -127,13 +235,94 @@ ar_entry_free(struct ar_entry > > > > > > *route_e) > > > > > > free(route_e); > > > > > > } > > > > > > > > > > > > +static void > > > > > > +publish_lport_addresses(struct hmap *sync_routes, > > > > > > + const struct > > > > > > sbrec_datapath_binding > > > > > > *sb_db, > > > > > > + const struct ovn_port > > > > > > *logical_port, > > > > > > + struct lport_addresses *addresses, > > > > > > + const struct ovn_port > > > > > > *tracking_port) > > > > > > +{ > > > > > > + for (size_t i = 0; i < addresses->n_ipv4_addrs; i++) { > > > > > > + const struct ipv4_netaddr *addr = &addresses- > > > > > > > ipv4_addrs[i]; > > > > > > + char *addr_s = xasprintf("%s/32", addr->addr_s); > > > > > > + ar_add_entry(sync_routes, sb_db, logical_port->sb, > > > > > > + addr_s, tracking_port->sb); > > > > > > + } > > > > > > + for (size_t i = 0; i < addresses->n_ipv6_addrs; i++) { > > > > > > + if (in6_is_lla(&addresses->ipv6_addrs[i].network)) > > > > > > { > > > > > > + continue; > > > > > > + } > > > > > > + const struct ipv6_netaddr *addr = &addresses- > > > > > > > ipv6_addrs[i]; > > > > > > + char *addr_s = xasprintf("%s/128", addr->addr_s); > > > > > > + ar_add_entry(sync_routes, sb_db, logical_port->sb, > > > > > > + addr_s, tracking_port->sb); > > > > > > + } > > > > > > +} > > > > > > + > > > > > > +/* Collect all IP addresses connected to the out_port of a > > > > > > route. > > > > > > + * This traverses all LSPs on the LS connected to the > > > > > > out_port. */ > > > > > > +static void > > > > > > +publish_host_routes(struct hmap *sync_routes, > > > > > > + const struct lr_stateful_table > > > > > > *lr_stateful_table, > > > > > > + const struct parsed_route *route, > > > > > > + struct advertised_route_sync_data > > > > > > *data) > > > > > > +{ > > > > > > + struct ovn_port *port; > > > > > > + struct ovn_datapath *lsp_od = route->out_port->peer- > > > > > > >od; > > > > > > + > > > > > > + if (!lsp_od->nbs) { > > > > > > + return; > > > > > > + } > > > > > > + > > > > > > + /* We need to track the LS we are publishing routes > > > > > > from, > > > > > > so > > > > > > that we can > > > > > > + * recompute when any port on there changes. */ > > > > > > + uuidset_insert(&data->nb_ls, &lsp_od->nbs- > > > > > > >header_.uuid); > > > > > > + HMAP_FOR_EACH (port, dp_node, &lsp_od->ports) { > > > > > > + if (port->peer) { > > > > > > + /* This is a LSP connected to an LRP */ > > > > > > + struct lport_addresses *addresses = &port- > > > > > > >peer- > > > > > > > lrp_networks; > > > > > > + publish_lport_addresses(sync_routes, route- > > > > > > >od- > > > > > > > sb, > > > > > > + route->out_port, > > > > > > + addresses, port- > > > > > > >peer); > > > > > > + > > > > > > + const struct lr_stateful_record > > > > > > *lr_stateful_rec; > > > > > > + lr_stateful_rec = > > > > > > lr_stateful_table_find_by_index( > > > > > > + lr_stateful_table, port->peer->od->index); > > > > > > + /* We also need to track this LR as we need to > > > > > > recompute > > > > > > when > > > > > > + * any of its IPs change. */ > > > > > > + uuidset_insert(&data->nb_lr, > > > > > > + &lr_stateful_rec->nbr_uuid); > > > > > > + struct ovn_port_routable_addresses addrs = > > > > > > get_op_addresses( > > > > > > + port->peer, lr_stateful_rec, false); > > > > > > > > > > If I understand correctly, the only purpose of the call to > > > > > get_op_addresses is to get NAT addresses with > > > > > "add_route=true" > > > > > option. > > > > > I'm still unsure whether this fits here. With the follow-up > > > > > series > > > > > dedicated to advertising NAT/LB addresses, wouldn't it be > > > > > better > > > > > if NAT > > > > > addresses were exclusively handled in there? i.e. only when a > > > > > dynamic- > > > > > routing-redistribute=nat option is set? > > > > > > > > Hi Martin, > > > > > > > > i still see it in the same way as commented on the other patch. > > > > > > > > Lets assume we have LR1 with LRP1. LRP1 has 192.168.0.1/24 > > > > configured. > > > > Also we have LR2 with LRP2. LRP1 and LRP2 are connected > > > > (directly > > > > or via LS) > > > > LRP2 has 192.168.0.10/24 configured. Also LR2 has a nat rule > > > > with > > > > an external > > > > IP of 192.168.0.20 configured. > > > > > > > > If we enable dynamic-routing on LR1 and redistribute connected > > > > routes we > > > > would get an advertisement of 192.168.0.0/24. This means both > > > > the > > > > LRP > > > > addresses and the NAT addresses are in the scope of the > > > > advertised > > > > route. Traffic to other IPs of that subnet would just be > > > > dropped by > > > > LR1 > > > > if it would receive it (as there is no destination). > > > > > > > > The idea is that the "connected-as-host" setting does not > > > > change > > > > the > > > > reachable addresses in any way. So we still should be able to > > > > reach > > > > both > > > > LRPs and the NAT addresses. > > > > The only change this setting does is how the routes are > > > > advertised. > > > > But > > > > it should not change the behaviour for any routeable address. > > > > > > > > > > I tend to agree with Felix on this one. I think it makes sense > > > that > > > if > > > a NAT/LB IP is part of the router port subnet then redistributing > > > "connected" (either aggregated or as host routes) should include > > > that > > > IP > > > too. > > Hi Martin, > > > > > I see your point and it does make sense. So you propose that > > "connected-as-host" would be the only option that also considers > > NAT/LB/LRP IPs of neighboring routers and "nat"/"lb-vip" options > > would > > control only advertising of NAT/LB addresses configured on the > > "local" > > LR? > > Yes, that would be my feeling. > > However that is only based on my assumption that the neighboring > "nat" > and "lb-vip" IPs are actually in the same subnet as the "local" LRP. > I
Would it not? Your implementation seems to go only over neighboring NATs/LBs with "add_route=true", so they should be reachable from the router that advertises them. > am not sure if we have any reason to announce such IPs if they are in > different subnets. > I guess that would not even work, but i am not sure about that. > Yeah, this is a good point, and actually a part about which I'm not sure about either. On one hand, it doesn't sound right to have external NAT IP on a LRP that doesn't belong to any of the LRP subnets. However, that's exactly the test scenario that Frode proposed at the bottom of his email[0]. Maybe he can chime in with more details. It also feels like that the "option:add_route"[1] was created just for this scenario. Otherwise why would neighboring routers need explicit flows for the NAT IP, if it was part of the subnet. > > > > In my mind I was thinking about these "as equal" in a sense that > > "connected-as-host" would concern only neighboring LRP addresses, > > "nat" > > would deal with NAT external IPs (both local and neighboring) and > > same > > for the "lb-vips". This would give more granular control over which > > addresses are advertised. Though admittedly I'm not sure if there's > > a > > real use-case for this granular control. > > > > With this implementation (and the above scenario described by > > Felix), > > to achieve that both LR1 and LR2 advertise NAT addresses of LR2, > > they > > would have to have following configuration > > * LR1: dynamic-routing-redistribute="connected-as-host" > > * LR2: dynamic-routing-redistribute="nat" > > > > This kind of "asymmetric" configuration may be bit unexpected, but > > it's > > nothing that couldn't be explained in the documentation. > > I would find that problematic. Once you assume that both LR1 and LR2 > are > actually doing dynamic-routing with different peers outside of OVN > this > gets really confusing. Ok, it seems we have an agreement then. I'll update my patch series asap. (Just one note, not sure if you noticed, but I left a small comment regarding the docs in my previous email). Thanks, Martin. [0] https://mail.openvswitch.org/pipermail/ovs-dev/2025-January/420169.html [1] https://man7.org/linux/man-pages/man5/ovn-nb.5.html#NAT_TABLE > > But that is just my feeling on this one. > > Thanks a lot, > Felix > > > > > > > > > > > > > > In contrast to that i would understand the > > > > dynamic-routing-redistribute=nat setting to advertise NAT rules > > > > that are > > > > configured on LR1. Because these would currently not be > > > > announced > > > > in any > > > > way. > > > > > > > > I hope that clarifies my perspective. > > > > > > > > Thanks a lot, > > > > Felix > > > > > > > > > > > > > > Thanks, > > > > > Martin. > > > > > > > > > > > + for (size_t i = 0; i < addrs.n_addrs; i++) { > > > > > > + publish_lport_addresses(sync_routes, > > > > > > route- > > > > > > > od->sb, > > > > > > + route->out_port, > > > > > > + &addrs.laddrs[i], > > > > > > + port->peer); > > > > > > + } > > > > > > + destroy_routable_addresses(&addrs); > > > > > > + } else { > > > > > > + /* This is just a plain LSP */ > > > > > > + for (size_t i = 0; i < port->n_lsp_addrs; i++) > > > > > > { > > > > > > + publish_lport_addresses(sync_routes, > > > > > > route- > > > > > > > od->sb, > > > > > > + route->out_port, > > > > > > + &port- > > > > > > >lsp_addrs[i], > > > > > > + port); > > > > > > + } > > > > > > + } > > > > > > + } > > > > > > +} > > > > > > + > > > > > > static void > > > > > > advertised_route_table_sync( > > > > > > struct ovsdb_idl_txn *ovnsb_txn, > > > > > > const struct sbrec_advertised_route_table > > > > > > *sbrec_advertised_route_table, > > > > > > - const struct hmap *parsed_routes) > > > > > > + const struct lr_stateful_table *lr_stateful_table, > > > > > > + const struct hmap *parsed_routes, > > > > > > + struct advertised_route_sync_data *data) > > > > > > { > > > > > > struct hmap sync_routes = > > > > > > HMAP_INITIALIZER(&sync_routes); > > > > > > + struct uuidset host_route_lrps = > > > > > > UUIDSET_INITIALIZER(&host_route_lrps); > > > > > > const struct parsed_route *route; > > > > > > > > > > > > struct ar_entry *route_e; > > > > > > @@ -148,9 +337,21 @@ advertised_route_table_sync( > > > > > > if (!route->od->dynamic_routing) { > > > > > > continue; > > > > > > } > > > > > > - if (route->source == ROUTE_SOURCE_CONNECTED && > > > > > > - !route->out_port- > > > > > > >dynamic_routing_connected) { > > > > > > - continue; > > > > > > + if (route->source == ROUTE_SOURCE_CONNECTED) { > > > > > > + if (!route->out_port- > > > > > > >dynamic_routing_connected) { > > > > > > + continue; > > > > > > + } > > > > > > + /* If we advertise host routes, we only need > > > > > > to do > > > > > > so > > > > > > once per > > > > > > + * LRP. */ > > > > > > + const struct uuid *lrp_uuid = > > > > > > + &route->out_port->nbrp->header_.uuid; > > > > > > + if (route->out_port- > > > > > > > dynamic_routing_connected_as_host_routes && > > > > > > + !uuidset_contains(&host_route_lrps, > > > > > > lrp_uuid)) > > > > > > { > > > > > > + uuidset_insert(&host_route_lrps, > > > > > > lrp_uuid); > > > > > > + publish_host_routes(&sync_routes, > > > > > > lr_stateful_table, > > > > > > + route, data); > > > > > > + continue; > > > > > > + } > > > > > > } > > > > > > if (route->source == ROUTE_SOURCE_STATIC && > > > > > > !route->out_port->dynamic_routing_static) > > > > > > { > > > > > > @@ -160,14 +361,15 @@ advertised_route_table_sync( > > > > > > char *ip_prefix = normalize_v46_prefix(&route- > > > > > > >prefix, > > > > > > route- > > > > > > >plen); > > > > > > route_e = ar_add_entry(&sync_routes, route->od- > > > > > > >sb, > > > > > > - route->out_port->sb, > > > > > > ip_prefix); > > > > > > + route->out_port->sb, > > > > > > ip_prefix, > > > > > > NULL); > > > > > > } > > > > > > + uuidset_destroy(&host_route_lrps); > > > > > > > > > > > > SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH_SAFE (sb_route, > > > > > > > > > > > > sbrec_advertised_route_table) { > > > > > > route_e = ar_find(&sync_routes, sb_route- > > > > > > >datapath, > > > > > > - sb_route->logical_port, > > > > > > - sb_route->ip_prefix); > > > > > > + sb_route->logical_port, > > > > > > sb_route- > > > > > > > ip_prefix, > > > > > > + sb_route->tracked_port); > > > > > > if (route_e) { > > > > > > hmap_remove(&sync_routes, &route_e->hmap_node); > > > > > > ar_entry_free(route_e); > > > > > > @@ -182,6 +384,7 @@ advertised_route_table_sync( > > > > > > sbrec_advertised_route_set_datapath(sr, route_e- > > > > > > > sb_db); > > > > > > sbrec_advertised_route_set_logical_port(sr, > > > > > > route_e- > > > > > > > logical_port); > > > > > > sbrec_advertised_route_set_ip_prefix(sr, route_e- > > > > > > > ip_prefix); > > > > > > + sbrec_advertised_route_set_tracked_port(sr, > > > > > > route_e- > > > > > > > tracked_port); > > > > > > ar_entry_free(route_e); > > > > > > } > > > > > > > > > > > > diff --git a/northd/en-advertised-route-sync.h b/northd/en- > > > > > > advertised-route-sync.h > > > > > > index 30e7cae1f..1f24fd329 100644 > > > > > > --- a/northd/en-advertised-route-sync.h > > > > > > +++ b/northd/en-advertised-route-sync.h > > > > > > @@ -17,10 +17,21 @@ > > > > > > #define EN_ADVERTISED_ROUTE_SYNC_H 1 > > > > > > > > > > > > #include "lib/inc-proc-eng.h" > > > > > > +#include "lib/uuidset.h" > > > > > > > > > > > > struct advertised_route_sync_data { > > > > > > + /* Contains the uuids of all NB Logical Routers where we > > > > > > used a > > > > > > + * lr_stateful_record during computation. */ > > > > > > + struct uuidset nb_lr; > > > > > > + /* Contains the uuids of all NB Logical Switches where > > > > > > we > > > > > > rely on > > > > > > port > > > > > > + * changes for host routes. */ > > > > > > + struct uuidset nb_ls; > > > > > > }; > > > > > > > > > > > > +bool > > > > > > advertised_route_sync_lr_stateful_change_handler(struct > > > > > > engine_node *, > > > > > > + void > > > > > > *data); > > > > > > +bool advertised_route_sync_northd_change_handler(struct > > > > > > engine_node > > > > > > *, > > > > > > + void > > > > > > *data); > > > > > > void *en_advertised_route_sync_init(struct engine_node *, > > > > > > struct > > > > > > engine_arg *); > > > > > > void en_advertised_route_sync_cleanup(void *data); > > > > > > void en_advertised_route_sync_run(struct engine_node *, > > > > > > void > > > > > > *data); > > > > > > diff --git a/northd/inc-proc-northd.c b/northd/inc-proc- > > > > > > northd.c > > > > > > index fa7ca6f2d..86b7b999e 100644 > > > > > > --- a/northd/inc-proc-northd.c > > > > > > +++ b/northd/inc-proc-northd.c > > > > > > @@ -284,6 +284,10 @@ void inc_proc_northd_init(struct > > > > > > ovsdb_idl_loop > > > > > > *nb, > > > > > > engine_add_input(&en_advertised_route_sync, > > > > > > &en_routes, > > > > > > NULL); > > > > > > engine_add_input(&en_advertised_route_sync, > > > > > > &en_sb_advertised_route, > > > > > > NULL); > > > > > > + engine_add_input(&en_advertised_route_sync, > > > > > > &en_lr_stateful, > > > > > > + > > > > > > advertised_route_sync_lr_stateful_change_handler); > > > > > > + engine_add_input(&en_advertised_route_sync, > > > > > > &en_northd, > > > > > > + > > > > > > advertised_route_sync_northd_change_handler); > > > > > > > > > > > > engine_add_input(&en_learned_route_sync, &en_routes, > > > > > > NULL); > > > > > > engine_add_input(&en_learned_route_sync, > > > > > > &en_sb_learned_route, > > > > > > NULL); > > > > > > diff --git a/northd/northd.c b/northd/northd.c > > > > > > index 5cbf6c800..3f14cc75c 100644 > > > > > > --- a/northd/northd.c > > > > > > +++ b/northd/northd.c > > > > > > @@ -1122,19 +1122,6 @@ build_datapaths(struct ovsdb_idl_txn > > > > > > *ovnsb_txn, > > > > > > ods_build_array_index(lr_datapaths); > > > > > > } > > > > > > > > > > > > -/* Structure representing logical router port > > > > > > - * routable addresses. This includes DNAT and Load > > > > > > Balancer > > > > > > - * addresses. This structure will only be filled in if the > > > > > > - * router port is a gateway router port. Otherwise, all > > > > > > pointers > > > > > > - * will be NULL and n_addrs will be 0. > > > > > > - */ > > > > > > -struct ovn_port_routable_addresses { > > > > > > - /* The parsed routable addresses */ > > > > > > - struct lport_addresses *laddrs; > > > > > > - /* Number of items in the laddrs array */ > > > > > > - size_t n_addrs; > > > > > > -}; > > > > > > - > > > > > > static bool lsp_can_be_inc_processed(const struct > > > > > > nbrec_logical_switch_port *); > > > > > > > > > > > > /* This function returns true if 'op' is a gateway router > > > > > > port. > > > > > > @@ -1169,7 +1156,7 @@ is_cr_port(const struct ovn_port *op) > > > > > > return op->primary_port; > > > > > > } > > > > > > > > > > > > -static void > > > > > > +void > > > > > > destroy_routable_addresses(struct > > > > > > ovn_port_routable_addresses > > > > > > *ra) > > > > > > { > > > > > > for (size_t i = 0; i < ra->n_addrs; i++) { > > > > > > @@ -1182,12 +1169,14 @@ static char > > > > > > **get_nat_addresses(const > > > > > > struct > > > > > > ovn_port *op, size_t *n, > > > > > > bool routable_only, bool > > > > > > include_lb_ips, > > > > > > const struct > > > > > > lr_stateful_record *); > > > > > > > > > > > > -static struct ovn_port_routable_addresses > > > > > > -get_op_routable_addresses(struct ovn_port *op, > > > > > > - const struct lr_stateful_record > > > > > > *lr_stateful_rec) > > > > > > +struct ovn_port_routable_addresses > > > > > > +get_op_addresses(struct ovn_port *op, > > > > > > + const struct lr_stateful_record > > > > > > *lr_stateful_rec, > > > > > > + bool routable_only) > > > > > > { > > > > > > size_t n; > > > > > > - char **nats = get_nat_addresses(op, &n, true, true, > > > > > > lr_stateful_rec); > > > > > > + char **nats = get_nat_addresses(op, &n, routable_only, > > > > > > true, > > > > > > + lr_stateful_rec); > > > > > > > > > > > > if (!nats) { > > > > > > return (struct ovn_port_routable_addresses) { > > > > > > @@ -1220,6 +1209,13 @@ get_op_routable_addresses(struct > > > > > > ovn_port *op, > > > > > > }; > > > > > > } > > > > > > > > > > > > +static struct ovn_port_routable_addresses > > > > > > +get_op_routable_addresses(struct ovn_port *op, > > > > > > + const struct lr_stateful_record > > > > > > *lr_stateful_rec) > > > > > > +{ > > > > > > + return get_op_addresses(op, lr_stateful_rec, true); > > > > > > +} > > > > > > + > > > > > > > > > > > > static void > > > > > > ovn_port_set_nb(struct ovn_port *op, > > > > > > @@ -2267,6 +2263,9 @@ join_logical_ports_lrp(struct hmap > > > > > > *ports, > > > > > > > > > > > > op->prefix_delegation = smap_get_bool(&op->nbrp- > > > > > > >options, > > > > > > > > > > > > "prefix_delegation", > > > > > > false); > > > > > > + op->dynamic_routing_connected_as_host_routes = > > > > > > smap_get_bool( > > > > > > + &op->nbrp->options, > > > > > > + "dynamic-routing-connected-as-host-routes", > > > > > > false); > > > > > > parse_dynamic_routing_redistribute(&op->nbrp->options, > > > > > > &op- > > > > > > > dynamic_routing_connected, > > > > > > od- > > > > > > > dynamic_routing_connected, > > > > > > diff --git a/northd/northd.h b/northd/northd.h > > > > > > index 75a3df617..15bd01c05 100644 > > > > > > --- a/northd/northd.h > > > > > > +++ b/northd/northd.h > > > > > > @@ -25,6 +25,7 @@ > > > > > > #include "openvswitch/hmap.h" > > > > > > #include "simap.h" > > > > > > #include "ovs-thread.h" > > > > > > +#include "en-lr-stateful.h" > > > > > > > > > > > > struct northd_input { > > > > > > /* Northbound table references */ > > > > > > @@ -625,6 +626,8 @@ struct ovn_port { > > > > > > * If the option is unset it will be initialized based > > > > > > on > > > > > > the > > > > > > nbr > > > > > > * option. */ > > > > > > bool dynamic_routing_connected; > > > > > > + /* nbrp option "dynamic-routing-connected-as-host- > > > > > > routes" > > > > > > is > > > > > > "true". */ > > > > > > + bool dynamic_routing_connected_as_host_routes; > > > > > > /* nbrp option "dynamic-routing-redistribute" contains > > > > > > "static". > > > > > > * If the option is unset it will be initialized based > > > > > > on > > > > > > the > > > > > > nbr > > > > > > * option. */ > > > > > > @@ -935,4 +938,23 @@ void build_igmp_lflows(struct hmap > > > > > > *igmp_groups, > > > > > > const struct hmap *ls_datapaths, > > > > > > struct lflow_table *lflows, > > > > > > struct lflow_ref *lflow_ref); > > > > > > +/* Structure representing logical router port routable > > > > > > addresses. > > > > > > This > > > > > > + * includes DNAT and Load Balancer addresses. This > > > > > > structure > > > > > > will > > > > > > only > > > > > > + * be filled in if the router port is a gateway router > > > > > > port. > > > > > > Otherwise, > > > > > > + * all pointers will be NULL and n_addrs will be 0. > > > > > > + */ > > > > > > +struct ovn_port_routable_addresses { > > > > > > + /* The parsed routable addresses */ > > > > > > + struct lport_addresses *laddrs; > > > > > > + /* Number of items in the laddrs array */ > > > > > > + size_t n_addrs; > > > > > > +}; > > > > > > + > > > > > > +struct ovn_port_routable_addresses get_op_addresses( > > > > > > + struct ovn_port *op, > > > > > > + const struct lr_stateful_record *lr_stateful_rec, > > > > > > + bool routable_only); > > > > > > + > > > > > > +void destroy_routable_addresses(struct > > > > > > ovn_port_routable_addresses > > > > > > *ra); > > > > > > + > > > > > > #endif /* NORTHD_H */ > > > > > > diff --git a/ovn-nb.xml b/ovn-nb.xml > > > > > > index bf785e4d3..d0bdb5058 100644 > > > > > > --- a/ovn-nb.xml > > > > > > +++ b/ovn-nb.xml > > > > > > @@ -3752,6 +3752,33 @@ or > > > > > > key="dynamic-routing-redistribute" > > > > > > table="Logical_Router"/> > > > > > > will be > > > > > > used. > > > > > > </column> > > > > > > + <column name="options" key="dynamic-routing- > > > > > > connected- > > > > > > as-host- > > > > > > routes" > > > > > > + type='{"type": "boolean"}'> > > > > > > + Only relevant if <ref column="options" > > > > > > key="dynamic- > > > > > > routing" > > > > > > + table="Logical_Router"/> on the respective > > > > > > Logical_Router is > > > > > > set > > > > > > + to <code>true</code> and also > > > > > > + <ref column="options" key="dynamic-routing- > > > > > > connected"/> is > > > > > > enabled on > > > > > > + the LR or LRP. > > > > > > + > > > > > > + If set to true the prefix connected to the LRP is > > > > > > not > > > > > > advertised as a > > > > > > + whole. Rather each individual IP address that is > > > > > > actually in > > > > > > use inside > > > > > > + this prefix is announced as a host route. Default > > > > > > is > > > > > > false. > > > > With the discussion above in mind, I think this documentation could > > add > > a note that it includes NAT/LB VIPs as well. > > > > Thanks, > > Martin. > > > > > > > > + > > > > > > + This can be used to: > > > > > > + <ul> > > > > > > + <li> > > > > > > + allow the fabric outside of OVN to drop > > > > > > traffic > > > > > > towards > > > > > > IP > > > > > > + addresses that are not actually used. This > > > > > > traffic > > > > > > would > > > > > > otherwise > > > > > > + hit this LR and then be dropped. > > > > > > + </li> > > > > > > + > > > > > > + <li> > > > > > > + If this LR has multiple LRPs connected to the > > > > > > fabric on > > > > > > different > > > > > > + chassis: allows the fabric outside of OVN to > > > > > > steer > > > > > > packets to the > > > > > > + chassis which already hosts this backing ip > > > > > > address. > > > > > > + </li> > > > > > > + </ul> > > > > > > + </column> > > > > > > </group> > > > > > > > > > > > > <group title="Attachment"> > > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > > > > > > index 52a458e35..18d367fa5 100644 > > > > > > --- a/tests/ovn-northd.at > > > > > > +++ b/tests/ovn-northd.at > > > > > > @@ -14845,6 +14845,66 @@ AT_CHECK([grep -w > > > > > > "lr_in_ip_routing" > > > > > > lr0flows | ovn_strip_lflows], [0], [dnl > > > > > > AT_CLEANUP > > > > > > ]) > > > > > > > > > > > > +OVN_FOR_EACH_NORTHD_NO_HV([ > > > > > > +AT_SETUP([dynamic-routing - host routes]) > > > > > > +AT_KEYWORDS([dynamic-routing]) > > > > > > +ovn_start > > > > > > + > > > > > > +# We start with announcing routes on a lr with 2 lrps. > > > > > > +# LRP lr0-sw0 is connected to ls sw0. > > > > > > +check ovn-nbctl lr-add lr0 > > > > > > +check ovn-nbctl set Logical_Router lr0 option:dynamic- > > > > > > routing=true \ > > > > > > + option:dynamic-routing- > > > > > > redistribute="connected;static" > > > > > > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 > > > > > > 10.0.0.1/24 > > > > > > +sw0=$(fetch_column port_binding _uuid logical_port=lr0- > > > > > > sw0) > > > > > > +check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 > > > > > > 10.0.1.1/24 > > > > > > +sw1=$(fetch_column port_binding _uuid logical_port=lr0- > > > > > > sw1) > > > > > > +check ovn-nbctl ls-add sw0 > > > > > > +check ovn-nbctl lsp-add sw0 sw0-lr0 > > > > > > +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-lr0 > > > > > > type=router options:router-port=lr0-sw0 > > > > > > +check_row_count Advertised_Route 2 tracked_port='[[]]' > > > > > > +datapath=$(fetch_column datapath_binding _uuid > > > > > > external_ids:name=lr0) > > > > > > + > > > > > > +# Configuring the LRP lr0-sw0 to send host routes. > > > > > > +# As sw0 is quite empty we will only see the addresses of > > > > > > lr0- > > > > > > sw0. > > > > > > +check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 > > > > > > options:dynamic-routing-connected-as-host-routes=true > > > > > > +check_row_count Advertised_Route 2 > > > > > > +check_column 10.0.0.1/32 Advertised_Route ip_prefix > > > > > > datapath=$datapath logical_port=$sw0 > > > > > > +check_column $sw0 Advertised_Route tracked_port > > > > > > datapath=$datapath > > > > > > logical_port=$sw0 > > > > > > + > > > > > > +# Adding a VIF to the LS sw0 will advertise it as well. > > > > > > +check ovn-nbctl lsp-add sw0 sw0-vif0 > > > > > > +check ovn-nbctl --wait=sb lsp-set-addresses sw0-vif0 > > > > > > "00:aa:bb:cc:dd:ee 10.0.0.2" > > > > > > +vif0=$(fetch_column port_binding _uuid logical_port=sw0- > > > > > > vif0) > > > > > > +check_row_count Advertised_Route 3 > > > > > > +check_row_count Advertised_Route 2 tracked_port!='[[]]' > > > > > > +check_column $vif0 Advertised_Route tracked_port > > > > > > datapath=$datapath > > > > > > logical_port=$sw0 ip_prefix=10.0.0.2/32 > > > > > > + > > > > > > +# Adding a LR lr1 to the LS sw0 will advertise the LRP of > > > > > > the > > > > > > new > > > > > > router. > > > > > > +check ovn-nbctl lr-add lr1 > > > > > > +check ovn-nbctl lrp-add lr1 lr1-sw0 00:00:00:01:ff:01 > > > > > > 10.0.0.10/24 > > > > > > +check ovn-nbctl lsp-add sw0 sw0-lr1 > > > > > > +lr1=$(fetch_column port_binding _uuid logical_port=lr1- > > > > > > sw0) > > > > > > +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-lr1 > > > > > > type=router options:router-port=lr1-sw0 > > > > > > +check_row_count Advertised_Route 4 > > > > > > +check_row_count Advertised_Route 3 tracked_port!='[[]]' > > > > > > +check_column $lr1 Advertised_Route tracked_port > > > > > > datapath=$datapath > > > > > > logical_port=$sw0 ip_prefix=10.0.0.10/32 > > > > > > + > > > > > > +# Adding a NAT rule to lr1 will advertise it as well. > > > > > > +check ovn-nbctl --wait=sb lr-nat-add lr1 dnat_and_snat > > > > > > 10.0.0.100 > > > > > > 192.168.0.1 > > > > > > +check_row_count Advertised_Route 5 > > > > > > +check_row_count Advertised_Route 4 tracked_port!='[[]]' > > > > > > +check_column $lr1 Advertised_Route tracked_port > > > > > > datapath=$datapath > > > > > > logical_port=$sw0 ip_prefix=10.0.0.100/32 > > > > > > + > > > > > > +# Adding a static route to lr1 will be advertised just > > > > > > normally. > > > > > > +check ovn-nbctl --wait=sb lr-route-add lr0 172.16.0.0/24 > > > > > > 10.0.0.200 > > > > > > +check_row_count Advertised_Route 6 > > > > > > +check_row_count Advertised_Route 4 tracked_port!='[[]]' > > > > > > +check_row_count Advertised_Route 1 datapath=$datapath > > > > > > logical_port=$sw0 ip_prefix=172.16.0.0/24 > > > > > > + > > > > > > +AT_CLEANUP > > > > > > +]) > > > > > > + > > > > > > OVN_FOR_EACH_NORTHD_NO_HV([ > > > > > > AT_SETUP([dynamic-routing incremental processing]) > > > > > > AT_KEYWORDS([dynamic-routing]) > > > > > > @@ -14868,13 +14928,16 @@ check_engine_stats lflow > > > > > > recompute > > > > > > nocompute > > > > > > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > > > > > > > check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > -check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw0 > > > > > > 00:00:00:00:ff:01 > > > > > > 10.0.0.1/24 > > > > > > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 > > > > > > 10.0.0.1/24 > > > > > > +check ovn-nbctl ls-add sw0 > > > > > > +check ovn-nbctl lsp-add sw0 sw0-lr0 > > > > > > +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-lr0 > > > > > > type=router options:router-port=lr0-sw0 > > > > > > sw0=$(fetch_column port_binding _uuid logical_port=lr0- > > > > > > sw0) > > > > > > check_engine_stats northd recompute compute > > > > > > -check_engine_stats routes recompute nocompute > > > > > > -check_engine_stats advertised_route_sync recompute > > > > > > nocompute > > > > > > -check_engine_stats learned_route_sync recompute nocompute > > > > > > -check_engine_stats lflow recompute nocompute > > > > > > +check_engine_stats routes recompute compute > > > > > > +check_engine_stats advertised_route_sync recompute compute > > > > > > +check_engine_stats learned_route_sync recompute compute > > > > > > +check_engine_stats lflow recompute compute > > > > > > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > > > > > > > check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > @@ -14980,6 +15043,46 @@ check_engine_stats > > > > > > learned_route_sync > > > > > > recompute nocompute > > > > > > check_engine_stats lflow recompute nocompute > > > > > > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > +check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 > > > > > > options:dynamic-routing-connected-as-host-routes=true > > > > > > +check_engine_stats northd recompute nocompute > > > > > > +check_engine_stats routes recompute nocompute > > > > > > +check_engine_stats advertised_route_sync recompute > > > > > > nocompute > > > > > > +check_engine_stats learned_route_sync recompute nocompute > > > > > > +check_engine_stats lflow recompute nocompute > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > + > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > +check ovn-nbctl lsp-add sw0 sw0-vif0 > > > > > > +check ovn-nbctl --wait=sb lsp-set-addresses sw0-vif0 > > > > > > "00:aa:bb:cc:dd:ee 10.0.0.2" > > > > > > +check_engine_stats northd norecompute compute > > > > > > +check_engine_stats routes norecompute compute > > > > > > +check_engine_stats advertised_route_sync recompute > > > > > > nocompute > > > > > > +check_engine_stats learned_route_sync norecompute compute > > > > > > +check_engine_stats lflow norecompute compute > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > + > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > +check ovn-nbctl lr-add lr1 > > > > > > +check ovn-nbctl lrp-add lr1 lr1-sw0 00:00:00:01:ff:01 > > > > > > 10.0.0.10/24 > > > > > > +check ovn-nbctl lsp-add sw0 sw0-lr1 > > > > > > +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-lr1 > > > > > > type=router options:router-port=lr1-sw0 > > > > > > +check_engine_stats northd recompute compute > > > > > > +check_engine_stats routes recompute compute > > > > > > +check_engine_stats advertised_route_sync recompute > > > > > > nocompute > > > > > > +check_engine_stats learned_route_sync recompute compute > > > > > > +check_engine_stats lflow recompute compute > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > + > > > > > > +check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > +check ovn-nbctl --wait=sb lr-nat-add lr1 dnat_and_snat > > > > > > 10.0.0.100 > > > > > > 192.168.0.1 > > > > > > +check_engine_stats northd norecompute compute > > > > > > +check_engine_stats routes norecompute compute > > > > > > +check_engine_stats advertised_route_sync recompute > > > > > > nocompute > > > > > > +check_engine_stats learned_route_sync norecompute compute > > > > > > +check_engine_stats lflow norecompute compute > > > > > > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > > > > > > + > > > > > > check as northd ovn-appctl -t ovn-northd inc-engine/clear- > > > > > > stats > > > > > > check ovn-nbctl --wait=sb lrp-del lr0-sw0 > > > > > > check_engine_stats northd recompute compute > > > > > > > > > > _______________________________________________ > > > > > 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 > > > > > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
