From: Dumitru Ceara <dce...@redhat.com> This adds a multinode test that verifies OVN's integration with a BGP-L2EVPN provider (FRR). The test ensures that: - Remote EVPN VTEPs are learned properly. - OVN owned MACs are advertised to the remote BGP speakers through EVPN. - Traffic flows properly between the OVN node and hosts connected to the external BGP speaker.
Reported-at: https://issues.redhat.com/browse/FDP-1542 Co-Authored-by: Ales Musil <amu...@redhat.com> Signed-off-by: Ales Musil <amu...@redhat.com> Signed-off-by: Dumitru Ceara <dce...@redhat.com> --- Changes in v2: - Improved test to check EVPN macs before and after the workload is running. --- tests/multinode-bgp-macros.at | 166 +++++++++++++++++++++++++++++++++- tests/multinode.at | 134 ++++++++++++++++++++++++++- 2 files changed, 293 insertions(+), 7 deletions(-) diff --git a/tests/multinode-bgp-macros.at b/tests/multinode-bgp-macros.at index 803aef909..4545d570c 100644 --- a/tests/multinode-bgp-macros.at +++ b/tests/multinode-bgp-macros.at @@ -4,13 +4,50 @@ OVS_START_SHELL_HELPERS -# m_setup_external_frr_router NODE BGP_AS BGP_IP +# m_setup_external_frr_vrf NODE VNI LOCAL_IP NETNS +# +# Sets up a VRF in NETNS so that it can be used by FRR running in that +# namespace to advertise EVPN routes. +m_setup_external_frr_vrf() { + local node=$1 vni=$2 local_ip=$3 ns=$4 + + local ns_prefix="ip netns exec $ns " + local vrf=vrf-$vni + local br=br-$vni + local vxlan=vxlan-$vni + local lo=lo-$vni + + check m_as $node $ns_prefix ip link add $vrf type vrf table $vni + on_exit "m_as $node $ns_prefix ip link del $vrf" + check m_as $node $ns_prefix ip link set $vrf up + + # Add VNI bridge. + check m_as $node $ns_prefix ip link add $br type bridge + on_exit "m_as $node $ns_prefix ip link del $br" + check m_as $node $ns_prefix ip link set $br master $vrf addrgenmode none + check m_as $node $ns_prefix ip link set dev $br up + + # Add VXLAN VTEP for the VNI. + check m_as $node $ns_prefix ip link add $vxlan type vxlan id $vni dstport 4789 local $local_ip nolearning + on_exit "m_as $node $ns_prefix ip link del $vxlan" + check m_as $node $ns_prefix ip link set dev $vxlan up + check m_as $node $ns_prefix ip link set $vxlan master $br + + # Add a dummy loopback to the VNI bridge to be used for advertising local + # MACs. + check m_as $node $ns_prefix ip link add name $lo type dummy + on_exit "m_as $node $ns_prefix ip link del $lo" + check m_as $node $ns_prefix ip link set $lo master $br + check m_as $node $ns_prefix ip link set $lo up +} + +# m_setup_external_frr_router NODE BGP_AS BGP_ROUTER_ID BGP_IP [VNI] # # Sets up an external FRR BGP speaker 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 m_setup_external_frr_router() { - local node=$1 bgp_as=$2 bgp_ip=$3 + local node=$1 bgp_as=$2 bgp_router_id=$3 bgp_ip=$4 vni=$5 local br_name=br-$node check m_as $node ovs-vsctl add-br $br_name @@ -55,27 +92,38 @@ m_setup_external_frr_router() { on_exit "m_as $node systemctl stop frr" check m_as $node systemctl start frr - local frr_flags="-N frr-ns" + 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 router bgp $bgp_as - bgp router-id $bgp_ip + bgp router-id $bgp_router_id + no bgp ebgp-requires-policy neighbor ext1 interface remote-as external address-family ipv4 unicast neighbor ext1 soft-reconfiguration inbound neighbor ext1 prefix-list accept-all in exit-address-family - ! address-family ipv6 unicast neighbor ext1 soft-reconfiguration inbound neighbor ext1 activate exit-address-family + address-family l2vpn evpn + neighbor ext1 activate + advertise-all-vni + exit-address-family " | podman exec -i $node vtysh $frr_flags + + # Configure VRF, if any provided. + bgp_ip=$(echo $bgp_ip | cut -f 1 -d '/') + if [[[ -n "$vni" ]]]; then + m_setup_external_frr_vrf $node $vni $bgp_ip frr-ns + fi } # m_ovn_frr_router_name NODE @@ -218,4 +266,112 @@ m_setup_ovn_frr_router() { " | podman exec -i $node vtysh } +# m_setup_host_frr_vrf NODE VNI LOCAL_IP +# +# Sets up a VRF in the default net namespace so that it can be used by OVN +# and FRR to advertise EVPN routes. +m_setup_host_frr_vrf() { + local node=$1 vni=$2 local_ip=$3 + + local vrf=vrf-$vni + local br=br-$vni + local vxlan=vxlan-$vni + local lo=lo-$vni + local vxlan_port=4789 + + # Create a VXLAN tunnel OVS interface to be used by ovn-controller for + # forwarding/receiving traffic from dynamic remote VTEPs. + check m_as $node ovs-vsctl add-port br-int vxlan-ovs \ + -- set interface vxlan-ovs type=vxlan \ + options:local_ip=flow options:remote_ip=flow options:key=flow \ + options:dst_port=$vxlan_port + + # Setup a VRF for the VNI. + check m_as $node ip link add $vrf type vrf table $vni + on_exit "m_as $node ip link del $vrf" + check m_as $node ip link set $vrf up + + # Add VNI bridge. + check m_as $node ip link add $br type bridge + on_exit "m_as $node ip link del $br" + check m_as $node ip link set $br master $vrf addrgenmode none + check m_as $node ip link set dev $br up + + # Add VXLAN VTEP for the VNI (linked to the OVS vxlan_sys_<port> interface). + # Use a dstport different than the one used by OVS. + # This is fine because we don't actually want traffic to pass through + # the $vxlan interface. FRR should read the dstport from the linked + # vxlan_sys_${vxlan_port} device. + local dstport=$((60000 + $vni)) + check m_as $node ip link add $vxlan type vxlan \ + dev vxlan_sys_${vxlan_port} \ + id $vni dstport $dstport local $local_ip nolearning + on_exit "m_as $node ip link del $vxlan" + check m_as $node ip link set dev $vxlan up + check m_as $node ip link set $vxlan master $br + + # Add a dummy loopback to the VNI bridge to be used for advertising local + # MACs. + check m_as $node ip link add name $lo type dummy + on_exit "m_as $node ip link del $lo" + check m_as $node ip link set $lo master $br + check m_as $node ip link set $lo up +} + +# m_setup_host_frr_router NODE BGP_AS BGP_ROUTER_ID BGP_IP VNI +# +# Sets up an FRR BGP speaker 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_setup_host_frr_router() { + local node=$1 bgp_as=$2 bgp_router_id=$3 bgp_ip=$4 vni=$5 + + 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) + + # Configure the BGP IP on the bridge LOCAL interface. + check m_as $node ip addr add $bgp_ip dev $br_name + check m_as $node ip link set $br_name up + + # NOTE: we set "no bgp ebgp-requires-policy" to simplify EVPN deployments. + echo "configure + 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 ipv4 unicast + redistribute kernel + neighbor $br_name prefix-list no-default out + exit-address-family + address-family ipv6 unicast + neighbor $br_name soft-reconfiguration inbound + neighbor $br_name prefix-list no-default out + redistribute kernel + neighbor $br_name activate + exit-address-family + address-family l2vpn evpn + neighbor $br_name activate + advertise-all-vni + exit-address-family + " | podman exec -i $node vtysh + + # Configure VRF. + bgp_ip=$(echo $bgp_ip | cut -f 1 -d '/') + m_setup_host_frr_vrf $node $vni $bgp_ip +} + OVS_END_SHELL_HELPERS diff --git a/tests/multinode.at b/tests/multinode.at index 8ac625b6b..f217e5c46 100644 --- a/tests/multinode.at +++ b/tests/multinode.at @@ -3055,10 +3055,10 @@ add_guest_vm_and_connections() { $default_route $default_route_gw $lrp_guest_join } -m_setup_external_frr_router ovn-gw-1 4200000100 41.41.41.41 +m_setup_external_frr_router ovn-gw-1 4200000100 41.41.41.41 41.41.41.41/32 m_setup_ovn_frr_router ovn-gw-1 4210000000 14.14.14.14 12:fb:d6:66:99:1c 10 -m_setup_external_frr_router ovn-gw-2 4200000200 42.42.42.42 +m_setup_external_frr_router ovn-gw-2 4200000200 42.42.42.42 42.42.42.42/32 m_setup_ovn_frr_router ovn-gw-2 4210000000 24.24.24.24 22:fb:d6:66:99:2c 20 OVS_WAIT_UNTIL([m_as ovn-gw-2 vtysh -c 'show bgp vrf ovnvrf20 neighbors' | grep -qE 'Connections established 1']) @@ -3613,3 +3613,133 @@ check_migration_between_gw1_and_gw2 "kill_gw1" AT_CLEANUP ]) + +AT_SETUP([ovn multinode bgp L2 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.42.$vni.11 +host_bgp_ip_gw1=42.42.$vni.12 +ext_bgp_ip_gw2=42.42.$vni.21 +host_bgp_ip_gw2=42.42.$vni.22 + +m_setup_external_frr_router ovn-gw-1 4200000100 $ext_bgp_ip_gw1 $ext_bgp_ip_gw1/24 $vni +m_setup_host_frr_router ovn-gw-1 4210000000 $host_bgp_ip_gw1 $host_bgp_ip_gw1/24 $vni + +m_setup_external_frr_router ovn-gw-2 4200000200 $ext_bgp_ip_gw2 $ext_bgp_ip_gw2/24 $vni +m_setup_host_frr_router ovn-gw-2 4210000000 $host_bgp_ip_gw2 $host_bgp_ip_gw2/24 $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']) + +# 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 + +check multinode_nbctl 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 + +# Configure "workloads" (VIF LSPs) on both chassis. +check multinode_nbctl lsp-add ls w1 \ + -- lsp-set-addresses w1 "00:00:00:00:00:01 10.0.0.11" +check multinode_nbctl lsp-add ls w2 \ + -- lsp-set-addresses w2 "00:00:00:00:00:02 10.0.0.12" + +check m_as ovn-gw-1 /data/create_fake_vm.sh w1 w1 00:00:00:00:00:01 1500 10.0.0.11 24 10.0.0.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 10.0.0.12 24 10.0.0.1 1000::12/64 1000::1 +m_wait_for_ports_up + +# Enable EVPN support for the distributed logical switch and redistribute +# local FDBs. +check multinode_nbctl set logical_switch ls \ + other_config:dynamic-routing-vni=$vni \ + other_config:dynamic-routing-redistribute=fdb +check multinode_nbctl --wait=hv sync + +OVS_WAIT_UNTIL([m_as ovn-gw-1 bridge fdb | grep vxlan-10 | grep -q "00:00:00:00:00:00"]) +OVS_WAIT_UNTIL([m_as ovn-gw-2 bridge fdb | grep vxlan-10 | grep -q "00:00:00:00:00:00"]) + +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 +]) + +AS_BOX([Check traffic to "fabric" hosts - simulate external workloads]) +check m_as ovn-gw-1 ip link add evpn_host type veth peer evpn_host_peer +on_exit "m_as ovn-gw-1 ip link del evpn_host" +check m_as ovn-gw-1 ip link set netns frr-ns evpn_host_peer +check m_as ovn-gw-1 ip netns exec frr-ns ip link set evpn_host_peer master br-10 +check m_as ovn-gw-1 ip netns exec frr-ns ip link set evpn_host_peer up +check m_as ovn-gw-1 ip link set evpn_host addr 00:00:00:00:01:00 +check m_as ovn-gw-1 ip addr add dev evpn_host 10.0.0.41/24 +check m_as ovn-gw-1 ip link set evpn_host up + +check m_as ovn-gw-2 ip link add evpn_host type veth peer evpn_host_peer +on_exit "m_as ovn-gw-2 ip link del evpn_host" +check m_as ovn-gw-2 ip link set netns frr-ns evpn_host_peer +check m_as ovn-gw-2 ip netns exec frr-ns ip link set evpn_host_peer master br-10 +check m_as ovn-gw-2 ip netns exec frr-ns ip link set evpn_host_peer up +check m_as ovn-gw-2 ip link set evpn_host addr 00:00:00:00:02:00 +check m_as ovn-gw-2 ip addr add dev evpn_host 10.0.0.42/24 +check m_as ovn-gw-2 ip link set evpn_host up + +AS_BOX([Checking EVPN MACs on External BGP host]) +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ip netns exec frr-ns vtysh --vty_socket /run/frr/frr-ns -c 'show evpn mac vni all'], [0], [dnl + +VNI 10 #MACs (local and remote) 2 + +Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy +MAC Type Flags Intf/Remote ES/VTEP VLAN Seq #'s +00:00:00:00:00:01 remote 42.42.10.12 0/0 +00:00:00:00:01:00 local evpn_host_peer 0/0 +]) + +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ip netns exec frr-ns vtysh --vty_socket /run/frr/frr-ns -c 'show evpn mac vni all'], [0], [dnl + +VNI 10 #MACs (local and remote) 2 + +Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy +MAC Type Flags Intf/Remote ES/VTEP VLAN Seq #'s +00:00:00:00:00:02 remote 42.42.10.22 0/0 +00:00:00:00:02:00 local evpn_host_peer 0/0 +]) + +AS_BOX([Check traffic to "fabric" hosts - ping from fabric]) +OVS_WAIT_UNTIL([m_as ovn-gw-1 ping -W 1 -c 1 10.0.0.11]) +OVS_WAIT_UNTIL([m_as ovn-gw-2 ping -W 1 -c 1 10.0.0.12]) + +# Remove "workloads" (VIF LSPs) on both chassis. +check multinode_nbctl --wait=hv lsp-del w1 -- lsp-del w2 + +AS_BOX([Checking EVPN MACs on External BGP host]) +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ip netns exec frr-ns vtysh --vty_socket /run/frr/frr-ns -c 'show evpn mac vni all'], [0], [dnl + +VNI 10 #MACs (local and remote) 1 + +Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy +MAC Type Flags Intf/Remote ES/VTEP VLAN Seq #'s +00:00:00:00:01:00 local evpn_host_peer 0/0 +]) + +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ip netns exec frr-ns vtysh --vty_socket /run/frr/frr-ns -c 'show evpn mac vni all'], [0], [dnl + +VNI 10 #MACs (local and remote) 1 + +Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy +MAC Type Flags Intf/Remote ES/VTEP VLAN Seq #'s +00:00:00:00:02:00 local evpn_host_peer 0/0 +]) + +AT_CLEANUP -- 2.50.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev