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
