<div dir="ltr"><div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Oct 27, 2025 at 9:02 AM Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Currently, there is no support for tracking tx_offload capabilities and<br>
there is no separation between port capabilities and queue<br>
capabilities. This is an issue if a test case requires a tx_offload<br>
capability or if a test case requires that a card supports a capability<br>
on a queue. This causes test cases with said requirements to not be<br>
skipped when appropriate. Add tx_offload capabilities and distinguish<br>
capabilities between ports and queues.<br>
<br>
Signed-off-by: Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>><br>
Signed-off-by: Jeremy Spewock <<a href="mailto:jspewock@iol.unh.edu" target="_blank">jspewock@iol.unh.edu</a>><br>
---<br>
 dts/api/capabilities.py                   | 126 ++++++++++--<br>
 dts/api/testpmd/__init__.py               |  97 ++++++++-<br>
 dts/api/testpmd/types.py                  | 227 +++++++++++++++-------<br>
 dts/framework/parser.py                   |  30 +++<br>
 dts/framework/testbed_model/capability.py | 109 +++++++++--<br>
 dts/tests/TestSuite_checksum_offload.py   |  10 +-<br>
 dts/tests/TestSuite_pmd_buffer_scatter.py |   4 +-<br>
 dts/tests/TestSuite_vlan.py               |   4 +-<br>
 8 files changed, 489 insertions(+), 118 deletions(-)<br>
<br>
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py<br>
index 1a79413f6f..243759668f 100644<br>
--- a/dts/api/capabilities.py<br>
+++ b/dts/api/capabilities.py<br>
@@ -77,45 +77,65 @@ class NicCapability(IntEnum):<br>
     #: Scattered packets Rx enabled.<br>
     SCATTERED_RX_ENABLED = 0<br>
     #: Device supports VLAN stripping.<br>
-    RX_OFFLOAD_VLAN_STRIP = auto()<br>
+    PORT_RX_OFFLOAD_VLAN_STRIP = auto()<br>
+    QUEUE_RX_OFFLOAD_VLAN_STRIP = auto()<br>
     #: Device supports L3 checksum offload.<br>
-    RX_OFFLOAD_IPV4_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_IPV4_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_IPV4_CKSUM = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_UDP_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_UDP_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_UDP_CKSUM = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_TCP_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_TCP_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_TCP_CKSUM = auto()<br>
     #: Device supports Large Receive Offload.<br>
-    RX_OFFLOAD_TCP_LRO = auto()<br>
+    PORT_RX_OFFLOAD_TCP_LRO = auto()<br>
+    QUEUE_RX_OFFLOAD_TCP_LRO = auto()<br>
     #: Device supports QinQ (queue in queue) offload.<br>
-    RX_OFFLOAD_QINQ_STRIP = auto()<br>
+    PORT_RX_OFFLOAD_QINQ_STRIP = auto()<br>
+    QUEUE_RX_OFFLOAD_QINQ_STRIP = auto()<br>
     #: Device supports inner packet L3 checksum.<br>
-    RX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
     #: Device supports MACsec.<br>
-    RX_OFFLOAD_MACSEC_STRIP = auto()<br>
+    PORT_RX_OFFLOAD_MACSEC_STRIP = auto()<br>
+    QUEUE_RX_OFFLOAD_MACSEC_STRIP = auto()<br>
     #: Device supports filtering of a VLAN Tag identifier.<br>
-    RX_OFFLOAD_VLAN_FILTER = auto()<br>
+    PORT_RX_OFFLOAD_VLAN_FILTER = auto()<br>
+    QUEUE_RX_OFFLOAD_VLAN_FILTER = auto()<br>
     #: Device supports VLAN offload.<br>
-    RX_OFFLOAD_VLAN_EXTEND = auto()<br>
+    PORT_RX_OFFLOAD_VLAN_EXTEND = auto()<br>
+    QUEUE_RX_OFFLOAD_VLAN_EXTEND = auto()<br>
     #: Device supports receiving segmented mbufs.<br>
-    RX_OFFLOAD_SCATTER = auto()<br>
+    PORT_RX_OFFLOAD_SCATTER = auto()<br>
+    QUEUE_RX_OFFLOAD_SCATTER = auto()<br>
     #: Device supports Timestamp.<br>
-    RX_OFFLOAD_TIMESTAMP = auto()<br>
+    PORT_RX_OFFLOAD_TIMESTAMP = auto()<br>
+    QUEUE_RX_OFFLOAD_TIMESTAMP = auto()<br>
     #: Device supports crypto processing while packet is received in NIC.<br>
-    RX_OFFLOAD_SECURITY = auto()<br>
+    PORT_RX_OFFLOAD_SECURITY = auto()<br>
+    QUEUE_RX_OFFLOAD_SECURITY = auto()<br>
     #: Device supports CRC stripping.<br>
-    RX_OFFLOAD_KEEP_CRC = auto()<br>
+    PORT_RX_OFFLOAD_KEEP_CRC = auto()<br>
+    QUEUE_RX_OFFLOAD_KEEP_CRC = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_SCTP_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_SCTP_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_SCTP_CKSUM = auto()<br>
     #: Device supports inner packet L4 checksum.<br>
-    RX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
+    PORT_RX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
     #: Device supports RSS hashing.<br>
-    RX_OFFLOAD_RSS_HASH = auto()<br>
+    PORT_RX_OFFLOAD_RSS_HASH = auto()<br>
+    QUEUE_RX_OFFLOAD_RSS_HASH = auto()<br>
     #: Device supports scatter Rx packets to segmented mbufs.<br>
-    RX_OFFLOAD_BUFFER_SPLIT = auto()<br>
+    PORT_RX_OFFLOAD_BUFFER_SPLIT = auto()<br>
+    QUEUE_RX_OFFLOAD_BUFFER_SPLIT = auto()<br>
     #: Device supports all checksum capabilities.<br>
-    RX_OFFLOAD_CHECKSUM = auto()<br>
+    PORT_RX_OFFLOAD_CHECKSUM = auto()<br>
+    QUEUE_RX_OFFLOAD_CHECKSUM = auto()<br>
     #: Device supports all VLAN capabilities.<br>
-    RX_OFFLOAD_VLAN = auto()<br>
+    PORT_RX_OFFLOAD_VLAN = auto()<br>
+    QUEUE_RX_OFFLOAD_VLAN = auto()<br>
     #: Device supports Rx queue setup after device started.<br>
     RUNTIME_RX_QUEUE_SETUP = auto()<br>
     #: Device supports Tx queue setup after device started.<br>
@@ -132,6 +152,72 @@ class NicCapability(IntEnum):<br>
     FLOW_CTRL = auto()<br>
     #: Device is running on a physical function.<br>
     PHYSICAL_FUNCTION = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_VLAN_INSERT = auto()<br>
+    QUEUE_TX_OFFLOAD_VLAN_INSERT = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_IPV4_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_IPV4_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_UDP_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_UDP_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_TCP_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_TCP_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_SCTP_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_SCTP_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_TCP_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_TCP_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_UDP_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_UDP_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_QINQ_INSERT = auto()<br>
+    QUEUE_TX_OFFLOAD_QINQ_INSERT = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_VXLAN_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_VXLAN_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_GRE_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_GRE_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_IPIP_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_IPIP_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_GENEVE_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_GENEVE_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_MACSEC_INSERT = auto()<br>
+    QUEUE_TX_OFFLOAD_MACSEC_INSERT = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_MT_LOCKFREE = auto()<br>
+    QUEUE_TX_OFFLOAD_MT_LOCKFREE = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_MULTI_SEGS = auto()<br>
+    QUEUE_TX_OFFLOAD_MULTI_SEGS = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_MBUF_FAST_FREE = auto()<br>
+    QUEUE_TX_OFFLOAD_MBUF_FAST_FREE = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_SECURITY = auto()<br>
+    QUEUE_TX_OFFLOAD_SECURITY = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_UDP_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_UDP_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_IP_TNL_TSO = auto()<br>
+    QUEUE_TX_OFFLOAD_IP_TNL_TSO = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
+    QUEUE_TX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
+    #:<br>
+    PORT_TX_OFFLOAD_SEND_ON_TIMESTAMP = auto()<br>
+    QUEUE_TX_OFFLOAD_SEND_ON_TIMESTAMP = auto()<br>
<br>
<br>
 def requires_link_topology(<br>
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py<br>
index 9e9cbaf495..aadb7f4e70 100644<br>
--- a/dts/api/testpmd/__init__.py<br>
+++ b/dts/api/testpmd/__init__.py<br>
@@ -39,6 +39,8 @@<br>
     FlowRule,<br>
     RxOffloadCapabilities,<br>
     RxOffloadCapability,<br>
+    RxOffloadConfiguration,<br>
+    RxTxLiteralSwitch,<br>
     TestPmdDevice,<br>
     TestPmdPort,<br>
     TestPmdPortFlowCtrl,<br>
@@ -46,6 +48,9 @@<br>
     TestPmdQueueInfo,<br>
     TestPmdRxqInfo,<br>
     TestPmdVerbosePacket,<br>
+    TxOffloadCapabilities,<br>
+    TxOffloadCapability,<br>
+    TxOffloadConfiguration,<br>
     VLANOffloadFlag,<br>
 )<br>
 from framework.context import get_ctx<br>
@@ -1190,13 +1195,14 @@ def _update_capabilities_from_flag(<br>
         unsupported_capabilities: MutableSet["NicCapability"],<br>
         flag_class: type[Flag],<br>
         supported_flags: Flag,<br>
+        prefix: str = "",<br>
     ) -> None:<br>
         """Divide all flags from `flag_class` into supported and unsupported."""<br>
         for flag in flag_class:<br>
             if flag in supported_flags:<br>
-                supported_capabilities.add(NicCapability[str(<a href="http://flag.name" rel="noreferrer" target="_blank">flag.name</a>)])<br>
+                supported_capabilities.add(NicCapability[f"{prefix}{<a href="http://flag.name" rel="noreferrer" target="_blank">flag.name</a>}"])<br>
             else:<br>
-                unsupported_capabilities.add(NicCapability[str(<a href="http://flag.name" rel="noreferrer" target="_blank">flag.name</a>)])<br>
+                unsupported_capabilities.add(NicCapability[f"{prefix}{<a href="http://flag.name" rel="noreferrer" target="_blank">flag.name</a>}"])<br>
<br>
     @_requires_started_ports<br>
     def get_capabilities_rxq_info(<br>
@@ -1293,6 +1299,55 @@ def get_capabilities_physical_function(<br>
         else:<br>
             unsupported_capabilities.add(NicCapability.PHYSICAL_FUNCTION)<br>
<br>
+    @staticmethod<br>
+    def get_offload_capabilities_func(<br>
+        rxtx: RxTxLiteralSwitch,<br>
+    ) -> Callable[["TestPmd", MutableSet["NicCapability"], MutableSet["NicCapability"]], None]:<br>
+        """High-order function that returns a method for gathering Rx/Tx offload capabilities.<br>
+<br>
+        Args:<br>
+            rxtx: whether to gather the rx or tx capabilities in the returned method.<br>
+<br>
+        Returns:<br>
+            A method for gathering Rx/Tx offload capabilities that meets the required structure.<br>
+        """<br>
+<br>
+        def get_capabilities(<br>
+            self: "TestPmd",<br>
+            supported_capabilities: MutableSet["NicCapability"],<br>
+            unsupported_capabilities: MutableSet["NicCapability"],<br>
+        ) -> None:<br>
+            """Get all rx/tx offload capabilities and divide them into supported and unsupported.<br>
+<br>
+            Args:<br>
+                self: The shell instance to get the capabilities from.<br>
+                supported_capabilities: Supported capabilities will be added to this set.<br>
+                unsupported_capabilities: Unsupported capabilities will be added to this set.<br>
+            """<br>
+            self._<a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(f"Getting {rxtx} offload capabilities.")<br>
+            command = f"show port {self.ports[0].id} {rxtx}_offload capabilities"<br>
+            offload_capabilities_out = self.send_command(command)<br>
+<br>
+            capabilities = TxOffloadCapabilities if rxtx == "tx" else RxOffloadCapabilities<br>
+            offload_capabilities = capabilities.parse(offload_capabilities_out)<br>
+<br>
+            self._update_capabilities_from_flag(<br>
+                supported_capabilities,<br>
+                unsupported_capabilities,<br>
+                TxOffloadCapability if rxtx == "tx" else RxOffloadCapability,<br>
+                offload_capabilities.per_port | offload_capabilities.per_queue,<br>
+                prefix=f"PORT_{rxtx.upper()}_OFFLOAD_",<br>
+            )<br>
+            self._update_capabilities_from_flag(<br>
+                supported_capabilities,<br>
+                unsupported_capabilities,<br>
+                TxOffloadCapability if rxtx == "tx" else RxOffloadCapability,<br>
+                offload_capabilities.per_queue,<br>
+                prefix=f"QUEUE_{rxtx.upper()}_OFFLOAD_",<br>
+            )<br>
+<br>
+        return get_capabilities<br>
+<br>
     @_requires_stopped_ports<br>
     def set_port_mbuf_fast_free(<br>
         self,<br>
@@ -1352,3 +1407,41 @@ def set_queue_mbuf_fast_free(<br>
             raise InteractiveCommandExecutionError(<br>
                 f"Failed to get offload config on port {port_id}, queue {queue_id}:\n{output}"<br>
             )<br>
+<br>
+    @_requires_started_ports<br>
+    def get_offload_config(<br>
+        self,<br>
+        port_id: int,<br>
+        rxtx: RxTxLiteralSwitch,<br>
+        /,<br>
+        verify: bool = True,<br>
+    ) -> RxOffloadConfiguration | TxOffloadConfiguration:<br>
+        """Get the Rx or Tx offload configuration of the queues from the given port.<br>
+<br>
+        Args:<br>
+            port_id: The port ID that contains the desired queues.<br>
+            rxtx: Whether to get the Rx or Tx configuration of the given queues.<br>
+            verify: If :data:`True` the output of the command will be scanned in an attempt to<br>
+                verify that the offload configuration was retrieved successfully on all queues.<br>
+<br>
+        Returns:<br>
+            An offload configuration containing the capabilities of the port and queues.<br>
+<br>
+        Raises:<br>
+            InteractiveCommandExecutionError: If all queue offload configurations could not be<br>
+                retrieved.<br>
+        """<br>
+        config_output = self.send_command(f"show port {port_id} {rxtx}_offload configuration")<br>
+        if verify:<br>
+            if (<br>
+                f"Rx Offloading Configuration of port {port_id}" not in config_output<br>
+                and f"Tx Offloading Configuration of port {port_id}" not in config_output<br>
+            ):<br>
+                self._logger.debug(f"Get port offload config error\n{config_output}")<br>
+                raise InteractiveCommandExecutionError(<br>
+                    f"Failed to get offload config on port {port_id}:\n{config_output}"<br>
+                )<br>
+        if rxtx == "rx":<br>
+            return RxOffloadConfiguration.parse(config_output)<br>
+        else:<br>
+            return TxOffloadConfiguration.parse(config_output)<br>
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py<br>
index d1ebf6f2d1..7e28235b18 100644<br>
--- a/dts/api/testpmd/types.py<br>
+++ b/dts/api/testpmd/types.py<br>
@@ -18,6 +18,8 @@<br>
 from framework.parser import ParserFn, TextParser<br>
 from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum<br>
<br>
+RxTxLiteralSwitch = Literal["rx", "tx"]<br>
+<br>
<br>
 class TestPmdDevice:<br>
     """The data of a device that testpmd can recognize.<br>
@@ -1246,7 +1248,99 @@ class TestPmdVerbosePacket(TextParser):<br>
     )<br>
<br>
<br>
-class RxOffloadCapability(Flag):<br>
+class OffloadCapability(Flag):<br>
+    """Flags generated from RxOffloadCapabilites and TxOffloadCapabilities classes."""<br>
+<br>
+    @classmethod<br>
+    def from_string(cls, line: str) -> Self:<br>
+        """Make an instance from a string containing the flag names separated with a space.<br>
+<br>
+        Args:<br>
+            line: The line to parse.<br>
+<br>
+        Returns:<br>
+            A new instance containing all found flags.<br>
+        """<br>
+        flag = cls(0)<br>
+        for flag_name in line.split():<br>
+            flag |= cls[flag_name]<br>
+        return flag<br>
+<br>
+    @classmethod<br>
+    def from_list(cls, lines: list[str]) -> list[Self]:<br>
+        """Gather capabilities from a list of strings.<br>
+<br>
+        Args:<br>
+            lines: The list of capabilities to make flags from.<br>
+        """<br>
+        return [cls.from_string(line) for line in lines]<br>
+<br>
+    @classmethod<br>
+    def make_parser(<br>
+        cls, port_or_queue: Literal["port", "queue"], /, find_multiple: bool = False<br>
+    ) -> ParserFn:<br>
+        """Make a parser function.<br>
+<br>
+        Args:<br>
+            port_or_queue: If :data:`True`, will return capabilities per port. If :data:`False`,<br>
+                will return capabilities per queue.<br>
+            find_multiple: If :data:`True`, will use :func:`TextParser.find_all` to find all<br>
+                matches for the regex query and return a list of instances based on those matches.<br>
+                If :data:`False`, will return a single instance of the flag based off a single<br>
+                match.<br>
+<br>
+        Returns:<br>
+            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a<br>
+                parser function that makes an instance of this flag from text.<br>
+        """<br>
+        granularity = port_or_queue.capitalize()<br>
+        regex = rf"{granularity}[\s\[\]\d]+:(.*)$"<br>
+        if find_multiple:<br>
+            return TextParser.wrap(TextParser.find_all(regex, re.MULTILINE), cls.from_list)<br>
+        return TextParser.wrap(TextParser.find(regex, re.MULTILINE), cls.from_string)<br>
+<br>
+<br>
+class TxOffloadCapability(OffloadCapability):<br>
+    """Tx offload capabilities of a device.<br>
+<br>
+    The flags are taken from ``lib/ethdev/rte_ethdev.h``.<br>
+    They're prefixed with ``RTE_ETH_TX_OFFLOAD`` in ``lib/ethdev/rte_ethdev.h``<br>
+    instead of ``TX_OFFLOAD``, which is what testpmd changes the prefix to.<br>
+<br>
+    The ``TX_OFFLOAD`` prefix has been preserved so that the same flag names can be used<br>
+    in :class:`NicCapability`. The prefix is needed in :class:`NicCapability` since there's<br>
+    no other qualifier which would sufficiently distinguish it from other capabilities.<br>
+<br>
+    References:<br>
+        DPDK lib: ``lib/ethdev/rte_ethdev.h``<br>
+        testpmd display function: ``app/test-pmd/cmdline.c:print_rx_offloads()``<br>
+    """<br>
+<br>
+    VLAN_INSERT = auto()<br>
+    IPV4_CKSUM = auto()<br>
+    UDP_CKSUM = auto()<br>
+    TCP_CKSUM = auto()<br>
+    SCTP_CKSUM = auto()<br>
+    TCP_TSO = auto()<br>
+    UDP_TSO = auto()<br>
+    OUTER_IPV4_CKSUM = auto()<br>
+    QINQ_INSERT = auto()<br>
+    VXLAN_TNL_TSO = auto()<br>
+    GRE_TNL_TSO = auto()<br>
+    IPIP_TNL_TSO = auto()<br>
+    GENEVE_TNL_TSO = auto()<br>
+    MACSEC_INSERT = auto()<br>
+    MT_LOCKFREE = auto()<br>
+    MULTI_SEGS = auto()<br>
+    MBUF_FAST_FREE = auto()<br>
+    SECURITY = auto()<br>
+    UDP_TNL_TSO = auto()<br>
+    IP_TNL_TSO = auto()<br>
+    OUTER_UDP_CKSUM = auto()<br>
+    SEND_ON_TIMESTAMP = auto()<br>
+<br>
+<br>
+class RxOffloadCapability(OffloadCapability):<br>
     """Rx offload capabilities of a device.<br>
<br>
     The flags are taken from ``lib/ethdev/rte_ethdev.h``.<br>
@@ -1265,102 +1359,103 @@ class RxOffloadCapability(Flag):<br>
     """<br>
<br>
     #:<br>
-    RX_OFFLOAD_VLAN_STRIP = auto()<br>
+    VLAN_STRIP = auto()<br>
     #: Device supports L3 checksum offload.<br>
-    RX_OFFLOAD_IPV4_CKSUM = auto()<br>
+    IPV4_CKSUM = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_UDP_CKSUM = auto()<br>
+    UDP_CKSUM = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_TCP_CKSUM = auto()<br>
+    TCP_CKSUM = auto()<br>
     #: Device supports Large Receive Offload.<br>
-    RX_OFFLOAD_TCP_LRO = auto()<br>
+    TCP_LRO = auto()<br>
     #: Device supports QinQ (queue in queue) offload.<br>
-    RX_OFFLOAD_QINQ_STRIP = auto()<br>
+    QINQ_STRIP = auto()<br>
     #: Device supports inner packet L3 checksum.<br>
-    RX_OFFLOAD_OUTER_IPV4_CKSUM = auto()<br>
+    OUTER_IPV4_CKSUM = auto()<br>
     #: Device supports MACsec.<br>
-    RX_OFFLOAD_MACSEC_STRIP = auto()<br>
+    MACSEC_STRIP = auto()<br>
     #: Device supports filtering of a VLAN Tag identifier.<br>
-    RX_OFFLOAD_VLAN_FILTER = 1 << 9<br>
+    VLAN_FILTER = 1 << 9<br>
     #: Device supports VLAN offload.<br>
-    RX_OFFLOAD_VLAN_EXTEND = auto()<br>
+    VLAN_EXTEND = auto()<br>
     #: Device supports receiving segmented mbufs.<br>
-    RX_OFFLOAD_SCATTER = 1 << 13<br>
+    SCATTER = 1 << 13<br>
     #: Device supports Timestamp.<br>
-    RX_OFFLOAD_TIMESTAMP = auto()<br>
+    TIMESTAMP = auto()<br>
     #: Device supports crypto processing while packet is received in NIC.<br>
-    RX_OFFLOAD_SECURITY = auto()<br>
+    SECURITY = auto()<br>
     #: Device supports CRC stripping.<br>
-    RX_OFFLOAD_KEEP_CRC = auto()<br>
+    KEEP_CRC = auto()<br>
     #: Device supports L4 checksum offload.<br>
-    RX_OFFLOAD_SCTP_CKSUM = auto()<br>
+    SCTP_CKSUM = auto()<br>
     #: Device supports inner packet L4 checksum.<br>
-    RX_OFFLOAD_OUTER_UDP_CKSUM = auto()<br>
+    OUTER_UDP_CKSUM = auto()<br>
     #: Device supports RSS hashing.<br>
-    RX_OFFLOAD_RSS_HASH = auto()<br>
+    RSS_HASH = auto()<br>
     #: Device supports<br>
-    RX_OFFLOAD_BUFFER_SPLIT = auto()<br>
+    BUFFER_SPLIT = auto()<br>
     #: Device supports all checksum capabilities.<br>
-    RX_OFFLOAD_CHECKSUM = RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_UDP_CKSUM | RX_OFFLOAD_TCP_CKSUM<br>
+    CHECKSUM = IPV4_CKSUM | UDP_CKSUM | TCP_CKSUM<br>
     #: Device supports all VLAN capabilities.<br>
-    RX_OFFLOAD_VLAN = (<br>
-        RX_OFFLOAD_VLAN_STRIP<br>
-        | RX_OFFLOAD_VLAN_FILTER<br>
-        | RX_OFFLOAD_VLAN_EXTEND<br>
-        | RX_OFFLOAD_QINQ_STRIP<br>
-    )<br>
+    VLAN = VLAN_STRIP | VLAN_FILTER | VLAN_EXTEND | QINQ_STRIP<br>
<br>
-    @classmethod<br>
-    def from_string(cls, line: str) -> Self:<br>
-        """Make an instance from a string containing the flag names separated with a space.<br>
<br>
-        Args:<br>
-            line: The line to parse.<br>
+@dataclass<br>
+class OffloadCapabilities(TextParser):<br>
+    """The result of testpmd's ``show port <port_id> rx/tx_offload capabilities`` command."""<br>
<br>
-        Returns:<br>
-            A new instance containing all found flags.<br>
-        """<br>
-        flag = cls(0)<br>
-        for flag_name in line.split():<br>
-            flag |= cls[f"RX_OFFLOAD_{flag_name}"]<br>
-        return flag<br>
+    port_id: int = field(metadata=TextParser.find_int(r"Offloading Capabilities of port (\d+) :"))<br>
+    #: Per-queue offload capabilities.<br>
+    per_queue: OffloadCapability<br>
+    #: Capabilities other than per-queue offload capabilities.<br>
+    per_port: OffloadCapability<br>
<br>
-    @classmethod<br>
-    def make_parser(cls, per_port: bool) -> ParserFn:<br>
-        """Make a parser function.<br>
<br>
-        Args:<br>
-            per_port: If :data:`True`, will return capabilities per port. If :data:`False`,<br>
-                will return capabilities per queue.<br>
+@dataclass<br>
+class RxOffloadCapabilities(OffloadCapabilities):<br>
+    """Extends :class:`OffloadCapabilities` with Rx specific functionality."""<br>
<br>
-        Returns:<br>
-            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a<br>
-                parser function that makes an instance of this flag from text.<br>
-        """<br>
-        granularity = "Port" if per_port else "Queue"<br>
-        return TextParser.wrap(<br>
-            TextParser.find(rf"Per {granularity}\s+:(.*)$", re.MULTILINE),<br>
-            cls.from_string,<br>
-        )<br>
+    per_queue: OffloadCapability = field(metadata=RxOffloadCapability.make_parser("queue"))<br>
+    per_port: OffloadCapability = field(metadata=RxOffloadCapability.make_parser("port"))<br>
<br>
<br>
 @dataclass<br>
-class RxOffloadCapabilities(TextParser):<br>
-    """The result of testpmd's ``show port <port_id> rx_offload capabilities`` command.<br>
+class TxOffloadCapabilities(OffloadCapabilities):<br>
+    """Extends :class:`OffloadCapabilities` with Tx specific functionality."""<br>
<br>
-    References:<br>
-        testpmd command function: ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa()``<br>
-        testpmd display function: ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa_parsed()``<br>
-    """<br>
+    per_queue: OffloadCapability = field(metadata=TxOffloadCapability.make_parser("queue"))<br>
+    per_port: OffloadCapability = field(metadata=TxOffloadCapability.make_parser("port"))<br>
<br>
-    #:<br>
-    port_id: int = field(<br>
-        metadata=TextParser.find_int(r"Rx Offloading Capabilities of port (\d+) :")<br>
+<br>
+@dataclass<br>
+class OffloadConfiguration(TextParser):<br>
+    """The result of testpmd's ``show port <port_id> rx/tx_offload configuration`` command."""<br>
+<br>
+    port_id: int = field(metadata=TextParser.find_int(r"Offloading Configuration of port (\d+) :"))<br>
+    #: Queue offload configurations.<br>
+    queues: list[OffloadCapability]<br>
+    #: Port offload configuration.<br>
+    port: OffloadCapability<br></blockquote><div><br></div><div>One final nit - I think attribute names queue_configs and port_config would read more clearly. What do you think?</div><div> </div><div>Reviewed-by: Patrick Robb <<a href="mailto:probb@iol.unh.edu">probb@iol.unh.edu</a>></div></div></div>
</div>