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.


>  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.


> + 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.


> +
> +# 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]>

Regards,
Ales
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to