On 9/17/25 6:27 PM, Frode Nordahl wrote: > On 9/17/25 14:21, Dumitru Ceara wrote: >> 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 <fnord...@ubuntu.com> >>> --- >> >> 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. > > This case I did miss, thank you for pointing it out. > >> 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.: > > Expanding some of our structs did cross my mind, but I opted like you > that it was a bit too much at this stage. > >> 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. > > I also did not find any obvious near struct to expand upon so I agree. > >>> + } >>> + >>> 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 > > Thanks a lot, I made use of this to test this in our environment and I > can confirm it still works as expected. > >> 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. > > Thank you for your thorough review, this works for me! >
Cool, thanks a lot for trying it out! Applied to main and backported down to 25.03. Regards, Dumitru _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev