aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCui,Cheng <cheng.cui@intel.com>2022-05-19 06:48:04 +0000
committerPeter Mikus <peter.mikus@protonmail.ch>2022-06-09 05:10:18 +0000
commit75ef277536c43096e7646430568d97d2cc489238 (patch)
tree6f590a8d2704c2a7e57f1d343f2523f31b63182f
parent13a3991322bc77e5bc88c6b3970c191adb487f7d (diff)
Add Test Suite for VPP WireGuard
Signed-off-by: Cui,Cheng <cheng.cui@intel.com> Change-Id: I71e257e5f16a887a40ff83d782a9ebdee9d05c34 Signed-off-by: Cui,Cheng <cheng.cui@intel.com>
-rw-r--r--docs/tag_documentation.rst4
-rw-r--r--resources/api/vpp/supported_crcs.yaml6
-rw-r--r--resources/libraries/python/WireGuardUtil.py278
-rw-r--r--resources/libraries/robot/wireguard/wireguard.robot58
-rw-r--r--tests/vpp/perf/ip4_tunnels/10ge2p1x710-ethip4udpwireguard1tnlsw-ip4base-ndrpdr.robot168
5 files changed, 513 insertions, 1 deletions
diff --git a/docs/tag_documentation.rst b/docs/tag_documentation.rst
index e8eb64743b..d9861a218e 100644
--- a/docs/tag_documentation.rst
+++ b/docs/tag_documentation.rst
@@ -539,6 +539,10 @@ Encapsulation Tags
All test cases with IPSEC.
+.. topic:: WIREGUARD
+
+ All test cases with WIREGUARD.
+
.. topic:: SRv6
All test cases with Segment routing over IPv6 dataplane.
diff --git a/resources/api/vpp/supported_crcs.yaml b/resources/api/vpp/supported_crcs.yaml
index fc7308acf1..30ea8bf6a2 100644
--- a/resources/api/vpp/supported_crcs.yaml
+++ b/resources/api/vpp/supported_crcs.yaml
@@ -301,7 +301,11 @@
vxlan_add_del_tunnel_v3_reply: '0x5383d31f' # dev
# vxlan_gpe_tunnel_dump / details # honeycomb
# vxlan_tunnel_dump /details # unused L2 keyword: Get VXLAN dump
-# Please keep alphabetic order.
+ wireguard_interface_create: '0xa530137e'
+ wireguard_interface_create_reply: '0x5383d31f'
+ wireguard_peer_add: '0x9b8aad61'
+ wireguard_peer_add_reply: '0x084a0cd3'
+ # Please keep alphabetic order.
# Use bash command "env LC_COLLATE=C sort -u" if not clear.
# Hint to see the currently used command messages:
diff --git a/resources/libraries/python/WireGuardUtil.py b/resources/libraries/python/WireGuardUtil.py
new file mode 100644
index 0000000000..d8d2396164
--- /dev/null
+++ b/resources/libraries/python/WireGuardUtil.py
@@ -0,0 +1,278 @@
+# Copyright (c) 2022 Intel and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""WireGuard utilities library."""
+
+from ipaddress import ip_address
+from cryptography.hazmat.primitives.serialization import Encoding, \
+ PrivateFormat, PublicFormat, NoEncryption
+from cryptography.hazmat.primitives.asymmetric.x25519 import \
+ X25519PrivateKey
+
+from resources.libraries.python.InterfaceUtil import InterfaceUtil
+from resources.libraries.python.IPUtil import IPUtil
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
+
+class WireGuardUtil:
+ """This class defines the methods to set WireGuard."""
+
+ @staticmethod
+ def public_key_bytes(k):
+ """Return the public key as byte.
+
+ :param k: Generated public key.
+ :type: x25519._X25519PublicKey object
+ :returns: Public key.
+ :rtype: bytes
+ """
+ return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
+
+ @staticmethod
+ def private_key_bytes(k):
+ """Return the private key as byte.
+
+ :param k: Generated private key.
+ :type: x25519._X25519PrivateKey object
+ :returns: Private key.
+ :rtype: bytes
+ """
+ return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
+
+ @staticmethod
+ def generate_wireguard_privatekey_and_pubkey():
+ """Generate a pair of WireGuard Private key and Public key.
+
+ :returns: A pair of privatekey and publickey
+ :rtype: x25519._X25519PublicKey object
+ """
+ privatekey = X25519PrivateKey.generate()
+ pubkey = privatekey.public_key()
+ private_key = WireGuardUtil.private_key_bytes(privatekey)
+ public_key = WireGuardUtil.public_key_bytes(pubkey)
+ return private_key, public_key
+
+ @staticmethod
+ def vpp_wireguard_create_interface(
+ node, listen_port, wg_src, private_key):
+ """Create WireGuard interface.
+
+ :param node: VPP node to add config on.
+ :param listen_port: WireGuard interface listen port.
+ :param wg_src: WireGuard srouce IPv4.
+ :param private_key: WireGuard interface private key
+ :type node: dict
+ :type listen_port: int
+ :type wg_src: str
+ :type private_key: bytes
+ :returns: Wireguard interface sw_if_index.
+ :rtype: int
+ """
+ cmd = u"wireguard_interface_create"
+ err_msg = f"Failed to create wireguard interface" \
+ f"on host {node[u'host']}"
+ src_ip = ip_address(wg_src)
+ args = dict(
+ interface=dict(
+ port=int(listen_port),
+ src_ip=src_ip,
+ private_key=private_key,
+ generate_key=False
+ )
+ )
+ with PapiSocketExecutor(node) as papi_exec:
+ wg_sw_index = \
+ papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
+ return wg_sw_index
+
+ @staticmethod
+ def vpp_wireguard_add_peer(
+ node, interface, peer_pubkey, endpoint_ip,
+ allowed_ips, n_allowed_ips, dst_port, keepalive_time):
+ """Add a peer for WireGuard interface.
+
+ :param node: VPP node to add config on.
+ :param interface: WireGuard interface sw_if_index.
+ :param peer_pubkey: Public key of wireguard interface peer.
+ :param endpoint_ip: Peer source IPv4.
+ :param allowed_ips: WireGuard interface allowed ips list.
+ :param n_allowed_ips: Number of allowed ips.
+ :param dst_port: WireGuard destination port.
+ :param keepaliva time: WireGuard persistent keepalive time.
+ :type node: dict
+ :type interface: int
+ :type peer_pubkey: bytes
+ :type endpoint_ip: str
+ :type allowed_ips: list
+ :type n_allowed_ips: int
+ :type dst_port: int
+ :type keepalive_time: int
+ """
+ endpoint_ip = ip_address(endpoint_ip)
+ wg_name = InterfaceUtil.vpp_get_interface_name(
+ node, sw_if_index=interface
+ )
+ cmd = u"wireguard_peer_add"
+ err_msg = f"Failed to add wireguard interface" \
+ f"{wg_name} peer on host {node[u'host']}"
+ args = dict(
+ peer=dict(
+ public_key=peer_pubkey,
+ port=int(dst_port),
+ endpoint=endpoint_ip,
+ sw_if_index=interface,
+ persistent_keepalive=int(keepalive_time),
+ n_allowed_ips=int(n_allowed_ips),
+ allowed_ips=allowed_ips
+ )
+ )
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args).get_reply(err_msg)
+
+ @staticmethod
+ def _wireguard_create_tunnel_interface_on_dut(
+ node, if1_key, if2_mac_addr, src_ip, peer_endpoint_ip,
+ peer_allowed_ips, peer_n_allowed_ips, dut_wg_ip, port,
+ keepalive_time, dut_private_key, peer_pubkey):
+ """Create WireGuard tunnel interface on one DUT node using PAPI.
+
+ :param node: VPP node as DUT to create tunnel interface.
+ :param if1_key: VPP node as DUT interface key from topology file.
+ :param if2_mac_addr: Vpp node on the other end/ TG node
+ (in case of 2-node topology) interface mac address.
+ :param src_ip: WireGuard source IPv4 address.
+ :param peer_endpoint_ip: Peer source IPv4 address.
+ :param peer_allowed_ips: WireGuard peer interface allowed ip list.
+ :param peer_n_allowed ips: Number of peer allowed ips.
+ :param dut_wg_ip: WireGuard interface ip address on DUT.
+ :param port: WireGuard interface listen port or
+ Peer interface destination port.
+ :param keepalive_time: WireGuard persistent keepalive time.
+ :param dut_private_key: WireGuard interface private key of DUT.
+ :param peer_pubkey: WireGuard Peer interface public key.
+ :type nodes: dict
+ :type if1_key: str
+ :type if2_mac_addr: str
+ :type src_ip: src
+ :type peer_endpoint_ip: src
+ :type peer_allowed_ips: list
+ :type peer_n_allowed_ips: int
+ :type dut_wg_ip: src
+ :type port: int
+ :type keepalive_time: int
+ :type dut_private_key: bytes
+ :type peer_pubkey: bytes
+ """
+ #Set IP address on VPP node interface
+ IPUtil.vpp_interface_set_ip_address(node, if1_key, src_ip, 24)
+ IPUtil.vpp_add_ip_neighbor(
+ node, if1_key, peer_endpoint_ip, if2_mac_addr
+ )
+ #Create Wireguard interface on DUT
+ dut_wg_sw_index = WireGuardUtil.vpp_wireguard_create_interface(
+ node, port, src_ip, dut_private_key
+ )
+ #Add wireguard peer
+ WireGuardUtil.vpp_wireguard_add_peer(
+ node, dut_wg_sw_index, peer_pubkey, peer_endpoint_ip,
+ peer_allowed_ips, peer_n_allowed_ips, port, keepalive_time
+ )
+ #Set wireguard interface up
+ InterfaceUtil.set_interface_state(node, dut_wg_sw_index, state=u'up')
+ #Set wireguard interface IP address
+ cmd = u'sw_interface_add_del_address'
+ args = dict(
+ sw_if_index=dut_wg_sw_index,
+ is_add=True,
+ del_all=False,
+ prefix=IPUtil.create_prefix_object(ip_address(dut_wg_ip), 24)
+ )
+ err_msg = f"Failed to set IP address on wg interface " \
+ f"on host {node[u'host']}"
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args).get_reply(err_msg)
+ #Set route on VPP node as DUT wg interface
+ for allowed_ip in peer_allowed_ips:
+ traffic_addr = ip_address(
+ allowed_ip[u'address'][u'un'][u'ip4']
+ )
+ prefix_len = allowed_ip[u'len']
+ IPUtil.vpp_route_add(
+ node, traffic_addr, prefix_len,
+ gateway=(traffic_addr+1).compressed,
+ interface=dut_wg_sw_index
+ )
+
+ @staticmethod
+ def vpp_wireguard_create_tunnel_interface_on_duts(
+ nodes, if1_key, if2_key, if1_ip_addr, if2_ip_addr,
+ if1_mac_addr, if2_mac_addr, wg_if1_ip_addr, wg_if2_ip_addr,
+ n_allowed_ips, port, keepalive_time, raddr_ip1, raddr_ip2):
+ """Create WireGuard tunnel interfaces between two VPP nodes.
+
+ :param nodes: VPP nodes to create tunnel interfaces.
+ :param if1_key: VPP node 1 interface key from topology file.
+ :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
+ :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
+ :param if2_ip_addr: VPP node 2 / TG node
+ (in case of 2-node topology) interface IPv4/IPv6 address.
+ :param if1_mac_addr: VPP node1 interface mac address.
+ :param if2_mac_addr: VPP node2 interface mac address.
+ :param wg_if1_ip_addr: VPP node 1 WireGuard interface IPv4 address.
+ :param wg_if2_ip_addr: VPP node 2 WireGuard interface IPv4 address.
+ :param allowed_ips: WireGuard interface allowed ip list.
+ :param n_allowed_ips: Number of allowed ips.
+ :param port: WireGuard interface listen port or
+ Peer interface destination port.
+ :param keepalive_time: WireGuard persistent keepalive time.
+ :param raddr_ip1: Policy selector remote IPv4/IPv6 start address
+ for the first tunnel in direction node1->node2.
+ :param raddr_ip2: Policy selector remote IPv4/IPv6 start address
+ for the first tunnel in direction node2->node1.
+ :type nodes: dict
+ :type if1_key: str
+ :type if2_key: str
+ :type if1_ip_addr: str
+ :type if2_ip_addr: str
+ :type if1_mac_addr: str
+ :type if2_mac_addr: str
+ :type wg_if1_ip_addr: str
+ :type wg_if2_ip_addr: str
+ :type allowed_ips: str
+ :type n_allowed_ips: int
+ :type port: int
+ :type keepalive_time: int
+ :type raddr_ip1: str
+ :type raddr_ip2: str
+ """
+ dut1_privatekey, dut1_pubkey = \
+ WireGuardUtil.generate_wireguard_privatekey_and_pubkey()
+ dut2_privatekey, dut2_pubkey = \
+ WireGuardUtil.generate_wireguard_privatekey_and_pubkey()
+ raddr_ip1 = ip_address(raddr_ip1)
+ raddr_ip2 = ip_address(raddr_ip2)
+ dut1_allowed_ips = \
+ [IPUtil.create_prefix_object(raddr_ip2, 24),]
+ dut2_allowed_ips = \
+ [IPUtil.create_prefix_object(raddr_ip1, 24),]
+ #Configure WireGuard interface on DUT1
+ WireGuardUtil._wireguard_create_tunnel_interface_on_dut(
+ nodes[u'DUT1'], if1_key, if2_mac_addr, if1_ip_addr, if2_ip_addr,
+ dut1_allowed_ips, n_allowed_ips, wg_if1_ip_addr, port,
+ keepalive_time, dut1_privatekey, dut2_pubkey
+ )
+ #Configure WireGuard interface on DUT2
+ WireGuardUtil._wireguard_create_tunnel_interface_on_dut(
+ nodes[u'DUT2'], if2_key, if1_mac_addr, if2_ip_addr, if1_ip_addr,
+ dut2_allowed_ips, n_allowed_ips, wg_if2_ip_addr, port,
+ keepalive_time, dut2_privatekey, dut1_pubkey
+ )
diff --git a/resources/libraries/robot/wireguard/wireguard.robot b/resources/libraries/robot/wireguard/wireguard.robot
new file mode 100644
index 0000000000..ca794d0c36
--- /dev/null
+++ b/resources/libraries/robot/wireguard/wireguard.robot
@@ -0,0 +1,58 @@
+# Copyright (c) 2022 Intel and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Library | String
+| Library | resources.libraries.python.InterfaceUtil
+| Library | resources.libraries.python.WireGuardUtil
+| Library | resources.libraries.python.IPUtil
+| Library | resources.libraries.python.IPv6Util
+|
+| Documentation | Wireguard keywords.
+
+*** Keywords ***
+| Generate keys for WireGuard
+| | [Documentation] | Generate a pair of keys for WireGuard
+| |
+| | ... | _NOTE:_ This KW sets following test case variable:
+| | ... | - private_key - wireguard Private key. Type: bytes
+| | ... | - pub_key - wireguard public key. Type: bytes
+| |
+| | ... | *Example:*
+| | ... | \| ${private_key} | ${pub_key} |
+| | ... | \| Generate Wireguard Privatekey and Pubkey \|
+| |
+| | ${private_key} | ${pub_key} | Generate Wireguard Privatekey and Pubkey
+| | Set Test Variable | ${private_key}
+| | Set Test Variable | ${pub_key}
+
+| Initialize WireGuard in 3-node circular topology
+| | [Documentation]
+| | ... | Set UP state on VPP interfaces in path on nodes in 3-node circular
+| | ... | topology. Get the interface MAC addresses and setup ARP on VPP
+| | ... | interfaces towards TG. Setup IPv4 addresses with /24 prefix on DUT-TG
+| | ... | links. Set routing for decrypted traffic on both DUT nodes
+| | ... | with prefix /8 and next hop of neighbour TG interface IPv4 address.
+| |
+| | VPP Interface Set IP Address
+| | ... | ${dut1} | ${DUT1_${int}1}[0] | ${dut1_if1_ip4} | 24
+| | VPP Interface Set IP Address
+| | ... | ${dut2} | ${DUT2_${int}2}[0] | ${dut2_if2_ip4} | 24
+| | VPP Add IP Neighbor
+| | ... | ${dut1} | ${DUT1_${int}1}[0] | ${tg_if1_ip4} | ${TG_pf1_mac}[0]
+| | VPP Add IP Neighbor
+| | ... | ${dut2} | ${DUT2_${int}2}[0] | ${tg_if2_ip4} | ${TG_pf2_mac}[0]
+| | Vpp Route Add | ${dut1} | ${laddr_ip4} | 8 | gateway=${tg_if1_ip4}
+| | ... | interface=${DUT1_${int}1}[0]
+| | Vpp Route Add | ${dut2} | ${raddr_ip4} | 8 | gateway=${tg_if2_ip4}
+| | ... | interface=${DUT2_${int}2}[0]
diff --git a/tests/vpp/perf/ip4_tunnels/10ge2p1x710-ethip4udpwireguard1tnlsw-ip4base-ndrpdr.robot b/tests/vpp/perf/ip4_tunnels/10ge2p1x710-ethip4udpwireguard1tnlsw-ip4base-ndrpdr.robot
new file mode 100644
index 0000000000..a27e2357a7
--- /dev/null
+++ b/tests/vpp/perf/ip4_tunnels/10ge2p1x710-ethip4udpwireguard1tnlsw-ip4base-ndrpdr.robot
@@ -0,0 +1,168 @@
+# Copyright (c) 2022 Intel and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Resource | resources/libraries/robot/shared/default.robot
+| Resource | resources/libraries/robot/wireguard/wireguard.robot
+|
+| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | NDRPDR | TNL_1
+| ... | IP4FWD | NIC_Intel-X710 | WIREGUARD | DRV_VFIO_PCI
+| ... | RXQ_SIZE_0 | TXQ_SIZE_0
+| ... | ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+|
+| Suite Setup | Setup suite topology interfaces | performance
+| Suite Teardown | Tear down suite | performance
+| Test Setup | Setup test | performance
+|
+| Test Template | Local Template
+|
+| Documentation | **RFC2544: Pkt throughput IPv4 WireGuard tunnel mode.**
+| ... |
+| ... | - **[Top] Network Topologies:** TG-DUT1-DUT2-TG 3-node circular \
+| ... | topology with single links between nodes.
+| ... |
+| ... | - **[Enc] Packet Encapsulations:** Eth-IPv4 on TG-DUTn, \
+| ... | Eth-IPv4-UDP-WireGuard on DUT1-DUT2.
+| ... |
+| ... | - **[Cfg] DUT configuration:** DUT1 and DUT2 are configured with \
+| ... | single WireGuard tunnels between them. DUTs get IPv4 traffic from TG, \
+| ... | and send to another DUT, where packets are decrypted and sent back \
+| ... | to TG.
+| ... |
+| ... | - **[Ver] TG verification:** TG finds and reports throughput NDR (Non \
+| ... | Drop Rate) with zero packet loss tolerance and throughput PDR \
+| ... | (Partial Drop Rate) with non-zero packet loss tolerance (LT) \
+| ... | expressed in percentage of packets transmitted. NDR and PDR are \
+| ... | discovered for different Ethernet L2 frame sizes using MLRsearch \
+| ... | library.
+| ... | Test packets are generated by TG on \
+| ... | links to DUTs. TG traffic profile contains two L3 flow-groups \
+| ... | (flow-group per direction, number of flows per flow-group equals to \
+| ... | number of WireGuard tunnels) with all packets \
+| ... | containing Ethernet header, IPv4 header with IP protocol=61 and \
+| ... | static payload. MAC addresses are matching MAC addresses of the TG \
+| ... | node interfaces. Incrementing of IP.dst (IPv4 destination address) \
+| ... | is applied to both streams.
+| ... |
+| ... | - **[Ref] Applicable standard specifications:** RFC4303 and RFC2544.
+
+
+*** Variables ***
+| @{plugins_to_enable}= | dpdk_plugin.so | perfmon_plugin.so
+| ... | crypto_native_plugin.so | crypto_ipsecmb_plugin.so
+| ... | wireguard_plugin.so | crypto_openssl_plugin.so
+| ${crypto_type}= | ${None}
+| ${nic_name}= | Intel-X710
+| ${nic_driver}= | vfio-pci
+| ${nic_rxq_size}= | 0
+| ${nic_txq_size}= | 0
+| ${nic_pfs}= | 2
+| ${nic_vfs}= | 0
+| ${osi_layer}= | L3
+| ${overhead}= | ${60}
+| ${tg_if1_ip4}= | 192.168.10.2
+| ${dut1_if1_ip4}= | 192.168.10.1
+| ${dut1_if2_ip4}= | 200.0.0.1
+| ${dut2_if1_ip4}= | 200.0.0.2
+| ${dut2_if2_ip4}= | 192.168.20.1
+| ${tg_if2_ip4}= | 192.168.20.2
+| ${wg_if1_ip4}= | 192.168.110.1
+| ${wg_if2_ip4}= | 192.168.120.1
+| ${raddr_ip4}= | 20.0.0.0
+| ${laddr_ip4}= | 10.0.0.0
+| ${n_tunnels}= | ${1}
+| ${listen_port}= | ${51820}
+| ${keepalive_time}= | ${256}
+# Traffic profile:
+| ${traffic_profile}= | trex-stl-3n-ethip4-ip4dst${n_tunnels}
+
+*** Keywords ***
+| Local Template
+| | [Documentation]
+| | ... | - **[Cfg]** DUT runs wireguard TUNNEL. \
+| | ... | Each DUT uses ${phy_cores} physical core(s) for worker threads.
+| | ... | - **[Ver]** Measure NDR and PDR values using MLRsearch algorithm.
+| |
+| | ... | *Arguments:*
+| | ... | - frame_size - Framesize in Bytes in integer or string (IMIX_v4_1).
+| | ... | Type: integer, string
+| | ... | - phy_cores - Number of physical cores. Type: integer
+| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
+| |
+| | [Arguments] | ${frame_size} | ${phy_cores} | ${rxq}=${None}
+| |
+| | Set Test Variable | \${frame_size}
+| |
+| | Given Set Max Rate And Jumbo
+| | And Add worker threads to all DUTs | ${phy_cores} | ${rxq}
+| | And Pre-initialize layer driver | ${nic_driver}
+| | And Apply startup configuration on all VPP DUTs
+| | When Initialize layer driver | ${nic_driver}
+| | And Initialize layer interface
+| | And Initialize WireGuard in 3-node circular topology
+| | And VPP WireGuard Create Tunnel Interface On DUTs
+| | ... | ${nodes} | ${DUT1_${int}2}[0] | ${DUT2_${int}1}[0]
+| | ... | ${dut1_if2_ip4} | ${dut2_if1_ip4} | ${DUT1_${int}2_mac}[0]
+| | ... | ${DUT2_${int}1_mac}[0] | ${wg_if1_ip4} | ${wg_if2_ip4}
+| | ... | ${n_tunnels} | ${listen_port} | ${keepalive_time}
+| | ... | ${laddr_ip4} | ${raddr_ip4}
+| | Then Find NDR and PDR intervals using optimized search
+
+*** Test Cases ***
+| 64B-1c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 64B | 1C
+| | frame_size=${64} | phy_cores=${1}
+
+| 64B-2c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 64B | 2C
+| | frame_size=${64} | phy_cores=${2}
+
+| 64B-4c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 64B | 4C
+| | frame_size=${64} | phy_cores=${4}
+
+| 1518B-1c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 1518B | 1C
+| | frame_size=${1518} | phy_cores=${1}
+
+| 1518B-2c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 1518B | 2C
+| | frame_size=${1518} | phy_cores=${2}
+
+| 1518B-4c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 1518B | 4C
+| | frame_size=${1518} | phy_cores=${4}
+
+| 9000B-1c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 9000B | 1C
+| | frame_size=${9000} | phy_cores=${1}
+
+| 9000B-2c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 9000B | 2C
+| | frame_size=${9000} | phy_cores=${2}
+
+| 9000B-4c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | 9000B | 4C
+| | frame_size=${9000} | phy_cores=${4}
+
+| IMIX-1c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | IMIX | 1C
+| | frame_size=IMIX_v4_1 | phy_cores=${1}
+
+| IMIX-2c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | IMIX | 2C
+| | frame_size=IMIX_v4_1 | phy_cores=${2}
+
+| IMIX-4c-ethip4udpwireguard1tnlsw-ip4base-ndrpdr
+| | [Tags] | IMIX | 4C
+| | frame_size=IMIX_v4_1 | phy_cores=${4}