On 9/16/25 8:25 PM, Frode Nordahl wrote:
> The commit in the fixes tag introduced options to map between the
> system interface routes are learned on, and an LRP.
> 
> However, when an LR has LRPs with and without the option,
> routes are attempted associated with LRPs without the option,
> contrary to the documented behavior.
> 
> This was hidden in the original dynamic-routing - Gateway Router
> test by populating all LRPs with the option, however this does
> not represent a real world use case.
> 
> Ensure only LRPs with the option are considered when at least one
> of LRPs in an LR has the option set.
> 
> Reported-at: https://launchpad.net/bugs/2123914
> Fixes: d7d886eca553 ("controller: Support learning routes per iface.")
> Signed-off-by: Frode Nordahl <[email protected]>
> ---

Hi Frode,

Thanks for the fix!

>  controller/route.c  | 14 ++++++++++++++
>  tests/system-ovn.at |  7 ++++---
>  2 files changed, 18 insertions(+), 3 deletions(-)
> 
> diff --git a/controller/route.c b/controller/route.c
> index 7afa2578d..56c1bd528 100644
> --- a/controller/route.c
> +++ b/controller/route.c
> @@ -152,6 +152,7 @@ route_run(struct route_ctx_in *r_ctx_in,
>  {
>      struct advertise_datapath_entry *ad;
>      const struct local_datapath *ld;
> +    bool lr_has_port_name_filter;

Nit: I'd move the declaration inside the loop to reduce its scope, it's
not needed anywhere else.  While at it, I'd also move the declaration of
'ad' inside the two loops.

>      struct smap port_mapping = SMAP_INITIALIZER(&port_mapping);
>  
>      build_port_mapping(&port_mapping, 
> r_ctx_in->dynamic_routing_port_mapping);
> @@ -161,6 +162,7 @@ route_run(struct route_ctx_in *r_ctx_in,
>              continue;
>          }
>          ad = NULL;
> +        lr_has_port_name_filter = false;


I think we still have a slight bug because route_exchange_find_port()
can return NULL early if the chassis-redirect port associated to the
local_peer is not bound on the current chassis.  That means
lr_has_port_name_filter would not be set to 'true' if all
chassis-redirect ports are bound to different chassis.

We could change northd to mark the datapath as "having at least one
port with dynamic-routing-port-name set" but I think we can also
just change route_exchange_find_port() to return the port name, e.g.:

const struct sbrec_port_binding*
route_exchange_find_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                         const struct sbrec_chassis *chassis,
                         const struct sbrec_port_binding *pb,
                         const char **dynamic_routing_port_name)
{
    if (dynamic_routing_port_name) {
        *dynamic_routing_port_name = NULL;
    }

    if (!pb) {
        return NULL;
    }
    if (route_exchange_relevant_port(pb)) {
        if (dynamic_routing_port_name) {
            *dynamic_routing_port_name =
                smap_get(&pb->options, "dynamic-routing-port-name");
        }
        return pb;
    }

    const struct sbrec_port_binding *cr_pb =
        lport_get_cr_port(sbrec_port_binding_by_name, pb, NULL);

    if (!cr_pb) {
        return NULL;
    }

    if (dynamic_routing_port_name) {
        *dynamic_routing_port_name =
            smap_get(&cr_pb->options, "dynamic-routing-port-name");
    }

    if (!lport_pb_is_chassis_resident(chassis, cr_pb)) {
        return NULL;
    }

    if (route_exchange_relevant_port(cr_pb)) {
        return cr_pb;
    }
    return NULL;
}

>  
>          /* This is a LR datapath, find LRPs with route exchange options
>           * that are bound locally. */
> @@ -209,6 +211,8 @@ route_run(struct route_ctx_in *r_ctx_in,

This used to be:

            const char *port_name = smap_get(&repb->options,                    
                                                                                
                                                                              
                                            "dynamic-routing-port-name");       
                                                                                
                                                                              
            if (!port_name) {

But with the amended route_exchange_find_port() we could remove the lookup
from here.

>                  /* No port-name set, so we learn routes from all ports. */
>                  smap_add(&ad->bound_ports, local_peer->logical_port, "");
>              } else {
> +                lr_has_port_name_filter = true;

With the amended route_exchange_find_port() we would have to move this
just after that call.

> +
>                  /* If a port_name is set the we filter for the name as set in
>                   * the port-mapping or the interface name of the local
>                   * binding. If the port is not in the port_mappings and not
> @@ -224,6 +228,16 @@ route_run(struct route_ctx_in *r_ctx_in,
>          }
>  
>          if (ad) {

Nit: I'd add a comment here, maybe something like:

            /* If at least one bound port has dynamic-routing-port-name
             * configured, ignore the ones that don't. */

> +            if (lr_has_port_name_filter) {
> +                struct smap_node *node;
> +
> +                SMAP_FOR_EACH_SAFE (node, &ad->bound_ports) {
> +                    if (node->value && *node->value == '\0') {

As we skipped patch 1/2 this should just be:

if (!node->value) {
    [...]
}

> +                        smap_remove_node(&ad->bound_ports, node);
> +                    }
> +                }

Alternatively, we could change 'struct advertise_datapath_entry' and
add the lr_has_port_name_filter bool as a field there.  We could
propagate that all the way down to sb_sync_learned_routes().  But I'm
not sure I like that more so let's not do it for now.

> +            }
> +
>              tracked_datapath_add(ld->datapath, TRACKED_RESOURCE_NEW,
>                                   r_ctx_out->tracked_re_datapaths);
>              hmap_insert(r_ctx_out->announce_routes, &ad->node,
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 8e356df6f..b2319b180 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -16374,8 +16374,7 @@ check ovn-nbctl lr-add internet \
>  check ovn-nbctl lrp-add internet internet-public \
>          00:00:02:01:02:03 192.0.2.1/24 \
>      -- set Logical_Router_Port internet-public \
> -            options:dynamic-routing-redistribute="connected,static" \
> -            options:dynamic-routing-port-name=wedontlearnstuffhere
> +            options:dynamic-routing-redistribute="connected,static"
>  check ovn-nbctl lsp-add public public-internet \
>      -- set Logical_Switch_Port public-internet type=router \
>              options:router-port=internet-public \
> @@ -16548,7 +16547,9 @@ check ovn-nbctl set Logical_Router_Port internet-phys 
> \
>  check_row_count Learned_Route 0
>  check ip route add 233.252.0.0/24 via 192.168.10.10 dev lo onlink vrf 
> ovnvrf1337
>  check ovn-nbctl --wait=hv sync
> -check_row_count Learned_Route 1
> +# With a Gateway Router all LRPs are locally bound, and without explicit
> +# mapping/filtering they will all learn the route.
> +check_row_count Learned_Route 2
>  lp=$(fetch_column port_binding _uuid logical_port=internet-phys)
>  check_row_count Learned_Route 1 logical_port=$lp ip_prefix=233.252.0.0/24 
> nexthop=192.168.10.10
>  

I'm not sure it's very easy to read my suggestions above as they
refer to code that's not in the diff so I pushed a commit to my
fork with the suggested changes:

https://github.com/dceara/ovn/commit/959c2e5

Please let me know if this looks OK to you too.  If so, I could
just squash it with your patch and avoid the need for a v2.

For reference here's the incremental diff inline as well:

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 330227933d..874fe0cfa1 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -5240,7 +5240,7 @@ route_runtime_data_handler(struct engine_node *node, void 
*data)
             struct tracked_lport *lport = shash_node->data;
 
             if (route_exchange_find_port(sbrec_port_binding_by_name, chassis,
-                                         lport->pb)) {
+                                         lport->pb, NULL)) {
                 /* XXX: Until we get I-P support for route exchange we need to
                  * request recompute. */
                 return EN_UNHANDLED;
@@ -5320,7 +5320,7 @@ route_sb_port_binding_data_handler(struct engine_node 
*node, void *data)
         }
 
         if (route_exchange_find_port(sbrec_port_binding_by_name,
-                                     chassis, sbrec_pb)) {
+                                     chassis, sbrec_pb, NULL)) {
             /* XXX: Until we get I-P support for route exchange we need to
              * request recompute. */
             return EN_UNHANDLED;
diff --git a/controller/route.c b/controller/route.c
index ea7c3060a2..db4aa4122b 100644
--- a/controller/route.c
+++ b/controller/route.c
@@ -52,12 +52,21 @@ advertise_route_hash(const struct in6_addr *dst, unsigned 
int plen)
 const struct sbrec_port_binding*
 route_exchange_find_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                          const struct sbrec_chassis *chassis,
-                         const struct sbrec_port_binding *pb)
+                         const struct sbrec_port_binding *pb,
+                         const char **dynamic_routing_port_name)
 {
+    if (dynamic_routing_port_name) {
+        *dynamic_routing_port_name = NULL;
+    }
+
     if (!pb) {
         return NULL;
     }
     if (route_exchange_relevant_port(pb)) {
+        if (dynamic_routing_port_name) {
+            *dynamic_routing_port_name =
+                smap_get(&pb->options, "dynamic-routing-port-name");
+        }
         return pb;
     }
 
@@ -68,6 +77,11 @@ route_exchange_find_port(struct ovsdb_idl_index 
*sbrec_port_binding_by_name,
         return NULL;
     }
 
+    if (dynamic_routing_port_name) {
+        *dynamic_routing_port_name =
+            smap_get(&cr_pb->options, "dynamic-routing-port-name");
+    }
+
     if (!lport_pb_is_chassis_resident(chassis, cr_pb)) {
         return NULL;
     }
@@ -150,9 +164,7 @@ void
 route_run(struct route_ctx_in *r_ctx_in,
           struct route_ctx_out *r_ctx_out)
 {
-    struct advertise_datapath_entry *ad;
     const struct local_datapath *ld;
-    bool lr_has_port_name_filter;
     struct smap port_mapping = SMAP_INITIALIZER(&port_mapping);
 
     build_port_mapping(&port_mapping, r_ctx_in->dynamic_routing_port_mapping);
@@ -161,18 +173,23 @@ route_run(struct route_ctx_in *r_ctx_in,
         if (vector_is_empty(&ld->peer_ports) || ld->is_switch) {
             continue;
         }
-        ad = NULL;
-        lr_has_port_name_filter = false;
+        struct advertise_datapath_entry *ad = NULL;
+        bool lr_has_port_name_filter = false;
 
         /* This is a LR datapath, find LRPs with route exchange options
          * that are bound locally. */
         const struct peer_ports *peers;
         VECTOR_FOR_EACH_PTR (&ld->peer_ports, peers) {
             const struct sbrec_port_binding *local_peer = peers->local;
+            const char *port_name;
+
             const struct sbrec_port_binding *repb =
                 route_exchange_find_port(r_ctx_in->sbrec_port_binding_by_name,
                                          r_ctx_in->chassis,
-                                         local_peer);
+                                         local_peer, &port_name);
+            if (port_name) {
+                lr_has_port_name_filter = true;
+            }
             if (!repb) {
                 continue;
             }
@@ -205,15 +222,11 @@ route_run(struct route_ctx_in *r_ctx_in,
                          ad->db->tunnel_key);
             }
 
-            const char *port_name = smap_get(&repb->options,
-                                            "dynamic-routing-port-name");
             if (!port_name) {
                 /* No port-name set, so we learn routes from all ports. */
                 smap_add_nocopy(&ad->bound_ports,
                                 xstrdup(local_peer->logical_port), NULL);
             } else {
-                lr_has_port_name_filter = true;
-
                 /* If a port_name is set the we filter for the name as set in
                  * the port-mapping or the interface name of the local
                  * binding. If the port is not in the port_mappings and not
@@ -229,11 +242,13 @@ route_run(struct route_ctx_in *r_ctx_in,
         }
 
         if (ad) {
+            /* If at least one bound port has dynamic-routing-port-name
+             * configured, ignore the ones that don't. */
             if (lr_has_port_name_filter) {
                 struct smap_node *node;
 
                 SMAP_FOR_EACH_SAFE (node, &ad->bound_ports) {
-                    if (node->value && *node->value == '\0') {
+                    if (!node->value) {
                         smap_remove_node(&ad->bound_ports, node);
                     }
                 }
@@ -249,8 +264,9 @@ route_run(struct route_ctx_in *r_ctx_in,
     const struct sbrec_advertised_route *route;
     SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH (route,
                                            r_ctx_in->advertised_route_table) {
-        ad = advertise_datapath_find(r_ctx_out->announce_routes,
-                                     route->datapath);
+        struct advertise_datapath_entry *ad =
+            advertise_datapath_find(r_ctx_out->announce_routes,
+                                    route->datapath);
         if (!ad) {
             continue;
         }
diff --git a/controller/route.h b/controller/route.h
index 09aff89ffe..c2c92e70b6 100644
--- a/controller/route.h
+++ b/controller/route.h
@@ -80,7 +80,8 @@ struct advertise_route_entry {
 const struct sbrec_port_binding *route_exchange_find_port(
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_chassis *chassis,
-    const struct sbrec_port_binding *pb);
+    const struct sbrec_port_binding *pb,
+    const char **dynamic_routing_port_name);
 uint32_t advertise_route_hash(const struct in6_addr *dst, unsigned int plen);
 void route_run(struct route_ctx_in *, struct route_ctx_out *);
 void route_cleanup(struct hmap *announce_routes);
---

Regards,
Dumitru


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

Reply via email to