On 10/15/25 12:56 PM, Ales Musil wrote:
> On Tue, Oct 14, 2025 at 2:14 PM Dumitru Ceara via dev <
> [email protected]> wrote:
>
>> This adds a multinode test that verifies OVN's integration with a
>> BGP-L3EVPN provider (FRR). The test ensures that:
>> - Remote EVPN VTEPs are learned properly.
>> - OVN properly learns Type-5 EVPN routes (IP prefix routes).
>> - Traffic flows properly between the OVN node and hosts connected to the
>> external BGP speaker.
>>
>> NOTE: There's a FIXME in the test tracking the fact that OVN currently
>> doesn't support advertising OVN IPs via EVPN. Once that part is
>> implemented the new test will have to slightly be adjusted as the
>> blackhole route it currently adds will not be needed anymore.
>>
>> Reported-at: https://issues.redhat.com/browse/FDP-1391
>> Signed-off-by: Dumitru Ceara <[email protected]>
>> ---
>>
>
> Hi Dumitru,
> thank you for the patch. I have just two small comments.
>
Hi Ales,
Thanks for the review!
>
>> tests/multinode-bgp-macros.at | 141 +++++++++++++++++++++++++++++++--
>> tests/multinode.at | 144 ++++++++++++++++++++++++++++++++++
>> 2 files changed, 280 insertions(+), 5 deletions(-)
>>
>> diff --git a/tests/multinode-bgp-macros.at b/tests/multinode-bgp-macros.at
>> index 06d1806eae..01fe3da503 100644
>> --- a/tests/multinode-bgp-macros.at
>> +++ b/tests/multinode-bgp-macros.at
>> @@ -4,6 +4,20 @@
>>
>> OVS_START_SHELL_HELPERS
>>
>> +# m_frr_ns_flags NETNS
>> +#
>> +# Builds the argument flags string to be passed to FRR when running in a
>> +# network namespace.
>> +m_frr_ns_flags() {
>> + local ns=$1
>> +
>> + if m_is_fedora; then
>> + echo "--vty_socket /run/frr/$ns"
>> + else
>> + echo "-N $ns"
>> + fi
>> +}
>> +
>> # m_setup_external_frr_vrf NODE VNI VXLAN_IP LOCAL_MAC LOCAL_IP NETNS
>> #
>> # Sets up a VRF in NETNS so that it can be used by FRR running in that
>> @@ -104,13 +118,9 @@ m_setup_external_frr_router() {
>> # address.
>> m_config_external_frr_router() {
>> local node=$1 bgp_as=$2 bgp_router_id=$3 bgp_ip=$4 bgp_mac=$5 vni=$6
>> + local frr_flags=$(m_frr_ns_flags frr-ns)
>> local br_name=br-$node
>>
>> - frr_flags="-N frr-ns"
>> - if m_is_fedora; then
>> - frr_flags="--vty_socket /run/frr/frr-ns"
>> - fi
>> -
>> # NOTE: we set "no bgp ebgp-requires-policy" to simplify EVPN
>> deployments.
>> echo "configure
>> ip prefix-list accept-all seq 5 permit any
>> @@ -140,6 +150,64 @@ m_config_external_frr_router() {
>> fi
>> }
>>
>> +# m_config_external_frr_router_l3 NODE BGP_AS BGP_ROUTER_ID BGP_IP
>> BGP_MAC VNI
>> +#
>> +# Configures an external FRR BGP speaker for L3 EVPN in a network
>> namespace
>> +# on the ovn-fake-multinode node NODE. The BGP autonomous system is
>> +# configured to be BGP_AS. The speaker uses as BGP IP address, BGP_IP and
>> +# BGP_MAC as mac address.
>> +m_config_external_frr_router_l3() {
>> + local node=$1 bgp_as=$2 bgp_router_id=$3 bgp_ip=$4 bgp_mac=$5 vni=$6
>> + local frr_flags=$(m_frr_ns_flags frr-ns)
>> + local br_name=br-$node
>> +
>> + # NOTE: we set "no bgp ebgp-requires-policy" to simplify EVPN
>> deployments.
>> + echo "configure
>> + ip prefix-list accept-all seq 5 permit any
>> +
>> + vrf vrf-$vni
>> + vni $vni
>> + exit-vrf
>> +
>> + router bgp $bgp_as
>> + bgp router-id $bgp_router_id
>> + no bgp ebgp-requires-policy
>> +
>> + neighbor ext1 soft-reconfiguration inbound
>> + neighbor ext1 interface remote-as external
>> +
>> + address-family l2vpn evpn
>> + neighbor ext1 activate
>> + advertise-all-vni
>> + advertise-svi-ip
>> + exit-address-family
>> + exit
>> +
>> + router bgp $bgp_as vrf vrf-$vni
>> + bgp router-id $bgp_router_id
>> + no bgp ebgp-requires-policy
>> +
>> + address-family ipv4 unicast
>> + redistribute kernel
>> + redistribute connected
>> + exit-address-family
>> +
>> + address-family ipv6 unicast
>> + redistribute kernel
>> + redistribute connected
>> + exit-address-family
>> +
>> + address-family l2vpn evpn
>> + advertise ipv4 unicast
>> + exit-address-family
>> + exit
>> + " | podman exec -i $node vtysh $frr_flags
>> +
>> + # Configure VRF, if any provided.
>> + vxlan_ip=$(echo $bgp_ip | cut -f 1 -d '/')
>> + m_setup_external_frr_vrf $node $vni $vxlan_ip $bgp_mac $bgp_ip frr-ns
>> +}
>> +
>> # m_ovn_frr_router_name NODE
>> m_ovn_frr_router_name() {
>> local node=$1
>> @@ -415,4 +483,67 @@ m_config_host_frr_router() {
>> m_setup_host_frr_vrf $node $vni $vxlan_ip $bgp_mac $bgp_ip
>> }
>>
>> +# m_config_host_frr_router_l3 NODE BGP_AS BGP_ROUTER_ID BGP_IP BGP_MAC VNI
>> +#
>> +# Sets up an FRR BGP speaker speaker for L3 EVPN in the default network
>> +# namespace on the ovn-fake-multinode node NODE. This speaker is running
>> on
>> +# a ovs bridge interface (simulating the fabric connection).
>> +#
>> +# The BGP autonomous system is configured to be BGP_AS and the FRR
>> instance
>> +# runs in vrf-VNI.
>> +m_config_host_frr_router_l3() {
>> + local node=$1 bgp_as=$2 bgp_router_id=$3 bgp_ip=$4 bgp_mac=$5 vni=$6
>> +
>> + local br_name=br-$node physnet=physnet_${node}_ext0
>> + local lr=$(m_ovn_frr_router_name $node)
>> + local lrp=$(m_ovn_frr_router_port_name $node)
>> + local ls=$(m_ovn_frr_switch_name $node)
>> + local lsp=$(m_ovn_frr_switch_port_name $node)
>> + local lsp_bgp=$(m_ovn_frr_switch_bgp_port_name $node)
>> + local lsp_ln=$(m_ovn_frr_switch_localnet_port_name $node)
>> +
>> + # NOTE: we set "no bgp ebgp-requires-policy" to simplify EVPN
>> deployments.
>> + echo "configure
>> + vrf vrf-$vni
>> + vni $vni
>> + exit-vrf
>> +
>> + ip prefix-list no-default seq 5 deny 0.0.0.0/0
>> + ip prefix-list no-default seq 10 permit 0.0.0.0/0 le 32
>> +
>> + ipv6 prefix-list no-default seq 5 deny ::/0
>> + ipv6 prefix-list no-default seq 10 permit ::/0 le 128
>> +
>> + router bgp ${bgp_as}
>> + bgp router-id $bgp_router_id
>> + no bgp ebgp-requires-policy
>> + neighbor $br_name interface remote-as external
>> +
>> + address-family l2vpn evpn
>> + neighbor $br_name activate
>> + advertise-all-vni
>> + advertise-svi-ip
>> + exit-address-family
>> + exit
>> +
>> + router bgp ${bgp_as} vrf vrf-$vni
>> + bgp router-id $bgp_router_id
>> + no bgp ebgp-requires-policy
>> +
>> + address-family ipv4 unicast
>> + redistribute kernel
>> + redistribute connected
>> + exit-address-family
>> +
>> + address-family l2vpn evpn
>> + advertise ipv4 unicast
>> + exit-address-family
>> + exit
>> + " | podman exec -i $node vtysh
>> +
>> + # Configure VRF.
>> + vxlan_ip=$(echo $bgp_ip | cut -f 1 -d '/')
>> + m_setup_host_frr_vrf $node $vni $vxlan_ip $bgp_mac $bgp_ip
>> +}
>> +
>> OVS_END_SHELL_HELPERS
>> diff --git a/tests/multinode.at b/tests/multinode.at
>> index 604a9bd0f2..c7541aea44 100644
>> --- a/tests/multinode.at
>> +++ b/tests/multinode.at
>> @@ -3864,6 +3864,150 @@ OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-ofctl
>> dump-flows br-int table=OFTABLE_GET
>>
>> AT_CLEANUP
>>
>> +AT_SETUP([ovn multinode bgp L3 EVPN])
>> +check_fake_multinode_setup
>> +
>> +# Delete the multinode NB and OVS resources before starting the test.
>> +cleanup_multinode_resources
>> +
>> +CHECK_VRF()
>> +
>> +vni=10
>> +ext_bgp_ip_gw1=42.10.$vni.11
>> +ext_bgp_mac_gw1=00:00:01:00:00:$vni
>> +host_bgp_ip_gw1=42.10.$vni.12
>> +host_bgp_mac_gw1=00:00:00:01:00:$vni
>> +ext_bgp_ip_gw2=42.20.$vni.21
>> +ext_bgp_mac_gw2=00:00:02:00:00:$vni
>> +host_bgp_ip_gw2=42.20.$vni.22
>> +host_bgp_mac_gw2=00:00:00:02:00:$vni
>> +
>> +# Create an flat, distributed OVN localnet switch, with EVPN configured.
>> + check m_as ovn-gw-1 ovs-vsctl set open .
>> external-ids:ovn-bridge-mappings=public:br-ex
>> + check m_as ovn-gw-2 ovs-vsctl set open .
>> external-ids:ovn-bridge-mappings=public:br-ex
>> + check m_as ovn-gw-1 ovs-vsctl set open .
>> external-ids:ovn-evpn-local-ip=$host_bgp_ip_gw1
>> + check m_as ovn-gw-2 ovs-vsctl set open .
>> external-ids:ovn-evpn-local-ip=$host_bgp_ip_gw2
>> + # The tunnels need to be created before the call to
>> "m_setup_host_frr_router()".
>>
>
> The comment is slightly inaccurate, it should be "m_config_host_frr_router"
> instead.
>
Good point, thanks for spotting this!
>
>> + check m_as ovn-gw-1 ovs-vsctl set open .
>> external-ids:ovn-evpn-vxlan-ports=4789
>> + check m_as ovn-gw-2 ovs-vsctl set open .
>> external-ids:ovn-evpn-vxlan-ports=4789
>> +
>> +m_setup_external_frr_router ovn-gw-1
>> $ext_bgp_ip_gw1/24
>> +m_config_external_frr_router_l3 ovn-gw-1 4200000100 $ext_bgp_ip_gw1
>> $ext_bgp_ip_gw1/24 $ext_bgp_mac_gw1 $vni
>> +m_setup_host_frr_router ovn-gw-1 4210000000 $host_bgp_ip_gw1
>> $host_bgp_ip_gw1/24 $host_bgp_mac_gw1 $vni
>> +m_config_host_frr_router_l3 ovn-gw-1 4210000000 $host_bgp_ip_gw1
>> $host_bgp_ip_gw1/24 $host_bgp_mac_gw1 $vni
>> +
>> +m_setup_external_frr_router ovn-gw-2
>> $ext_bgp_ip_gw2/24
>> +m_config_external_frr_router_l3 ovn-gw-2 4200000200 $ext_bgp_ip_gw2
>> $ext_bgp_ip_gw2/24 $ext_bgp_mac_gw2 $vni
>> +m_setup_host_frr_router ovn-gw-2 4210000000 $host_bgp_ip_gw2
>> $host_bgp_ip_gw2/24 $host_bgp_mac_gw2 $vni
>> +m_config_host_frr_router_l3 ovn-gw-2 4210000000 $host_bgp_ip_gw2
>> $host_bgp_ip_gw2/24 $host_bgp_mac_gw2 $vni
>> +
>> +OVS_WAIT_UNTIL([m_as ovn-gw-1 vtysh -c 'show bgp neighbors' | grep -qE
>> 'Connections established 1'])
>> +OVS_WAIT_UNTIL([m_as ovn-gw-2 vtysh -c 'show bgp neighbors' | grep -qE
>> 'Connections established 1'])
>> +
>> +AS_BOX([Simulate external "fabric" hosts])
>> +check m_as ovn-gw-1 ip link add name lo-wl-$vni type dummy
>> +on_exit "m_as ovn-gw-1 ip link del lo-wl-$vni"
>> +check m_as ovn-gw-1 ip link set lo-wl-$vni address 00:01:01:01:01:$vni
>> +check m_as ovn-gw-1 ip link set lo-wl-$vni master vrf-$vni
>> +check m_as ovn-gw-1 ip a a dev lo-wl-$vni 77.77.1.$vni/32
>> +check m_as ovn-gw-1 ip link set lo-wl-$vni up
>> +
>> +check m_as ovn-gw-2 ip link add name lo-wl-$vni type dummy
>> +on_exit "m_as ovn-gw-2 ip link del lo-wl-$vni"
>> +check m_as ovn-gw-2 ip link set lo-wl-$vni address 00:02:02:02:02:$vni
>> +check m_as ovn-gw-2 ip link set lo-wl-$vni master vrf-$vni
>> +check m_as ovn-gw-2 ip a a dev lo-wl-$vni 77.77.2.$vni/32
>> +check m_as ovn-gw-2 ip link set lo-wl-$vni up
>> +
>> +check multinode_nbctl
>> \
>> + -- lr-add lr
>> \
>> + -- set logical_router lr options:dynamic-routing=true
>> \
>> + options:requested-tnl-key=$vni
>> \
>> + -- lrp-add lr lr-gw1 $host_bgp_mac_gw1 $host_bgp_ip_gw1/24
>> \
>> + -- lrp-set-gateway-chassis lr-gw1 ovn-gw-1 10
>> \
>> + -- lrp-add lr lr-gw2 $host_bgp_mac_gw2 $host_bgp_ip_gw2/24
>> \
>> + -- lrp-set-gateway-chassis lr-gw2 ovn-gw-2 10
>> \
>> + -- lrp-add lr lr-int1 00:00:00:00:01:02 30.0.1.1/24
>> \
>> + -- lrp-set-options lr-int1 dynamic-routing-redistribute=connected
>> \
>> + -- lrp-add lr lr-int2 00:00:00:00:01:02 30.0.2.1/24
>> \
>> + -- lrp-set-options lr-int2 dynamic-routing-redistribute=connected
>> \
>> + -- ls-add ls
>> \
>> + -- lsp-add ls ls-ln
>> \
>> + -- lsp-set-type ls-ln localnet
>> \
>> + -- lsp-set-addresses ls-ln unknown
>> \
>> + -- lsp-set-options ls-ln network_name=public
>> \
>> + -- lsp-add ls ls-lr-gw1
>> \
>> + -- lsp-set-type ls-lr-gw1 router
>> \
>> + -- lsp-set-options ls-lr-gw1 router-port=lr-gw1
>> \
>> + -- lsp-set-addresses ls-lr-gw1 router
>> \
>> + -- lsp-add ls ls-lr-gw2
>> \
>> + -- lsp-set-type ls-lr-gw2 router
>> \
>> + -- lsp-set-options ls-lr-gw2 router-port=lr-gw2
>> \
>> + -- lsp-set-addresses ls-lr-gw2 router
>> \
>> + -- ls-add ls-int1
>> \
>> + -- lsp-add ls-int1 ls-int1-lr
>> \
>> + -- lsp-set-type ls-int1-lr router
>> \
>> + -- lsp-set-options ls-int1-lr router-port=lr-int1
>> \
>> + -- lsp-set-addresses ls-int1-lr router
>> \
>> + -- ls-add ls-int2
>> \
>> + -- lsp-add ls-int2 ls-int2-lr
>> \
>> + -- lsp-set-type ls-int2-lr router
>> \
>> + -- lsp-set-options ls-int2-lr router-port=lr-int2
>> \
>> + -- lsp-set-addresses ls-int2-lr router
>> +
>> +check multinode_nbctl \
>> + -- lsp-add ls-int1 w1 \
>> + -- lsp-set-addresses w1 "00:00:00:00:00:01 30.0.1.11" \
>> + -- lsp-add ls-int2 w2 \
>> + -- lsp-set-addresses w2 "00:00:00:00:00:02 30.0.2.11"
>> +
>> +check m_as ovn-gw-1 /data/create_fake_vm.sh w1 w1 \
>> + 00:00:00:00:00:01 1500 30.0.1.11 24 30.0.1.1 1000::11/64 1000::1
>> +check m_as ovn-gw-2 /data/create_fake_vm.sh w2 w2 \
>> + 00:00:00:00:00:02 1500 30.0.2.11 24 30.0.2.1 1000::11/64 1000::1
>> +
>> +m_wait_for_ports_up
>> +
>> +# Enable EVPN support for the distributed logical switch.
>> +check multinode_nbctl set logical_switch ls \
>> + other_config:dynamic-routing-vni=$vni
>> +
>> +check multinode_nbctl --wait=hv sync
>> +dp_key=$(m_fetch_column Datapath_Binding tunnel_key external_ids:name=ls)
>> +
>> +OVS_WAIT_UNTIL([m_as ovn-gw-1 bridge fdb | grep vxlan-10 | grep -q
>> "$ext_bgp_mac_gw1"])
>> +OVS_WAIT_UNTIL([m_as ovn-gw-2 bridge fdb | grep vxlan-10 | grep -q
>> "$ext_bgp_mac_gw2"])
>> +
>> +AS_BOX([Checking Remote VTEPs learned in OVN])
>> +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
>> +])
>> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([m_as ovn-gw-2 ovn-appctl
>> evpn/remote-vtep-list | sort], [0], [dnl
>> +IP: $ext_bgp_ip_gw2, port: 4789, vni: 10
>> +])
>> +
>> +# TODO FIXME: https://issues.redhat.com/browse/FDP-1390
>> +# OVN should support advertising router IPs on EVPN logical switches
>> connected
>> +# to logical routers. Until then simulate that with a blackhole route.
>> +check m_as ovn-gw-1 ip route add table $vni blackhole $host_bgp_ip_gw1/32
>> +on_exit "m_as ovn-gw-1 ip route del table $vni $host_bgp_ip_gw1/32"
>> +check m_as ovn-gw-2 ip route add table $vni blackhole $host_bgp_ip_gw2/32
>> +on_exit "m_as ovn-gw-2 ip route del table $vni $host_bgp_ip_gw2/32"
>> +
>> +AS_BOX([Check traffic to "fabric" hosts - ping from fabric])
>> +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-2 ip netns exec frr-ns ip vrf exec vrf-$vni
>> ping -c1 30.0.2.11])
>>
>
> As discussed offline we should add a check that there is only
> FDB entry for the bridge MAC.
>
Ack, done.
>
>> +
>> +# Check that OVN learned the ARPs.
>> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([m_as ovn-gw-1 ovn-appctl evpn/vtep-arp-list
>> | cut -d',' -f2- | sort], [0], [dnl
>> + VNI: $vni, MAC: 00:00:01:00:00:10, IP: 42.10.10.11, dp_key: $dp_key
>> +])
>> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([m_as ovn-gw-2 ovn-appctl evpn/vtep-arp-list
>> | cut -d',' -f2- | sort], [0], [dnl
>> + VNI: $vni, MAC: 00:00:02:00:00:10, IP: 42.20.10.21, dp_key: $dp_key
>> +])
>> +
>> +AT_CLEANUP
>> +
>> AT_SETUP([redirect-bridged to non-gw destination switch port])
>>
>> check_fake_multinode_setup
>> --
>> 2.51.0
>>
>> _______________________________________________
>> dev mailing list
>> [email protected]
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>>
> With the above addressed:
> Acked-by: Ales Musil <[email protected]>
>
Thanks for the review! Applied to main.
Regards,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev