This patch adds userspace DPDK with rte_flow offload unit
tests. It can be run with the 'make check-rte-offloads'
command; however, it requires a list of VFs that can be
used for offloaded ports. More details on how to set this
up can be found in the testing.rst file.

Also note that currently not all tests are passing. This is
true for both the old rte_flow offload and the new offload
infrastructure.

Signed-off-by: Eelco Chaudron <echau...@redhat.com>
---

Note that the below tests are failing with the system-traffic.at
test-suite on both the main and the rework branch:

12. system-traffic.at:273: 12. datapath - ping over bond 
(system-traffic.at:273): FAILED (system-traffic.at:281)
13. system-traffic.at:298: 13. datapath - ping over vxlan tunnel 
(system-traffic.at:298): FAILED (system-traffic.at:327)
15. system-traffic.at:450: 15. datapath - ping vlan over vxlan tunnel 
(system-traffic.at:450): FAILED (system-traffic.at:487)
16. system-traffic.at:497: 16. datapath - ping over vxlan6 tunnel 
(system-traffic.at:497): FAILED (system-traffic.at:528)
17. system-traffic.at:559: 17. datapath - ping over gre tunnel 
(system-traffic.at:559): FAILED (system-traffic.at:586)
19. system-traffic.at:707: 19. datapath - ping over ip6gre L2 tunnel 
(system-traffic.at:707): FAILED (system-traffic.at:739)
24. system-traffic.at:916: 24. datapath - ping over geneve tunnel 
(system-traffic.at:916): FAILED (system-traffic.at:945)
26. system-traffic.at:1030: 26. datapath - flow resume with geneve tun_metadata 
(system-traffic.at:1030): FAILED (system-traffic.at:1063)
27. system-traffic.at:1081: 27. datapath - ping over geneve6 tunnel 
(system-traffic.at:1081): FAILED (system-traffic.at:1112)
29. system-traffic.at:1190: 29. datapath - bridging two geneve tunnels 
(system-traffic.at:1190): FAILED (system-traffic.at:1264)
31. system-traffic.at:1299: 31. datapath - ping over gre tunnel by simulated 
packets (system-traffic.at:1299): FAILED (ovs-macros.at:242)
36. system-traffic.at:1547: 36. datapath - ping over srv6 tunnel 
(system-traffic.at:1547): FAILED (system-traffic.at:1578)
37. system-traffic.at:1608: 37. datapath - ping6 over srv6 tunnel 
(system-traffic.at:1608): FAILED (ovs-macros.at:242)
47. system-traffic.at:2026: 47. datapath - basic truncate action 
(system-traffic.at:2026): FAILED (system-traffic.at:2038)
48. system-traffic.at:2141: 48. datapath - truncate and output to gre tunnel by 
simulated packets (system-traffic.at:2141): FAILED (system-traffic.at:2175)
49. system-traffic.at:2271: 49. datapath - truncate and output to gre tunnel 
(system-traffic.at:2271): FAILED (system-traffic.at:2303)
51. system-traffic.at:2427: 51. datapath - drop action 
(system-traffic.at:2427): FAILED (ovs-macros.at:242)
52. system-traffic.at:2458: 52. datapath - simulated flow action update 
(system-traffic.at:2458): FAILED (system-traffic.at:2495)
53. system-traffic.at:2521: 53. datapath - netdev offload software fallback 
(system-traffic.at:2521): FAILED (system-traffic.at:2533)
55. system-traffic.at:2632: 55. datapath - Dump OF rules corresponding to UFID 
(system-traffic.at:2632): FAILED (system-traffic.at:2689)

> After this test, remove ovs-netdev/br0 to make the tests pass
  again, as the failures in the remainder of this run are due
  to them not being cleaned up.

 # ip link del ovs-netdev; ip link del br0
 # make check-rte-offloads TESTSUITEFLAGS="56-" \
     OVS_RTE_FLOW_VF_PCI_ADDRS="0000:17:00.0,0 0000:17:00.0,1 0000:17:00.0,2 
0000:17:00.0,3 0000:17:00.0,4 0000:17:00.0,5"

61. system-traffic.at:2984: 61. mpls - decap header dp-support 
(system-traffic.at:2984): FAILED (ovs-macros.at:242)
62. system-traffic.at:3028: 62. mpls - decap header slow-path 
(system-traffic.at:3028): FAILED (ovs-macros.at:242)
66. system-traffic.at:3177: 66. conntrack - controller 
(system-traffic.at:3177): FAILED (system-traffic.at:3213)
67. system-traffic.at:3223: 67. conntrack - force commit 
(system-traffic.at:3223): FAILED (system-traffic.at:3261)
71. system-traffic.at:3703: 71. conntrack - IPv6 ping (system-traffic.at:3703): 
FAILED (ovs-macros.at:242)
89. system-traffic.at:4493: 89. conntrack - ICMP related 
(system-traffic.at:4493): FAILED (system-traffic.at:4518)
91. system-traffic.at:4586: 91. conntrack - ICMP related 2 
(system-traffic.at:4586): FAILED (system-traffic.at:4618)
118. system-traffic.at:5872: 118. conntrack - resubmit to ct multiple times 
(system-traffic.at:5872): FAILED (system-traffic.at:5899)
120. system-traffic.at:6018: 120. conntrack - SCTP SNAT with port range 
(system-traffic.at:6018): FAILED (system-traffic.at:6065)
124. system-traffic.at:6201: 124. conntrack - commit, recirc 
(system-traffic.at:6201): FAILED (system-traffic.at:6239)
146. system-traffic.at:7689: 146. conntrack - ICMP related with NAT 
(system-traffic.at:7689): FAILED (system-traffic.at:7735)
167. system-traffic.at:8883: 167. conntrack - DNAT load balancing 
(system-traffic.at:8883): FAILED (system-traffic.at:8966)
168. system-traffic.at:8970: 168. conntrack - DNAT load balancing with NC 
(system-traffic.at:8970): FAILED (system-traffic.at:9055)
171. system-traffic.at:9194: 171. conntrack - Multiple ICMP traverse 
(system-traffic.at:9194): FAILED (system-traffic.at:9237)
173. system-traffic.at:9294: 173. conntrack - ICMP from different source 
related with NAT (system-traffic.at:9294): FAILED (system-traffic.at:9303)
183. system-traffic.at:9811: 183. nsh - decap header (system-traffic.at:9811): 
FAILED (ovs-macros.at:242)
185. system-traffic.at:9904: 185. nsh - forward (system-traffic.at:9904): 
FAILED (system-traffic.at:9964)

> Flaky tests (note that their might be more flaky tests
  which passed the first run):

25. system-traffic.at:976: 25. datapath - ping over geneve tunnel, delete flow 
regression (system-traffic.at:976): FAILED (system-traffic.at:1015)
54. system-traffic.at:2566: 54. datapath - Neighbor Discovery with loose match 
(system-traffic.at:2566): FAILED (system-traffic.at:2603)
84. system-traffic.at:4299: 84. conntrack - ct_label (system-traffic.at:4299): 
FAILED (system-traffic.at:4336)
106. system-traffic.at:5226: 106. conntrack - IPv6 fragmentation + vlan 
(system-traffic.at:5226): FAILED (system-traffic.at:5256)
123. system-traffic.at:6159: 123. conntrack - IPv6 HTTP 
(system-traffic.at:6159): FAILED (ovs-macros.at:242)
144. system-traffic.at:7593: 144. conntrack - DNAT with additional SNAT 
(system-traffic.at:7593): FAILED (system-traffic.at:7625)
---
 Documentation/topics/testing.rst       |  19 +++
 tests/.gitignore                       |   3 +
 tests/automake.mk                      |  24 +++
 tests/ofproto-macros.at                |   2 +-
 tests/system-rte-offloads-macros.at    | 195 +++++++++++++++++++++++++
 tests/system-rte-offloads-testsuite.at |  28 ++++
 tests/system-rte-offloads.at           |  97 ++++++++++++
 7 files changed, 367 insertions(+), 1 deletion(-)
 create mode 100644 tests/system-rte-offloads-macros.at
 create mode 100644 tests/system-rte-offloads-testsuite.at
 create mode 100644 tests/system-rte-offloads.at

diff --git a/Documentation/topics/testing.rst b/Documentation/topics/testing.rst
index dbeaf2d93..5c45647c2 100644
--- a/Documentation/topics/testing.rst
+++ b/Documentation/topics/testing.rst
@@ -363,6 +363,25 @@ manual to figure out how to `Configure hugepages`_.
 All the features documented under `Unit Tests`_ are available for the DPDK
 testsuite.
 
+Userspace datapath with DPDK and rte_flow offload
+'''''''''''''''''''''''''''''''''''''''''''''''''
+
+To invoke the userspace datapath tests with DPDK and rte_flow offload, the
+same prerequisites apply as above. In addition, six Virtual Function (VF)
+interfaces must be preconfigured and capable of hardware offloading traffic
+between each other.
+
+These six VFs need to be passed as a list of PF PCI addresses with their
+corresponding VF indexes in the OVS_RTE_FLOW_VF_PCI_ADDRS variable.
+For example::
+
+    OVS_RTE_FLOW_VF_PCI_ADDRS="0000:17:00.0,0 0000:17:00.0,1 0000:17:00.0,2 
0000:17:00.0,3 0000:17:00.0,4 0000:17:00.0,5"
+
+To invoke the rte_flow offloads testsuite with the userspace datapath, run::
+
+    make check-rte-offloads \
+        OVS_RTE_FLOW_VF_PCI_ADDRS="0000:17:00.0,0 0000:17:00.0,1 
0000:17:00.0,2 0000:17:00.0,3 0000:17:00.0,4 0000:17:00.0,5"
+
 Userspace datapath: Testing and Validation of CPU-specific Optimizations
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
diff --git a/tests/.gitignore b/tests/.gitignore
index 3a8c45975..05ffd2533 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -32,6 +32,9 @@
 /system-offloads-testsuite
 /system-offloads-testsuite.dir/
 /system-offloads-testsuite.log
+/system-rte-offloads-testsuite
+/system-rte-offloads-testsuite.dir/
+/system-rte-offloads-testsuite.log
 /test-aes128
 /test-atomic
 /test-bundle
diff --git a/tests/automake.mk b/tests/automake.mk
index 59f538761..750bef7a9 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -7,6 +7,7 @@ EXTRA_DIST += \
        $(SYSTEM_TSO_TESTSUITE_AT) \
        $(SYSTEM_AFXDP_TESTSUITE_AT) \
        $(SYSTEM_OFFLOADS_TESTSUITE_AT) \
+       $(SYSTEM_RTE_OFFLOADS_TESTSUITE_AT) \
        $(SYSTEM_DPDK_TESTSUITE_AT) \
        $(OVSDB_CLUSTER_TESTSUITE_AT) \
        $(TESTSUITE) \
@@ -15,6 +16,7 @@ EXTRA_DIST += \
        $(SYSTEM_TSO_TESTSUITE) \
        $(SYSTEM_AFXDP_TESTSUITE) \
        $(SYSTEM_OFFLOADS_TESTSUITE) \
+       $(SYSTEM_RTE_OFFLOADS_TESTSUITE) \
        $(SYSTEM_DPDK_TESTSUITE) \
        $(OVSDB_CLUSTER_TESTSUITE) \
        tests/atlocal.in \
@@ -186,6 +188,13 @@ SYSTEM_OFFLOADS_TESTSUITE_AT = \
        tests/system-offloads-testsuite.at \
        tests/system-offloads-testsuite-macros.at
 
+SYSTEM_RTE_OFFLOADS_TESTSUITE_AT = \
+       tests/system-common-macros.at \
+       tests/system-dpdk-macros.at \
+       tests/system-rte-offloads.at \
+       tests/system-rte-offloads-macros.at \
+       tests/system-rte-offloads-testsuite.at
+
 SYSTEM_DPDK_TESTSUITE_AT = \
        tests/system-common-macros.at \
        tests/system-dpdk-macros.at \
@@ -202,6 +211,7 @@ SYSTEM_USERSPACE_TESTSUITE = 
$(srcdir)/tests/system-userspace-testsuite
 SYSTEM_TSO_TESTSUITE = $(srcdir)/tests/system-tso-testsuite
 SYSTEM_AFXDP_TESTSUITE = $(srcdir)/tests/system-afxdp-testsuite
 SYSTEM_OFFLOADS_TESTSUITE = $(srcdir)/tests/system-offloads-testsuite
+SYSTEM_RTE_OFFLOADS_TESTSUITE = $(srcdir)/tests/system-rte-offloads-testsuite
 SYSTEM_DPDK_TESTSUITE = $(srcdir)/tests/system-dpdk-testsuite
 OVSDB_CLUSTER_TESTSUITE = $(srcdir)/tests/ovsdb-cluster-testsuite
 DISTCLEANFILES += tests/atconfig tests/atlocal
@@ -315,6 +325,12 @@ check-offloads-valgrind: all $(valgrind_wrappers) 
$(check_DATA)
        @echo 
'----------------------------------------------------------------------'
        @echo 'Valgrind output can be found in 
tests/system-offloads-testsuite.dir/*/valgrind.*'
        @echo 
'----------------------------------------------------------------------'
+check-rte-offloads-valgrind: all $(valgrind_wrappers) $(check_DATA)
+       $(SHELL) '$(SYSTEM_RTE_OFFLOADS_TESTSUITE)' -C tests 
VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d 
$(TESTSUITEFLAGS) -j1
+       @echo
+       @echo 
'----------------------------------------------------------------------'
+       @echo 'Valgrind output can be found in 
tests/system-rte-offloads-testsuite.dir/*/valgrind.*'
+       @echo 
'----------------------------------------------------------------------'
 check-tso-valgrind: all $(valgrind_wrappers) $(check_DATA)
        $(SHELL) '$(SYSTEM_TSO_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' 
AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1
        @echo
@@ -357,6 +373,10 @@ check-offloads: all
        set $(SHELL) '$(SYSTEM_OFFLOADS_TESTSUITE)' -C tests  
AUTOTEST_PATH='$(AUTOTEST_PATH)'; \
        "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" 
--recheck)
 
+check-rte-offloads: all
+       set $(SHELL) '$(SYSTEM_RTE_OFFLOADS_TESTSUITE)' -C tests  
AUTOTEST_PATH='$(AUTOTEST_PATH)'; \
+       "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" 
--recheck)
+
 check-dpdk: all
        set $(SHELL) '$(SYSTEM_DPDK_TESTSUITE)' -C tests  
AUTOTEST_PATH='$(AUTOTEST_PATH)'; \
        "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" 
--recheck)
@@ -402,6 +422,10 @@ $(SYSTEM_OFFLOADS_TESTSUITE): package.m4 
$(SYSTEM_TESTSUITE_AT) $(SYSTEM_OFFLOAD
        $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
        $(AM_V_at)mv $@.tmp $@
 
+$(SYSTEM_RTE_OFFLOADS_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) 
$(SYSTEM_RTE_OFFLOADS_TESTSUITE_AT) $(COMMON_MACROS_AT)
+       $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+       $(AM_V_at)mv $@.tmp $@
+
 $(SYSTEM_DPDK_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) 
$(SYSTEM_DPDK_TESTSUITE_AT) $(COMMON_MACROS_AT)
        $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
        $(AM_V_at)mv $@.tmp $@
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index 8894dd711..2f3fde623 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -127,7 +127,7 @@ strip_xids () {
 
 # Changes all 'used:...' to say 'used:0.0', to make output easier to compare.
 strip_used () {
-    sed 's/used:[[0-9]]\.[[0-9]]*/used:0.0/'
+    sed -E 's/used:[[0-9]]+\.[[0-9]]*/used:0.0/'
 }
 
 # Removes all 'duration=...' to make output easier to compare.
diff --git a/tests/system-rte-offloads-macros.at 
b/tests/system-rte-offloads-macros.at
new file mode 100644
index 000000000..af25b2f6a
--- /dev/null
+++ b/tests/system-rte-offloads-macros.at
@@ -0,0 +1,195 @@
+AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc.
+
+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.])
+
+# OVS_RTE_FLOW_PRE_CHECK()
+#
+# Check prerequisites for DPDK rte_flow tests. Following settings are checked:
+#  - Existence of at least six VFs and their representors.
+#
+m4_define([OVS_RTE_FLOW_PRE_CHECK], [
+  OVS_DPDK_PRE_CHECK()
+  AT_SKIP_IF(
+    [test "$(printf '%s' "$OVS_RTE_FLOW_VF_PCI_ADDRS" | wc -w)" -ne 6])
+
+  AT_SKIP_IF([! ovs_rte_flow_verify_vf_cfg "$OVS_RTE_FLOW_VF_PCI_ADDRS"])
+])
+
+# OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args])
+#
+# This is a copy from system-dpdk-macro.at with two small changes:
+#  1) Use OVS_RTE_FLOW_PRE_CHECK() instead of OVS_DPDK_PRE_CHECK().
+#  2) Start with pci enabled for PF devices.
+#
+m4_define([OVS_TRAFFIC_VSWITCHD_START],
+  [
+   OVS_RTE_FLOW_PRE_CHECK()
+   OVS_WAIT_WHILE([ip link show ovs-netdev])
+   ALLOW_LIST=$(echo "$OVS_RTE_FLOW_VF_PCI_ADDRS" | awk '{
+     for (i=1; i<=NF; i++) {
+       split($i, parts, ",");
+       addr = parts[1];
+       if (addr != "" && !seen[addr]) {
+         seen[addr] = 1;
+         if (result == "")
+           result = "--allow " addr;
+         else
+           result = result " --allow " addr;
+        }
+     }
+     print result;
+   }')
+   OVS_DPDK_START([$ALLOW_LIST], [--disable-system],
+                  [-- set Open_vSwitch . other_config:hw-offload=true $3])
+   dnl Add bridges, ports, etc.
+   OVS_WAIT_WHILE([ip link show br0])
+   AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [],
+            [| uuidfilt])], [0], [$2])
+])
+
+# OVS_TRAFFIC_VSWITCHD_STOP([ALLOWLIST], [extra_cmds])
+#
+# This is a copy from system-dpdk-macro.at adding some rte_flow specific error
+# message exclusion.
+#
+m4_define([OVS_TRAFFIC_VSWITCHD_STOP],
+  [OVS_DPDK_STOP_VSWITCHD([dnl
+$1";/mlx5_net: Failed to update link status: /d"])
+   AT_CHECK([:; $2])
+])
+
+# ADD_{VF|VETH}([port], [namespace], [ovs-br], [ip_addr] [mac_addr], [gateway],
+#               [ip_addr_flags])
+#
+# Simulate the ADD_VETH() macro defined in system-common-macros.at, allowing
+# us to run the existing system-traffic unit tests without any test-specific
+# changes.  This is consistent with how DPDK and AF_XDP run the system tests,
+# except that we are not using actual veth devices, but VF representer ports.
+#
+m4_define([ADD_VF],
+    [ USER_PORT=$1
+      PORT_NO=${USER_PORT##*[!0-9]}
+
+      AT_CHECK([[[ "$PORT_NO" -ge 0 ]] && [[ "$PORT_NO" -le 6 ]] || return 66])
+      PORT_CFG=$(echo $OVS_RTE_FLOW_VF_PCI_ADDRS | cut -d' ' -f$((PORT_NO+1)))
+      PF_PCI=$(ovs_rte_get_pci_id "$PORT_CFG")
+      VF_IDX=$(ovs_rte_get_vf_idx "$PORT_CFG")
+      REP=$(ovs_rte_get_representor_netdev $PF_PCI $VF_IDX) || return 77
+
+      AT_CHECK([ip link set $REP name $1])
+      AT_CHECK([ip link set $1 netns $2])
+      AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \
+                set interface ovs-$1 external-ids:iface-id="$1" -- \
+                set interface ovs-$1 type=dpdk -- \
+                set interface ovs-$1 \
+                  options:dpdk-devargs=$PF_PCI,representor=vf$VF_IDX])
+      NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7])
+      NS_CHECK_EXEC([$2], [ip link set dev $1 up])
+      if test -n "$5"; then
+        NS_CHECK_EXEC([$2], [ip link set dev $1 address $5])
+      else
+        NS_CHECK_EXEC([$2],
+                      [ip link set dev $1 address 02:00:00:00:EC:0$PORT_NO])
+      fi
+      if test -n "$6"; then
+        NS_CHECK_EXEC([$2], [ip route add default via $6])
+      fi
+      on_exit "ip netns exec $2 ip link set $1 netns 1 \
+               && ip link set $1 name $REP"
+    ]
+)
+m4_define([ADD_VETH], [ADD_VF($@)])
+
+# DUMP_DP_IP_CLEAN_SORTED()
+#
+# Clean up and sort the ovs-dpctl dump-flow output for comparing.
+#
+m4_define([DUMP_DP_IP_CLEAN_SORTED], [dnl
+  grep 'eth_type(0x0800)' \
+    | sed -e 
's/eth(src=[[a-z0-9:]]*,dst=[[a-z0-9:]]*)/eth(macs)/;s/recirc_id(0),//' \
+    | strip_used | strip_ptype | sort])
+
+OVS_START_SHELL_HELPERS
+
+# ovs_rte_is_valid_pci_vf_addr()
+#
+# Check if the give PF PCI address and the VF number are valid.
+#
+ovs_rte_is_valid_pci_vf_addr() {
+    PCI_ID='[[0-9a-fA-F]]{4}:[[0-9a-fA-F]]{2}:[[0-9a-fA-F]]{2}\.[[0-7]]'
+    echo "$1" | grep -E -q "^$PCI_ID,[[0-9]]+$" && return 0 || return 1
+}
+
+# ovs_rte_get_pci_id()
+#
+ovs_rte_get_pci_id() {
+    printf '%s\n' "${1%%,*}"
+}
+
+# ovs_rte_get_vf_idx()
+#
+ovs_rte_get_vf_idx() {
+    printf '%s\n' "${1##*,}"
+}
+
+# ovr_rte_get_representor_netdev(<PF_PCI>, <VF_INDEX>)
+#
+# This function tries to find the representor netdev for the give PF's VF.
+#
+ovs_rte_get_representor_netdev() {
+    PF_PCI=$1
+    VF_IDX=$2
+
+    VF_NET_DIR="/sys/bus/pci/devices/$PF_PCI/virtfn$VF_IDX/net"
+
+    if [[ ! -d "$VF_NET_DIR" ]]; then
+        echo "ERROR: VF $VF_IDX for PF $PF_PCI does not exist" >&2
+        return 1
+    fi
+
+    for iface in "$VF_NET_DIR"/*; do
+        if [[ -e "$iface" ]]; then
+            basename "$iface"
+            return 0
+        fi
+    done
+
+    echo "ERROR: No representor netdev found for VF $VF_IDX on PF $PF_PCI" >&2
+    return 1
+}
+
+# ovs_rte_flow_verify_vf_cfg()
+#
+# Verify that the given PF PCI addresses and corresponding VF IDs in
+# OVS_RTE_FLOW_VF_PCI_ADDRS are valid, exist, and have corresponding
+# representor ports.
+#
+ovs_rte_flow_verify_vf_cfg() {
+    i=0
+
+    for addr in $1; do
+        ovs_rte_is_valid_pci_vf_addr "$addr" || return 1
+
+        PCI_ID=$(ovs_rte_get_pci_id "$addr")
+        VF_IDX=$(ovs_rte_get_vf_idx "$addr")
+
+        REP=$(ovs_rte_get_representor_netdev $PCI_ID $VF_IDX) || return 1
+
+        echo "ovs-p$i: PF PCI $PCI_ID with VF $VF_IDX has representor $REP"
+        i=$((i + 1))
+    done
+
+    return 0
+}
+
+OVS_END_SHELL_HELPERS
\ No newline at end of file
diff --git a/tests/system-rte-offloads-testsuite.at 
b/tests/system-rte-offloads-testsuite.at
new file mode 100644
index 000000000..5d64ff68b
--- /dev/null
+++ b/tests/system-rte-offloads-testsuite.at
@@ -0,0 +1,28 @@
+AT_INIT
+
+AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc.
+
+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.])
+
+m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS])
+
+m4_include([tests/ovs-macros.at])
+m4_include([tests/ovsdb-macros.at])
+m4_include([tests/ofproto-macros.at])
+m4_include([tests/system-common-macros.at])
+m4_include([tests/system-userspace-macros.at])
+m4_include([tests/system-dpdk-macros.at])
+m4_include([tests/system-rte-offloads-macros.at])
+
+m4_include([tests/system-rte-offloads.at])
+m4_include([tests/system-traffic.at])
diff --git a/tests/system-rte-offloads.at b/tests/system-rte-offloads.at
new file mode 100644
index 000000000..03fe00287
--- /dev/null
+++ b/tests/system-rte-offloads.at
@@ -0,0 +1,97 @@
+AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc.
+
+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.])
+
+AT_BANNER([rte_flow offload unit tests])
+
+AT_SETUP([rte_flow - ping between two hardware offloaded ports])
+OVS_RTE_FLOW_PRE_CHECK()
+OVS_TRAFFIC_VSWITCHD_START()
+
+AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
+ADD_NAMESPACES(at_ns0, at_ns1)
+ADD_VF(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VF(p1, at_ns1, br0, "10.1.1.2/24")
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], 
[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+OVS_WAIT_UNTIL_EQUAL(
+  [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \
+   | DUMP_DP_IP_CLEAN_SORTED], [dnl
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([rte_flow - ping between six hardware offloaded ports])
+OVS_RTE_FLOW_PRE_CHECK()
+OVS_TRAFFIC_VSWITCHD_START()
+
+AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:max-idle=20000])
+ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3, at_ns4, at_ns5)
+ADD_VF(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VF(p1, at_ns1, br0, "10.1.1.2/24")
+ADD_VF(p2, at_ns2, br0, "10.1.1.3/24")
+ADD_VF(p3, at_ns3, br0, "10.1.1.4/24")
+ADD_VF(p4, at_ns4, br0, "10.1.1.5/24")
+ADD_VF(p5, at_ns5, br0, "10.1.1.6/24")
+
+for NS in $(seq 0 5); do
+    START=$((NS + 2))
+    for IP in $(seq "$START" 6); do
+        NS_CHECK_EXEC([at_ns$NS], [ping -q -c 3 -i 0.3 -W 2 10.1.1.$IP | 
FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+    done
+done
+
+OVS_WAIT_UNTIL_EQUAL(
+  [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \
+   | DUMP_DP_IP_CLEAN_SORTED], [dnl
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p2
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p3
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p4
+in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p5
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p2
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p3
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p4
+in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p5
+in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0
+in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p3
+in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p4
+in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p5
+in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0
+in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p2
+in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p4
+in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p5
+in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0
+in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p2
+in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p3
+in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p5
+in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p0
+in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p1
+in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p2
+in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p3
+in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, 
bytes:196, used:0.0s, actions:ovs-p4])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
-- 
2.50.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to