diff mbox series

[ovs-dev,v0] northd: Avoid learning route with nexthop in different subnet from LRP.

Message ID 20251205182035.1003724-1-lucas.vdias@luizalabs.com
State Under Review
Delegated to: Dumitru Ceara
Headers show
Series [ovs-dev,v0] northd: Avoid learning route with nexthop in different subnet from LRP. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning

Commit Message

Lucas Vargas Dias Dec. 5, 2025, 6:20 p.m. UTC
Learned routes must have the nexthop reachable. So, if logical router
has two ports in differents subnets, route must be learned in just
one which is the port in the same subnet from nexthop.

Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
---
 northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
 tests/ovn-northd.at            | 14 ++++++-
 tests/system-ovn.at            | 74 +++++++++++++++++-----------------
 3 files changed, 93 insertions(+), 39 deletions(-)

Comments

Dumitru Ceara Dec. 12, 2025, 3:29 p.m. UTC | #1
On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
> Learned routes must have the nexthop reachable. So, if logical router
> has two ports in differents subnets, route must be learned in just
> one which is the port in the same subnet from nexthop.
> 
> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
> ---

Hi Lucas,

Thanks for the patch!

I'm not sure I understand the problem though, please see below.

>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
>  tests/ovn-northd.at            | 14 ++++++-
>  tests/system-ovn.at            | 74 +++++++++++++++++-----------------
>  3 files changed, 93 insertions(+), 39 deletions(-)
> 
> diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c
> index f22aaa664..4c9de8651 100644
> --- a/northd/en-learned-route-sync.c
> +++ b/northd/en-learned-route-sync.c
> @@ -227,6 +227,50 @@ routes_table_sync(
>              sbrec_learned_route_delete(sb_route);
>              continue;
>          }
> +        bool is_same_subnet = false;
> +        for (size_t i = 0; !is_same_subnet &&
> +             i < sb_route->logical_port->n_mac;
> +             i++) {
> +            struct lport_addresses logical_port_addrs;
> +            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
> +                                       &logical_port_addrs)) {
> +                destroy_lport_addresses(&logical_port_addrs);
> +                continue;
> +            }
> +            ovs_be32 neigh_prefix_v4;
> +            struct in6_addr neigh_prefix_v6;
> +
> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
> +                for (size_t j = 0; j < logical_port_addrs.n_ipv4_addrs;
> +                     j++) {
> +                    struct ipv4_netaddr address =
> +                        logical_port_addrs.ipv4_addrs[j];
> +                    if (address.network ==
> +                        (neigh_prefix_v4 & address.mask)) {
> +                        is_same_subnet = true;
> +                        break;
> +                    }
> +                }
> +            } else if (ipv6_parse(sb_route->nexthop, &neigh_prefix_v6)) {
> +                for (size_t j = 0; j < logical_port_addrs.n_ipv6_addrs; j++) {
> +                    struct ipv6_netaddr address =
> +                        logical_port_addrs.ipv6_addrs[j];
> +                    struct in6_addr neigh_prefix =
> +                        ipv6_addr_bitand(&neigh_prefix_v6, &address.mask);
> +                    if (ipv6_addr_equals(&address.network, &neigh_prefix)) {
> +                        is_same_subnet = true;
> +                        break;
> +                    }
> +                }
> +            }
> +            destroy_lport_addresses(&logical_port_addrs);
> +        }
> +
> +
> +        if (!is_same_subnet) {
> +            sbrec_learned_route_delete(sb_route);
> +            continue;
> +        }
>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
>                                       &lr_datapaths->datapaths,
>                                       sb_route);
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 864854c56..0d1d06196 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
>  ])
>  
>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on lr0-sw1.
> -# This is not reachable so will not produce a lflow.
> +# This is not reachable so will not produce a lflow. Also, it'll be removed by northd

As the comment used to say, while the route is learned, ovn-northd
doesn't actually produce logical flows for it because the next hop is
not reachable on any network configured on lr0-sw1.

So yes, the route is in SB.Learned_Route, but it doesn't affect routing
in any way.  It's exactly the same as configuring a NB Static_Route with
a wrong next hop.

Moreover, your code removes the Learned_Route in northd.  But
Learned_Routes are fully managed (created & deleted) by ovn-controller.

So next time ovn-controller runs it will just relearn the route
(re-create the SB.Learned_Route record).  So this only creates
unnecessary churn as far as I understand.

Or is there a use case I missed?

Regards,
Dumitru


>  check_uuid ovn-sbctl create Learned_Route \
>      datapath=$datapath                    \
>      logical_port=$sw1                     \
> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
>      nexthop=\"2001:db8:ffff::20\"
>  check ovn-nbctl --wait=sb sync
>  check_row_count Advertised_Route 4
> -check_row_count Learned_Route 2
> +check_row_count Learned_Route 1
> +check_row_count Learned_Route 0 logical_port=$sw1 ip_prefix=\"2001:db8:2::/64\"
> +
>  ovn-sbctl dump-flows lr0 > lr0flows
>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_ip_routing   ), priority=0    , match=(1), action=(drop;)
> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
>  # active.
>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
> +
> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on lr0-sw1.
> +# Northd doesn not remove now because lrp have address in same subnet from nexthop
> +check_uuid ovn-sbctl create Learned_Route \
> +    datapath=$datapath                    \
> +    logical_port=$sw1                     \
> +    ip_prefix=\"2001:db8:2::/64\"         \
> +    nexthop=\"2001:db8:ffff::20\"
>  check_row_count Advertised_Route 5
>  check_row_count Learned_Route 2
>  ovn-sbctl dump-flows lr0 > lr0flows
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 1cbbdfa58..6e87fb266 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>        options:dynamic-routing-port-name=mylearninglsp
>  
> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
>  check ovn-nbctl --wait=hv sync
> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20
> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.10.20
>  
>  # Stopping the ovn-controller will clean up the route entries created by it.
>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it will
> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>  OVN_CLEANUP_CONTROLLER([hv1])
>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Starting it again will add the routes again.
>  start_daemon ovn-controller
> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>  blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Changing the vrf name will switch to the new one.
>  # The old vrf will be removed.
> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>  blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Stopping with --restart will not touch the routes.
>  OVN_CONTROLLER_EXIT([],[--restart])
> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>  blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # When we now stop the ovn-controller it will remove the VRF.
>  start_daemon ovn-controller
> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 192.0.2.21 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Bind "vip" port locally and check the virtual IP is added in the VRF.
>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>  blackhole 192.0.2.30 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  check ovn-sbctl clear Port_Binding vip virtual-parent
>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 192.0.2.21 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Remove the backoff period, so we can bind it right away.
>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>  blackhole 192.0.2.30 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Simulate "vip" bound to a different chassis.
>  check ovn-sbctl clear Port_Binding vip virtual-parent
> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 192.0.2.21 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Check with dynamic-routing-redistribute-local-only=false.
>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>  blackhole 192.0.2.30 proto ovn metric 1000
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Remove the backoff period, so we can bind it right away.
>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>  blackhole 192.0.2.30 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  OVN_CLEANUP_CONTROLLER([hv1])
>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via 192.168.10.10 dev lo onlink vrf ovnvrf1337
>  check ovn-nbctl --wait=hv sync
>  # 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
> +check_row_count Learned_Route 1
>  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
>  
> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>      options:dynamic-routing-port-name=mylearninglsp
>  
> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
>  check ovn-nbctl --wait=hv sync
> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20
> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.10.20
>  
>  # Stopping the ovn-controller will clean up the route entries created by it.
>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it will
> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>  OVN_CLEANUP_CONTROLLER([hv1])
>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Starting it again will add the routes again.
>  start_daemon ovn-controller
> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>  blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Stopping with --restart will not touch the routes.
>  OVN_CONTROLLER_EXIT([],[--restart])
> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>  blackhole 192.0.2.20 proto ovn metric 100
>  blackhole 198.51.100.0/24 proto ovn metric 1000
>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
>  
>  # Now we set maintain-vrf again and stop the ovn-controller.
>  # It will then remove the VRF.
> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and lrp2])
>  check ovn-nbctl --wait=hv \
>      remove logical_router_port lrp2 options dynamic-routing-port-name
> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
>                                  nexthop=2.2.2.2      \
>                                  logical_port=$lrp1
>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>                                  nexthop=2.2.2.2      \
>                                  logical_port=$lrp2
> -check_row_count Learned_Route 2
> +check_row_count Learned_Route 1
>  
>  OVN_CLEANUP_CONTROLLER([hv1])
>
Lucas Vargas Dias Dec. 18, 2025, 1:50 p.m. UTC | #2
Hi Dumitru,
thanks for the review.

Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
> > Learned routes must have the nexthop reachable. So, if logical router
> > has two ports in differents subnets, route must be learned in just
> > one which is the port in the same subnet from nexthop.
> >
> > Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
> > ---
>
> Hi Lucas,
>
> Thanks for the patch!
>
> I'm not sure I understand the problem though, please see below.
>
> >  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
> >  tests/ovn-northd.at            | 14 ++++++-
> >  tests/system-ovn.at            | 74 +++++++++++++++++-----------------
> >  3 files changed, 93 insertions(+), 39 deletions(-)
> >
> > diff --git a/northd/en-learned-route-sync.c
> b/northd/en-learned-route-sync.c
> > index f22aaa664..4c9de8651 100644
> > --- a/northd/en-learned-route-sync.c
> > +++ b/northd/en-learned-route-sync.c
> > @@ -227,6 +227,50 @@ routes_table_sync(
> >              sbrec_learned_route_delete(sb_route);
> >              continue;
> >          }
> > +        bool is_same_subnet = false;
> > +        for (size_t i = 0; !is_same_subnet &&
> > +             i < sb_route->logical_port->n_mac;
> > +             i++) {
> > +            struct lport_addresses logical_port_addrs;
> > +            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
> > +                                       &logical_port_addrs)) {
> > +                destroy_lport_addresses(&logical_port_addrs);
> > +                continue;
> > +            }
> > +            ovs_be32 neigh_prefix_v4;
> > +            struct in6_addr neigh_prefix_v6;
> > +
> > +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
> > +                for (size_t j = 0; j < logical_port_addrs.n_ipv4_addrs;
> > +                     j++) {
> > +                    struct ipv4_netaddr address =
> > +                        logical_port_addrs.ipv4_addrs[j];
> > +                    if (address.network ==
> > +                        (neigh_prefix_v4 & address.mask)) {
> > +                        is_same_subnet = true;
> > +                        break;
> > +                    }
> > +                }
> > +            } else if (ipv6_parse(sb_route->nexthop, &neigh_prefix_v6))
> {
> > +                for (size_t j = 0; j < logical_port_addrs.n_ipv6_addrs;
> j++) {
> > +                    struct ipv6_netaddr address =
> > +                        logical_port_addrs.ipv6_addrs[j];
> > +                    struct in6_addr neigh_prefix =
> > +                        ipv6_addr_bitand(&neigh_prefix_v6,
> &address.mask);
> > +                    if (ipv6_addr_equals(&address.network,
> &neigh_prefix)) {
> > +                        is_same_subnet = true;
> > +                        break;
> > +                    }
> > +                }
> > +            }
> > +            destroy_lport_addresses(&logical_port_addrs);
> > +        }
> > +
> > +
> > +        if (!is_same_subnet) {
> > +            sbrec_learned_route_delete(sb_route);
> > +            continue;
> > +        }
> >          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
> >                                       &lr_datapaths->datapaths,
> >                                       sb_route);
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 864854c56..0d1d06196 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >  ])
> >
> >  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> lr0-sw1.
> > -# This is not reachable so will not produce a lflow.
> > +# This is not reachable so will not produce a lflow. Also, it'll be
> removed by northd
>
> As the comment used to say, while the route is learned, ovn-northd
> doesn't actually produce logical flows for it because the next hop is
> not reachable on any network configured on lr0-sw1.
>
> So yes, the route is in SB.Learned_Route, but it doesn't affect routing
> in any way.  It's exactly the same as configuring a NB Static_Route with
> a wrong next hop.
>
> Moreover, your code removes the Learned_Route in northd.  But
> Learned_Routes are fully managed (created & deleted) by ovn-controller.
>
> So next time ovn-controller runs it will just relearn the route
> (re-create the SB.Learned_Route record).  So this only creates
> unnecessary churn as far as I understand.
>
> Or is there a use case I missed?
>
>
You're right, it's more appropriate to add the code in ovn controller
and northd will not generate flows. However, I would like to avoid the
garbage on the southbound.


Regards,
Lucas


> Regards,
> Dumitru
>
>
> >  check_uuid ovn-sbctl create Learned_Route \
> >      datapath=$datapath                    \
> >      logical_port=$sw1                     \
> > @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
> >      nexthop=\"2001:db8:ffff::20\"
> >  check ovn-nbctl --wait=sb sync
> >  check_row_count Advertised_Route 4
> > -check_row_count Learned_Route 2
> > +check_row_count Learned_Route 1
> > +check_row_count Learned_Route 0 logical_port=$sw1
> ip_prefix=\"2001:db8:2::/64\"
> > +
> >  ovn-sbctl dump-flows lr0 > lr0flows
> >  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
> action=(drop;)
> > @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >  # active.
> >  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
> >      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
> > +
> > +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> lr0-sw1.
> > +# Northd doesn not remove now because lrp have address in same subnet
> from nexthop
> > +check_uuid ovn-sbctl create Learned_Route \
> > +    datapath=$datapath                    \
> > +    logical_port=$sw1                     \
> > +    ip_prefix=\"2001:db8:2::/64\"         \
> > +    nexthop=\"2001:db8:ffff::20\"
> >  check_row_count Advertised_Route 5
> >  check_row_count Learned_Route 2
> >  ovn-sbctl dump-flows lr0 > lr0flows
> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> > index 1cbbdfa58..6e87fb266 100644
> > --- a/tests/system-ovn.at
> > +++ b/tests/system-ovn.at
> > @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
> >  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >        options:dynamic-routing-port-name=mylearninglsp
> >
> > -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 30 proto zebra
> > -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 40 proto zebra
> > +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 30 proto zebra
> > +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 40 proto zebra
> >  check ovn-nbctl --wait=hv sync
> > -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> nexthop=192.168.20.20
> > +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> nexthop=192.168.10.20
> >
> >  # Stopping the ovn-controller will clean up the route entries created
> by it.
> >  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
> will
> > @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
> Logical_Router_Port internet-phys \
> >  OVN_CLEANUP_CONTROLLER([hv1])
> >  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Starting it again will add the routes again.
> >  start_daemon ovn-controller
> > @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >  blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Changing the vrf name will switch to the new one.
> >  # The old vrf will be removed.
> > @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >  blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Stopping with --restart will not touch the routes.
> >  OVN_CONTROLLER_EXIT([],[--restart])
> > @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >  blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # When we now stop the ovn-controller it will remove the VRF.
> >  start_daemon ovn-controller
> > @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 192.0.2.21 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Bind "vip" port locally and check the virtual IP is added in the VRF.
> >  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
> > @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >  blackhole 192.0.2.30 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  check ovn-sbctl clear Port_Binding vip virtual-parent
> >  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
> > @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 192.0.2.21 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Remove the backoff period, so we can bind it right away.
> >  check ovn-sbctl remove Port_Binding vip options vport-backoff
> > @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >  blackhole 192.0.2.30 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Simulate "vip" bound to a different chassis.
> >  check ovn-sbctl clear Port_Binding vip virtual-parent
> > @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 192.0.2.21 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Check with dynamic-routing-redistribute-local-only=false.
> >  check ovn-nbctl --wait=hv set logical_router_port internet-public \
> > @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >  blackhole 192.0.2.30 proto ovn metric 1000
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Remove the backoff period, so we can bind it right away.
> >  check ovn-sbctl remove Port_Binding vip options vport-backoff
> > @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >  blackhole 192.0.2.30 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  OVN_CLEANUP_CONTROLLER([hv1])
> >  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
> > @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
> 192.168.10.10 dev lo onlink vrf ovnvrf1337
> >  check ovn-nbctl --wait=hv sync
> >  # 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
> > +check_row_count Learned_Route 1
> >  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
> >
> > @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
> >  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >      options:dynamic-routing-port-name=mylearninglsp
> >
> > -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 30 proto zebra
> > -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 40 proto zebra
> > +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 30 proto zebra
> > +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
> vrf ovnvrf1337 metric 40 proto zebra
> >  check ovn-nbctl --wait=hv sync
> > -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> nexthop=192.168.20.20
> > +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> nexthop=192.168.10.20
> >
> >  # Stopping the ovn-controller will clean up the route entries created
> by it.
> >  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
> will
> > @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
> Logical_Router_Port internet-phys \
> >  OVN_CLEANUP_CONTROLLER([hv1])
> >  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Starting it again will add the routes again.
> >  start_daemon ovn-controller
> > @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >  blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Stopping with --restart will not touch the routes.
> >  OVN_CONTROLLER_EXIT([],[--restart])
> > @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >  blackhole 192.0.2.20 proto ovn metric 100
> >  blackhole 198.51.100.0/24 proto ovn metric 1000
> >  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> onlink
> > -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> onlink])
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> onlink
> > +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> onlink])
> >
> >  # Now we set maintain-vrf again and stop the ovn-controller.
> >  # It will then remove the VRF.
> > @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
> >  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and lrp2])
> >  check ovn-nbctl --wait=hv \
> >      remove logical_router_port lrp2 options dynamic-routing-port-name
> > -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> > +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
> >                                  nexthop=2.2.2.2      \
> >                                  logical_port=$lrp1
> >  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >                                  nexthop=2.2.2.2      \
> >                                  logical_port=$lrp2
> > -check_row_count Learned_Route 2
> > +check_row_count Learned_Route 1
> >
> >  OVN_CLEANUP_CONTROLLER([hv1])
> >
>
>
Dumitru Ceara Dec. 18, 2025, 2:56 p.m. UTC | #3
On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
> Hi Dumitru,

Hi Lucas,

> thanks for the review.
> 
> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
>>> Learned routes must have the nexthop reachable. So, if logical router
>>> has two ports in differents subnets, route must be learned in just
>>> one which is the port in the same subnet from nexthop.
>>>
>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
>>> ---
>>
>> Hi Lucas,
>>
>> Thanks for the patch!
>>
>> I'm not sure I understand the problem though, please see below.
>>
>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
>>>  tests/ovn-northd.at            | 14 ++++++-
>>>  tests/system-ovn.at            | 74 +++++++++++++++++-----------------
>>>  3 files changed, 93 insertions(+), 39 deletions(-)
>>>
>>> diff --git a/northd/en-learned-route-sync.c
>> b/northd/en-learned-route-sync.c
>>> index f22aaa664..4c9de8651 100644
>>> --- a/northd/en-learned-route-sync.c
>>> +++ b/northd/en-learned-route-sync.c
>>> @@ -227,6 +227,50 @@ routes_table_sync(
>>>              sbrec_learned_route_delete(sb_route);
>>>              continue;
>>>          }
>>> +        bool is_same_subnet = false;
>>> +        for (size_t i = 0; !is_same_subnet &&
>>> +             i < sb_route->logical_port->n_mac;
>>> +             i++) {
>>> +            struct lport_addresses logical_port_addrs;
>>> +            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
>>> +                                       &logical_port_addrs)) {
>>> +                destroy_lport_addresses(&logical_port_addrs);
>>> +                continue;
>>> +            }
>>> +            ovs_be32 neigh_prefix_v4;
>>> +            struct in6_addr neigh_prefix_v6;
>>> +
>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
>>> +                for (size_t j = 0; j < logical_port_addrs.n_ipv4_addrs;
>>> +                     j++) {
>>> +                    struct ipv4_netaddr address =
>>> +                        logical_port_addrs.ipv4_addrs[j];
>>> +                    if (address.network ==
>>> +                        (neigh_prefix_v4 & address.mask)) {
>>> +                        is_same_subnet = true;
>>> +                        break;
>>> +                    }
>>> +                }
>>> +            } else if (ipv6_parse(sb_route->nexthop, &neigh_prefix_v6))
>> {
>>> +                for (size_t j = 0; j < logical_port_addrs.n_ipv6_addrs;
>> j++) {
>>> +                    struct ipv6_netaddr address =
>>> +                        logical_port_addrs.ipv6_addrs[j];
>>> +                    struct in6_addr neigh_prefix =
>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
>> &address.mask);
>>> +                    if (ipv6_addr_equals(&address.network,
>> &neigh_prefix)) {
>>> +                        is_same_subnet = true;
>>> +                        break;
>>> +                    }
>>> +                }
>>> +            }
>>> +            destroy_lport_addresses(&logical_port_addrs);
>>> +        }
>>> +
>>> +
>>> +        if (!is_same_subnet) {
>>> +            sbrec_learned_route_delete(sb_route);
>>> +            continue;
>>> +        }
>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
>>>                                       &lr_datapaths->datapaths,
>>>                                       sb_route);
>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>> index 864854c56..0d1d06196 100644
>>> --- a/tests/ovn-northd.at
>>> +++ b/tests/ovn-northd.at
>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>>>  ])
>>>
>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>> lr0-sw1.
>>> -# This is not reachable so will not produce a lflow.
>>> +# This is not reachable so will not produce a lflow. Also, it'll be
>> removed by northd
>>
>> As the comment used to say, while the route is learned, ovn-northd
>> doesn't actually produce logical flows for it because the next hop is
>> not reachable on any network configured on lr0-sw1.
>>
>> So yes, the route is in SB.Learned_Route, but it doesn't affect routing
>> in any way.  It's exactly the same as configuring a NB Static_Route with
>> a wrong next hop.
>>
>> Moreover, your code removes the Learned_Route in northd.  But
>> Learned_Routes are fully managed (created & deleted) by ovn-controller.
>>
>> So next time ovn-controller runs it will just relearn the route
>> (re-create the SB.Learned_Route record).  So this only creates
>> unnecessary churn as far as I understand.
>>
>> Or is there a use case I missed?
>>
>>
> You're right, it's more appropriate to add the code in ovn controller
> and northd will not generate flows. However, I would like to avoid the
> garbage on the southbound.
> 

I'm still not sure what "garbage" we're avoiding.  The router learns a
"dynamic" route from a routing protocol (like any traditional router).
Then there's some logic (in northd in our case) that decides whether
that route should be used for actual traffic forwarding (i.e., is
installed in the forwarding table in traditional routers).

If the port it's learnt on doesn't have an IP in a subnet that includes
the next hop, northd doesn't "install" the route (as logical flows).

Also, I don't think you can move the check to ovn-controller.  We
already check for LRP subnets that match the next hop in ovn-northd.  We
don't want to duplicate that logic to ovn-controller IMO.

Regards,
Dumitru

> 
> Regards,
> Lucas
> 
> 
>> Regards,
>> Dumitru
>>
>>
>>>  check_uuid ovn-sbctl create Learned_Route \
>>>      datapath=$datapath                    \
>>>      logical_port=$sw1                     \
>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
>>>      nexthop=\"2001:db8:ffff::20\"
>>>  check ovn-nbctl --wait=sb sync
>>>  check_row_count Advertised_Route 4
>>> -check_row_count Learned_Route 2
>>> +check_row_count Learned_Route 1
>>> +check_row_count Learned_Route 0 logical_port=$sw1
>> ip_prefix=\"2001:db8:2::/64\"
>>> +
>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
>> action=(drop;)
>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>>>  # active.
>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
>>> +
>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>> lr0-sw1.
>>> +# Northd doesn not remove now because lrp have address in same subnet
>> from nexthop
>>> +check_uuid ovn-sbctl create Learned_Route \
>>> +    datapath=$datapath                    \
>>> +    logical_port=$sw1                     \
>>> +    ip_prefix=\"2001:db8:2::/64\"         \
>>> +    nexthop=\"2001:db8:ffff::20\"
>>>  check_row_count Advertised_Route 5
>>>  check_row_count Learned_Route 2
>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>> index 1cbbdfa58..6e87fb266 100644
>>> --- a/tests/system-ovn.at
>>> +++ b/tests/system-ovn.at
>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>        options:dynamic-routing-port-name=mylearninglsp
>>>
>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 30 proto zebra
>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 40 proto zebra
>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 30 proto zebra
>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 40 proto zebra
>>>  check ovn-nbctl --wait=hv sync
>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>> nexthop=192.168.20.20
>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>> nexthop=192.168.10.20
>>>
>>>  # Stopping the ovn-controller will clean up the route entries created
>> by it.
>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
>> will
>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
>> Logical_Router_Port internet-phys \
>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Starting it again will add the routes again.
>>>  start_daemon ovn-controller
>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Changing the vrf name will switch to the new one.
>>>  # The old vrf will be removed.
>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Stopping with --restart will not touch the routes.
>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # When we now stop the ovn-controller it will remove the VRF.
>>>  start_daemon ovn-controller
>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Bind "vip" port locally and check the virtual IP is added in the VRF.
>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Remove the backoff period, so we can bind it right away.
>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Simulate "vip" bound to a different chassis.
>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Check with dynamic-routing-redistribute-local-only=false.
>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>  blackhole 192.0.2.30 proto ovn metric 1000
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Remove the backoff period, so we can bind it right away.
>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
>>>  check ovn-nbctl --wait=hv sync
>>>  # 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
>>> +check_row_count Learned_Route 1
>>>  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
>>>
>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>      options:dynamic-routing-port-name=mylearninglsp
>>>
>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 30 proto zebra
>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 40 proto zebra
>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 30 proto zebra
>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink
>> vrf ovnvrf1337 metric 40 proto zebra
>>>  check ovn-nbctl --wait=hv sync
>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>> nexthop=192.168.20.20
>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>> nexthop=192.168.10.20
>>>
>>>  # Stopping the ovn-controller will clean up the route entries created
>> by it.
>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
>> will
>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
>> Logical_Router_Port internet-phys \
>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Starting it again will add the routes again.
>>>  start_daemon ovn-controller
>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Stopping with --restart will not touch the routes.
>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>> onlink
>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>> onlink])
>>>
>>>  # Now we set maintain-vrf again and stop the ovn-controller.
>>>  # It will then remove the VRF.
>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and lrp2])
>>>  check ovn-nbctl --wait=hv \
>>>      remove logical_router_port lrp2 options dynamic-routing-port-name
>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
>>>                                  nexthop=2.2.2.2      \
>>>                                  logical_port=$lrp1
>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>                                  nexthop=2.2.2.2      \
>>>                                  logical_port=$lrp2
>>> -check_row_count Learned_Route 2
>>> +check_row_count Learned_Route 1
>>>
>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>
>>
>>
>
Lucas Vargas Dias Jan. 2, 2026, 5:29 p.m. UTC | #4
Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
> > Hi Dumitru,
>
> Hi Lucas,
>
> > thanks for the review.
> >
> > Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com>
> > escreveu:
> >
> >> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
> >>> Learned routes must have the nexthop reachable. So, if logical router
> >>> has two ports in differents subnets, route must be learned in just
> >>> one which is the port in the same subnet from nexthop.
> >>>
> >>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
> >>> ---
> >>
> >> Hi Lucas,
> >>
> >> Thanks for the patch!
> >>
> >> I'm not sure I understand the problem though, please see below.
> >>
> >>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
> >>>  tests/ovn-northd.at            | 14 ++++++-
> >>>  tests/system-ovn.at            | 74
> +++++++++++++++++-----------------
> >>>  3 files changed, 93 insertions(+), 39 deletions(-)
> >>>
> >>> diff --git a/northd/en-learned-route-sync.c
> >> b/northd/en-learned-route-sync.c
> >>> index f22aaa664..4c9de8651 100644
> >>> --- a/northd/en-learned-route-sync.c
> >>> +++ b/northd/en-learned-route-sync.c
> >>> @@ -227,6 +227,50 @@ routes_table_sync(
> >>>              sbrec_learned_route_delete(sb_route);
> >>>              continue;
> >>>          }
> >>> +        bool is_same_subnet = false;
> >>> +        for (size_t i = 0; !is_same_subnet &&
> >>> +             i < sb_route->logical_port->n_mac;
> >>> +             i++) {
> >>> +            struct lport_addresses logical_port_addrs;
> >>> +            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
> >>> +                                       &logical_port_addrs)) {
> >>> +                destroy_lport_addresses(&logical_port_addrs);
> >>> +                continue;
> >>> +            }
> >>> +            ovs_be32 neigh_prefix_v4;
> >>> +            struct in6_addr neigh_prefix_v6;
> >>> +
> >>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
> >>> +                for (size_t j = 0; j <
> logical_port_addrs.n_ipv4_addrs;
> >>> +                     j++) {
> >>> +                    struct ipv4_netaddr address =
> >>> +                        logical_port_addrs.ipv4_addrs[j];
> >>> +                    if (address.network ==
> >>> +                        (neigh_prefix_v4 & address.mask)) {
> >>> +                        is_same_subnet = true;
> >>> +                        break;
> >>> +                    }
> >>> +                }
> >>> +            } else if (ipv6_parse(sb_route->nexthop,
> &neigh_prefix_v6))
> >> {
> >>> +                for (size_t j = 0; j <
> logical_port_addrs.n_ipv6_addrs;
> >> j++) {
> >>> +                    struct ipv6_netaddr address =
> >>> +                        logical_port_addrs.ipv6_addrs[j];
> >>> +                    struct in6_addr neigh_prefix =
> >>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
> >> &address.mask);
> >>> +                    if (ipv6_addr_equals(&address.network,
> >> &neigh_prefix)) {
> >>> +                        is_same_subnet = true;
> >>> +                        break;
> >>> +                    }
> >>> +                }
> >>> +            }
> >>> +            destroy_lport_addresses(&logical_port_addrs);
> >>> +        }
> >>> +
> >>> +
> >>> +        if (!is_same_subnet) {
> >>> +            sbrec_learned_route_delete(sb_route);
> >>> +            continue;
> >>> +        }
> >>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
> >>>                                       &lr_datapaths->datapaths,
> >>>                                       sb_route);
> >>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> >>> index 864854c56..0d1d06196 100644
> >>> --- a/tests/ovn-northd.at
> >>> +++ b/tests/ovn-northd.at
> >>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
> >> ovn_strip_lflows], [0], [dnl
> >>>  ])
> >>>
> >>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> >> lr0-sw1.
> >>> -# This is not reachable so will not produce a lflow.
> >>> +# This is not reachable so will not produce a lflow. Also, it'll be
> >> removed by northd
> >>
> >> As the comment used to say, while the route is learned, ovn-northd
> >> doesn't actually produce logical flows for it because the next hop is
> >> not reachable on any network configured on lr0-sw1.
> >>
> >> So yes, the route is in SB.Learned_Route, but it doesn't affect routing
> >> in any way.  It's exactly the same as configuring a NB Static_Route with
> >> a wrong next hop.
> >>
> >> Moreover, your code removes the Learned_Route in northd.  But
> >> Learned_Routes are fully managed (created & deleted) by ovn-controller.
> >>
> >> So next time ovn-controller runs it will just relearn the route
> >> (re-create the SB.Learned_Route record).  So this only creates
> >> unnecessary churn as far as I understand.
> >>
> >> Or is there a use case I missed?
> >>
> >>
> > You're right, it's more appropriate to add the code in ovn controller
> > and northd will not generate flows. However, I would like to avoid the
> > garbage on the southbound.
> >
>
> I'm still not sure what "garbage" we're avoiding.  The router learns a
> "dynamic" route from a routing protocol (like any traditional router).
> Then there's some logic (in northd in our case) that decides whether
> that route should be used for actual traffic forwarding (i.e., is
> installed in the forwarding table in traditional routers).
>
> If the port it's learnt on doesn't have an IP in a subnet that includes
> the next hop, northd doesn't "install" the route (as logical flows).
>
> Also, I don't think you can move the check to ovn-controller.  We
> already check for LRP subnets that match the next hop in ovn-northd.  We
> don't want to duplicate that logic to ovn-controller IMO.
>
>
Hi Dumitru,
By "garbage" I mean useless entries that are just increasing the size of
southbound.


Regards,
Lucas


> Regards,
> Dumitru
>
> >
> > Regards,
> > Lucas
> >
> >
> >> Regards,
> >> Dumitru
> >>
> >>
> >>>  check_uuid ovn-sbctl create Learned_Route \
> >>>      datapath=$datapath                    \
> >>>      logical_port=$sw1                     \
> >>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
> >>>      nexthop=\"2001:db8:ffff::20\"
> >>>  check ovn-nbctl --wait=sb sync
> >>>  check_row_count Advertised_Route 4
> >>> -check_row_count Learned_Route 2
> >>> +check_row_count Learned_Route 1
> >>> +check_row_count Learned_Route 0 logical_port=$sw1
> >> ip_prefix=\"2001:db8:2::/64\"
> >>> +
> >>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
> [0],
> >> [dnl
> >>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
> >> action=(drop;)
> >>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows
> |
> >> ovn_strip_lflows], [0], [dnl
> >>>  # active.
> >>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
> >>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
> >>> +
> >>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> >> lr0-sw1.
> >>> +# Northd doesn not remove now because lrp have address in same subnet
> >> from nexthop
> >>> +check_uuid ovn-sbctl create Learned_Route \
> >>> +    datapath=$datapath                    \
> >>> +    logical_port=$sw1                     \
> >>> +    ip_prefix=\"2001:db8:2::/64\"         \
> >>> +    nexthop=\"2001:db8:ffff::20\"
> >>>  check_row_count Advertised_Route 5
> >>>  check_row_count Learned_Route 2
> >>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> >>> index 1cbbdfa58..6e87fb266 100644
> >>> --- a/tests/system-ovn.at
> >>> +++ b/tests/system-ovn.at
> >>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
> >>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>        options:dynamic-routing-port-name=mylearninglsp
> >>>
> >>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 30 proto zebra
> >>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 40 proto zebra
> >>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 30 proto zebra
> >>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 40 proto zebra
> >>>  check ovn-nbctl --wait=hv sync
> >>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >> nexthop=192.168.20.20
> >>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >> nexthop=192.168.10.20
> >>>
> >>>  # Stopping the ovn-controller will clean up the route entries created
> >> by it.
> >>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
> >> will
> >>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
> >> Logical_Router_Port internet-phys \
> >>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Starting it again will add the routes again.
> >>>  start_daemon ovn-controller
> >>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Changing the vrf name will switch to the new one.
> >>>  # The old vrf will be removed.
> >>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Stopping with --restart will not touch the routes.
> >>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # When we now stop the ovn-controller it will remove the VRF.
> >>>  start_daemon ovn-controller
> >>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Bind "vip" port locally and check the virtual IP is added in the
> VRF.
> >>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
> >>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
> >>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Remove the backoff period, so we can bind it right away.
> >>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Simulate "vip" bound to a different chassis.
> >>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Check with dynamic-routing-redistribute-local-only=false.
> >>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
> >>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>  blackhole 192.0.2.30 proto ovn metric 1000
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Remove the backoff period, so we can bind it right away.
> >>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
> >>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
> >> 192.168.10.10 dev lo onlink vrf ovnvrf1337
> >>>  check ovn-nbctl --wait=hv sync
> >>>  # 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
> >>> +check_row_count Learned_Route 1
> >>>  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
> >>>
> >>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
> >>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>      options:dynamic-routing-port-name=mylearninglsp
> >>>
> >>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 30 proto zebra
> >>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 40 proto zebra
> >>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 30 proto zebra
> >>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> onlink
> >> vrf ovnvrf1337 metric 40 proto zebra
> >>>  check ovn-nbctl --wait=hv sync
> >>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >> nexthop=192.168.20.20
> >>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >> nexthop=192.168.10.20
> >>>
> >>>  # Stopping the ovn-controller will clean up the route entries created
> >> by it.
> >>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
> >> will
> >>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
> >> Logical_Router_Port internet-phys \
> >>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Starting it again will add the routes again.
> >>>  start_daemon ovn-controller
> >>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Stopping with --restart will not touch the routes.
> >>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >> onlink
> >>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >> onlink])
> >>>
> >>>  # Now we set maintain-vrf again and stop the ovn-controller.
> >>>  # It will then remove the VRF.
> >>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
> >>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
> lrp2])
> >>>  check ovn-nbctl --wait=hv \
> >>>      remove logical_router_port lrp2 options dynamic-routing-port-name
> >>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
> >>>                                  nexthop=2.2.2.2      \
> >>>                                  logical_port=$lrp1
> >>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>>                                  nexthop=2.2.2.2      \
> >>>                                  logical_port=$lrp2
> >>> -check_row_count Learned_Route 2
> >>> +check_row_count Learned_Route 1
> >>>
> >>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>
> >>
> >>
> >
>
>
Dumitru Ceara Jan. 5, 2026, 11:41 a.m. UTC | #5
On 1/2/26 6:29 PM, Lucas Vargas Dias wrote:
> Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
>>> Hi Dumitru,
>>
>> Hi Lucas,
>>
>>> thanks for the review.
>>>
>>> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com>
>>> escreveu:
>>>
>>>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
>>>>> Learned routes must have the nexthop reachable. So, if logical router
>>>>> has two ports in differents subnets, route must be learned in just
>>>>> one which is the port in the same subnet from nexthop.
>>>>>
>>>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
>>>>> ---
>>>>
>>>> Hi Lucas,
>>>>
>>>> Thanks for the patch!
>>>>
>>>> I'm not sure I understand the problem though, please see below.
>>>>
>>>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
>>>>>  tests/ovn-northd.at            | 14 ++++++-
>>>>>  tests/system-ovn.at            | 74
>> +++++++++++++++++-----------------
>>>>>  3 files changed, 93 insertions(+), 39 deletions(-)
>>>>>
>>>>> diff --git a/northd/en-learned-route-sync.c
>>>> b/northd/en-learned-route-sync.c
>>>>> index f22aaa664..4c9de8651 100644
>>>>> --- a/northd/en-learned-route-sync.c
>>>>> +++ b/northd/en-learned-route-sync.c
>>>>> @@ -227,6 +227,50 @@ routes_table_sync(
>>>>>              sbrec_learned_route_delete(sb_route);
>>>>>              continue;
>>>>>          }
>>>>> +        bool is_same_subnet = false;
>>>>> +        for (size_t i = 0; !is_same_subnet &&
>>>>> +             i < sb_route->logical_port->n_mac;
>>>>> +             i++) {
>>>>> +            struct lport_addresses logical_port_addrs;
>>>>> +            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
>>>>> +                                       &logical_port_addrs)) {
>>>>> +                destroy_lport_addresses(&logical_port_addrs);
>>>>> +                continue;
>>>>> +            }
>>>>> +            ovs_be32 neigh_prefix_v4;
>>>>> +            struct in6_addr neigh_prefix_v6;
>>>>> +
>>>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
>>>>> +                for (size_t j = 0; j <
>> logical_port_addrs.n_ipv4_addrs;
>>>>> +                     j++) {
>>>>> +                    struct ipv4_netaddr address =
>>>>> +                        logical_port_addrs.ipv4_addrs[j];
>>>>> +                    if (address.network ==
>>>>> +                        (neigh_prefix_v4 & address.mask)) {
>>>>> +                        is_same_subnet = true;
>>>>> +                        break;
>>>>> +                    }
>>>>> +                }
>>>>> +            } else if (ipv6_parse(sb_route->nexthop,
>> &neigh_prefix_v6))
>>>> {
>>>>> +                for (size_t j = 0; j <
>> logical_port_addrs.n_ipv6_addrs;
>>>> j++) {
>>>>> +                    struct ipv6_netaddr address =
>>>>> +                        logical_port_addrs.ipv6_addrs[j];
>>>>> +                    struct in6_addr neigh_prefix =
>>>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
>>>> &address.mask);
>>>>> +                    if (ipv6_addr_equals(&address.network,
>>>> &neigh_prefix)) {
>>>>> +                        is_same_subnet = true;
>>>>> +                        break;
>>>>> +                    }
>>>>> +                }
>>>>> +            }
>>>>> +            destroy_lport_addresses(&logical_port_addrs);
>>>>> +        }
>>>>> +
>>>>> +
>>>>> +        if (!is_same_subnet) {
>>>>> +            sbrec_learned_route_delete(sb_route);
>>>>> +            continue;
>>>>> +        }
>>>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
>>>>>                                       &lr_datapaths->datapaths,
>>>>>                                       sb_route);
>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>>> index 864854c56..0d1d06196 100644
>>>>> --- a/tests/ovn-northd.at
>>>>> +++ b/tests/ovn-northd.at
>>>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
>>>> ovn_strip_lflows], [0], [dnl
>>>>>  ])
>>>>>
>>>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>> lr0-sw1.
>>>>> -# This is not reachable so will not produce a lflow.
>>>>> +# This is not reachable so will not produce a lflow. Also, it'll be
>>>> removed by northd
>>>>
>>>> As the comment used to say, while the route is learned, ovn-northd
>>>> doesn't actually produce logical flows for it because the next hop is
>>>> not reachable on any network configured on lr0-sw1.
>>>>
>>>> So yes, the route is in SB.Learned_Route, but it doesn't affect routing
>>>> in any way.  It's exactly the same as configuring a NB Static_Route with
>>>> a wrong next hop.
>>>>
>>>> Moreover, your code removes the Learned_Route in northd.  But
>>>> Learned_Routes are fully managed (created & deleted) by ovn-controller.
>>>>
>>>> So next time ovn-controller runs it will just relearn the route
>>>> (re-create the SB.Learned_Route record).  So this only creates
>>>> unnecessary churn as far as I understand.
>>>>
>>>> Or is there a use case I missed?
>>>>
>>>>
>>> You're right, it's more appropriate to add the code in ovn controller
>>> and northd will not generate flows. However, I would like to avoid the
>>> garbage on the southbound.
>>>
>>
>> I'm still not sure what "garbage" we're avoiding.  The router learns a
>> "dynamic" route from a routing protocol (like any traditional router).
>> Then there's some logic (in northd in our case) that decides whether
>> that route should be used for actual traffic forwarding (i.e., is
>> installed in the forwarding table in traditional routers).
>>
>> If the port it's learnt on doesn't have an IP in a subnet that includes
>> the next hop, northd doesn't "install" the route (as logical flows).
>>
>> Also, I don't think you can move the check to ovn-controller.  We
>> already check for LRP subnets that match the next hop in ovn-northd.  We
>> don't want to duplicate that logic to ovn-controller IMO.
>>
>>
> Hi Dumitru,
> By "garbage" I mean useless entries that are just increasing the size of
> southbound.
> 

Hi Lucas,

But do we really expect a lot of routes to be learned via next hops that
are not reachable locally?  That sounds like an unusual routing
configuration.

Regards,
Dumitru

> 
> Regards,
> Lucas
> 
> 
>> Regards,
>> Dumitru
>>
>>>
>>> Regards,
>>> Lucas
>>>
>>>
>>>> Regards,
>>>> Dumitru
>>>>
>>>>
>>>>>  check_uuid ovn-sbctl create Learned_Route \
>>>>>      datapath=$datapath                    \
>>>>>      logical_port=$sw1                     \
>>>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
>>>>>      nexthop=\"2001:db8:ffff::20\"
>>>>>  check ovn-nbctl --wait=sb sync
>>>>>  check_row_count Advertised_Route 4
>>>>> -check_row_count Learned_Route 2
>>>>> +check_row_count Learned_Route 1
>>>>> +check_row_count Learned_Route 0 logical_port=$sw1
>>>> ip_prefix=\"2001:db8:2::/64\"
>>>>> +
>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
>> [0],
>>>> [dnl
>>>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
>>>> action=(drop;)
>>>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows
>> |
>>>> ovn_strip_lflows], [0], [dnl
>>>>>  # active.
>>>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
>>>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
>>>>> +
>>>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>> lr0-sw1.
>>>>> +# Northd doesn not remove now because lrp have address in same subnet
>>>> from nexthop
>>>>> +check_uuid ovn-sbctl create Learned_Route \
>>>>> +    datapath=$datapath                    \
>>>>> +    logical_port=$sw1                     \
>>>>> +    ip_prefix=\"2001:db8:2::/64\"         \
>>>>> +    nexthop=\"2001:db8:ffff::20\"
>>>>>  check_row_count Advertised_Route 5
>>>>>  check_row_count Learned_Route 2
>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>>>> index 1cbbdfa58..6e87fb266 100644
>>>>> --- a/tests/system-ovn.at
>>>>> +++ b/tests/system-ovn.at
>>>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>        options:dynamic-routing-port-name=mylearninglsp
>>>>>
>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>  check ovn-nbctl --wait=hv sync
>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>> nexthop=192.168.20.20
>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>> nexthop=192.168.10.20
>>>>>
>>>>>  # Stopping the ovn-controller will clean up the route entries created
>>>> by it.
>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
>>>> will
>>>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
>>>> Logical_Router_Port internet-phys \
>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Starting it again will add the routes again.
>>>>>  start_daemon ovn-controller
>>>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Changing the vrf name will switch to the new one.
>>>>>  # The old vrf will be removed.
>>>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Stopping with --restart will not touch the routes.
>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # When we now stop the ovn-controller it will remove the VRF.
>>>>>  start_daemon ovn-controller
>>>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Bind "vip" port locally and check the virtual IP is added in the
>> VRF.
>>>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
>>>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
>>>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Simulate "vip" bound to a different chassis.
>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Check with dynamic-routing-redistribute-local-only=false.
>>>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
>>>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>  blackhole 192.0.2.30 proto ovn metric 1000
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
>>>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
>>>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
>>>>>  check ovn-nbctl --wait=hv sync
>>>>>  # 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
>>>>> +check_row_count Learned_Route 1
>>>>>  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
>>>>>
>>>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>      options:dynamic-routing-port-name=mylearninglsp
>>>>>
>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>> onlink
>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>  check ovn-nbctl --wait=hv sync
>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>> nexthop=192.168.20.20
>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>> nexthop=192.168.10.20
>>>>>
>>>>>  # Stopping the ovn-controller will clean up the route entries created
>>>> by it.
>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise it
>>>> will
>>>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
>>>> Logical_Router_Port internet-phys \
>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Starting it again will add the routes again.
>>>>>  start_daemon ovn-controller
>>>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Stopping with --restart will not touch the routes.
>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>> onlink
>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>> onlink])
>>>>>
>>>>>  # Now we set maintain-vrf again and stop the ovn-controller.
>>>>>  # It will then remove the VRF.
>>>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
>>>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
>> lrp2])
>>>>>  check ovn-nbctl --wait=hv \
>>>>>      remove logical_router_port lrp2 options dynamic-routing-port-name
>>>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
>>>>>                                  nexthop=2.2.2.2      \
>>>>>                                  logical_port=$lrp1
>>>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>>                                  nexthop=2.2.2.2      \
>>>>>                                  logical_port=$lrp2
>>>>> -check_row_count Learned_Route 2
>>>>> +check_row_count Learned_Route 1
>>>>>
>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>
>>>>
>>>>
>>>
>>
>>
>
Lucas Vargas Dias Jan. 5, 2026, 5:50 p.m. UTC | #6
Hi Dumitru,


I'm testing a scenario with a logical router that has a logical_router_port
used to ovn-ic and another logical router port used to DR.
I'm learning in all logical router ports, I would like to learn just in
logical router port from DR.
Follow an example of configuration:

ovn-nbctl -- lr-add dcx1
# LRP used to Dynamic routing
ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
lrp-set-options rp-public
ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
ovn-nbctl set logical_router dcx1
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocols=BGP,BFD
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocol-redirect=bgpvrf1002
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-maintain-vrf=true
ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4

# LRPs used to ovn-ic
ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"


ovn-sbctl list learned_route
_uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
external_ids        : {}
ip_prefix           : "10.0.0.0/24"
logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
nexthop             : "169.254.254.1"

_uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
external_ids        : {}
ip_prefix           : "10.0.0.0/24"
logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
nexthop             : "169.254.254.1"


ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
_uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
additional_chassis  : []
additional_encap    : []
chassis             : []
datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
encap               : []
external_ids        : {}
gateway_chassis     : []
ha_chassis_group    : []
logical_port        : ts1001-dcx1
mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
mirror_port         : []
mirror_rules        : []
nat_addresses       : []
options             : {chassis-redirect-port=cr-ts1001-dcx1,
peer=ts1001-dcx1-rp}
parent_port         : []
port_security       : []
requested_additional_chassis: []
requested_chassis   : []
tag                 : []
tunnel_key          : 3
type                : patch
up                  : false
virtual_parent      : []



ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
_uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
additional_chassis  : []
additional_encap    : []
chassis             : []
datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
encap               : []
external_ids        : {}
gateway_chassis     : []
ha_chassis_group    : []
logical_port        : rp-public
mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
mirror_port         : []
mirror_rules        : []
nat_addresses       : []
options             : {chassis-redirect-port=cr-rp-public, peer=public-rp}
parent_port         : []
port_security       : []
requested_additional_chassis: []
requested_chassis   : []
tag                 : []
tunnel_key          : 1
type                : patch
up                  : false
virtual_parent      : []



Regards,
Lucas


Em seg., 5 de jan. de 2026 às 08:41, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 1/2/26 6:29 PM, Lucas Vargas Dias wrote:
> > Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com>
> > escreveu:
> >
> >> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
> >>> Hi Dumitru,
> >>
> >> Hi Lucas,
> >>
> >>> thanks for the review.
> >>>
> >>> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com
> >
> >>> escreveu:
> >>>
> >>>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
> >>>>> Learned routes must have the nexthop reachable. So, if logical router
> >>>>> has two ports in differents subnets, route must be learned in just
> >>>>> one which is the port in the same subnet from nexthop.
> >>>>>
> >>>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
> >>>>> ---
> >>>>
> >>>> Hi Lucas,
> >>>>
> >>>> Thanks for the patch!
> >>>>
> >>>> I'm not sure I understand the problem though, please see below.
> >>>>
> >>>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
> >>>>>  tests/ovn-northd.at            | 14 ++++++-
> >>>>>  tests/system-ovn.at            | 74
> >> +++++++++++++++++-----------------
> >>>>>  3 files changed, 93 insertions(+), 39 deletions(-)
> >>>>>
> >>>>> diff --git a/northd/en-learned-route-sync.c
> >>>> b/northd/en-learned-route-sync.c
> >>>>> index f22aaa664..4c9de8651 100644
> >>>>> --- a/northd/en-learned-route-sync.c
> >>>>> +++ b/northd/en-learned-route-sync.c
> >>>>> @@ -227,6 +227,50 @@ routes_table_sync(
> >>>>>              sbrec_learned_route_delete(sb_route);
> >>>>>              continue;
> >>>>>          }
> >>>>> +        bool is_same_subnet = false;
> >>>>> +        for (size_t i = 0; !is_same_subnet &&
> >>>>> +             i < sb_route->logical_port->n_mac;
> >>>>> +             i++) {
> >>>>> +            struct lport_addresses logical_port_addrs;
> >>>>> +            if
> (!extract_lsp_addresses(sb_route->logical_port->mac[i],
> >>>>> +                                       &logical_port_addrs)) {
> >>>>> +                destroy_lport_addresses(&logical_port_addrs);
> >>>>> +                continue;
> >>>>> +            }
> >>>>> +            ovs_be32 neigh_prefix_v4;
> >>>>> +            struct in6_addr neigh_prefix_v6;
> >>>>> +
> >>>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
> >>>>> +                for (size_t j = 0; j <
> >> logical_port_addrs.n_ipv4_addrs;
> >>>>> +                     j++) {
> >>>>> +                    struct ipv4_netaddr address =
> >>>>> +                        logical_port_addrs.ipv4_addrs[j];
> >>>>> +                    if (address.network ==
> >>>>> +                        (neigh_prefix_v4 & address.mask)) {
> >>>>> +                        is_same_subnet = true;
> >>>>> +                        break;
> >>>>> +                    }
> >>>>> +                }
> >>>>> +            } else if (ipv6_parse(sb_route->nexthop,
> >> &neigh_prefix_v6))
> >>>> {
> >>>>> +                for (size_t j = 0; j <
> >> logical_port_addrs.n_ipv6_addrs;
> >>>> j++) {
> >>>>> +                    struct ipv6_netaddr address =
> >>>>> +                        logical_port_addrs.ipv6_addrs[j];
> >>>>> +                    struct in6_addr neigh_prefix =
> >>>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
> >>>> &address.mask);
> >>>>> +                    if (ipv6_addr_equals(&address.network,
> >>>> &neigh_prefix)) {
> >>>>> +                        is_same_subnet = true;
> >>>>> +                        break;
> >>>>> +                    }
> >>>>> +                }
> >>>>> +            }
> >>>>> +            destroy_lport_addresses(&logical_port_addrs);
> >>>>> +        }
> >>>>> +
> >>>>> +
> >>>>> +        if (!is_same_subnet) {
> >>>>> +            sbrec_learned_route_delete(sb_route);
> >>>>> +            continue;
> >>>>> +        }
> >>>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
> >>>>>                                       &lr_datapaths->datapaths,
> >>>>>                                       sb_route);
> >>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> >>>>> index 864854c56..0d1d06196 100644
> >>>>> --- a/tests/ovn-northd.at
> >>>>> +++ b/tests/ovn-northd.at
> >>>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing"
> lr0flows |
> >>>> ovn_strip_lflows], [0], [dnl
> >>>>>  ])
> >>>>>
> >>>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> >>>> lr0-sw1.
> >>>>> -# This is not reachable so will not produce a lflow.
> >>>>> +# This is not reachable so will not produce a lflow. Also, it'll be
> >>>> removed by northd
> >>>>
> >>>> As the comment used to say, while the route is learned, ovn-northd
> >>>> doesn't actually produce logical flows for it because the next hop is
> >>>> not reachable on any network configured on lr0-sw1.
> >>>>
> >>>> So yes, the route is in SB.Learned_Route, but it doesn't affect
> routing
> >>>> in any way.  It's exactly the same as configuring a NB Static_Route
> with
> >>>> a wrong next hop.
> >>>>
> >>>> Moreover, your code removes the Learned_Route in northd.  But
> >>>> Learned_Routes are fully managed (created & deleted) by
> ovn-controller.
> >>>>
> >>>> So next time ovn-controller runs it will just relearn the route
> >>>> (re-create the SB.Learned_Route record).  So this only creates
> >>>> unnecessary churn as far as I understand.
> >>>>
> >>>> Or is there a use case I missed?
> >>>>
> >>>>
> >>> You're right, it's more appropriate to add the code in ovn controller
> >>> and northd will not generate flows. However, I would like to avoid the
> >>> garbage on the southbound.
> >>>
> >>
> >> I'm still not sure what "garbage" we're avoiding.  The router learns a
> >> "dynamic" route from a routing protocol (like any traditional router).
> >> Then there's some logic (in northd in our case) that decides whether
> >> that route should be used for actual traffic forwarding (i.e., is
> >> installed in the forwarding table in traditional routers).
> >>
> >> If the port it's learnt on doesn't have an IP in a subnet that includes
> >> the next hop, northd doesn't "install" the route (as logical flows).
> >>
> >> Also, I don't think you can move the check to ovn-controller.  We
> >> already check for LRP subnets that match the next hop in ovn-northd.  We
> >> don't want to duplicate that logic to ovn-controller IMO.
> >>
> >>
> > Hi Dumitru,
> > By "garbage" I mean useless entries that are just increasing the size of
> > southbound.
> >
>
> Hi Lucas,
>
> But do we really expect a lot of routes to be learned via next hops that
> are not reachable locally?  That sounds like an unusual routing
> configuration.
>
> Regards,
> Dumitru
>
> >
> > Regards,
> > Lucas
> >
> >
> >> Regards,
> >> Dumitru
> >>
> >>>
> >>> Regards,
> >>> Lucas
> >>>
> >>>
> >>>> Regards,
> >>>> Dumitru
> >>>>
> >>>>
> >>>>>  check_uuid ovn-sbctl create Learned_Route \
> >>>>>      datapath=$datapath                    \
> >>>>>      logical_port=$sw1                     \
> >>>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
> >>>>>      nexthop=\"2001:db8:ffff::20\"
> >>>>>  check ovn-nbctl --wait=sb sync
> >>>>>  check_row_count Advertised_Route 4
> >>>>> -check_row_count Learned_Route 2
> >>>>> +check_row_count Learned_Route 1
> >>>>> +check_row_count Learned_Route 0 logical_port=$sw1
> >>>> ip_prefix=\"2001:db8:2::/64\"
> >>>>> +
> >>>>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
> >> [0],
> >>>> [dnl
> >>>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
> >>>> action=(drop;)
> >>>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing"
> lr0flows
> >> |
> >>>> ovn_strip_lflows], [0], [dnl
> >>>>>  # active.
> >>>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
> >>>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
> >>>>> +
> >>>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
> >>>> lr0-sw1.
> >>>>> +# Northd doesn not remove now because lrp have address in same
> subnet
> >>>> from nexthop
> >>>>> +check_uuid ovn-sbctl create Learned_Route \
> >>>>> +    datapath=$datapath                    \
> >>>>> +    logical_port=$sw1                     \
> >>>>> +    ip_prefix=\"2001:db8:2::/64\"         \
> >>>>> +    nexthop=\"2001:db8:ffff::20\"
> >>>>>  check_row_count Advertised_Route 5
> >>>>>  check_row_count Learned_Route 2
> >>>>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> >>>>> index 1cbbdfa58..6e87fb266 100644
> >>>>> --- a/tests/system-ovn.at
> >>>>> +++ b/tests/system-ovn.at
> >>>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
> >>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>>>        options:dynamic-routing-port-name=mylearninglsp
> >>>>>
> >>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>  check ovn-nbctl --wait=hv sync
> >>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>> nexthop=192.168.20.20
> >>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>> nexthop=192.168.10.20
> >>>>>
> >>>>>  # Stopping the ovn-controller will clean up the route entries
> created
> >>>> by it.
> >>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
> it
> >>>> will
> >>>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
> >>>> Logical_Router_Port internet-phys \
> >>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Starting it again will add the routes again.
> >>>>>  start_daemon ovn-controller
> >>>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Changing the vrf name will switch to the new one.
> >>>>>  # The old vrf will be removed.
> >>>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Stopping with --restart will not touch the routes.
> >>>>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # When we now stop the ovn-controller it will remove the VRF.
> >>>>>  start_daemon ovn-controller
> >>>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Bind "vip" port locally and check the virtual IP is added in the
> >> VRF.
> >>>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
> >>>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
> >>>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Remove the backoff period, so we can bind it right away.
> >>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Simulate "vip" bound to a different chassis.
> >>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Check with dynamic-routing-redistribute-local-only=false.
> >>>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
> >>>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>>>  blackhole 192.0.2.30 proto ovn metric 1000
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Remove the backoff period, so we can bind it right away.
> >>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
> >>>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
> >>>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
> >>>>>  check ovn-nbctl --wait=hv sync
> >>>>>  # 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
> >>>>> +check_row_count Learned_Route 1
> >>>>>  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
> >>>>>
> >>>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
> >>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>>>      options:dynamic-routing-port-name=mylearninglsp
> >>>>>
> >>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >> onlink
> >>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>  check ovn-nbctl --wait=hv sync
> >>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>> nexthop=192.168.20.20
> >>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>> nexthop=192.168.10.20
> >>>>>
> >>>>>  # Stopping the ovn-controller will clean up the route entries
> created
> >>>> by it.
> >>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
> it
> >>>> will
> >>>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
> >>>> Logical_Router_Port internet-phys \
> >>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Starting it again will add the routes again.
> >>>>>  start_daemon ovn-controller
> >>>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Stopping with --restart will not touch the routes.
> >>>>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
> >>>> onlink
> >>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
> >>>> onlink])
> >>>>>
> >>>>>  # Now we set maintain-vrf again and stop the ovn-controller.
> >>>>>  # It will then remove the VRF.
> >>>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
> >>>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
> >> lrp2])
> >>>>>  check ovn-nbctl --wait=hv \
> >>>>>      remove logical_router_port lrp2 options
> dynamic-routing-port-name
> >>>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
> >>>>>                                  nexthop=2.2.2.2      \
> >>>>>                                  logical_port=$lrp1
> >>>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>>>>                                  nexthop=2.2.2.2      \
> >>>>>                                  logical_port=$lrp2
> >>>>> -check_row_count Learned_Route 2
> >>>>> +check_row_count Learned_Route 1
> >>>>>
> >>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >
>
>
Dumitru Ceara Jan. 6, 2026, 4:39 p.m. UTC | #7
On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
> Hi Dumitru,
> 

Hi Lucas,

> 
> I'm testing a scenario with a logical router that has a logical_router_port
> used to ovn-ic and another logical router port used to DR.
> I'm learning in all logical router ports, I would like to learn just in
> logical router port from DR.
> Follow an example of configuration:
> 

Thanks for the sample config!

> ovn-nbctl -- lr-add dcx1
> # LRP used to Dynamic routing
> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
> lrp-set-options rp-public
> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002

Btw, there's a better way to configure the VRF ID:

ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
Relying on the requested-tnl-key is not that great, I'd recommend using
the new option from now on.

> ovn-nbctl set logical_router dcx1
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocols=BGP,BFD
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocol-redirect=bgpvrf1002
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-maintain-vrf=true
> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> 
> # LRPs used to ovn-ic
> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"
> 
> 
> ovn-sbctl list learned_route
> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> external_ids        : {}
> ip_prefix           : "10.0.0.0/24"
> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> nexthop             : "169.254.254.1"
> 
> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> external_ids        : {}
> ip_prefix           : "10.0.0.0/24"
> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
> nexthop             : "169.254.254.1"
> 
> 
> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
> additional_chassis  : []
> additional_encap    : []
> chassis             : []
> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> encap               : []
> external_ids        : {}
> gateway_chassis     : []
> ha_chassis_group    : []
> logical_port        : ts1001-dcx1
> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
> mirror_port         : []
> mirror_rules        : []
> nat_addresses       : []
> options             : {chassis-redirect-port=cr-ts1001-dcx1,
> peer=ts1001-dcx1-rp}
> parent_port         : []
> port_security       : []
> requested_additional_chassis: []
> requested_chassis   : []
> tag                 : []
> tunnel_key          : 3
> type                : patch
> up                  : false
> virtual_parent      : []
> 
> 
> 
> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> additional_chassis  : []
> additional_encap    : []
> chassis             : []
> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> encap               : []
> external_ids        : {}
> gateway_chassis     : []
> ha_chassis_group    : []
> logical_port        : rp-public
> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
> mirror_port         : []
> mirror_rules        : []
> nat_addresses       : []
> options             : {chassis-redirect-port=cr-rp-public, peer=public-rp}
> parent_port         : []
> port_security       : []
> requested_additional_chassis: []
> requested_chassis   : []
> tag                 : []
> tunnel_key          : 1
> type                : patch
> up                  : false
> virtual_parent      : []
> 
> 

I think this looks similar to the use case Felix had when he added the
LRP.options:dynamic-routing-port-name support:

https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541

Without that config option ovn-controller learns routes on all DGPs, in
your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
happens to be ovn-chassis-4).

As you always bind rp-public to ovn-chassis-4 you could also set:

ovn-nbctl set logical_router_port rp-public
options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>

e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).

Then ovn-controller will only learn routes with logical_port=rp-public

Would that work for you?

Regards,
Dumitru

> 
> Regards,
> Lucas
> 
> 
> Em seg., 5 de jan. de 2026 às 08:41, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 1/2/26 6:29 PM, Lucas Vargas Dias wrote:
>>> Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com>
>>> escreveu:
>>>
>>>> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
>>>>> Hi Dumitru,
>>>>
>>>> Hi Lucas,
>>>>
>>>>> thanks for the review.
>>>>>
>>>>> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com
>>>
>>>>> escreveu:
>>>>>
>>>>>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
>>>>>>> Learned routes must have the nexthop reachable. So, if logical router
>>>>>>> has two ports in differents subnets, route must be learned in just
>>>>>>> one which is the port in the same subnet from nexthop.
>>>>>>>
>>>>>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
>>>>>>> ---
>>>>>>
>>>>>> Hi Lucas,
>>>>>>
>>>>>> Thanks for the patch!
>>>>>>
>>>>>> I'm not sure I understand the problem though, please see below.
>>>>>>
>>>>>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
>>>>>>>  tests/ovn-northd.at            | 14 ++++++-
>>>>>>>  tests/system-ovn.at            | 74
>>>> +++++++++++++++++-----------------
>>>>>>>  3 files changed, 93 insertions(+), 39 deletions(-)
>>>>>>>
>>>>>>> diff --git a/northd/en-learned-route-sync.c
>>>>>> b/northd/en-learned-route-sync.c
>>>>>>> index f22aaa664..4c9de8651 100644
>>>>>>> --- a/northd/en-learned-route-sync.c
>>>>>>> +++ b/northd/en-learned-route-sync.c
>>>>>>> @@ -227,6 +227,50 @@ routes_table_sync(
>>>>>>>              sbrec_learned_route_delete(sb_route);
>>>>>>>              continue;
>>>>>>>          }
>>>>>>> +        bool is_same_subnet = false;
>>>>>>> +        for (size_t i = 0; !is_same_subnet &&
>>>>>>> +             i < sb_route->logical_port->n_mac;
>>>>>>> +             i++) {
>>>>>>> +            struct lport_addresses logical_port_addrs;
>>>>>>> +            if
>> (!extract_lsp_addresses(sb_route->logical_port->mac[i],
>>>>>>> +                                       &logical_port_addrs)) {
>>>>>>> +                destroy_lport_addresses(&logical_port_addrs);
>>>>>>> +                continue;
>>>>>>> +            }
>>>>>>> +            ovs_be32 neigh_prefix_v4;
>>>>>>> +            struct in6_addr neigh_prefix_v6;
>>>>>>> +
>>>>>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
>>>>>>> +                for (size_t j = 0; j <
>>>> logical_port_addrs.n_ipv4_addrs;
>>>>>>> +                     j++) {
>>>>>>> +                    struct ipv4_netaddr address =
>>>>>>> +                        logical_port_addrs.ipv4_addrs[j];
>>>>>>> +                    if (address.network ==
>>>>>>> +                        (neigh_prefix_v4 & address.mask)) {
>>>>>>> +                        is_same_subnet = true;
>>>>>>> +                        break;
>>>>>>> +                    }
>>>>>>> +                }
>>>>>>> +            } else if (ipv6_parse(sb_route->nexthop,
>>>> &neigh_prefix_v6))
>>>>>> {
>>>>>>> +                for (size_t j = 0; j <
>>>> logical_port_addrs.n_ipv6_addrs;
>>>>>> j++) {
>>>>>>> +                    struct ipv6_netaddr address =
>>>>>>> +                        logical_port_addrs.ipv6_addrs[j];
>>>>>>> +                    struct in6_addr neigh_prefix =
>>>>>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
>>>>>> &address.mask);
>>>>>>> +                    if (ipv6_addr_equals(&address.network,
>>>>>> &neigh_prefix)) {
>>>>>>> +                        is_same_subnet = true;
>>>>>>> +                        break;
>>>>>>> +                    }
>>>>>>> +                }
>>>>>>> +            }
>>>>>>> +            destroy_lport_addresses(&logical_port_addrs);
>>>>>>> +        }
>>>>>>> +
>>>>>>> +
>>>>>>> +        if (!is_same_subnet) {
>>>>>>> +            sbrec_learned_route_delete(sb_route);
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
>>>>>>>                                       &lr_datapaths->datapaths,
>>>>>>>                                       sb_route);
>>>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>>>>> index 864854c56..0d1d06196 100644
>>>>>>> --- a/tests/ovn-northd.at
>>>>>>> +++ b/tests/ovn-northd.at
>>>>>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing"
>> lr0flows |
>>>>>> ovn_strip_lflows], [0], [dnl
>>>>>>>  ])
>>>>>>>
>>>>>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>>>> lr0-sw1.
>>>>>>> -# This is not reachable so will not produce a lflow.
>>>>>>> +# This is not reachable so will not produce a lflow. Also, it'll be
>>>>>> removed by northd
>>>>>>
>>>>>> As the comment used to say, while the route is learned, ovn-northd
>>>>>> doesn't actually produce logical flows for it because the next hop is
>>>>>> not reachable on any network configured on lr0-sw1.
>>>>>>
>>>>>> So yes, the route is in SB.Learned_Route, but it doesn't affect
>> routing
>>>>>> in any way.  It's exactly the same as configuring a NB Static_Route
>> with
>>>>>> a wrong next hop.
>>>>>>
>>>>>> Moreover, your code removes the Learned_Route in northd.  But
>>>>>> Learned_Routes are fully managed (created & deleted) by
>> ovn-controller.
>>>>>>
>>>>>> So next time ovn-controller runs it will just relearn the route
>>>>>> (re-create the SB.Learned_Route record).  So this only creates
>>>>>> unnecessary churn as far as I understand.
>>>>>>
>>>>>> Or is there a use case I missed?
>>>>>>
>>>>>>
>>>>> You're right, it's more appropriate to add the code in ovn controller
>>>>> and northd will not generate flows. However, I would like to avoid the
>>>>> garbage on the southbound.
>>>>>
>>>>
>>>> I'm still not sure what "garbage" we're avoiding.  The router learns a
>>>> "dynamic" route from a routing protocol (like any traditional router).
>>>> Then there's some logic (in northd in our case) that decides whether
>>>> that route should be used for actual traffic forwarding (i.e., is
>>>> installed in the forwarding table in traditional routers).
>>>>
>>>> If the port it's learnt on doesn't have an IP in a subnet that includes
>>>> the next hop, northd doesn't "install" the route (as logical flows).
>>>>
>>>> Also, I don't think you can move the check to ovn-controller.  We
>>>> already check for LRP subnets that match the next hop in ovn-northd.  We
>>>> don't want to duplicate that logic to ovn-controller IMO.
>>>>
>>>>
>>> Hi Dumitru,
>>> By "garbage" I mean useless entries that are just increasing the size of
>>> southbound.
>>>
>>
>> Hi Lucas,
>>
>> But do we really expect a lot of routes to be learned via next hops that
>> are not reachable locally?  That sounds like an unusual routing
>> configuration.
>>
>> Regards,
>> Dumitru
>>
>>>
>>> Regards,
>>> Lucas
>>>
>>>
>>>> Regards,
>>>> Dumitru
>>>>
>>>>>
>>>>> Regards,
>>>>> Lucas
>>>>>
>>>>>
>>>>>> Regards,
>>>>>> Dumitru
>>>>>>
>>>>>>
>>>>>>>  check_uuid ovn-sbctl create Learned_Route \
>>>>>>>      datapath=$datapath                    \
>>>>>>>      logical_port=$sw1                     \
>>>>>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
>>>>>>>      nexthop=\"2001:db8:ffff::20\"
>>>>>>>  check ovn-nbctl --wait=sb sync
>>>>>>>  check_row_count Advertised_Route 4
>>>>>>> -check_row_count Learned_Route 2
>>>>>>> +check_row_count Learned_Route 1
>>>>>>> +check_row_count Learned_Route 0 logical_port=$sw1
>>>>>> ip_prefix=\"2001:db8:2::/64\"
>>>>>>> +
>>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
>>>> [0],
>>>>>> [dnl
>>>>>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
>>>>>> action=(drop;)
>>>>>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing"
>> lr0flows
>>>> |
>>>>>> ovn_strip_lflows], [0], [dnl
>>>>>>>  # active.
>>>>>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
>>>>>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
>>>>>>> +
>>>>>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>>>> lr0-sw1.
>>>>>>> +# Northd doesn not remove now because lrp have address in same
>> subnet
>>>>>> from nexthop
>>>>>>> +check_uuid ovn-sbctl create Learned_Route \
>>>>>>> +    datapath=$datapath                    \
>>>>>>> +    logical_port=$sw1                     \
>>>>>>> +    ip_prefix=\"2001:db8:2::/64\"         \
>>>>>>> +    nexthop=\"2001:db8:ffff::20\"
>>>>>>>  check_row_count Advertised_Route 5
>>>>>>>  check_row_count Learned_Route 2
>>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>>>>>> index 1cbbdfa58..6e87fb266 100644
>>>>>>> --- a/tests/system-ovn.at
>>>>>>> +++ b/tests/system-ovn.at
>>>>>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
>>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>>>        options:dynamic-routing-port-name=mylearninglsp
>>>>>>>
>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>> nexthop=192.168.20.20
>>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>> nexthop=192.168.10.20
>>>>>>>
>>>>>>>  # Stopping the ovn-controller will clean up the route entries
>> created
>>>>>> by it.
>>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
>> it
>>>>>> will
>>>>>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
>>>>>> Logical_Router_Port internet-phys \
>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Starting it again will add the routes again.
>>>>>>>  start_daemon ovn-controller
>>>>>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Changing the vrf name will switch to the new one.
>>>>>>>  # The old vrf will be removed.
>>>>>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Stopping with --restart will not touch the routes.
>>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # When we now stop the ovn-controller it will remove the VRF.
>>>>>>>  start_daemon ovn-controller
>>>>>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Bind "vip" port locally and check the virtual IP is added in the
>>>> VRF.
>>>>>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
>>>>>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
>>>>>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Simulate "vip" bound to a different chassis.
>>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Check with dynamic-routing-redistribute-local-only=false.
>>>>>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
>>>>>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>>>  blackhole 192.0.2.30 proto ovn metric 1000
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
>>>>>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
>>>>>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>>  # 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
>>>>>>> +check_row_count Learned_Route 1
>>>>>>>  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
>>>>>>>
>>>>>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
>>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>>>      options:dynamic-routing-port-name=mylearninglsp
>>>>>>>
>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>> onlink
>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>> nexthop=192.168.20.20
>>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>> nexthop=192.168.10.20
>>>>>>>
>>>>>>>  # Stopping the ovn-controller will clean up the route entries
>> created
>>>>>> by it.
>>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
>> it
>>>>>> will
>>>>>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
>>>>>> Logical_Router_Port internet-phys \
>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Starting it again will add the routes again.
>>>>>>>  start_daemon ovn-controller
>>>>>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Stopping with --restart will not touch the routes.
>>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>> onlink
>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>> onlink])
>>>>>>>
>>>>>>>  # Now we set maintain-vrf again and stop the ovn-controller.
>>>>>>>  # It will then remove the VRF.
>>>>>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
>>>>>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
>>>> lrp2])
>>>>>>>  check ovn-nbctl --wait=hv \
>>>>>>>      remove logical_router_port lrp2 options
>> dynamic-routing-port-name
>>>>>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
>>>>>>>                                  nexthop=2.2.2.2      \
>>>>>>>                                  logical_port=$lrp1
>>>>>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>>>>                                  nexthop=2.2.2.2      \
>>>>>>>                                  logical_port=$lrp2
>>>>>>> -check_row_count Learned_Route 2
>>>>>>> +check_row_count Learned_Route 1
>>>>>>>
>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>
>>
>
Dumitru Ceara Jan. 7, 2026, 8:40 a.m. UTC | #8
Hi Lucas,

On 1/6/26 5:39 PM, Dumitru Ceara wrote:
> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
>> Hi Dumitru,
>>
> 
> Hi Lucas,
> 
>>
>> I'm testing a scenario with a logical router that has a logical_router_port
>> used to ovn-ic and another logical router port used to DR.
>> I'm learning in all logical router ports, I would like to learn just in
>> logical router port from DR.
>> Follow an example of configuration:
>>
> 
> Thanks for the sample config!
> 
>> ovn-nbctl -- lr-add dcx1
>> # LRP used to Dynamic routing
>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
>> lrp-set-options rp-public
>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
>> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
> 
> Btw, there's a better way to configure the VRF ID:
> 
> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
> Relying on the requested-tnl-key is not that great, I'd recommend using
> the new option from now on.
> 
>> ovn-nbctl set logical_router dcx1
>> options:dynamic-routing-redistribute="connected,static"
>> ovn-nbctl --wait=sb set logical_router_port rp-public
>> options:routing-protocols=BGP,BFD
>> ovn-nbctl --wait=sb set logical_router_port rp-public
>> options:routing-protocol-redirect=bgpvrf1002
>> ovn-nbctl --wait=sb set logical_router_port rp-public
>> options:dynamic-routing-redistribute="connected,static"
>> ovn-nbctl --wait=sb set logical_router_port rp-public
>> options:dynamic-routing-maintain-vrf=true
>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
>>
>> # LRPs used to ovn-ic
>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
>> ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"
>>
>>
>> ovn-sbctl list learned_route
>> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>> external_ids        : {}
>> ip_prefix           : "10.0.0.0/24"
>> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>> nexthop             : "169.254.254.1"
>>
>> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>> external_ids        : {}
>> ip_prefix           : "10.0.0.0/24"
>> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
>> nexthop             : "169.254.254.1"
>>
>>
>> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
>> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
>> additional_chassis  : []
>> additional_encap    : []
>> chassis             : []
>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>> encap               : []
>> external_ids        : {}
>> gateway_chassis     : []
>> ha_chassis_group    : []
>> logical_port        : ts1001-dcx1
>> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
>> mirror_port         : []
>> mirror_rules        : []
>> nat_addresses       : []
>> options             : {chassis-redirect-port=cr-ts1001-dcx1,
>> peer=ts1001-dcx1-rp}
>> parent_port         : []
>> port_security       : []
>> requested_additional_chassis: []
>> requested_chassis   : []
>> tag                 : []
>> tunnel_key          : 3
>> type                : patch
>> up                  : false
>> virtual_parent      : []
>>
>>
>>
>> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>> additional_chassis  : []
>> additional_encap    : []
>> chassis             : []
>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>> encap               : []
>> external_ids        : {}
>> gateway_chassis     : []
>> ha_chassis_group    : []
>> logical_port        : rp-public
>> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
>> mirror_port         : []
>> mirror_rules        : []
>> nat_addresses       : []
>> options             : {chassis-redirect-port=cr-rp-public, peer=public-rp}
>> parent_port         : []
>> port_security       : []
>> requested_additional_chassis: []
>> requested_chassis   : []
>> tag                 : []
>> tunnel_key          : 1
>> type                : patch
>> up                  : false
>> virtual_parent      : []
>>
>>
> 
> I think this looks similar to the use case Felix had when he added the
> LRP.options:dynamic-routing-port-name support:
> 
> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
> 
> Without that config option ovn-controller learns routes on all DGPs, in
> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
> ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
> happens to be ovn-chassis-4).
> 
> As you always bind rp-public to ovn-chassis-4 you could also set:
> 
> ovn-nbctl set logical_router_port rp-public
> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> 
> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
> 
> Then ovn-controller will only learn routes with logical_port=rp-public
> 
> Would that work for you?
> 

I also wanted to point out that we're working on adding support for
disabling dynamic route learning (potentially per router port).  CC-ing
Mairtin too as he's working on it:

https://issues.redhat.com/browse/FDP-2750

If the new configuration described in this issue above will be supported
both for routers and indvidually for router ports you could selectively
disable route learning on your other distributed gateway ports
(ts1001-dcx1).

At this moment, we're targeting adding support for this new feature in
the upcoming 26.03 release.

Regards,
Dumitru

> Regards,
> Dumitru
> 
>>
>> Regards,
>> Lucas
>>
>>
>> Em seg., 5 de jan. de 2026 às 08:41, Dumitru Ceara <dceara@redhat.com>
>> escreveu:
>>
>>> On 1/2/26 6:29 PM, Lucas Vargas Dias wrote:
>>>> Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com>
>>>> escreveu:
>>>>
>>>>> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
>>>>>> Hi Dumitru,
>>>>>
>>>>> Hi Lucas,
>>>>>
>>>>>> thanks for the review.
>>>>>>
>>>>>> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <dceara@redhat.com
>>>>
>>>>>> escreveu:
>>>>>>
>>>>>>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
>>>>>>>> Learned routes must have the nexthop reachable. So, if logical router
>>>>>>>> has two ports in differents subnets, route must be learned in just
>>>>>>>> one which is the port in the same subnet from nexthop.
>>>>>>>>
>>>>>>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
>>>>>>>> ---
>>>>>>>
>>>>>>> Hi Lucas,
>>>>>>>
>>>>>>> Thanks for the patch!
>>>>>>>
>>>>>>> I'm not sure I understand the problem though, please see below.
>>>>>>>
>>>>>>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
>>>>>>>>  tests/ovn-northd.at            | 14 ++++++-
>>>>>>>>  tests/system-ovn.at            | 74
>>>>> +++++++++++++++++-----------------
>>>>>>>>  3 files changed, 93 insertions(+), 39 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/northd/en-learned-route-sync.c
>>>>>>> b/northd/en-learned-route-sync.c
>>>>>>>> index f22aaa664..4c9de8651 100644
>>>>>>>> --- a/northd/en-learned-route-sync.c
>>>>>>>> +++ b/northd/en-learned-route-sync.c
>>>>>>>> @@ -227,6 +227,50 @@ routes_table_sync(
>>>>>>>>              sbrec_learned_route_delete(sb_route);
>>>>>>>>              continue;
>>>>>>>>          }
>>>>>>>> +        bool is_same_subnet = false;
>>>>>>>> +        for (size_t i = 0; !is_same_subnet &&
>>>>>>>> +             i < sb_route->logical_port->n_mac;
>>>>>>>> +             i++) {
>>>>>>>> +            struct lport_addresses logical_port_addrs;
>>>>>>>> +            if
>>> (!extract_lsp_addresses(sb_route->logical_port->mac[i],
>>>>>>>> +                                       &logical_port_addrs)) {
>>>>>>>> +                destroy_lport_addresses(&logical_port_addrs);
>>>>>>>> +                continue;
>>>>>>>> +            }
>>>>>>>> +            ovs_be32 neigh_prefix_v4;
>>>>>>>> +            struct in6_addr neigh_prefix_v6;
>>>>>>>> +
>>>>>>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
>>>>>>>> +                for (size_t j = 0; j <
>>>>> logical_port_addrs.n_ipv4_addrs;
>>>>>>>> +                     j++) {
>>>>>>>> +                    struct ipv4_netaddr address =
>>>>>>>> +                        logical_port_addrs.ipv4_addrs[j];
>>>>>>>> +                    if (address.network ==
>>>>>>>> +                        (neigh_prefix_v4 & address.mask)) {
>>>>>>>> +                        is_same_subnet = true;
>>>>>>>> +                        break;
>>>>>>>> +                    }
>>>>>>>> +                }
>>>>>>>> +            } else if (ipv6_parse(sb_route->nexthop,
>>>>> &neigh_prefix_v6))
>>>>>>> {
>>>>>>>> +                for (size_t j = 0; j <
>>>>> logical_port_addrs.n_ipv6_addrs;
>>>>>>> j++) {
>>>>>>>> +                    struct ipv6_netaddr address =
>>>>>>>> +                        logical_port_addrs.ipv6_addrs[j];
>>>>>>>> +                    struct in6_addr neigh_prefix =
>>>>>>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
>>>>>>> &address.mask);
>>>>>>>> +                    if (ipv6_addr_equals(&address.network,
>>>>>>> &neigh_prefix)) {
>>>>>>>> +                        is_same_subnet = true;
>>>>>>>> +                        break;
>>>>>>>> +                    }
>>>>>>>> +                }
>>>>>>>> +            }
>>>>>>>> +            destroy_lport_addresses(&logical_port_addrs);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +
>>>>>>>> +        if (!is_same_subnet) {
>>>>>>>> +            sbrec_learned_route_delete(sb_route);
>>>>>>>> +            continue;
>>>>>>>> +        }
>>>>>>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
>>>>>>>>                                       &lr_datapaths->datapaths,
>>>>>>>>                                       sb_route);
>>>>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>>>>>> index 864854c56..0d1d06196 100644
>>>>>>>> --- a/tests/ovn-northd.at
>>>>>>>> +++ b/tests/ovn-northd.at
>>>>>>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing"
>>> lr0flows |
>>>>>>> ovn_strip_lflows], [0], [dnl
>>>>>>>>  ])
>>>>>>>>
>>>>>>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>>>>> lr0-sw1.
>>>>>>>> -# This is not reachable so will not produce a lflow.
>>>>>>>> +# This is not reachable so will not produce a lflow. Also, it'll be
>>>>>>> removed by northd
>>>>>>>
>>>>>>> As the comment used to say, while the route is learned, ovn-northd
>>>>>>> doesn't actually produce logical flows for it because the next hop is
>>>>>>> not reachable on any network configured on lr0-sw1.
>>>>>>>
>>>>>>> So yes, the route is in SB.Learned_Route, but it doesn't affect
>>> routing
>>>>>>> in any way.  It's exactly the same as configuring a NB Static_Route
>>> with
>>>>>>> a wrong next hop.
>>>>>>>
>>>>>>> Moreover, your code removes the Learned_Route in northd.  But
>>>>>>> Learned_Routes are fully managed (created & deleted) by
>>> ovn-controller.
>>>>>>>
>>>>>>> So next time ovn-controller runs it will just relearn the route
>>>>>>> (re-create the SB.Learned_Route record).  So this only creates
>>>>>>> unnecessary churn as far as I understand.
>>>>>>>
>>>>>>> Or is there a use case I missed?
>>>>>>>
>>>>>>>
>>>>>> You're right, it's more appropriate to add the code in ovn controller
>>>>>> and northd will not generate flows. However, I would like to avoid the
>>>>>> garbage on the southbound.
>>>>>>
>>>>>
>>>>> I'm still not sure what "garbage" we're avoiding.  The router learns a
>>>>> "dynamic" route from a routing protocol (like any traditional router).
>>>>> Then there's some logic (in northd in our case) that decides whether
>>>>> that route should be used for actual traffic forwarding (i.e., is
>>>>> installed in the forwarding table in traditional routers).
>>>>>
>>>>> If the port it's learnt on doesn't have an IP in a subnet that includes
>>>>> the next hop, northd doesn't "install" the route (as logical flows).
>>>>>
>>>>> Also, I don't think you can move the check to ovn-controller.  We
>>>>> already check for LRP subnets that match the next hop in ovn-northd.  We
>>>>> don't want to duplicate that logic to ovn-controller IMO.
>>>>>
>>>>>
>>>> Hi Dumitru,
>>>> By "garbage" I mean useless entries that are just increasing the size of
>>>> southbound.
>>>>
>>>
>>> Hi Lucas,
>>>
>>> But do we really expect a lot of routes to be learned via next hops that
>>> are not reachable locally?  That sounds like an unusual routing
>>> configuration.
>>>
>>> Regards,
>>> Dumitru
>>>
>>>>
>>>> Regards,
>>>> Lucas
>>>>
>>>>
>>>>> Regards,
>>>>> Dumitru
>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Lucas
>>>>>>
>>>>>>
>>>>>>> Regards,
>>>>>>> Dumitru
>>>>>>>
>>>>>>>
>>>>>>>>  check_uuid ovn-sbctl create Learned_Route \
>>>>>>>>      datapath=$datapath                    \
>>>>>>>>      logical_port=$sw1                     \
>>>>>>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
>>>>>>>>      nexthop=\"2001:db8:ffff::20\"
>>>>>>>>  check ovn-nbctl --wait=sb sync
>>>>>>>>  check_row_count Advertised_Route 4
>>>>>>>> -check_row_count Learned_Route 2
>>>>>>>> +check_row_count Learned_Route 1
>>>>>>>> +check_row_count Learned_Route 0 logical_port=$sw1
>>>>>>> ip_prefix=\"2001:db8:2::/64\"
>>>>>>>> +
>>>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>>>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
>>>>> [0],
>>>>>>> [dnl
>>>>>>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
>>>>>>> action=(drop;)
>>>>>>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing"
>>> lr0flows
>>>>> |
>>>>>>> ovn_strip_lflows], [0], [dnl
>>>>>>>>  # active.
>>>>>>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
>>>>>>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
>>>>>>>> +
>>>>>>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on
>>>>>>> lr0-sw1.
>>>>>>>> +# Northd doesn not remove now because lrp have address in same
>>> subnet
>>>>>>> from nexthop
>>>>>>>> +check_uuid ovn-sbctl create Learned_Route \
>>>>>>>> +    datapath=$datapath                    \
>>>>>>>> +    logical_port=$sw1                     \
>>>>>>>> +    ip_prefix=\"2001:db8:2::/64\"         \
>>>>>>>> +    nexthop=\"2001:db8:ffff::20\"
>>>>>>>>  check_row_count Advertised_Route 5
>>>>>>>>  check_row_count Learned_Route 2
>>>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
>>>>>>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>>>>>>> index 1cbbdfa58..6e87fb266 100644
>>>>>>>> --- a/tests/system-ovn.at
>>>>>>>> +++ b/tests/system-ovn.at
>>>>>>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
>>>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>>>>        options:dynamic-routing-port-name=mylearninglsp
>>>>>>>>
>>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>>> nexthop=192.168.20.20
>>>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>>> nexthop=192.168.10.20
>>>>>>>>
>>>>>>>>  # Stopping the ovn-controller will clean up the route entries
>>> created
>>>>>>> by it.
>>>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
>>> it
>>>>>>> will
>>>>>>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
>>>>>>> Logical_Router_Port internet-phys \
>>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Starting it again will add the routes again.
>>>>>>>>  start_daemon ovn-controller
>>>>>>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Changing the vrf name will switch to the new one.
>>>>>>>>  # The old vrf will be removed.
>>>>>>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Stopping with --restart will not touch the routes.
>>>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>>>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # When we now stop the ovn-controller it will remove the VRF.
>>>>>>>>  start_daemon ovn-controller
>>>>>>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Bind "vip" port locally and check the virtual IP is added in the
>>>>> VRF.
>>>>>>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
>>>>>>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
>>>>>>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>>>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Simulate "vip" bound to a different chassis.
>>>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
>>>>>>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Check with dynamic-routing-redistribute-local-only=false.
>>>>>>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public \
>>>>>>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>>>>  blackhole 192.0.2.30 proto ovn metric 1000
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Remove the backoff period, so we can bind it right away.
>>>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
>>>>>>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
>>>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
>>>>>>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
>>>>>>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
>>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>>>  # 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
>>>>>>>> +check_row_count Learned_Route 1
>>>>>>>>  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
>>>>>>>>
>>>>>>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
>>>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>>>>>>>      options:dynamic-routing-port-name=mylearninglsp
>>>>>>>>
>>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 30 proto zebra
>>>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
>>>>> onlink
>>>>>>> vrf ovnvrf1337 metric 40 proto zebra
>>>>>>>>  check ovn-nbctl --wait=hv sync
>>>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>>> nexthop=192.168.20.20
>>>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
>>>>>>> nexthop=192.168.10.20
>>>>>>>>
>>>>>>>>  # Stopping the ovn-controller will clean up the route entries
>>> created
>>>>>>> by it.
>>>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
>>> it
>>>>>>> will
>>>>>>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
>>>>>>> Logical_Router_Port internet-phys \
>>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Starting it again will add the routes again.
>>>>>>>>  start_daemon ovn-controller
>>>>>>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Stopping with --restart will not touch the routes.
>>>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
>>>>>>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
>>>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
>>>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
>>>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30
>>>>>>> onlink
>>>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40
>>>>>>> onlink])
>>>>>>>>
>>>>>>>>  # Now we set maintain-vrf again and stop the ovn-controller.
>>>>>>>>  # It will then remove the VRF.
>>>>>>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
>>>>>>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
>>>>> lrp2])
>>>>>>>>  check ovn-nbctl --wait=hv \
>>>>>>>>      remove logical_router_port lrp2 options
>>> dynamic-routing-port-name
>>>>>>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>>>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
>>>>>>>>                                  nexthop=2.2.2.2      \
>>>>>>>>                                  logical_port=$lrp1
>>>>>>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
>>>>>>>>                                  nexthop=2.2.2.2      \
>>>>>>>>                                  logical_port=$lrp2
>>>>>>>> -check_row_count Learned_Route 2
>>>>>>>> +check_row_count Learned_Route 1
>>>>>>>>
>>>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>
Lucas Vargas Dias Jan. 7, 2026, 12:27 p.m. UTC | #9
Hi Dumitru,

Thanks for the example of configuration.

Em ter., 6 de jan. de 2026 às 13:39, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
> > Hi Dumitru,
> >
>
> Hi Lucas,
>
> >
> > I'm testing a scenario with a logical router that has a
> logical_router_port
> > used to ovn-ic and another logical router port used to DR.
> > I'm learning in all logical router ports, I would like to learn just in
> > logical router port from DR.
> > Follow an example of configuration:
> >
>
> Thanks for the sample config!
>
> > ovn-nbctl -- lr-add dcx1
> > # LRP used to Dynamic routing
> > ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> --
> > lrp-set-options rp-public
> > ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> > ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
>
> Btw, there's a better way to configure the VRF ID:
>
> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
> Relying on the requested-tnl-key is not that great, I'd recommend using
> the new option from now on.
>
> > ovn-nbctl set logical_router dcx1
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocols=BGP,BFD
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocol-redirect=bgpvrf1002
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-maintain-vrf=true
> > ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >
> > # LRPs used to ovn-ic
> > ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> > ovn-nbctl set logical_router_port ts1001-dcx1
> ha_chassis_group="$HA_GROUP1"
> >
> >
> > ovn-sbctl list learned_route
> > _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
> > datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> > external_ids        : {}
> > ip_prefix           : "10.0.0.0/24"
> > logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> > nexthop             : "169.254.254.1"
> >
> > _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
> > datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> > external_ids        : {}
> > ip_prefix           : "10.0.0.0/24"
> > logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
> > nexthop             : "169.254.254.1"
> >
> >
> > ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
> > _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
> > additional_chassis  : []
> > additional_encap    : []
> > chassis             : []
> > datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> > encap               : []
> > external_ids        : {}
> > gateway_chassis     : []
> > ha_chassis_group    : []
> > logical_port        : ts1001-dcx1
> > mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
> > mirror_port         : []
> > mirror_rules        : []
> > nat_addresses       : []
> > options             : {chassis-redirect-port=cr-ts1001-dcx1,
> > peer=ts1001-dcx1-rp}
> > parent_port         : []
> > port_security       : []
> > requested_additional_chassis: []
> > requested_chassis   : []
> > tag                 : []
> > tunnel_key          : 3
> > type                : patch
> > up                  : false
> > virtual_parent      : []
> >
> >
> >
> > ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> > _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> > additional_chassis  : []
> > additional_encap    : []
> > chassis             : []
> > datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> > encap               : []
> > external_ids        : {}
> > gateway_chassis     : []
> > ha_chassis_group    : []
> > logical_port        : rp-public
> > mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
> > mirror_port         : []
> > mirror_rules        : []
> > nat_addresses       : []
> > options             : {chassis-redirect-port=cr-rp-public,
> peer=public-rp}
> > parent_port         : []
> > port_security       : []
> > requested_additional_chassis: []
> > requested_chassis   : []
> > tag                 : []
> > tunnel_key          : 1
> > type                : patch
> > up                  : false
> > virtual_parent      : []
> >
> >
>
> I think this looks similar to the use case Felix had when he added the
> LRP.options:dynamic-routing-port-name support:
>
>
> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
>
> Without that config option ovn-controller learns routes on all DGPs, in
> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
> ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
> happens to be ovn-chassis-4).
>
> As you always bind rp-public to ovn-chassis-4 you could also set:
>
> ovn-nbctl set logical_router_port rp-public
> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
>
> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
>
> Then ovn-controller will only learn routes with logical_port=rp-public
>
> Would that work for you?
>

It works partially. If I configured it exactly in the following order, it
works.
ovn-nbctl -- lr-add dcx1
# LRP used to Dynamic routing
ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
lrp-set-options rp-public
ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
ovn-nbctl set logical_router dcx1
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocols=BGP,BFD
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocol-redirect=bgpvrf1002
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-maintain-vrf=true
*ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-port-name=bgpvrf1002*
ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4

# LRPs used to ovn-ic
ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"


If I configure ovn-nbctl set logical_router_port rp-public
options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
after configured
the ha_chassis_group, route is learned in all LRPs.


ovn-nbctl -- lr-add dcx1
# LRP used to Dynamic routing
ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
lrp-set-options rp-public
ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
ovn-nbctl set logical_router dcx1
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocols=BGP,BFD
ovn-nbctl --wait=sb set logical_router_port rp-public
options:routing-protocol-redirect=bgpvrf1002
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-redistribute="connected,static"
ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-maintain-vrf=true
ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4

# LRPs used to ovn-ic
ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"

*ovn-nbctl --wait=sb set logical_router_port rp-public
options:dynamic-routing-port-name=bgpvrf1002*

Regards,
Lucas



> Regards,
> Dumitru
>
> >
> > Regards,
> > Lucas
> >
> >
> > Em seg., 5 de jan. de 2026 às 08:41, Dumitru Ceara <dceara@redhat.com>
> > escreveu:
> >
> >> On 1/2/26 6:29 PM, Lucas Vargas Dias wrote:
> >>> Em qui., 18 de dez. de 2025 às 11:56, Dumitru Ceara <dceara@redhat.com
> >
> >>> escreveu:
> >>>
> >>>> On 12/18/25 2:50 PM, Lucas Vargas Dias wrote:
> >>>>> Hi Dumitru,
> >>>>
> >>>> Hi Lucas,
> >>>>
> >>>>> thanks for the review.
> >>>>>
> >>>>> Em sex., 12 de dez. de 2025 às 12:29, Dumitru Ceara <
> dceara@redhat.com
> >>>
> >>>>> escreveu:
> >>>>>
> >>>>>> On 12/5/25 7:20 PM, Lucas Vargas Dias via dev wrote:
> >>>>>>> Learned routes must have the nexthop reachable. So, if logical
> router
> >>>>>>> has two ports in differents subnets, route must be learned in just
> >>>>>>> one which is the port in the same subnet from nexthop.
> >>>>>>>
> >>>>>>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>
> >>>>>>> ---
> >>>>>>
> >>>>>> Hi Lucas,
> >>>>>>
> >>>>>> Thanks for the patch!
> >>>>>>
> >>>>>> I'm not sure I understand the problem though, please see below.
> >>>>>>
> >>>>>>>  northd/en-learned-route-sync.c | 44 ++++++++++++++++++++
> >>>>>>>  tests/ovn-northd.at            | 14 ++++++-
> >>>>>>>  tests/system-ovn.at            | 74
> >>>> +++++++++++++++++-----------------
> >>>>>>>  3 files changed, 93 insertions(+), 39 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/northd/en-learned-route-sync.c
> >>>>>> b/northd/en-learned-route-sync.c
> >>>>>>> index f22aaa664..4c9de8651 100644
> >>>>>>> --- a/northd/en-learned-route-sync.c
> >>>>>>> +++ b/northd/en-learned-route-sync.c
> >>>>>>> @@ -227,6 +227,50 @@ routes_table_sync(
> >>>>>>>              sbrec_learned_route_delete(sb_route);
> >>>>>>>              continue;
> >>>>>>>          }
> >>>>>>> +        bool is_same_subnet = false;
> >>>>>>> +        for (size_t i = 0; !is_same_subnet &&
> >>>>>>> +             i < sb_route->logical_port->n_mac;
> >>>>>>> +             i++) {
> >>>>>>> +            struct lport_addresses logical_port_addrs;
> >>>>>>> +            if
> >> (!extract_lsp_addresses(sb_route->logical_port->mac[i],
> >>>>>>> +                                       &logical_port_addrs)) {
> >>>>>>> +                destroy_lport_addresses(&logical_port_addrs);
> >>>>>>> +                continue;
> >>>>>>> +            }
> >>>>>>> +            ovs_be32 neigh_prefix_v4;
> >>>>>>> +            struct in6_addr neigh_prefix_v6;
> >>>>>>> +
> >>>>>>> +            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
> >>>>>>> +                for (size_t j = 0; j <
> >>>> logical_port_addrs.n_ipv4_addrs;
> >>>>>>> +                     j++) {
> >>>>>>> +                    struct ipv4_netaddr address =
> >>>>>>> +                        logical_port_addrs.ipv4_addrs[j];
> >>>>>>> +                    if (address.network ==
> >>>>>>> +                        (neigh_prefix_v4 & address.mask)) {
> >>>>>>> +                        is_same_subnet = true;
> >>>>>>> +                        break;
> >>>>>>> +                    }
> >>>>>>> +                }
> >>>>>>> +            } else if (ipv6_parse(sb_route->nexthop,
> >>>> &neigh_prefix_v6))
> >>>>>> {
> >>>>>>> +                for (size_t j = 0; j <
> >>>> logical_port_addrs.n_ipv6_addrs;
> >>>>>> j++) {
> >>>>>>> +                    struct ipv6_netaddr address =
> >>>>>>> +                        logical_port_addrs.ipv6_addrs[j];
> >>>>>>> +                    struct in6_addr neigh_prefix =
> >>>>>>> +                        ipv6_addr_bitand(&neigh_prefix_v6,
> >>>>>> &address.mask);
> >>>>>>> +                    if (ipv6_addr_equals(&address.network,
> >>>>>> &neigh_prefix)) {
> >>>>>>> +                        is_same_subnet = true;
> >>>>>>> +                        break;
> >>>>>>> +                    }
> >>>>>>> +                }
> >>>>>>> +            }
> >>>>>>> +            destroy_lport_addresses(&logical_port_addrs);
> >>>>>>> +        }
> >>>>>>> +
> >>>>>>> +
> >>>>>>> +        if (!is_same_subnet) {
> >>>>>>> +            sbrec_learned_route_delete(sb_route);
> >>>>>>> +            continue;
> >>>>>>> +        }
> >>>>>>>          parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
> >>>>>>>                                       &lr_datapaths->datapaths,
> >>>>>>>                                       sb_route);
> >>>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> >>>>>>> index 864854c56..0d1d06196 100644
> >>>>>>> --- a/tests/ovn-northd.at
> >>>>>>> +++ b/tests/ovn-northd.at
> >>>>>>> @@ -15578,7 +15578,7 @@ AT_CHECK([grep -w "lr_in_ip_routing"
> >> lr0flows |
> >>>>>> ovn_strip_lflows], [0], [dnl
> >>>>>>>  ])
> >>>>>>>
> >>>>>>>  # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned
> on
> >>>>>> lr0-sw1.
> >>>>>>> -# This is not reachable so will not produce a lflow.
> >>>>>>> +# This is not reachable so will not produce a lflow. Also, it'll
> be
> >>>>>> removed by northd
> >>>>>>
> >>>>>> As the comment used to say, while the route is learned, ovn-northd
> >>>>>> doesn't actually produce logical flows for it because the next hop
> is
> >>>>>> not reachable on any network configured on lr0-sw1.
> >>>>>>
> >>>>>> So yes, the route is in SB.Learned_Route, but it doesn't affect
> >> routing
> >>>>>> in any way.  It's exactly the same as configuring a NB Static_Route
> >> with
> >>>>>> a wrong next hop.
> >>>>>>
> >>>>>> Moreover, your code removes the Learned_Route in northd.  But
> >>>>>> Learned_Routes are fully managed (created & deleted) by
> >> ovn-controller.
> >>>>>>
> >>>>>> So next time ovn-controller runs it will just relearn the route
> >>>>>> (re-create the SB.Learned_Route record).  So this only creates
> >>>>>> unnecessary churn as far as I understand.
> >>>>>>
> >>>>>> Or is there a use case I missed?
> >>>>>>
> >>>>>>
> >>>>> You're right, it's more appropriate to add the code in ovn controller
> >>>>> and northd will not generate flows. However, I would like to avoid
> the
> >>>>> garbage on the southbound.
> >>>>>
> >>>>
> >>>> I'm still not sure what "garbage" we're avoiding.  The router learns a
> >>>> "dynamic" route from a routing protocol (like any traditional router).
> >>>> Then there's some logic (in northd in our case) that decides whether
> >>>> that route should be used for actual traffic forwarding (i.e., is
> >>>> installed in the forwarding table in traditional routers).
> >>>>
> >>>> If the port it's learnt on doesn't have an IP in a subnet that
> includes
> >>>> the next hop, northd doesn't "install" the route (as logical flows).
> >>>>
> >>>> Also, I don't think you can move the check to ovn-controller.  We
> >>>> already check for LRP subnets that match the next hop in ovn-northd.
> We
> >>>> don't want to duplicate that logic to ovn-controller IMO.
> >>>>
> >>>>
> >>> Hi Dumitru,
> >>> By "garbage" I mean useless entries that are just increasing the size
> of
> >>> southbound.
> >>>
> >>
> >> Hi Lucas,
> >>
> >> But do we really expect a lot of routes to be learned via next hops that
> >> are not reachable locally?  That sounds like an unusual routing
> >> configuration.
> >>
> >> Regards,
> >> Dumitru
> >>
> >>>
> >>> Regards,
> >>> Lucas
> >>>
> >>>
> >>>> Regards,
> >>>> Dumitru
> >>>>
> >>>>>
> >>>>> Regards,
> >>>>> Lucas
> >>>>>
> >>>>>
> >>>>>> Regards,
> >>>>>> Dumitru
> >>>>>>
> >>>>>>
> >>>>>>>  check_uuid ovn-sbctl create Learned_Route \
> >>>>>>>      datapath=$datapath                    \
> >>>>>>>      logical_port=$sw1                     \
> >>>>>>> @@ -15586,7 +15586,9 @@ check_uuid ovn-sbctl create Learned_Route \
> >>>>>>>      nexthop=\"2001:db8:ffff::20\"
> >>>>>>>  check ovn-nbctl --wait=sb sync
> >>>>>>>  check_row_count Advertised_Route 4
> >>>>>>> -check_row_count Learned_Route 2
> >>>>>>> +check_row_count Learned_Route 1
> >>>>>>> +check_row_count Learned_Route 0 logical_port=$sw1
> >>>>>> ip_prefix=\"2001:db8:2::/64\"
> >>>>>>> +
> >>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>>>>>>  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows],
> >>>> [0],
> >>>>>> [dnl
> >>>>>>>    table=??(lr_in_ip_routing   ), priority=0    , match=(1),
> >>>>>> action=(drop;)
> >>>>>>> @@ -15605,6 +15607,14 @@ AT_CHECK([grep -w "lr_in_ip_routing"
> >> lr0flows
> >>>> |
> >>>>>> ovn_strip_lflows], [0], [dnl
> >>>>>>>  # active.
> >>>>>>>  check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
> >>>>>>>      networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
> >>>>>>> +
> >>>>>>> +# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned
> on
> >>>>>> lr0-sw1.
> >>>>>>> +# Northd doesn not remove now because lrp have address in same
> >> subnet
> >>>>>> from nexthop
> >>>>>>> +check_uuid ovn-sbctl create Learned_Route \
> >>>>>>> +    datapath=$datapath                    \
> >>>>>>> +    logical_port=$sw1                     \
> >>>>>>> +    ip_prefix=\"2001:db8:2::/64\"         \
> >>>>>>> +    nexthop=\"2001:db8:ffff::20\"
> >>>>>>>  check_row_count Advertised_Route 5
> >>>>>>>  check_row_count Learned_Route 2
> >>>>>>>  ovn-sbctl dump-flows lr0 > lr0flows
> >>>>>>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> >>>>>>> index 1cbbdfa58..6e87fb266 100644
> >>>>>>> --- a/tests/system-ovn.at
> >>>>>>> +++ b/tests/system-ovn.at
> >>>>>>> @@ -15404,10 +15404,10 @@ wait_for_ports_up mylearninglsp
> >>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>>>>>        options:dynamic-routing-port-name=mylearninglsp
> >>>>>>>
> >>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>>>  check ovn-nbctl --wait=hv sync
> >>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>>>> nexthop=192.168.20.20
> >>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>>>> nexthop=192.168.10.20
> >>>>>>>
> >>>>>>>  # Stopping the ovn-controller will clean up the route entries
> >> created
> >>>>>> by it.
> >>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
> >> it
> >>>>>> will
> >>>>>>> @@ -15417,8 +15417,8 @@ check ovn-nbctl --wait=hv set
> >>>>>> Logical_Router_Port internet-phys \
> >>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Starting it again will add the routes again.
> >>>>>>>  start_daemon ovn-controller
> >>>>>>> @@ -15431,8 +15431,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Changing the vrf name will switch to the new one.
> >>>>>>>  # The old vrf will be removed.
> >>>>>>> @@ -15449,8 +15449,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Stopping with --restart will not touch the routes.
> >>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>>>>>> @@ -15462,8 +15462,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # When we now stop the ovn-controller it will remove the VRF.
> >>>>>>>  start_daemon ovn-controller
> >>>>>>> @@ -15494,8 +15494,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Bind "vip" port locally and check the virtual IP is added in the
> >>>> VRF.
> >>>>>>>  NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
> >>>>>>> @@ -15511,8 +15511,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
> >>>>>>> @@ -15524,8 +15524,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Remove the backoff period, so we can bind it right away.
> >>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>>>>>> @@ -15543,8 +15543,8 @@ blackhole 192.0.2.21 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Simulate "vip" bound to a different chassis.
> >>>>>>>  check ovn-sbctl clear Port_Binding vip virtual-parent
> >>>>>>> @@ -15559,8 +15559,8 @@ blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.21 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Check with dynamic-routing-redistribute-local-only=false.
> >>>>>>>  check ovn-nbctl --wait=hv set logical_router_port internet-public
> \
> >>>>>>> @@ -15576,8 +15576,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>>>>>  blackhole 192.0.2.30 proto ovn metric 1000
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Remove the backoff period, so we can bind it right away.
> >>>>>>>  check ovn-sbctl remove Port_Binding vip options vport-backoff
> >>>>>>> @@ -15596,8 +15596,8 @@ blackhole 192.0.2.22 proto ovn metric 1000
> >>>>>>>  blackhole 192.0.2.30 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>>>  AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
> >>>>>>> @@ -15857,7 +15857,7 @@ check ip route add 233.253.0.0/24 via
> >>>>>> 192.168.10.10 dev lo onlink vrf ovnvrf1337
> >>>>>>>  check ovn-nbctl --wait=hv sync
> >>>>>>>  # 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
> >>>>>>> +check_row_count Learned_Route 1
> >>>>>>>  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
> >>>>>>>
> >>>>>>> @@ -15892,10 +15892,10 @@ wait_for_ports_up mylearninglsp
> >>>>>>>  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> >>>>>>>      options:dynamic-routing-port-name=mylearninglsp
> >>>>>>>
> >>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>>>> -check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 30 proto zebra
> >>>>>>> +check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll
> >>>> onlink
> >>>>>> vrf ovnvrf1337 metric 40 proto zebra
> >>>>>>>  check ovn-nbctl --wait=hv sync
> >>>>>>> -check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>>>> nexthop=192.168.20.20
> >>>>>>> +check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24
> >>>>>> nexthop=192.168.10.20
> >>>>>>>
> >>>>>>>  # Stopping the ovn-controller will clean up the route entries
> >> created
> >>>>>> by it.
> >>>>>>>  # We first need to unset dynamic-routing-maintain-vrf as otherwise
> >> it
> >>>>>> will
> >>>>>>> @@ -15905,8 +15905,8 @@ check ovn-nbctl --wait=hv set
> >>>>>> Logical_Router_Port internet-phys \
> >>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>>>  OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Starting it again will add the routes again.
> >>>>>>>  start_daemon ovn-controller
> >>>>>>> @@ -15919,8 +15919,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Stopping with --restart will not touch the routes.
> >>>>>>>  OVN_CONTROLLER_EXIT([],[--restart])
> >>>>>>> @@ -15932,8 +15932,8 @@ blackhole 192.0.2.10 proto ovn metric 100
> >>>>>>>  blackhole 192.0.2.20 proto ovn metric 100
> >>>>>>>  blackhole 198.51.100.0/24 proto ovn metric 1000
> >>>>>>>  233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> -233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 30
> >>>>>> onlink
> >>>>>>> +233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric
> 40
> >>>>>> onlink])
> >>>>>>>
> >>>>>>>  # Now we set maintain-vrf again and stop the ovn-controller.
> >>>>>>>  # It will then remove the VRF.
> >>>>>>> @@ -16925,13 +16925,13 @@ check_row_count Learned_Route 1
> >>>>>>>  AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and
> >>>> lrp2])
> >>>>>>>  check ovn-nbctl --wait=hv \
> >>>>>>>      remove logical_router_port lrp2 options
> >> dynamic-routing-port-name
> >>>>>>> -check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>>>>>> +check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
> >>>>>>>                                  nexthop=2.2.2.2      \
> >>>>>>>                                  logical_port=$lrp1
> >>>>>>>  check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
> >>>>>>>                                  nexthop=2.2.2.2      \
> >>>>>>>                                  logical_port=$lrp2
> >>>>>>> -check_row_count Learned_Route 2
> >>>>>>> +check_row_count Learned_Route 1
> >>>>>>>
> >>>>>>>  OVN_CLEANUP_CONTROLLER([hv1])
> >>>>>>>
> >>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >
>
>
Dumitru Ceara Jan. 7, 2026, 12:46 p.m. UTC | #10
On 1/7/26 1:27 PM, Lucas Vargas Dias wrote:
> Hi Dumitru,
> 

Hi Lucas,

> Thanks for the example of configuration.
> 
> Em ter., 6 de jan. de 2026 às 13:39, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
>>> Hi Dumitru,
>>>
>>
>> Hi Lucas,
>>
>>>
>>> I'm testing a scenario with a logical router that has a
>> logical_router_port
>>> used to ovn-ic and another logical router port used to DR.
>>> I'm learning in all logical router ports, I would like to learn just in
>>> logical router port from DR.
>>> Follow an example of configuration:
>>>
>>
>> Thanks for the sample config!
>>
>>> ovn-nbctl -- lr-add dcx1
>>> # LRP used to Dynamic routing
>>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
>> --
>>> lrp-set-options rp-public
>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
>>> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
>>
>> Btw, there's a better way to configure the VRF ID:
>>
>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
>> Relying on the requested-tnl-key is not that great, I'd recommend using
>> the new option from now on.
>>
>>> ovn-nbctl set logical_router dcx1
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocols=BGP,BFD
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocol-redirect=bgpvrf1002
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-maintain-vrf=true
>>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
>>>
>>> # LRPs used to ovn-ic
>>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
>>> ovn-nbctl set logical_router_port ts1001-dcx1
>> ha_chassis_group="$HA_GROUP1"
>>>
>>>
>>> ovn-sbctl list learned_route
>>> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>> external_ids        : {}
>>> ip_prefix           : "10.0.0.0/24"
>>> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>> nexthop             : "169.254.254.1"
>>>
>>> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>> external_ids        : {}
>>> ip_prefix           : "10.0.0.0/24"
>>> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
>>> nexthop             : "169.254.254.1"
>>>
>>>
>>> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
>>> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
>>> additional_chassis  : []
>>> additional_encap    : []
>>> chassis             : []
>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>> encap               : []
>>> external_ids        : {}
>>> gateway_chassis     : []
>>> ha_chassis_group    : []
>>> logical_port        : ts1001-dcx1
>>> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
>>> mirror_port         : []
>>> mirror_rules        : []
>>> nat_addresses       : []
>>> options             : {chassis-redirect-port=cr-ts1001-dcx1,
>>> peer=ts1001-dcx1-rp}
>>> parent_port         : []
>>> port_security       : []
>>> requested_additional_chassis: []
>>> requested_chassis   : []
>>> tag                 : []
>>> tunnel_key          : 3
>>> type                : patch
>>> up                  : false
>>> virtual_parent      : []
>>>
>>>
>>>
>>> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>> additional_chassis  : []
>>> additional_encap    : []
>>> chassis             : []
>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>> encap               : []
>>> external_ids        : {}
>>> gateway_chassis     : []
>>> ha_chassis_group    : []
>>> logical_port        : rp-public
>>> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
>>> mirror_port         : []
>>> mirror_rules        : []
>>> nat_addresses       : []
>>> options             : {chassis-redirect-port=cr-rp-public,
>> peer=public-rp}
>>> parent_port         : []
>>> port_security       : []
>>> requested_additional_chassis: []
>>> requested_chassis   : []
>>> tag                 : []
>>> tunnel_key          : 1
>>> type                : patch
>>> up                  : false
>>> virtual_parent      : []
>>>
>>>
>>
>> I think this looks similar to the use case Felix had when he added the
>> LRP.options:dynamic-routing-port-name support:
>>
>>
>> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
>>
>> Without that config option ovn-controller learns routes on all DGPs, in
>> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
>> ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
>> happens to be ovn-chassis-4).
>>
>> As you always bind rp-public to ovn-chassis-4 you could also set:
>>
>> ovn-nbctl set logical_router_port rp-public
>> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
>>
>> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
>>
>> Then ovn-controller will only learn routes with logical_port=rp-public
>>
>> Would that work for you?
>>
> 
> It works partially. If I configured it exactly in the following order, it
> works.
> ovn-nbctl -- lr-add dcx1
> # LRP used to Dynamic routing
> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
> lrp-set-options rp-public
> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> ovn-nbctl set logical_router dcx1
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocols=BGP,BFD
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocol-redirect=bgpvrf1002
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-maintain-vrf=true
> *ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-port-name=bgpvrf1002*
> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> 
> # LRPs used to ovn-ic
> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"
> 
> 
> If I configure ovn-nbctl set logical_router_port rp-public
> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> after configured
> the ha_chassis_group, route is learned in all LRPs.
> 
> 
> ovn-nbctl -- lr-add dcx1
> # LRP used to Dynamic routing
> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30 --
> lrp-set-options rp-public
> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> ovn-nbctl set logical_router dcx1
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocols=BGP,BFD
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:routing-protocol-redirect=bgpvrf1002
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-redistribute="connected,static"
> ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-maintain-vrf=true
> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> 
> # LRPs used to ovn-ic
> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> ovn-nbctl set logical_router_port ts1001-dcx1 ha_chassis_group="$HA_GROUP1"
> 
> *ovn-nbctl --wait=sb set logical_router_port rp-public
> options:dynamic-routing-port-name=bgpvrf1002*
> 

This sounds like an incremental processing bug in northd (I guess).

Can you try an "ovn-appctl -t ovn-northd inc-engine/recompute" on the
node where ovn-northd runs after you ended up in the broken state
(second way of configuring things)?

Does that "fix" the route learning?

Thanks,
Dumitru
Lucas Vargas Dias Jan. 7, 2026, 12:51 p.m. UTC | #11
I try ovn-appctl -t ovn-northd inc-engine/recompute and it doesn't fix the
route learning
I try ovn-appctl -t ovn-controller inc-engine/recompute in the
ovn-chassis-4, also

Regards,
Lucas

Em qua., 7 de jan. de 2026 às 09:46, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 1/7/26 1:27 PM, Lucas Vargas Dias wrote:
> > Hi Dumitru,
> >
>
> Hi Lucas,
>
> > Thanks for the example of configuration.
> >
> > Em ter., 6 de jan. de 2026 às 13:39, Dumitru Ceara <dceara@redhat.com>
> > escreveu:
> >
> >> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
> >>> Hi Dumitru,
> >>>
> >>
> >> Hi Lucas,
> >>
> >>>
> >>> I'm testing a scenario with a logical router that has a
> >> logical_router_port
> >>> used to ovn-ic and another logical router port used to DR.
> >>> I'm learning in all logical router ports, I would like to learn just in
> >>> logical router port from DR.
> >>> Follow an example of configuration:
> >>>
> >>
> >> Thanks for the sample config!
> >>
> >>> ovn-nbctl -- lr-add dcx1
> >>> # LRP used to Dynamic routing
> >>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> >> --
> >>> lrp-set-options rp-public
> >>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> >>> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
> >>
> >> Btw, there's a better way to configure the VRF ID:
> >>
> >> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
> >> Relying on the requested-tnl-key is not that great, I'd recommend using
> >> the new option from now on.
> >>
> >>> ovn-nbctl set logical_router dcx1
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocols=BGP,BFD
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocol-redirect=bgpvrf1002
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-maintain-vrf=true
> >>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >>>
> >>> # LRPs used to ovn-ic
> >>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> >>> ovn-nbctl set logical_router_port ts1001-dcx1
> >> ha_chassis_group="$HA_GROUP1"
> >>>
> >>>
> >>> ovn-sbctl list learned_route
> >>> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
> >>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>> external_ids        : {}
> >>> ip_prefix           : "10.0.0.0/24"
> >>> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>> nexthop             : "169.254.254.1"
> >>>
> >>> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
> >>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>> external_ids        : {}
> >>> ip_prefix           : "10.0.0.0/24"
> >>> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
> >>> nexthop             : "169.254.254.1"
> >>>
> >>>
> >>> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
> >>> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
> >>> additional_chassis  : []
> >>> additional_encap    : []
> >>> chassis             : []
> >>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>> encap               : []
> >>> external_ids        : {}
> >>> gateway_chassis     : []
> >>> ha_chassis_group    : []
> >>> logical_port        : ts1001-dcx1
> >>> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
> >>> mirror_port         : []
> >>> mirror_rules        : []
> >>> nat_addresses       : []
> >>> options             : {chassis-redirect-port=cr-ts1001-dcx1,
> >>> peer=ts1001-dcx1-rp}
> >>> parent_port         : []
> >>> port_security       : []
> >>> requested_additional_chassis: []
> >>> requested_chassis   : []
> >>> tag                 : []
> >>> tunnel_key          : 3
> >>> type                : patch
> >>> up                  : false
> >>> virtual_parent      : []
> >>>
> >>>
> >>>
> >>> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>> additional_chassis  : []
> >>> additional_encap    : []
> >>> chassis             : []
> >>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>> encap               : []
> >>> external_ids        : {}
> >>> gateway_chassis     : []
> >>> ha_chassis_group    : []
> >>> logical_port        : rp-public
> >>> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
> >>> mirror_port         : []
> >>> mirror_rules        : []
> >>> nat_addresses       : []
> >>> options             : {chassis-redirect-port=cr-rp-public,
> >> peer=public-rp}
> >>> parent_port         : []
> >>> port_security       : []
> >>> requested_additional_chassis: []
> >>> requested_chassis   : []
> >>> tag                 : []
> >>> tunnel_key          : 1
> >>> type                : patch
> >>> up                  : false
> >>> virtual_parent      : []
> >>>
> >>>
> >>
> >> I think this looks similar to the use case Felix had when he added the
> >> LRP.options:dynamic-routing-port-name support:
> >>
> >>
> >>
> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
> >>
> >> Without that config option ovn-controller learns routes on all DGPs, in
> >> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
> >> ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
> >> happens to be ovn-chassis-4).
> >>
> >> As you always bind rp-public to ovn-chassis-4 you could also set:
> >>
> >> ovn-nbctl set logical_router_port rp-public
> >> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> >>
> >> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
> >>
> >> Then ovn-controller will only learn routes with logical_port=rp-public
> >>
> >> Would that work for you?
> >>
> >
> > It works partially. If I configured it exactly in the following order, it
> > works.
> > ovn-nbctl -- lr-add dcx1
> > # LRP used to Dynamic routing
> > ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> --
> > lrp-set-options rp-public
> > ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> > ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> > ovn-nbctl set logical_router dcx1
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocols=BGP,BFD
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocol-redirect=bgpvrf1002
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-maintain-vrf=true
> > *ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-port-name=bgpvrf1002*
> > ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >
> > # LRPs used to ovn-ic
> > ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> > ovn-nbctl set logical_router_port ts1001-dcx1
> ha_chassis_group="$HA_GROUP1"
> >
> >
> > If I configure ovn-nbctl set logical_router_port rp-public
> > options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> > after configured
> > the ha_chassis_group, route is learned in all LRPs.
> >
> >
> > ovn-nbctl -- lr-add dcx1
> > # LRP used to Dynamic routing
> > ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> --
> > lrp-set-options rp-public
> > ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> > ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> > ovn-nbctl set logical_router dcx1
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocols=BGP,BFD
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:routing-protocol-redirect=bgpvrf1002
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-redistribute="connected,static"
> > ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-maintain-vrf=true
> > ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >
> > # LRPs used to ovn-ic
> > ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> > ovn-nbctl set logical_router_port ts1001-dcx1
> ha_chassis_group="$HA_GROUP1"
> >
> > *ovn-nbctl --wait=sb set logical_router_port rp-public
> > options:dynamic-routing-port-name=bgpvrf1002*
> >
>
> This sounds like an incremental processing bug in northd (I guess).
>
> Can you try an "ovn-appctl -t ovn-northd inc-engine/recompute" on the
> node where ovn-northd runs after you ended up in the broken state
> (second way of configuring things)?
>
> Does that "fix" the route learning?
>
> Thanks,
> Dumitru
>
>
>
Dumitru Ceara Jan. 7, 2026, 1:52 p.m. UTC | #12
On 1/7/26 1:51 PM, Lucas Vargas Dias wrote:
> I try ovn-appctl -t ovn-northd inc-engine/recompute and it doesn't fix the
> route learning
> I try ovn-appctl -t ovn-controller inc-engine/recompute in the
> ovn-chassis-4, also
> 

Oh, I see now, ovn-controller actually has a chance to learn on both
router ports before it sees the dynamic-routing-port-name option (which
is set later in this case).

All routes it learns afterwards will only be learned on rp-public.
However, the ones it already learned before the option was set it
never cleans up.

The problem is in the sb_sync_learned_routes() function:

    struct sbrec_learned_route *filter =
        sbrec_learned_route_index_init_row(sbrec_learned_route_by_datapath);
    sbrec_learned_route_index_set_datapath(filter, datapath);
    SBREC_LEARNED_ROUTE_FOR_EACH_EQUAL (sb_route, filter,
                                        sbrec_learned_route_by_datapath) {
        /* If the port is not local we don't care about it.
         * Some other ovn-controller will handle it.
         * We may not use smap_get since the value might be validly NULL. */
        if (!smap_get_node(bound_ports,
                           sb_route->logical_port->logical_port)) {
            continue;     <<<< at this point bound_ports only contains rp-public
        }
        route_add_entry(&sync_routes, sb_route, true);
    }
    sbrec_learned_route_index_destroy_row(filter);


So we never add the old route we learned for ts1001-dcx1 to 'sync_routes'.
This means the old route will never be flushed.

I guess we need to add another check here that also adds 'sb_route' to
'sync_routes' if its logical_port is bound locally.

Later in the function, ovn-controller will then flush it out, because it
will become stale.

Lucas, would you have time to work on a fix for this by any chance?
Otherwise, I'll open an issue to try to fix it on our side in the near
future.

Thanks,
Dumitru

> Regards,
> Lucas
> 
> Em qua., 7 de jan. de 2026 às 09:46, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 1/7/26 1:27 PM, Lucas Vargas Dias wrote:
>>> Hi Dumitru,
>>>
>>
>> Hi Lucas,
>>
>>> Thanks for the example of configuration.
>>>
>>> Em ter., 6 de jan. de 2026 às 13:39, Dumitru Ceara <dceara@redhat.com>
>>> escreveu:
>>>
>>>> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
>>>>> Hi Dumitru,
>>>>>
>>>>
>>>> Hi Lucas,
>>>>
>>>>>
>>>>> I'm testing a scenario with a logical router that has a
>>>> logical_router_port
>>>>> used to ovn-ic and another logical router port used to DR.
>>>>> I'm learning in all logical router ports, I would like to learn just in
>>>>> logical router port from DR.
>>>>> Follow an example of configuration:
>>>>>
>>>>
>>>> Thanks for the sample config!
>>>>
>>>>> ovn-nbctl -- lr-add dcx1
>>>>> # LRP used to Dynamic routing
>>>>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
>>>> --
>>>>> lrp-set-options rp-public
>>>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
>>>>> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
>>>>
>>>> Btw, there's a better way to configure the VRF ID:
>>>>
>>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
>>>> Relying on the requested-tnl-key is not that great, I'd recommend using
>>>> the new option from now on.
>>>>
>>>>> ovn-nbctl set logical_router dcx1
>>>>> options:dynamic-routing-redistribute="connected,static"
>>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>>>> options:routing-protocols=BGP,BFD
>>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>>>> options:routing-protocol-redirect=bgpvrf1002
>>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>>>> options:dynamic-routing-redistribute="connected,static"
>>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>>>> options:dynamic-routing-maintain-vrf=true
>>>>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
>>>>>
>>>>> # LRPs used to ovn-ic
>>>>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
>>>>> ovn-nbctl set logical_router_port ts1001-dcx1
>>>> ha_chassis_group="$HA_GROUP1"
>>>>>
>>>>>
>>>>> ovn-sbctl list learned_route
>>>>> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
>>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>>>> external_ids        : {}
>>>>> ip_prefix           : "10.0.0.0/24"
>>>>> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>>>> nexthop             : "169.254.254.1"
>>>>>
>>>>> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
>>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>>>> external_ids        : {}
>>>>> ip_prefix           : "10.0.0.0/24"
>>>>> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
>>>>> nexthop             : "169.254.254.1"
>>>>>
>>>>>
>>>>> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
>>>>> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
>>>>> additional_chassis  : []
>>>>> additional_encap    : []
>>>>> chassis             : []
>>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>>>> encap               : []
>>>>> external_ids        : {}
>>>>> gateway_chassis     : []
>>>>> ha_chassis_group    : []
>>>>> logical_port        : ts1001-dcx1
>>>>> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
>>>>> mirror_port         : []
>>>>> mirror_rules        : []
>>>>> nat_addresses       : []
>>>>> options             : {chassis-redirect-port=cr-ts1001-dcx1,
>>>>> peer=ts1001-dcx1-rp}
>>>>> parent_port         : []
>>>>> port_security       : []
>>>>> requested_additional_chassis: []
>>>>> requested_chassis   : []
>>>>> tag                 : []
>>>>> tunnel_key          : 3
>>>>> type                : patch
>>>>> up                  : false
>>>>> virtual_parent      : []
>>>>>
>>>>>
>>>>>
>>>>> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>>>> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
>>>>> additional_chassis  : []
>>>>> additional_encap    : []
>>>>> chassis             : []
>>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
>>>>> encap               : []
>>>>> external_ids        : {}
>>>>> gateway_chassis     : []
>>>>> ha_chassis_group    : []
>>>>> logical_port        : rp-public
>>>>> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
>>>>> mirror_port         : []
>>>>> mirror_rules        : []
>>>>> nat_addresses       : []
>>>>> options             : {chassis-redirect-port=cr-rp-public,
>>>> peer=public-rp}
>>>>> parent_port         : []
>>>>> port_security       : []
>>>>> requested_additional_chassis: []
>>>>> requested_chassis   : []
>>>>> tag                 : []
>>>>> tunnel_key          : 1
>>>>> type                : patch
>>>>> up                  : false
>>>>> virtual_parent      : []
>>>>>
>>>>>
>>>>
>>>> I think this looks similar to the use case Felix had when he added the
>>>> LRP.options:dynamic-routing-port-name support:
>>>>
>>>>
>>>>
>> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
>>>>
>>>> Without that config option ovn-controller learns routes on all DGPs, in
>>>> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
>>>> ts1001-dcx1 (because it has ha_chassis_group set and the active chassis
>>>> happens to be ovn-chassis-4).
>>>>
>>>> As you always bind rp-public to ovn-chassis-4 you could also set:
>>>>
>>>> ovn-nbctl set logical_router_port rp-public
>>>> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
>>>>
>>>> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
>>>>
>>>> Then ovn-controller will only learn routes with logical_port=rp-public
>>>>
>>>> Would that work for you?
>>>>
>>>
>>> It works partially. If I configured it exactly in the following order, it
>>> works.
>>> ovn-nbctl -- lr-add dcx1
>>> # LRP used to Dynamic routing
>>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
>> --
>>> lrp-set-options rp-public
>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
>>> ovn-nbctl set logical_router dcx1
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocols=BGP,BFD
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocol-redirect=bgpvrf1002
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-maintain-vrf=true
>>> *ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-port-name=bgpvrf1002*
>>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
>>>
>>> # LRPs used to ovn-ic
>>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
>>> ovn-nbctl set logical_router_port ts1001-dcx1
>> ha_chassis_group="$HA_GROUP1"
>>>
>>>
>>> If I configure ovn-nbctl set logical_router_port rp-public
>>> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
>>> after configured
>>> the ha_chassis_group, route is learned in all LRPs.
>>>
>>>
>>> ovn-nbctl -- lr-add dcx1
>>> # LRP used to Dynamic routing
>>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
>> --
>>> lrp-set-options rp-public
>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
>>> ovn-nbctl set logical_router dcx1
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocols=BGP,BFD
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:routing-protocol-redirect=bgpvrf1002
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-redistribute="connected,static"
>>> ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-maintain-vrf=true
>>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
>>>
>>> # LRPs used to ovn-ic
>>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
>>> ovn-nbctl set logical_router_port ts1001-dcx1
>> ha_chassis_group="$HA_GROUP1"
>>>
>>> *ovn-nbctl --wait=sb set logical_router_port rp-public
>>> options:dynamic-routing-port-name=bgpvrf1002*
>>>
>>
>> This sounds like an incremental processing bug in northd (I guess).
>>
>> Can you try an "ovn-appctl -t ovn-northd inc-engine/recompute" on the
>> node where ovn-northd runs after you ended up in the broken state
>> (second way of configuring things)?
>>
>> Does that "fix" the route learning?
>>
>> Thanks,
>> Dumitru
>>
>>
>>
>
Lucas Vargas Dias Jan. 7, 2026, 2:24 p.m. UTC | #13
Em qua., 7 de jan. de 2026 às 10:52, Dumitru Ceara <dceara@redhat.com>
escreveu:

> On 1/7/26 1:51 PM, Lucas Vargas Dias wrote:
> > I try ovn-appctl -t ovn-northd inc-engine/recompute and it doesn't fix
> the
> > route learning
> > I try ovn-appctl -t ovn-controller inc-engine/recompute in the
> > ovn-chassis-4, also
> >
>
> Oh, I see now, ovn-controller actually has a chance to learn on both
> router ports before it sees the dynamic-routing-port-name option (which
> is set later in this case).
>
> All routes it learns afterwards will only be learned on rp-public.
> However, the ones it already learned before the option was set it
> never cleans up.
>
> The problem is in the sb_sync_learned_routes() function:
>
>     struct sbrec_learned_route *filter =
>
> sbrec_learned_route_index_init_row(sbrec_learned_route_by_datapath);
>     sbrec_learned_route_index_set_datapath(filter, datapath);
>     SBREC_LEARNED_ROUTE_FOR_EACH_EQUAL (sb_route, filter,
>                                         sbrec_learned_route_by_datapath) {
>         /* If the port is not local we don't care about it.
>          * Some other ovn-controller will handle it.
>          * We may not use smap_get since the value might be validly NULL.
> */
>         if (!smap_get_node(bound_ports,
>                            sb_route->logical_port->logical_port)) {
>             continue;     <<<< at this point bound_ports only contains
> rp-public
>         }
>         route_add_entry(&sync_routes, sb_route, true);
>     }
>     sbrec_learned_route_index_destroy_row(filter);
>
>
> So we never add the old route we learned for ts1001-dcx1 to 'sync_routes'.
> This means the old route will never be flushed.
>
> I guess we need to add another check here that also adds 'sb_route' to
> 'sync_routes' if its logical_port is bound locally.
>
> Later in the function, ovn-controller will then flush it out, because it
> will become stale.
>
> Lucas, would you have time to work on a fix for this by any chance?
> Otherwise, I'll open an issue to try to fix it on our side in the near
> future.
>
>
Hi,

I have time to work on this fix.

Regards,
Lucas



> Thanks,
> Dumitru
>
> > Regards,
> > Lucas
> >
> > Em qua., 7 de jan. de 2026 às 09:46, Dumitru Ceara <dceara@redhat.com>
> > escreveu:
> >
> >> On 1/7/26 1:27 PM, Lucas Vargas Dias wrote:
> >>> Hi Dumitru,
> >>>
> >>
> >> Hi Lucas,
> >>
> >>> Thanks for the example of configuration.
> >>>
> >>> Em ter., 6 de jan. de 2026 às 13:39, Dumitru Ceara <dceara@redhat.com>
> >>> escreveu:
> >>>
> >>>> On 1/5/26 6:50 PM, Lucas Vargas Dias wrote:
> >>>>> Hi Dumitru,
> >>>>>
> >>>>
> >>>> Hi Lucas,
> >>>>
> >>>>>
> >>>>> I'm testing a scenario with a logical router that has a
> >>>> logical_router_port
> >>>>> used to ovn-ic and another logical router port used to DR.
> >>>>> I'm learning in all logical router ports, I would like to learn just
> in
> >>>>> logical router port from DR.
> >>>>> Follow an example of configuration:
> >>>>>
> >>>>
> >>>> Thanks for the sample config!
> >>>>
> >>>>> ovn-nbctl -- lr-add dcx1
> >>>>> # LRP used to Dynamic routing
> >>>>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04
> 169.254.254.2/30
> >>>> --
> >>>>> lrp-set-options rp-public
> >>>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> >>>>> ovn-nbctl set logical_router dcx1 options:requested-tnl-key=1002
> >>>>
> >>>> Btw, there's a better way to configure the VRF ID:
> >>>>
> >>>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002.
> >>>> Relying on the requested-tnl-key is not that great, I'd recommend
> using
> >>>> the new option from now on.
> >>>>
> >>>>> ovn-nbctl set logical_router dcx1
> >>>>> options:dynamic-routing-redistribute="connected,static"
> >>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>>>> options:routing-protocols=BGP,BFD
> >>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>>>> options:routing-protocol-redirect=bgpvrf1002
> >>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>>>> options:dynamic-routing-redistribute="connected,static"
> >>>>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>>>> options:dynamic-routing-maintain-vrf=true
> >>>>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >>>>>
> >>>>> # LRPs used to ovn-ic
> >>>>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> >>>>> ovn-nbctl set logical_router_port ts1001-dcx1
> >>>> ha_chassis_group="$HA_GROUP1"
> >>>>>
> >>>>>
> >>>>> ovn-sbctl list learned_route
> >>>>> _uuid               : e283e6a9-ef99-468a-bafe-0b8802208436
> >>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>>>> external_ids        : {}
> >>>>> ip_prefix           : "10.0.0.0/24"
> >>>>> logical_port        : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>>>> nexthop             : "169.254.254.1"
> >>>>>
> >>>>> _uuid               : 5524ae47-a81b-4dd0-8e90-baf171c73dcf
> >>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>>>> external_ids        : {}
> >>>>> ip_prefix           : "10.0.0.0/24"
> >>>>> logical_port        : 8b108f3d-7402-48f5-bf15-7471db49c066
> >>>>> nexthop             : "169.254.254.1"
> >>>>>
> >>>>>
> >>>>> ovn-sbctl list port_binding 8b108f3d-7402-48f5-bf15-7471db49c066
> >>>>> _uuid               : 8b108f3d-7402-48f5-bf15-7471db49c066
> >>>>> additional_chassis  : []
> >>>>> additional_encap    : []
> >>>>> chassis             : []
> >>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>>>> encap               : []
> >>>>> external_ids        : {}
> >>>>> gateway_chassis     : []
> >>>>> ha_chassis_group    : []
> >>>>> logical_port        : ts1001-dcx1
> >>>>> mac                 : ["00:01:11:00:11:15 169.254.11.5/24"]
> >>>>> mirror_port         : []
> >>>>> mirror_rules        : []
> >>>>> nat_addresses       : []
> >>>>> options             : {chassis-redirect-port=cr-ts1001-dcx1,
> >>>>> peer=ts1001-dcx1-rp}
> >>>>> parent_port         : []
> >>>>> port_security       : []
> >>>>> requested_additional_chassis: []
> >>>>> requested_chassis   : []
> >>>>> tag                 : []
> >>>>> tunnel_key          : 3
> >>>>> type                : patch
> >>>>> up                  : false
> >>>>> virtual_parent      : []
> >>>>>
> >>>>>
> >>>>>
> >>>>> ovn-sbctl list port_binding ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>>>> _uuid               : ae4c72f8-c30d-478f-86f5-2ed8d125f32c
> >>>>> additional_chassis  : []
> >>>>> additional_encap    : []
> >>>>> chassis             : []
> >>>>> datapath            : 2e170dbc-23cb-4de2-98b9-666de3700e71
> >>>>> encap               : []
> >>>>> external_ids        : {}
> >>>>> gateway_chassis     : []
> >>>>> ha_chassis_group    : []
> >>>>> logical_port        : rp-public
> >>>>> mac                 : ["00:00:02:01:02:04 169.254.254.2/30"]
> >>>>> mirror_port         : []
> >>>>> mirror_rules        : []
> >>>>> nat_addresses       : []
> >>>>> options             : {chassis-redirect-port=cr-rp-public,
> >>>> peer=public-rp}
> >>>>> parent_port         : []
> >>>>> port_security       : []
> >>>>> requested_additional_chassis: []
> >>>>> requested_chassis   : []
> >>>>> tag                 : []
> >>>>> tunnel_key          : 1
> >>>>> type                : patch
> >>>>> up                  : false
> >>>>> virtual_parent      : []
> >>>>>
> >>>>>
> >>>>
> >>>> I think this looks similar to the use case Felix had when he added the
> >>>> LRP.options:dynamic-routing-port-name support:
> >>>>
> >>>>
> >>>>
> >>
> https://github.com/ovn-org/ovn/blob/3708cf59ea3759152adaa875dbf244e2e4898650/ovn-nb.xml#L4512C35-L4541
> >>>>
> >>>> Without that config option ovn-controller learns routes on all DGPs,
> in
> >>>> your case rp-public (because it has gateway_chassis=ovn-chassis-4) and
> >>>> ts1001-dcx1 (because it has ha_chassis_group set and the active
> chassis
> >>>> happens to be ovn-chassis-4).
> >>>>
> >>>> As you always bind rp-public to ovn-chassis-4 you could also set:
> >>>>
> >>>> ovn-nbctl set logical_router_port rp-public
> >>>>
> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> >>>>
> >>>> e.g., the bgpvrf1002 port (assuming that's bound on ovn-chassis-4).
> >>>>
> >>>> Then ovn-controller will only learn routes with logical_port=rp-public
> >>>>
> >>>> Would that work for you?
> >>>>
> >>>
> >>> It works partially. If I configured it exactly in the following order,
> it
> >>> works.
> >>> ovn-nbctl -- lr-add dcx1
> >>> # LRP used to Dynamic routing
> >>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> >> --
> >>> lrp-set-options rp-public
> >>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> >>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> >>> ovn-nbctl set logical_router dcx1
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocols=BGP,BFD
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocol-redirect=bgpvrf1002
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-maintain-vrf=true
> >>> *ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-port-name=bgpvrf1002*
> >>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >>>
> >>> # LRPs used to ovn-ic
> >>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> >>> ovn-nbctl set logical_router_port ts1001-dcx1
> >> ha_chassis_group="$HA_GROUP1"
> >>>
> >>>
> >>> If I configure ovn-nbctl set logical_router_port rp-public
> >>>
> options:dynamic-routing-port-name=<a-LSP-that-is-bound-on-ovn-chassis-4>
> >>> after configured
> >>> the ha_chassis_group, route is learned in all LRPs.
> >>>
> >>>
> >>> ovn-nbctl -- lr-add dcx1
> >>> # LRP used to Dynamic routing
> >>> ovn-nbctl -- lrp-add dcx1 rp-public 00:00:02:01:02:04 169.254.254.2/30
> >> --
> >>> lrp-set-options rp-public
> >>> ovn-nbctl set logical_router dcx1 options:dynamic-routing=true
> >>> ovn-nbctl set logical_router dcx1 options:dynamic-routing-vrf-id=1002
> >>> ovn-nbctl set logical_router dcx1
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocols=BGP,BFD
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:routing-protocol-redirect=bgpvrf1002
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-redistribute="connected,static"
> >>> ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-maintain-vrf=true
> >>> ovn-nbctl lrp-set-gateway-chassis rp-public ovn-chassis-4
> >>>
> >>> # LRPs used to ovn-ic
> >>> ovn-nbctl lrp-add dcx1 ts1001-dcx1 12:00:01:d9:b5:d4 169.254.11.5/24
> >>> ovn-nbctl set logical_router_port ts1001-dcx1
> >> ha_chassis_group="$HA_GROUP1"
> >>>
> >>> *ovn-nbctl --wait=sb set logical_router_port rp-public
> >>> options:dynamic-routing-port-name=bgpvrf1002*
> >>>
> >>
> >> This sounds like an incremental processing bug in northd (I guess).
> >>
> >> Can you try an "ovn-appctl -t ovn-northd inc-engine/recompute" on the
> >> node where ovn-northd runs after you ended up in the broken state
> >> (second way of configuring things)?
> >>
> >> Does that "fix" the route learning?
> >>
> >> Thanks,
> >> Dumitru
> >>
> >>
> >>
> >
>
>
Dumitru Ceara Jan. 7, 2026, 3:34 p.m. UTC | #14
On 1/7/26 3:24 PM, Lucas Vargas Dias via dev wrote:
> Em qua., 7 de jan. de 2026 às 10:52, Dumitru Ceara <dceara@redhat.com>
> escreveu:
> 
>> On 1/7/26 1:51 PM, Lucas Vargas Dias wrote:
>>> I try ovn-appctl -t ovn-northd inc-engine/recompute and it doesn't fix
>> the
>>> route learning
>>> I try ovn-appctl -t ovn-controller inc-engine/recompute in the
>>> ovn-chassis-4, also
>>>
>>
>> Oh, I see now, ovn-controller actually has a chance to learn on both
>> router ports before it sees the dynamic-routing-port-name option (which
>> is set later in this case).
>>
>> All routes it learns afterwards will only be learned on rp-public.
>> However, the ones it already learned before the option was set it
>> never cleans up.
>>
>> The problem is in the sb_sync_learned_routes() function:
>>
>>     struct sbrec_learned_route *filter =
>>
>> sbrec_learned_route_index_init_row(sbrec_learned_route_by_datapath);
>>     sbrec_learned_route_index_set_datapath(filter, datapath);
>>     SBREC_LEARNED_ROUTE_FOR_EACH_EQUAL (sb_route, filter,
>>                                         sbrec_learned_route_by_datapath) {
>>         /* If the port is not local we don't care about it.
>>          * Some other ovn-controller will handle it.
>>          * We may not use smap_get since the value might be validly NULL.
>> */
>>         if (!smap_get_node(bound_ports,
>>                            sb_route->logical_port->logical_port)) {
>>             continue;     <<<< at this point bound_ports only contains
>> rp-public
>>         }
>>         route_add_entry(&sync_routes, sb_route, true);
>>     }
>>     sbrec_learned_route_index_destroy_row(filter);
>>
>>
>> So we never add the old route we learned for ts1001-dcx1 to 'sync_routes'.
>> This means the old route will never be flushed.
>>
>> I guess we need to add another check here that also adds 'sb_route' to
>> 'sync_routes' if its logical_port is bound locally.
>>
>> Later in the function, ovn-controller will then flush it out, because it
>> will become stale.
>>
>> Lucas, would you have time to work on a fix for this by any chance?
>> Otherwise, I'll open an issue to try to fix it on our side in the near
>> future.
>>
>>
> Hi,
> 
> I have time to work on this fix.
> 

Thanks a lot, looking forward to the patch!

Regards,
Dumitru

> Regards,
> Lucas
>
diff mbox series

Patch

diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c
index f22aaa664..4c9de8651 100644
--- a/northd/en-learned-route-sync.c
+++ b/northd/en-learned-route-sync.c
@@ -227,6 +227,50 @@  routes_table_sync(
             sbrec_learned_route_delete(sb_route);
             continue;
         }
+        bool is_same_subnet = false;
+        for (size_t i = 0; !is_same_subnet &&
+             i < sb_route->logical_port->n_mac;
+             i++) {
+            struct lport_addresses logical_port_addrs;
+            if (!extract_lsp_addresses(sb_route->logical_port->mac[i],
+                                       &logical_port_addrs)) {
+                destroy_lport_addresses(&logical_port_addrs);
+                continue;
+            }
+            ovs_be32 neigh_prefix_v4;
+            struct in6_addr neigh_prefix_v6;
+
+            if (ip_parse(sb_route->nexthop, &neigh_prefix_v4)) {
+                for (size_t j = 0; j < logical_port_addrs.n_ipv4_addrs;
+                     j++) {
+                    struct ipv4_netaddr address =
+                        logical_port_addrs.ipv4_addrs[j];
+                    if (address.network ==
+                        (neigh_prefix_v4 & address.mask)) {
+                        is_same_subnet = true;
+                        break;
+                    }
+                }
+            } else if (ipv6_parse(sb_route->nexthop, &neigh_prefix_v6)) {
+                for (size_t j = 0; j < logical_port_addrs.n_ipv6_addrs; j++) {
+                    struct ipv6_netaddr address =
+                        logical_port_addrs.ipv6_addrs[j];
+                    struct in6_addr neigh_prefix =
+                        ipv6_addr_bitand(&neigh_prefix_v6, &address.mask);
+                    if (ipv6_addr_equals(&address.network, &neigh_prefix)) {
+                        is_same_subnet = true;
+                        break;
+                    }
+                }
+            }
+            destroy_lport_addresses(&logical_port_addrs);
+        }
+
+
+        if (!is_same_subnet) {
+            sbrec_learned_route_delete(sb_route);
+            continue;
+        }
         parse_route_from_sbrec_route(parsed_routes_out, lr_ports,
                                      &lr_datapaths->datapaths,
                                      sb_route);
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 864854c56..0d1d06196 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -15578,7 +15578,7 @@  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
 ])
 
 # Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on lr0-sw1.
-# This is not reachable so will not produce a lflow.
+# This is not reachable so will not produce a lflow. Also, it'll be removed by northd
 check_uuid ovn-sbctl create Learned_Route \
     datapath=$datapath                    \
     logical_port=$sw1                     \
@@ -15586,7 +15586,9 @@  check_uuid ovn-sbctl create Learned_Route \
     nexthop=\"2001:db8:ffff::20\"
 check ovn-nbctl --wait=sb sync
 check_row_count Advertised_Route 4
-check_row_count Learned_Route 2
+check_row_count Learned_Route 1
+check_row_count Learned_Route 0 logical_port=$sw1 ip_prefix=\"2001:db8:2::/64\"
+
 ovn-sbctl dump-flows lr0 > lr0flows
 AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
   table=??(lr_in_ip_routing   ), priority=0    , match=(1), action=(drop;)
@@ -15605,6 +15607,14 @@  AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
 # active.
 check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \
     networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\""
+
+# Learn a route to 2001:db8:2::/64 via 2001:db8:ffff::20 learned on lr0-sw1.
+# Northd doesn not remove now because lrp have address in same subnet from nexthop
+check_uuid ovn-sbctl create Learned_Route \
+    datapath=$datapath                    \
+    logical_port=$sw1                     \
+    ip_prefix=\"2001:db8:2::/64\"         \
+    nexthop=\"2001:db8:ffff::20\"
 check_row_count Advertised_Route 5
 check_row_count Learned_Route 2
 ovn-sbctl dump-flows lr0 > lr0flows
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 1cbbdfa58..6e87fb266 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -15404,10 +15404,10 @@  wait_for_ports_up mylearninglsp
 check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
       options:dynamic-routing-port-name=mylearninglsp
 
-check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
-check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
+check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
+check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
 check ovn-nbctl --wait=hv sync
-check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20
+check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.10.20
 
 # Stopping the ovn-controller will clean up the route entries created by it.
 # We first need to unset dynamic-routing-maintain-vrf as otherwise it will
@@ -15417,8 +15417,8 @@  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
 OVN_CLEANUP_CONTROLLER([hv1])
 OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Starting it again will add the routes again.
 start_daemon ovn-controller
@@ -15431,8 +15431,8 @@  blackhole 192.0.2.10 proto ovn metric 100
 blackhole 192.0.2.20 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Changing the vrf name will switch to the new one.
 # The old vrf will be removed.
@@ -15449,8 +15449,8 @@  blackhole 192.0.2.10 proto ovn metric 100
 blackhole 192.0.2.20 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Stopping with --restart will not touch the routes.
 OVN_CONTROLLER_EXIT([],[--restart])
@@ -15462,8 +15462,8 @@  blackhole 192.0.2.10 proto ovn metric 100
 blackhole 192.0.2.20 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # When we now stop the ovn-controller it will remove the VRF.
 start_daemon ovn-controller
@@ -15494,8 +15494,8 @@  blackhole 192.0.2.20 proto ovn metric 100
 blackhole 192.0.2.21 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Bind "vip" port locally and check the virtual IP is added in the VRF.
 NS_EXEC([vif4], [arping -U -c 1 -w 2 -I vif4 192.0.2.30])
@@ -15511,8 +15511,8 @@  blackhole 192.0.2.21 proto ovn metric 100
 blackhole 192.0.2.30 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 check ovn-sbctl clear Port_Binding vip virtual-parent
 OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
@@ -15524,8 +15524,8 @@  blackhole 192.0.2.20 proto ovn metric 100
 blackhole 192.0.2.21 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Remove the backoff period, so we can bind it right away.
 check ovn-sbctl remove Port_Binding vip options vport-backoff
@@ -15543,8 +15543,8 @@  blackhole 192.0.2.21 proto ovn metric 100
 blackhole 192.0.2.30 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Simulate "vip" bound to a different chassis.
 check ovn-sbctl clear Port_Binding vip virtual-parent
@@ -15559,8 +15559,8 @@  blackhole 192.0.2.20 proto ovn metric 100
 blackhole 192.0.2.21 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Check with dynamic-routing-redistribute-local-only=false.
 check ovn-nbctl --wait=hv set logical_router_port internet-public \
@@ -15576,8 +15576,8 @@  blackhole 192.0.2.22 proto ovn metric 1000
 blackhole 192.0.2.30 proto ovn metric 1000
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Remove the backoff period, so we can bind it right away.
 check ovn-sbctl remove Port_Binding vip options vport-backoff
@@ -15596,8 +15596,8 @@  blackhole 192.0.2.22 proto ovn metric 1000
 blackhole 192.0.2.30 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 OVN_CLEANUP_CONTROLLER([hv1])
 AT_CHECK([ip vrf | grep -q ovnvrf1338], [1], [])
@@ -15857,7 +15857,7 @@  check ip route add 233.253.0.0/24 via 192.168.10.10 dev lo onlink vrf ovnvrf1337
 check ovn-nbctl --wait=hv sync
 # 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
+check_row_count Learned_Route 1
 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
 
@@ -15892,10 +15892,10 @@  wait_for_ports_up mylearninglsp
 check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
     options:dynamic-routing-port-name=mylearninglsp
 
-check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
-check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
+check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 30 proto zebra
+check ip route add 233.253.0.0/24 via 192.168.10.20 dev hv1-mll onlink vrf ovnvrf1337 metric 40 proto zebra
 check ovn-nbctl --wait=hv sync
-check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20
+check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.10.20
 
 # Stopping the ovn-controller will clean up the route entries created by it.
 # We first need to unset dynamic-routing-maintain-vrf as otherwise it will
@@ -15905,8 +15905,8 @@  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
 OVN_CLEANUP_CONTROLLER([hv1])
 OVN_ROUTE_EQUAL([ovnvrf1337], [dnl
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Starting it again will add the routes again.
 start_daemon ovn-controller
@@ -15919,8 +15919,8 @@  blackhole 192.0.2.10 proto ovn metric 100
 blackhole 192.0.2.20 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Stopping with --restart will not touch the routes.
 OVN_CONTROLLER_EXIT([],[--restart])
@@ -15932,8 +15932,8 @@  blackhole 192.0.2.10 proto ovn metric 100
 blackhole 192.0.2.20 proto ovn metric 100
 blackhole 198.51.100.0/24 proto ovn metric 1000
 233.252.0.0/24 via 192.168.10.10 dev lo proto zebra onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 onlink
-233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 onlink])
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 30 onlink
+233.253.0.0/24 via 192.168.10.20 dev hv1-mll proto zebra metric 40 onlink])
 
 # Now we set maintain-vrf again and stop the ovn-controller.
 # It will then remove the VRF.
@@ -16925,13 +16925,13 @@  check_row_count Learned_Route 1
 AS_BOX([No dynamic-routing-port-name: routes learned on lrp1 and lrp2])
 check ovn-nbctl --wait=hv \
     remove logical_router_port lrp2 options dynamic-routing-port-name
-check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
+check_row_count Learned_Route 0 ip_prefix=3.3.3.0/24 \
                                 nexthop=2.2.2.2      \
                                 logical_port=$lrp1
 check_row_count Learned_Route 1 ip_prefix=3.3.3.0/24 \
                                 nexthop=2.2.2.2      \
                                 logical_port=$lrp2
-check_row_count Learned_Route 2
+check_row_count Learned_Route 1
 
 OVN_CLEANUP_CONTROLLER([hv1])