On Tue, Mar 24, 2026 at 9:15 AM Felix Huettner <[email protected]> wrote:
> Am Tue, Mar 24, 2026 at 07:51:03AM +0100 schrieb Ales Musil: > > > On Sat, Mar 21, 2026 at 5:12 AM Han Zhou <[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. > > > > > > 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]> > > > --- > > > > > > > Hi Han, > > > > thank you for the patch, as discussed on the v1 > > adding Felix and Frode to cc in case this would > > be breaking change for them. > > Hi everyone, > > from my perspective this should be fine. For the plain ip routing case i > would expect that this should have no effective difference. > Thank you Felix, with that: Acked-by: Ales Musil <[email protected]> > > Thanks, > Felix > > > > > > > > northd/en-learned-route-sync.c | 2 +- > > > tests/multinode.at | 197 +++++++++++++++++++++++++++++++++ > > > tests/ovn-northd.at | 7 +- > > > 3 files changed, 202 insertions(+), 4 deletions(-) > > > > > > 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 > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > > > index 2ccf7a196999..3e7a6f7f8085 100644 > > > --- a/tests/ovn-northd.at > > > +++ b/tests/ovn-northd.at > > > @@ -15730,7 +15730,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. > > > +# Nexthop is off-LRP; northd emits a flow using the first IPv6 on > lr0-sw1. > > > check_uuid ovn-sbctl create Learned_Route \ > > > datapath=$datapath \ > > > logical_port=$sw1 \ > > > @@ -15746,6 +15746,7 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | > > > ovn_strip_lflows], [0], [dnl > > > table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && > > > ip4.dst == 172.16.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > > 10.0.0.11; reg5 = 10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = > > > "lr0-sw0"; flags.loopback = 1; reg9[[9]] = 1; next;) > > > table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && > > > ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 > = > > > 10.0.0.10; reg5 = 10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = > > > "lr0-sw0"; flags.loopback = 1; reg9[[9]] = 1; next;) > > > table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst == > > > 10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; > reg5 = > > > 10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; > flags.loopback > > > = 1; reg9[[9]] = 1; next;) > > > + table=??(lr_in_ip_routing ), priority=514 , match=(reg7 == 0 && > > > ip6.dst == 2001:db8:2::/64), action=(ip.ttl--; reg8[[0..15]] = 0; > xxreg0 = > > > 2001:db8:ffff::20; xxreg1 = 2001:db8::1; eth.src = 00:00:00:00:ff:02; > > > outport = "lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;) > > > table=??(lr_in_ip_routing ), priority=516 , match=(reg7 == 0 && > > > ip6.dst == 2001:db8:1::/64), action=(ip.ttl--; reg8[[0..15]] = 0; > xxreg0 = > > > 2001:db8::10; xxreg1 = 2001:db8::1; eth.src = 00:00:00:00:ff:02; > outport = > > > "lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;) > > > table=??(lr_in_ip_routing ), priority=518 , match=(inport == > > > "lr0-sw0" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = > 0; > > > xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:ff01; eth.src = > > > 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback = 1; reg9[[9]] > = 0; > > > next;) > > > table=??(lr_in_ip_routing ), priority=518 , match=(inport == > > > "lr0-sw1" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = > 0; > > > xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:ff02; eth.src = > > > 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback = 1; reg9[[9]] > = 0; > > > next;) > > > @@ -15753,8 +15754,8 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | > > > ovn_strip_lflows], [0], [dnl > > > ]) > > > > > > # If we now add 2001:db8:ffff::1/64 as an additional network to > lr0-sw1 we > > > -# will get another connected route and the previous received route > will be > > > -# active. > > > +# will get another connected route and the previous logical flow for > the > > > +# route will be updated with this new source IP. > > > check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw1 \ > > > networks="\"2001:db8::1/64\" \"2001:db8:ffff::1/64\"" > > > check_row_count Advertised_Route 5 > > > -- > > > 2.38.1 > > > > > > _______________________________________________ > > > dev mailing list > > > [email protected] > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > > > > > Regards, > > Ales > Regards, Ales _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
