On Sun, Mar 22, 2026 at 8:10 AM Lorenzo Bianconi <
[email protected]> wrote:
>
> On Mar 20, Han Zhou wrote:
> > On Fri, Mar 20, 2026 at 8:09 AM Lorenzo Bianconi <
> > [email protected]> wrote:
> > >
> > > > Dynamic routing was designed so BGP-learned routes are only accepted
> > > > when the nexthop lies in a subnet of the logical router port they
are
> > > > learned on—right for plain IP routing.
> > > >
> > > > With L3 EVPN (IP-VRF / type-5), routes can be learned on a port
with a
> > > > nexthop outside any configured LRP subnet (indirect nexthop).
> > > >
> > > > find_route_outport(..., force_out_port=false) then fails and the
route
> > > > never reaches the pipeline. Pass force_out_port=true for SB
> > > > Learned_Route parsing so that the routes can be handled properly.
> > >
> > > Hi Han,
> > >
> > > what about if the logical router port has configured multiple IPs and
the
> > given
> > > next-hop is reachable via the second configured IP and not the second
> > one? Is
> > > this a valid use-case?
> >
> > Hi Lorenzo, sorry that I didn't understand your question. I guess you
had
> > typo "via the second configured IP and not the s/second/first/ one" but
I
> > am still confused. If the next-hop is reachable via any of the
configured
> > IP then that IP will just be selected. Is that a problem?
>
> Hi Han,
>
> yes, sorry for the typo. What I mean is find_route_outport() will alwats
select
> the lrp first IP if lrp_addr_s pointer is NULL and force_out_port is true,
> correct? I was wondering if we have 2 IPs on the lrp and the nexhop is
actually
> reachable via the second one.
If the second IP subnet matches the nexthop, the below line should assign
the second IP to lrp_addr_s, and it won't be NULL.
*lrp_addr_s = lrp_find_member_ip(*out_port, nexthop);
The force_out_port takes effect only if there is no LRP IP matching the
nexthop.
Best,
Han
>
> Regards,
> Lorenzo
>
> >
> > Thanks,
> > Han
> >
> > >
> > > Regards,
> > > Lorenzo
> > >
> > > >
> > > > Add multinode L3 EVPN case with a transit router (non-L2-adjacent
> > > > VTEPs).
> > > >
> > > > Reported-at: https://redhat.atlassian.net/browse/FDP-3476
> > > > Fixes: 966ca1c919ce ("northd: Handle learned routes.")
> > > > Assisted-by: Cursor, with model: claude-4.6-opus-high
> > > > Signed-off-by: Han Zhou <[email protected]>
> > > > ---
> > > > northd/en-learned-route-sync.c | 2 +-
> > > > tests/multinode.at | 197
+++++++++++++++++++++++++++++++++
> > > > 2 files changed, 198 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/northd/en-learned-route-sync.c
> > b/northd/en-learned-route-sync.c
> > > > index a1bfa13ae172..4f7a12a28fdc 100644
> > > > --- a/northd/en-learned-route-sync.c
> > > > +++ b/northd/en-learned-route-sync.c
> > > > @@ -189,7 +189,7 @@ parse_route_from_sbrec_route(struct hmap
> > *parsed_routes_out,
> > > > if (!find_route_outport(lr_ports,
> > route->logical_port->logical_port,
> > > > "static route", route->ip_prefix,
> > route->nexthop,
> > > > IN6_IS_ADDR_V4MAPPED(nexthop),
> > > > - false,
> > > > + true,
> > > > &out_port, &lrp_addr_s)) {
> > > > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
1);
> > > > VLOG_WARN_RL(&rl, "could not find output port %s for
learned
> > route "
> > > > diff --git a/tests/multinode.at b/tests/multinode.at
> > > > index 6b9614126e26..bc1eb7a7c8e0 100644
> > > > --- a/tests/multinode.at
> > > > +++ b/tests/multinode.at
> > > > @@ -4580,6 +4580,203 @@ fe80::/64 dev lo-wl-10 proto kernel metric
256
> > pref medium])
> > > >
> > > > AT_CLEANUP
> > > >
> > > > +AT_SETUP([ovn multinode bgp L3 EVPN with intermediate router -
VTEPs
> > not L2 adjacent])
> > > > +check_fake_multinode_setup
> > > > +cleanup_multinode_resources
> > > > +CHECK_VRF()
> > > > +
> > > > +gw=ovn-gw-1
> > > > +vni=10
> > > > +ext_bgp_ip_gw1=42.10.10.11
> > > > +router_ext_ip=42.10.10.1
> > > > +ext_bgp_ip6_gw1=42:10:10::11
> > > > +ext_bgp_mac_gw1=00:00:01:00:00:$vni
> > > > +host_bgp_ip_gw1=42.10.20.12
> > > > +router_host_ip=42.10.20.1
> > > > +host_bgp_ip6_gw1=42:10:20::12
> > > > +host_bgp_mac_gw1=00:00:00:01:00:$vni
> > > > +nat_ip_gw1=42.10.20.13
> > > > +nat_ip6_gw1=42:10:20::13
> > > > +lb_ip_gw1=42.10.20.14
> > > > +lb_ip6_gw1=42:10:20::14
> > > > +ext_as=4200000100
> > > > +host_as=4210000000
> > > > +router_as=4290000000
> > > > +
> > > > +check m_as $gw ovs-vsctl add-br br-$gw
> > > > +on_exit "m_as $gw ovs-vsctl del-br br-$gw"
> > > > +check m_as $gw ip netns add router-ns
> > > > +on_exit "m_as $gw ip netns del router-ns"
> > > > +check m_as $gw ip link add host-to-r type veth peer name r-host
> > > > +on_exit "m_as $gw ip link del host-to-r"
> > > > +check m_as $gw ip link set r-host netns router-ns
> > > > +check m_as $gw ovs-vsctl add-port br-$gw host-to-r
> > > > +check m_as $gw ip link set host-to-r up
> > > > +check m_as $gw ip link set br-$gw up
> > > > +check m_as $gw ip netns exec router-ns ip link set lo up
> > > > +check m_as $gw ip netns exec router-ns ip addr add
$router_host_ip/24
> > dev r-host
> > > > +check m_as $gw ip netns exec router-ns ip link set r-host up
> > > > +
> > > > +check m_as $gw ip netns add frr-ns
> > > > +on_exit "m_as $gw ip netns del frr-ns"
> > > > +check m_as $gw ip link add ext1 type veth peer name ext1-r
> > > > +check m_as $gw ip link set ext1 netns frr-ns
> > > > +check m_as $gw ip link set ext1-r netns router-ns
> > > > +check m_as $gw ip netns exec frr-ns ip link set lo up
> > > > +check m_as $gw ip netns exec frr-ns ip addr add $ext_bgp_ip_gw1/24
dev
> > ext1
> > > > +check m_as $gw ip netns exec frr-ns ip link set ext1 up
> > > > +check m_as $gw ip netns exec router-ns ip addr add
$router_ext_ip/24
> > dev ext1-r
> > > > +check m_as $gw ip netns exec router-ns ip link set ext1-r up
> > > > +check m_as $gw ip netns exec router-ns sysctl -qw
net.ipv4.ip_forward=1
> > > > +
> > > > +check m_as $gw ovs-vsctl set open .
> > external-ids:ovn-bridge-mappings=public:br-ex
> > > > +check m_as $gw ovs-vsctl set open .
> > external-ids:ovn-evpn-local-ip=$host_bgp_ip_gw1
> > > > +check m_as $gw ovs-vsctl set open .
> > external-ids:ovn-evpn-vxlan-ports=4789
> > > > +
> > > > +# FRR in frr-ns (external EVPN speaker) and router-ns (BGP EVPN
> > transit between ext1-r and r-host).
> > > > +check m_as $gw sed -i 's/bgpd=no/bgpd=yes/g' /etc/frr/daemons
> > > > +check m_as $gw sed -i 's/StartLimitBurst=.*/StartLimitBurst=100/g'
> > /usr/lib/systemd/system/frr.service
> > > > +check m_as $gw systemctl daemon-reload
> > > > +for ns in frr-ns router-ns; do
> > > > + check m_as $gw mkdir -p /etc/frr/$ns
> > > > + check m_as $gw cp -r /etc/frr/daemons /etc/frr/frr.conf
> > /etc/frr/$ns/
> > > > + check m_as $gw rm -f /etc/frr/$ns/vtysh.conf
> > > > + check m_as $gw touch /etc/frr/$ns/vtysh.conf
> > > > +done
> > > > +check m_as $gw systemctl stop frr
> > > > +
> > > > +check m_as $gw mkdir -p /run/frr/frr-ns /run/frr/router-ns
> > > > +if m_as $gw getent passwd frr > /dev/null; then
> > > > + check m_as $gw chown -R frr:frr /run/frr/frr-ns
/run/frr/router-ns
> > > > +fi
> > > > +for ns in frr-ns router-ns; do
> > > > + for init in /usr/libexec/frr/frrinit.sh
/usr/lib/frr/frrinit.sh; do
> > > > + if m_as $gw test -x "$init"; then
> > > > + m_as $gw ip netns exec $ns "$init" start $ns
> > > > + break
> > > > + fi
> > > > + done
> > > > +done
> > > > +on_exit "m_as $gw ip netns exec frr-ns /usr/libexec/frr/frrinit.sh
> > stop frr-ns"
> > > > +on_exit "m_as $gw ip netns exec router-ns
/usr/libexec/frr/frrinit.sh
> > stop router-ns"
> > > > +check m_as $gw systemctl start frr
> > > > +m_as $gw vtysh -c 'configure' -c "no router bgp $router_as" -c
'exit'
> > > > +
> > > > +m_config_external_frr_router_l3 $gw $ext_as $ext_bgp_ip_gw1
> > $ext_bgp_ip_gw1/24 $ext_bgp_ip6_gw1/64 $ext_bgp_mac_gw1 $vni
> > > > +
> > > > +router_flags=$(m_frr_ns_flags router-ns)
> > > > +echo "configure
> > > > +ip prefix-list accept-all seq 5 permit any
> > > > +ipv6 prefix-list accept-all seq 5 permit any
> > > > +router bgp $router_as
> > > > + bgp router-id $router_ext_ip
> > > > + no bgp ebgp-requires-policy
> > > > + neighbor ext1-r interface remote-as $ext_as
> > > > + neighbor ext1-r passive
> > > > + neighbor r-host interface remote-as $host_as
> > > > + address-family ipv4 unicast
> > > > + neighbor ext1-r prefix-list accept-all in
> > > > + neighbor r-host prefix-list accept-all in
> > > > + redistribute connected
> > > > + exit-address-family
> > > > + address-family l2vpn evpn
> > > > + neighbor ext1-r activate
> > > > + neighbor r-host activate
> > > > + advertise-all-vni
> > > > + exit-address-family
> > > > +exit
> > > > +end
> > > > +" | podman exec -i $gw ip netns exec router-ns vtysh $router_flags
> > > > +
> > > > +check m_as $gw ip addr add $host_bgp_ip_gw1/24 dev br-$gw
> > > > +check m_as $gw ip link set br-$gw up
> > > > +
> > > > +check multinode_nbctl
> > \
> > > > + -- lr-add lr
> > \
> > > > + -- set logical_router lr options:dynamic-routing=true
> > \
> > > > + options:dynamic-routing-vrf-id=$vni
> > \
> > > > + -- lrp-add lr lr-gw1 $host_bgp_mac_gw1 $host_bgp_ip_gw1/24
> > $host_bgp_ip6_gw1/64 \
> > > > + -- lrp-set-gateway-chassis lr-gw1 $gw 10
> > \
> > > > + -- lrp-set-options lr-gw1
dynamic-routing-redistribute=nat,lb
> > \
> > > > + -- lrp-add lr lr-int1 00:00:00:00:01:02 30.0.1.1/24
30:1::1/64
> > \
> > > > + -- lrp-set-options lr-int1
> > dynamic-routing-redistribute=connected \
> > > > + -- ls-add ls
> > \
> > > > + -- lsp-add-localnet-port ls ls-ln public
> > \
> > > > + -- lsp-add-router-port ls ls-lr-gw1 lr-gw1
> > \
> > > > + -- ls-add ls-int1
> > \
> > > > + -- lsp-add-router-port ls-int1 ls-int1-lr lr-int1
> > > > +check multinode_nbctl
> > \
> > > > + -- lsp-add ls-int1 w1
> > \
> > > > + -- lsp-set-addresses w1 "00:00:00:00:00:01 30.0.1.11
30:1::11"
> > \
> > > > + -- lr-nat-add lr dnat_and_snat $nat_ip_gw1 30.0.1.11 w1
> > 00:00:00:00:01:11 \
> > > > + -- lr-nat-add lr dnat_and_snat $nat_ip6_gw1 30:1::11 w1
> > 00:00:00:00:01:11 \
> > > > + -- lb-add lb1 $lb_ip_gw1 30.0.1.11
> > \
> > > > + -- lb-add lb2 $lb_ip6_gw1 30:1::11
> > \
> > > > + -- lr-lb-add lr lb1
> > \
> > > > + -- lr-lb-add lr lb2
> > > > +check m_as $gw /data/create_fake_vm.sh w1 w1 \
> > > > + 00:00:00:00:00:01 1500 30.0.1.11 24 30.0.1.1 30:1::11/64
30:1::1
> > > > +m_wait_for_ports_up
> > > > +check multinode_nbctl --wait=hv sync
> > > > +check multinode_nbctl set logical_switch ls
> > \
> > > > + other_config:dynamic-routing-vni=$vni
> > \
> > > > + other_config:dynamic-routing-bridge-ifname=br-$vni
> > \
> > > > + other_config:dynamic-routing-vxlan-ifname=vxlan-$vni
> > \
> > > > + other_config:dynamic-routing-advertise-ifname=lo-$vni
> > \
> > > > + other_config:dynamic-routing-redistribute=fdb,ip
> > > > +check multinode_nbctl --wait=hv sync
> > > > +check m_as $gw ovs-ofctl add-flow br-$gw
> > "priority=100,ip,nw_dst=$host_bgp_ip_gw1,udp,tp_dst=4789,actions=LOCAL"
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 test -d
/sys/class/net/vxlan_sys_4789])
> > > > +m_as $gw vtysh -c 'configure' -c 'no router bgp 4290000000' -c 'no
> > router bgp 4210000000' -c 'exit'
> > > > +m_config_host_frr_router_l3 $gw $host_as $host_bgp_ip_gw1
> > $host_bgp_ip_gw1/24 $host_bgp_mac_gw1 $vni
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 vtysh -c 'show bgp neighbors' | grep
-qE
> > 'Connections established 1'])
> > > > +
> > > > +check m_as $gw ip link add name lo-wl-$vni type dummy
> > > > +on_exit "m_as $gw ip link del lo-wl-$vni"
> > > > +check m_as $gw ip link set lo-wl-$vni address 00:01:01:01:01:$vni
> > > > +check m_as $gw ip link set lo-wl-$vni master vrf-$vni
> > > > +check m_as $gw ip addr add dev lo-wl-$vni 77.77.1.$vni/32
> > > > +check m_as $gw ip -6 addr add dev lo-wl-$vni 77:77::1:$vni/64 nodad
> > > > +check m_as $gw ip link set lo-wl-$vni up
> > > > +
> > > > +# Extra fabric in frr-ns vrf: 99.99.99.99/32, 88.88.0.0/24 +
> > fabric-wl-ns workload.
> > > > +check m_as $gw ip netns exec frr-ns ip link add lo-99 type dummy
> > > > +on_exit "m_as $gw ip netns exec frr-ns ip link del lo-99"
> > > > +check m_as $gw ip netns exec frr-ns ip link set lo-99 master
vrf-$vni
> > > > +check m_as $gw ip netns exec frr-ns ip addr add 99.99.99.99/32 dev
> > lo-99
> > > > +check m_as $gw ip netns exec frr-ns ip link set lo-99 up
> > > > +check m_as $gw ip netns exec frr-ns sysctl -qw
net.ipv4.ip_forward=1
> > > > +check m_as $gw ip netns add fabric-wl-ns
> > > > +on_exit "m_as $gw ip netns del fabric-wl-ns"
> > > > +check m_as $gw ip netns exec frr-ns ip link add br-fabric-wl type
> > bridge
> > > > +check m_as $gw ip netns exec frr-ns ip link set br-fabric-wl master
> > vrf-$vni
> > > > +check m_as $gw ip netns exec frr-ns ip addr add 88.88.0.1/24 dev
> > br-fabric-wl
> > > > +check m_as $gw ip netns exec frr-ns ip link set br-fabric-wl up
> > > > +check m_as $gw ip netns exec frr-ns ip link add fabric-wl type veth
> > peer name fabric-wl-r
> > > > +check m_as $gw ip netns exec frr-ns ip link set fabric-wl-r master
> > br-fabric-wl
> > > > +check m_as $gw ip netns exec frr-ns ip link set fabric-wl-r up
> > > > +check m_as $gw ip netns exec frr-ns ip link set fabric-wl netns
> > fabric-wl-ns
> > > > +check m_as $gw ip netns exec fabric-wl-ns ip link set lo up
> > > > +check m_as $gw ip netns exec fabric-wl-ns ip addr add 88.88.0.10/24
> > dev fabric-wl
> > > > +check m_as $gw ip netns exec fabric-wl-ns ip link set fabric-wl up
> > > > +check m_as $gw ip netns exec fabric-wl-ns ip route add default via
> > 88.88.0.1
> > > > +
> > > > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([m_as ovn-gw-1 ovn-appctl
> > evpn/remote-vtep-list | sort], [0], [dnl
> > > > +IP: $ext_bgp_ip_gw1, port: 4789, vni: 10
> > > > +])
> > > > +
> > > > +m_wait_row_count Learned_Route 1 ip_prefix=88.88.0.0/24
> > > > +
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip route show
vrf
> > vrf-$vni | grep -q 30.0.1])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip vrf exec
> > vrf-$vni ping -c1 30.0.1.11])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip vrf exec
> > vrf-$vni ping -6 -c1 30:1::11])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip vrf exec
> > vrf-$vni ping -c1 $nat_ip_gw1])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip vrf exec
> > vrf-$vni ping -c1 $lb_ip_gw1])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec fabric-wl-ns ping -c1
> > 30.0.1.11])
> > > > +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip netns exec frr-ns ip vrf exec
> > vrf-$vni ping -I 99.99.99.99 -c1 30.0.1.11])
> > > > +
> > > > +AT_CLEANUP
> > > > +
> > > > AT_SETUP([redirect-bridged to non-gw destination switch port])
> > > >
> > > > check_fake_multinode_setup
> > > > --
> > > > 2.38.1
> > > >
> > > > _______________________________________________
> > > > dev mailing list
> > > > [email protected]
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev