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

Reply via email to