1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright(c) 2023 University of New Hampshire 3# Copyright(c) 2023 PANTHEON.tech s.r.o. 4# Copyright(c) 2024 Arm Limited 5 6"""Testpmd interactive shell. 7 8Typical usage example in a TestSuite:: 9 10 testpmd_shell = TestPmdShell(self.sut_node) 11 devices = testpmd_shell.get_devices() 12 for device in devices: 13 print(device) 14 testpmd_shell.close() 15""" 16 17import functools 18import re 19import time 20from collections.abc import Callable, MutableSet 21from dataclasses import dataclass, field 22from enum import Flag, auto 23from pathlib import PurePath 24from typing import TYPE_CHECKING, Any, ClassVar, Concatenate, ParamSpec, TypeAlias 25 26if TYPE_CHECKING: 27 from enum import Enum as NoAliasEnum 28else: 29 from aenum import NoAliasEnum 30 31from typing_extensions import Self, Unpack 32 33from framework.exception import InteractiveCommandExecutionError, InternalError 34from framework.params.testpmd import SimpleForwardingModes, TestPmdParams 35from framework.params.types import TestPmdParamsDict 36from framework.parser import ParserFn, TextParser 37from framework.remote_session.dpdk_shell import DPDKShell 38from framework.settings import SETTINGS 39from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList 40from framework.testbed_model.sut_node import SutNode 41from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum 42 43P = ParamSpec("P") 44TestPmdShellMethod = Callable[Concatenate["TestPmdShell", P], Any] 45 46TestPmdShellCapabilityMethod: TypeAlias = Callable[ 47 ["TestPmdShell", MutableSet["NicCapability"], MutableSet["NicCapability"]], None 48] 49 50TestPmdShellDecorator: TypeAlias = Callable[[TestPmdShellMethod], TestPmdShellMethod] 51 52TestPmdShellNicCapability = ( 53 TestPmdShellCapabilityMethod | tuple[TestPmdShellCapabilityMethod, TestPmdShellDecorator] 54) 55 56 57class TestPmdDevice: 58 """The data of a device that testpmd can recognize. 59 60 Attributes: 61 pci_address: The PCI address of the device. 62 """ 63 64 pci_address: str 65 66 def __init__(self, pci_address_line: str): 67 """Initialize the device from the testpmd output line string. 68 69 Args: 70 pci_address_line: A line of testpmd output that contains a device. 71 """ 72 self.pci_address = pci_address_line.strip().split(": ")[1].strip() 73 74 def __str__(self) -> str: 75 """The PCI address captures what the device is.""" 76 return self.pci_address 77 78 79class VLANOffloadFlag(Flag): 80 """Flag representing the VLAN offload settings of a NIC port.""" 81 82 #: 83 STRIP = auto() 84 #: 85 FILTER = auto() 86 #: 87 EXTEND = auto() 88 #: 89 QINQ_STRIP = auto() 90 91 @classmethod 92 def from_str_dict(cls, d): 93 """Makes an instance from a dict containing the flag member names with an "on" value. 94 95 Args: 96 d: A dictionary containing the flag members as keys and any string value. 97 98 Returns: 99 A new instance of the flag. 100 """ 101 flag = cls(0) 102 for name in cls.__members__: 103 if d.get(name) == "on": 104 flag |= cls[name] 105 return flag 106 107 @classmethod 108 def make_parser(cls) -> ParserFn: 109 """Makes a parser function. 110 111 Returns: 112 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 113 parser function that makes an instance of this flag from text. 114 """ 115 return TextParser.wrap( 116 TextParser.find( 117 r"VLAN offload:\s+" 118 r"strip (?P<STRIP>on|off), " 119 r"filter (?P<FILTER>on|off), " 120 r"extend (?P<EXTEND>on|off), " 121 r"qinq strip (?P<QINQ_STRIP>on|off)$", 122 re.MULTILINE, 123 named=True, 124 ), 125 cls.from_str_dict, 126 ) 127 128 129class RSSOffloadTypesFlag(Flag): 130 """Flag representing the RSS offload flow types supported by the NIC port.""" 131 132 #: 133 ipv4 = auto() 134 #: 135 ipv4_frag = auto() 136 #: 137 ipv4_tcp = auto() 138 #: 139 ipv4_udp = auto() 140 #: 141 ipv4_sctp = auto() 142 #: 143 ipv4_other = auto() 144 #: 145 ipv6 = auto() 146 #: 147 ipv6_frag = auto() 148 #: 149 ipv6_tcp = auto() 150 #: 151 ipv6_udp = auto() 152 #: 153 ipv6_sctp = auto() 154 #: 155 ipv6_other = auto() 156 #: 157 l2_payload = auto() 158 #: 159 ipv6_ex = auto() 160 #: 161 ipv6_tcp_ex = auto() 162 #: 163 ipv6_udp_ex = auto() 164 #: 165 port = auto() 166 #: 167 vxlan = auto() 168 #: 169 geneve = auto() 170 #: 171 nvgre = auto() 172 #: 173 user_defined_22 = auto() 174 #: 175 gtpu = auto() 176 #: 177 eth = auto() 178 #: 179 s_vlan = auto() 180 #: 181 c_vlan = auto() 182 #: 183 esp = auto() 184 #: 185 ah = auto() 186 #: 187 l2tpv3 = auto() 188 #: 189 pfcp = auto() 190 #: 191 pppoe = auto() 192 #: 193 ecpri = auto() 194 #: 195 mpls = auto() 196 #: 197 ipv4_chksum = auto() 198 #: 199 l4_chksum = auto() 200 #: 201 l2tpv2 = auto() 202 #: 203 ipv6_flow_label = auto() 204 #: 205 user_defined_38 = auto() 206 #: 207 user_defined_39 = auto() 208 #: 209 user_defined_40 = auto() 210 #: 211 user_defined_41 = auto() 212 #: 213 user_defined_42 = auto() 214 #: 215 user_defined_43 = auto() 216 #: 217 user_defined_44 = auto() 218 #: 219 user_defined_45 = auto() 220 #: 221 user_defined_46 = auto() 222 #: 223 user_defined_47 = auto() 224 #: 225 user_defined_48 = auto() 226 #: 227 user_defined_49 = auto() 228 #: 229 user_defined_50 = auto() 230 #: 231 user_defined_51 = auto() 232 #: 233 l3_pre96 = auto() 234 #: 235 l3_pre64 = auto() 236 #: 237 l3_pre56 = auto() 238 #: 239 l3_pre48 = auto() 240 #: 241 l3_pre40 = auto() 242 #: 243 l3_pre32 = auto() 244 #: 245 l2_dst_only = auto() 246 #: 247 l2_src_only = auto() 248 #: 249 l4_dst_only = auto() 250 #: 251 l4_src_only = auto() 252 #: 253 l3_dst_only = auto() 254 #: 255 l3_src_only = auto() 256 257 #: 258 ip = ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other | ipv6_ex 259 #: 260 udp = ipv4_udp | ipv6_udp | ipv6_udp_ex 261 #: 262 tcp = ipv4_tcp | ipv6_tcp | ipv6_tcp_ex 263 #: 264 sctp = ipv4_sctp | ipv6_sctp 265 #: 266 tunnel = vxlan | geneve | nvgre 267 #: 268 vlan = s_vlan | c_vlan 269 #: 270 all = ( 271 eth 272 | vlan 273 | ip 274 | tcp 275 | udp 276 | sctp 277 | l2_payload 278 | l2tpv3 279 | esp 280 | ah 281 | pfcp 282 | gtpu 283 | ecpri 284 | mpls 285 | l2tpv2 286 ) 287 288 @classmethod 289 def from_list_string(cls, names: str) -> Self: 290 """Makes a flag from a whitespace-separated list of names. 291 292 Args: 293 names: a whitespace-separated list containing the members of this flag. 294 295 Returns: 296 An instance of this flag. 297 """ 298 flag = cls(0) 299 for name in names.split(): 300 flag |= cls.from_str(name) 301 return flag 302 303 @classmethod 304 def from_str(cls, name: str) -> Self: 305 """Makes a flag matching the supplied name. 306 307 Args: 308 name: a valid member of this flag in text 309 Returns: 310 An instance of this flag. 311 """ 312 member_name = name.strip().replace("-", "_") 313 return cls[member_name] 314 315 @classmethod 316 def make_parser(cls) -> ParserFn: 317 """Makes a parser function. 318 319 Returns: 320 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 321 parser function that makes an instance of this flag from text. 322 """ 323 return TextParser.wrap( 324 TextParser.find(r"Supported RSS offload flow types:((?:\r?\n? \S+)+)", re.MULTILINE), 325 RSSOffloadTypesFlag.from_list_string, 326 ) 327 328 329class DeviceCapabilitiesFlag(Flag): 330 """Flag representing the device capabilities.""" 331 332 #: Device supports Rx queue setup after device started. 333 RUNTIME_RX_QUEUE_SETUP = auto() 334 #: Device supports Tx queue setup after device started. 335 RUNTIME_TX_QUEUE_SETUP = auto() 336 #: Device supports shared Rx queue among ports within Rx domain and switch domain. 337 RXQ_SHARE = auto() 338 #: Device supports keeping flow rules across restart. 339 FLOW_RULE_KEEP = auto() 340 #: Device supports keeping shared flow objects across restart. 341 FLOW_SHARED_OBJECT_KEEP = auto() 342 343 @classmethod 344 def make_parser(cls) -> ParserFn: 345 """Makes a parser function. 346 347 Returns: 348 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 349 parser function that makes an instance of this flag from text. 350 """ 351 return TextParser.wrap( 352 TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)"), 353 cls, 354 ) 355 356 357class DeviceErrorHandlingMode(StrEnum): 358 """Enum representing the device error handling mode.""" 359 360 #: 361 none = auto() 362 #: 363 passive = auto() 364 #: 365 proactive = auto() 366 #: 367 unknown = auto() 368 369 @classmethod 370 def make_parser(cls) -> ParserFn: 371 """Makes a parser function. 372 373 Returns: 374 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 375 parser function that makes an instance of this enum from text. 376 """ 377 return TextParser.wrap(TextParser.find(r"Device error handling mode: (\w+)"), cls) 378 379 380def make_device_private_info_parser() -> ParserFn: 381 """Device private information parser. 382 383 Ensures that we are not parsing invalid device private info output. 384 385 Returns: 386 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a parser 387 function that parses the device private info from the TestPmd port info output. 388 """ 389 390 def _validate(info: str): 391 info = info.strip() 392 if info == "none" or info.startswith("Invalid file") or info.startswith("Failed to dump"): 393 return None 394 return info 395 396 return TextParser.wrap(TextParser.find(r"Device private info:\s+([\s\S]+)"), _validate) 397 398 399class RxQueueState(StrEnum): 400 """RX queue states. 401 402 References: 403 DPDK lib: ``lib/ethdev/rte_ethdev.h`` 404 testpmd display function: ``app/test-pmd/config.c:get_queue_state_name()`` 405 """ 406 407 #: 408 stopped = auto() 409 #: 410 started = auto() 411 #: 412 hairpin = auto() 413 #: 414 unknown = auto() 415 416 @classmethod 417 def make_parser(cls) -> ParserFn: 418 """Makes a parser function. 419 420 Returns: 421 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 422 parser function that makes an instance of this enum from text. 423 """ 424 return TextParser.wrap(TextParser.find(r"Rx queue state: ([^\r\n]+)"), cls) 425 426 427@dataclass 428class TestPmdRxqInfo(TextParser): 429 """Representation of testpmd's ``show rxq info <port_id> <queue_id>`` command. 430 431 References: 432 testpmd command function: ``app/test-pmd/cmdline.c:cmd_showqueue()`` 433 testpmd display function: ``app/test-pmd/config.c:rx_queue_infos_display()`` 434 """ 435 436 #: 437 port_id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b ?, RX queue \d+\b")) 438 #: 439 queue_id: int = field(metadata=TextParser.find_int(r"Infos for port \d+\b ?, RX queue (\d+)\b")) 440 #: Mempool used by that queue 441 mempool: str = field(metadata=TextParser.find(r"Mempool: ([^\r\n]+)")) 442 #: Ring prefetch threshold 443 rx_prefetch_threshold: int = field( 444 metadata=TextParser.find_int(r"RX prefetch threshold: (\d+)\b") 445 ) 446 #: Ring host threshold 447 rx_host_threshold: int = field(metadata=TextParser.find_int(r"RX host threshold: (\d+)\b")) 448 #: Ring writeback threshold 449 rx_writeback_threshold: int = field( 450 metadata=TextParser.find_int(r"RX writeback threshold: (\d+)\b") 451 ) 452 #: Drives the freeing of Rx descriptors 453 rx_free_threshold: int = field(metadata=TextParser.find_int(r"RX free threshold: (\d+)\b")) 454 #: Drop packets if no descriptors are available 455 rx_drop_packets: bool = field(metadata=TextParser.find(r"RX drop packets: on")) 456 #: Do not start queue with rte_eth_dev_start() 457 rx_deferred_start: bool = field(metadata=TextParser.find(r"RX deferred start: on")) 458 #: Scattered packets Rx enabled 459 rx_scattered_packets: bool = field(metadata=TextParser.find(r"RX scattered packets: on")) 460 #: The state of the queue 461 rx_queue_state: str = field(metadata=RxQueueState.make_parser()) 462 #: Configured number of RXDs 463 number_of_rxds: int = field(metadata=TextParser.find_int(r"Number of RXDs: (\d+)\b")) 464 #: Hardware receive buffer size 465 rx_buffer_size: int | None = field( 466 default=None, metadata=TextParser.find_int(r"RX buffer size: (\d+)\b") 467 ) 468 #: Burst mode information 469 burst_mode: str | None = field( 470 default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)") 471 ) 472 473 474@dataclass 475class TestPmdPort(TextParser): 476 """Dataclass representing the result of testpmd's ``show port info`` command.""" 477 478 #: 479 id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b")) 480 #: 481 device_name: str = field(metadata=TextParser.find(r"Device name: ([^\r\n]+)")) 482 #: 483 driver_name: str = field(metadata=TextParser.find(r"Driver name: ([^\r\n]+)")) 484 #: 485 socket_id: int = field(metadata=TextParser.find_int(r"Connect to socket: (\d+)")) 486 #: 487 is_link_up: bool = field(metadata=TextParser.find("Link status: up")) 488 #: 489 link_speed: str = field(metadata=TextParser.find(r"Link speed: ([^\r\n]+)")) 490 #: 491 is_link_full_duplex: bool = field(metadata=TextParser.find("Link duplex: full-duplex")) 492 #: 493 is_link_autonegotiated: bool = field(metadata=TextParser.find("Autoneg status: On")) 494 #: 495 is_promiscuous_mode_enabled: bool = field(metadata=TextParser.find("Promiscuous mode: enabled")) 496 #: 497 is_allmulticast_mode_enabled: bool = field( 498 metadata=TextParser.find("Allmulticast mode: enabled") 499 ) 500 #: Maximum number of MAC addresses 501 max_mac_addresses_num: int = field( 502 metadata=TextParser.find_int(r"Maximum number of MAC addresses: (\d+)") 503 ) 504 #: Maximum configurable length of RX packet 505 max_hash_mac_addresses_num: int = field( 506 metadata=TextParser.find_int(r"Maximum number of MAC addresses of hash filtering: (\d+)") 507 ) 508 #: Minimum size of RX buffer 509 min_rx_bufsize: int = field(metadata=TextParser.find_int(r"Minimum size of RX buffer: (\d+)")) 510 #: Maximum configurable length of RX packet 511 max_rx_packet_length: int = field( 512 metadata=TextParser.find_int(r"Maximum configurable length of RX packet: (\d+)") 513 ) 514 #: Maximum configurable size of LRO aggregated packet 515 max_lro_packet_size: int = field( 516 metadata=TextParser.find_int(r"Maximum configurable size of LRO aggregated packet: (\d+)") 517 ) 518 519 #: Current number of RX queues 520 rx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of RX queues: (\d+)")) 521 #: Max possible RX queues 522 max_rx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible RX queues: (\d+)")) 523 #: Max possible number of RXDs per queue 524 max_queue_rxd_num: int = field( 525 metadata=TextParser.find_int(r"Max possible number of RXDs per queue: (\d+)") 526 ) 527 #: Min possible number of RXDs per queue 528 min_queue_rxd_num: int = field( 529 metadata=TextParser.find_int(r"Min possible number of RXDs per queue: (\d+)") 530 ) 531 #: RXDs number alignment 532 rxd_alignment_num: int = field(metadata=TextParser.find_int(r"RXDs number alignment: (\d+)")) 533 534 #: Current number of TX queues 535 tx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of TX queues: (\d+)")) 536 #: Max possible TX queues 537 max_tx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible TX queues: (\d+)")) 538 #: Max possible number of TXDs per queue 539 max_queue_txd_num: int = field( 540 metadata=TextParser.find_int(r"Max possible number of TXDs per queue: (\d+)") 541 ) 542 #: Min possible number of TXDs per queue 543 min_queue_txd_num: int = field( 544 metadata=TextParser.find_int(r"Min possible number of TXDs per queue: (\d+)") 545 ) 546 #: TXDs number alignment 547 txd_alignment_num: int = field(metadata=TextParser.find_int(r"TXDs number alignment: (\d+)")) 548 #: Max segment number per packet 549 max_packet_segment_num: int = field( 550 metadata=TextParser.find_int(r"Max segment number per packet: (\d+)") 551 ) 552 #: Max segment number per MTU/TSO 553 max_mtu_segment_num: int = field( 554 metadata=TextParser.find_int(r"Max segment number per MTU\/TSO: (\d+)") 555 ) 556 557 #: 558 device_capabilities: DeviceCapabilitiesFlag = field( 559 metadata=DeviceCapabilitiesFlag.make_parser(), 560 ) 561 #: 562 device_error_handling_mode: DeviceErrorHandlingMode | None = field( 563 default=None, metadata=DeviceErrorHandlingMode.make_parser() 564 ) 565 #: 566 device_private_info: str | None = field( 567 default=None, 568 metadata=make_device_private_info_parser(), 569 ) 570 571 #: 572 hash_key_size: int | None = field( 573 default=None, metadata=TextParser.find_int(r"Hash key size in bytes: (\d+)") 574 ) 575 #: 576 redirection_table_size: int | None = field( 577 default=None, metadata=TextParser.find_int(r"Redirection table size: (\d+)") 578 ) 579 #: 580 supported_rss_offload_flow_types: RSSOffloadTypesFlag = field( 581 default=RSSOffloadTypesFlag(0), metadata=RSSOffloadTypesFlag.make_parser() 582 ) 583 584 #: 585 mac_address: str | None = field( 586 default=None, metadata=TextParser.find(r"MAC address: ([A-Fa-f0-9:]+)") 587 ) 588 #: 589 fw_version: str | None = field( 590 default=None, metadata=TextParser.find(r"Firmware-version: ([^\r\n]+)") 591 ) 592 #: 593 dev_args: str | None = field(default=None, metadata=TextParser.find(r"Devargs: ([^\r\n]+)")) 594 #: Socket id of the memory allocation 595 mem_alloc_socket_id: int | None = field( 596 default=None, 597 metadata=TextParser.find_int(r"memory allocation on the socket: (\d+)"), 598 ) 599 #: 600 mtu: int | None = field(default=None, metadata=TextParser.find_int(r"MTU: (\d+)")) 601 602 #: 603 vlan_offload: VLANOffloadFlag | None = field( 604 default=None, 605 metadata=VLANOffloadFlag.make_parser(), 606 ) 607 608 #: Maximum size of RX buffer 609 max_rx_bufsize: int | None = field( 610 default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)") 611 ) 612 #: Maximum number of VFs 613 max_vfs_num: int | None = field( 614 default=None, metadata=TextParser.find_int(r"Maximum number of VFs: (\d+)") 615 ) 616 #: Maximum number of VMDq pools 617 max_vmdq_pools_num: int | None = field( 618 default=None, metadata=TextParser.find_int(r"Maximum number of VMDq pools: (\d+)") 619 ) 620 621 #: 622 switch_name: str | None = field( 623 default=None, metadata=TextParser.find(r"Switch name: ([\r\n]+)") 624 ) 625 #: 626 switch_domain_id: int | None = field( 627 default=None, metadata=TextParser.find_int(r"Switch domain Id: (\d+)") 628 ) 629 #: 630 switch_port_id: int | None = field( 631 default=None, metadata=TextParser.find_int(r"Switch Port Id: (\d+)") 632 ) 633 #: 634 switch_rx_domain: int | None = field( 635 default=None, metadata=TextParser.find_int(r"Switch Rx domain: (\d+)") 636 ) 637 638 639@dataclass 640class TestPmdPortStats(TextParser): 641 """Port statistics.""" 642 643 #: 644 port_id: int = field(metadata=TextParser.find_int(r"NIC statistics for port (\d+)")) 645 646 #: 647 rx_packets: int = field(metadata=TextParser.find_int(r"RX-packets:\s+(\d+)")) 648 #: 649 rx_missed: int = field(metadata=TextParser.find_int(r"RX-missed:\s+(\d+)")) 650 #: 651 rx_bytes: int = field(metadata=TextParser.find_int(r"RX-bytes:\s+(\d+)")) 652 #: 653 rx_errors: int = field(metadata=TextParser.find_int(r"RX-errors:\s+(\d+)")) 654 #: 655 rx_nombuf: int = field(metadata=TextParser.find_int(r"RX-nombuf:\s+(\d+)")) 656 657 #: 658 tx_packets: int = field(metadata=TextParser.find_int(r"TX-packets:\s+(\d+)")) 659 #: 660 tx_errors: int = field(metadata=TextParser.find_int(r"TX-errors:\s+(\d+)")) 661 #: 662 tx_bytes: int = field(metadata=TextParser.find_int(r"TX-bytes:\s+(\d+)")) 663 664 #: 665 rx_pps: int = field(metadata=TextParser.find_int(r"Rx-pps:\s+(\d+)")) 666 #: 667 rx_bps: int = field(metadata=TextParser.find_int(r"Rx-bps:\s+(\d+)")) 668 669 #: 670 tx_pps: int = field(metadata=TextParser.find_int(r"Tx-pps:\s+(\d+)")) 671 #: 672 tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)")) 673 674 675class PacketOffloadFlag(Flag): 676 """Flag representing the Packet Offload Features Flags in DPDK. 677 678 Values in this class are taken from the definitions in the RTE MBUF core library in DPDK 679 located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that flag values in this class will 680 match the values they are set to in said DPDK library with one exception; all values must be 681 unique. For example, the definitions for unknown checksum flags in ``rte_mbuf_core.h`` are all 682 set to :data:`0`, but it is valuable to distinguish between them in this framework. For this 683 reason flags that are not unique in the DPDK library are set either to values within the 684 RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or shifted 61+ bits for Tx. 685 686 References: 687 DPDK lib: ``lib/mbuf/rte_mbuf_core.h`` 688 """ 689 690 # RX flags 691 692 #: The RX packet is a 802.1q VLAN packet, and the tci has been saved in mbuf->vlan_tci. If the 693 #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN header has been stripped from 694 #: mbuf data, else it is still present. 695 RTE_MBUF_F_RX_VLAN = auto() 696 697 #: RX packet with RSS hash result. 698 RTE_MBUF_F_RX_RSS_HASH = auto() 699 700 #: RX packet with FDIR match indicate. 701 RTE_MBUF_F_RX_FDIR = auto() 702 703 #: This flag is set when the outermost IP header checksum is detected as wrong by the hardware. 704 RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD = 1 << 5 705 706 #: A vlan has been stripped by the hardware and its tci is saved in mbuf->vlan_tci. This can 707 #: only happen if vlan stripping is enabled in the RX configuration of the PMD. When 708 #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN must also be set. 709 RTE_MBUF_F_RX_VLAN_STRIPPED = auto() 710 711 #: No information about the RX IP checksum. Value is 0 in the DPDK library. 712 RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = 1 << 23 713 #: The IP checksum in the packet is wrong. 714 RTE_MBUF_F_RX_IP_CKSUM_BAD = 1 << 4 715 #: The IP checksum in the packet is valid. 716 RTE_MBUF_F_RX_IP_CKSUM_GOOD = 1 << 7 717 #: The IP checksum is not correct in the packet data, but the integrity of the IP header is 718 #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD in the DPDK 719 #: library. 720 RTE_MBUF_F_RX_IP_CKSUM_NONE = 1 << 24 721 722 #: No information about the RX L4 checksum. Value is 0 in the DPDK library. 723 RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = 1 << 25 724 #: The L4 checksum in the packet is wrong. 725 RTE_MBUF_F_RX_L4_CKSUM_BAD = 1 << 3 726 #: The L4 checksum in the packet is valid. 727 RTE_MBUF_F_RX_L4_CKSUM_GOOD = 1 << 8 728 #: The L4 checksum is not correct in the packet data, but the integrity of the L4 data is 729 #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD in the DPDK 730 #: library. 731 RTE_MBUF_F_RX_L4_CKSUM_NONE = 1 << 26 732 733 #: RX IEEE1588 L2 Ethernet PT Packet. 734 RTE_MBUF_F_RX_IEEE1588_PTP = 1 << 9 735 #: RX IEEE1588 L2/L4 timestamped packet. 736 RTE_MBUF_F_RX_IEEE1588_TMST = 1 << 10 737 738 #: FD id reported if FDIR match. 739 RTE_MBUF_F_RX_FDIR_ID = 1 << 13 740 #: Flexible bytes reported if FDIR match. 741 RTE_MBUF_F_RX_FDIR_FLX = 1 << 14 742 743 #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and RTE_MBUF_F_RX_VLAN_STRIPPED are set, the 2 VLANs 744 #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ_STRIPPED is set and 745 #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN is removed from packet data. 746 RTE_MBUF_F_RX_QINQ_STRIPPED = auto() 747 748 #: When packets are coalesced by a hardware or virtual driver, this flag can be set in the RX 749 #: mbuf, meaning that the m->tso_segsz field is valid and is set to the segment size of 750 #: original packets. 751 RTE_MBUF_F_RX_LRO = auto() 752 753 #: Indicate that security offload processing was applied on the RX packet. 754 RTE_MBUF_F_RX_SEC_OFFLOAD = 1 << 18 755 #: Indicate that security offload processing failed on the RX packet. 756 RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED = auto() 757 758 #: The RX packet is a double VLAN. If this flag is set, RTE_MBUF_F_RX_VLAN must also be set. If 759 #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs headers have been stripped 760 #: from mbuf data, else they are still present. 761 RTE_MBUF_F_RX_QINQ = auto() 762 763 #: No info about the outer RX L4 checksum. Value is 0 in the DPDK library. 764 RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = 1 << 27 765 #: The outer L4 checksum in the packet is wrong 766 RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = 1 << 21 767 #: The outer L4 checksum in the packet is valid 768 RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = 1 << 22 769 #: Invalid outer L4 checksum state. Value is 770 #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD in the DPDK library. 771 RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID = 1 << 28 772 773 # TX flags 774 775 #: Outer UDP checksum offload flag. This flag is used for enabling outer UDP checksum in PMD. 776 #: To use outer UDP checksum, the user either needs to enable the following in mbuf: 777 #: 778 #: a) Fill outer_l2_len and outer_l3_len in mbuf. 779 #: b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag. 780 #: c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_TX_OUTER_IPV6 flag. 781 #: 782 #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload flag. 783 RTE_MBUF_F_TX_OUTER_UDP_CKSUM = 1 << 41 784 785 #: UDP Fragmentation Offload flag. This flag is used for enabling UDP fragmentation in SW or in 786 #: HW. 787 RTE_MBUF_F_TX_UDP_SEG = auto() 788 789 #: Request security offload processing on the TX packet. To use Tx security offload, the user 790 #: needs to fill l2_len in mbuf indicating L2 header size and where L3 header starts. 791 #: Similarly, l3_len should also be filled along with ol_flags reflecting current L3 type. 792 RTE_MBUF_F_TX_SEC_OFFLOAD = auto() 793 794 #: Offload the MACsec. This flag must be set by the application to enable this offload feature 795 #: for a packet to be transmitted. 796 RTE_MBUF_F_TX_MACSEC = auto() 797 798 # Bits 45:48 are used for the tunnel type in ``lib/mbuf/rte_mbuf_core.h``, but some are modified 799 # in this Flag to maintain uniqueness. The tunnel type must be specified for TSO or checksum on 800 # the inner part of tunnel packets. These flags can be used with RTE_MBUF_F_TX_TCP_SEG for TSO, 801 # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and outer header lengths are required: 802 # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso_segsz for TSO. 803 804 #: 805 RTE_MBUF_F_TX_TUNNEL_VXLAN = 1 << 45 806 #: 807 RTE_MBUF_F_TX_TUNNEL_GRE = 1 << 46 808 #: Value is 3 << 45 in the DPDK library. 809 RTE_MBUF_F_TX_TUNNEL_IPIP = 1 << 61 810 #: 811 RTE_MBUF_F_TX_TUNNEL_GENEVE = 1 << 47 812 #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 << 45 in the DPDK library. 813 RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = 1 << 62 814 #: Value is 6 << 45 in the DPDK library. 815 RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 1 << 63 816 #: Value is 7 << 45 in the DPDK library. 817 RTE_MBUF_F_TX_TUNNEL_GTP = 1 << 64 818 #: 819 RTE_MBUF_F_TX_TUNNEL_ESP = 1 << 48 820 #: Generic IP encapsulated tunnel type, used for TSO and checksum offload. This can be used for 821 #: tunnels which are not standards or listed above. It is preferred to use specific tunnel 822 #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNNEL_IPIP if possible. The ethdev 823 #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO. Outer and inner checksums are done 824 #: according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel headers that 825 #: contain payload length, sequence id or checksum are not expected to be updated. Value is 826 #: 0xD << 45 in the DPDK library. 827 RTE_MBUF_F_TX_TUNNEL_IP = 1 << 65 828 #: Generic UDP encapsulated tunnel type, used for TSO and checksum offload. UDP tunnel type 829 #: implies outer IP layer. It can be used for tunnels which are not standards or listed above. 830 #: It is preferred to use specific tunnel flags like RTE_MBUF_F_TX_TUNNEL_VXLAN if possible. 831 #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO. Outer and inner checksums 832 #: are done according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel 833 #: headers that contain payload length, sequence id or checksum are not expected to be updated. 834 #: value is 0xE << 45 in the DPDK library. 835 RTE_MBUF_F_TX_TUNNEL_UDP = 1 << 66 836 837 #: Double VLAN insertion (QinQ) request to driver, driver may offload the insertion based on 838 #: device capability. Mbuf 'vlan_tci' & 'vlan_tci_outer' must be valid when this flag is set. 839 RTE_MBUF_F_TX_QINQ = 1 << 49 840 841 #: TCP segmentation offload. To enable this offload feature for a packet to be transmitted on 842 #: hardware supporting TSO: 843 #: 844 #: - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol_flags (this flag implies 845 #: RTE_MBUF_F_TX_TCP_CKSUM) 846 #: - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 847 #: * if it's IPv4, set the RTE_MBUF_F_TX_IP_CKSUM flag 848 #: - fill the mbuf offload information: l2_len, l3_len, l4_len, tso_segsz 849 RTE_MBUF_F_TX_TCP_SEG = auto() 850 851 #: TX IEEE1588 packet to timestamp. 852 RTE_MBUF_F_TX_IEEE1588_TMST = auto() 853 854 # Bits 52+53 used for L4 packet type with checksum enabled in ``lib/mbuf/rte_mbuf_core.h`` but 855 # some values must be modified in this framework to maintain uniqueness. To use hardware 856 # L4 checksum offload, the user needs to: 857 # 858 # - fill l2_len and l3_len in mbuf 859 # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCTP_CKSUM or 860 # RTE_MBUF_F_TX_UDP_CKSUM 861 # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 862 863 #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK library. 864 RTE_MBUF_F_TX_L4_NO_CKSUM = 1 << 67 865 #: TCP cksum of TX pkt. Computed by NIC. 866 RTE_MBUF_F_TX_TCP_CKSUM = 1 << 52 867 #: SCTP cksum of TX pkt. Computed by NIC. 868 RTE_MBUF_F_TX_SCTP_CKSUM = 1 << 53 869 #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPDK library. 870 RTE_MBUF_F_TX_UDP_CKSUM = 1 << 68 871 872 #: Offload the IP checksum in the hardware. The flag RTE_MBUF_F_TX_IPV4 should also be set by 873 #: the application, although a PMD will only check RTE_MBUF_F_TX_IP_CKSUM. 874 RTE_MBUF_F_TX_IP_CKSUM = 1 << 54 875 876 #: Packet is IPv4. This flag must be set when using any offload feature (TSO, L3 or L4 877 #: checksum) to tell the NIC that the packet is an IPv4 packet. If the packet is a tunneled 878 #: packet, this flag is related to the inner headers. 879 RTE_MBUF_F_TX_IPV4 = auto() 880 #: Packet is IPv6. This flag must be set when using an offload feature (TSO or L4 checksum) to 881 #: tell the NIC that the packet is an IPv6 packet. If the packet is a tunneled packet, this 882 #: flag is related to the inner headers. 883 RTE_MBUF_F_TX_IPV6 = auto() 884 #: VLAN tag insertion request to driver, driver may offload the insertion based on the device 885 #: capability. mbuf 'vlan_tci' field must be valid when this flag is set. 886 RTE_MBUF_F_TX_VLAN = auto() 887 888 #: Offload the IP checksum of an external header in the hardware. The flag 889 #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the application, although a PMD will only 890 #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM. 891 RTE_MBUF_F_TX_OUTER_IP_CKSUM = auto() 892 #: Packet outer header is IPv4. This flag must be set when using any outer offload feature (L3 893 #: or L4 checksum) to tell the NIC that the outer header of the tunneled packet is an IPv4 894 #: packet. 895 RTE_MBUF_F_TX_OUTER_IPV4 = auto() 896 #: Packet outer header is IPv6. This flag must be set when using any outer offload feature (L4 897 #: checksum) to tell the NIC that the outer header of the tunneled packet is an IPv6 packet. 898 RTE_MBUF_F_TX_OUTER_IPV6 = auto() 899 900 @classmethod 901 def from_list_string(cls, names: str) -> Self: 902 """Makes a flag from a whitespace-separated list of names. 903 904 Args: 905 names: a whitespace-separated list containing the members of this flag. 906 907 Returns: 908 An instance of this flag. 909 """ 910 flag = cls(0) 911 for name in names.split(): 912 flag |= cls.from_str(name) 913 return flag 914 915 @classmethod 916 def from_str(cls, name: str) -> Self: 917 """Makes a flag matching the supplied name. 918 919 Args: 920 name: a valid member of this flag in text 921 Returns: 922 An instance of this flag. 923 """ 924 member_name = name.strip().replace("-", "_") 925 return cls[member_name] 926 927 @classmethod 928 def make_parser(cls) -> ParserFn: 929 """Makes a parser function. 930 931 Returns: 932 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 933 parser function that makes an instance of this flag from text. 934 """ 935 return TextParser.wrap( 936 TextParser.find(r"ol_flags: ([^\n]+)"), 937 cls.from_list_string, 938 ) 939 940 941class RtePTypes(Flag): 942 """Flag representing possible packet types in DPDK verbose output. 943 944 Values in this class are derived from definitions in the RTE MBUF ptype library in DPDK located 945 in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of values in this class should match 946 the possible return options from the functions ``rte_get_ptype_*_name`` in ``rte_mbuf_ptype.c``. 947 948 References: 949 DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h`` 950 DPDK ptype name formatting functions: ``lib/mbuf/rte_mbuf_ptype.c:rte_get_ptype_*_name()`` 951 """ 952 953 # L2 954 #: Ethernet packet type. This is used for outer packet for tunneling cases. 955 L2_ETHER = auto() 956 #: Ethernet packet type for time sync. 957 L2_ETHER_TIMESYNC = auto() 958 #: ARP (Address Resolution Protocol) packet type. 959 L2_ETHER_ARP = auto() 960 #: LLDP (Link Layer Discovery Protocol) packet type. 961 L2_ETHER_LLDP = auto() 962 #: NSH (Network Service Header) packet type. 963 L2_ETHER_NSH = auto() 964 #: VLAN packet type. 965 L2_ETHER_VLAN = auto() 966 #: QinQ packet type. 967 L2_ETHER_QINQ = auto() 968 #: PPPOE packet type. 969 L2_ETHER_PPPOE = auto() 970 #: FCoE packet type.. 971 L2_ETHER_FCOE = auto() 972 #: MPLS packet type. 973 L2_ETHER_MPLS = auto() 974 #: No L2 packet information. 975 L2_UNKNOWN = auto() 976 977 # L3 978 #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling 979 #: cases, and does not contain any header option. 980 L3_IPV4 = auto() 981 #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling 982 #: cases, and contains header options. 983 L3_IPV4_EXT = auto() 984 #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling 985 #: cases, and does not contain any extension header. 986 L3_IPV6 = auto() 987 #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling 988 #: cases, and may or maynot contain header options. 989 L3_IPV4_EXT_UNKNOWN = auto() 990 #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling 991 #: cases, and contains extension headers. 992 L3_IPV6_EXT = auto() 993 #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling 994 #: cases, and may or maynot contain extension headers. 995 L3_IPV6_EXT_UNKNOWN = auto() 996 #: No L3 packet information. 997 L3_UNKNOWN = auto() 998 999 # L4 1000 #: TCP (Transmission Control Protocol) packet type. This is used for outer packet for tunneling 1001 #: cases. 1002 L4_TCP = auto() 1003 #: UDP (User Datagram Protocol) packet type. This is used for outer packet for tunneling cases. 1004 L4_UDP = auto() 1005 #: Fragmented IP (Internet Protocol) packet type. This is used for outer packet for tunneling 1006 #: cases and refers to those packets of any IP types which can be recognized as fragmented. A 1007 #: fragmented packet cannot be recognized as any other L4 types (RTE_PTYPE_L4_TCP, 1008 #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, RTE_PTYPE_L4_NONFRAG). 1009 L4_FRAG = auto() 1010 #: SCTP (Stream Control Transmission Protocol) packet type. This is used for outer packet for 1011 #: tunneling cases. 1012 L4_SCTP = auto() 1013 #: ICMP (Internet Control Message Protocol) packet type. This is used for outer packet for 1014 #: tunneling cases. 1015 L4_ICMP = auto() 1016 #: Non-fragmented IP (Internet Protocol) packet type. This is used for outer packet for 1017 #: tunneling cases and refers to those packets of any IP types, that cannot be recognized as 1018 #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_FRAG, 1019 #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP). 1020 L4_NONFRAG = auto() 1021 #: IGMP (Internet Group Management Protocol) packet type. 1022 L4_IGMP = auto() 1023 #: No L4 packet information. 1024 L4_UNKNOWN = auto() 1025 1026 # Tunnel 1027 #: IP (Internet Protocol) in IP (Internet Protocol) tunneling packet type. 1028 TUNNEL_IP = auto() 1029 #: GRE (Generic Routing Encapsulation) tunneling packet type. 1030 TUNNEL_GRE = auto() 1031 #: VXLAN (Virtual eXtensible Local Area Network) tunneling packet type. 1032 TUNNEL_VXLAN = auto() 1033 #: NVGRE (Network Virtualization using Generic Routing Encapsulation) tunneling packet type. 1034 TUNNEL_NVGRE = auto() 1035 #: GENEVE (Generic Network Virtualization Encapsulation) tunneling packet type. 1036 TUNNEL_GENEVE = auto() 1037 #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensible Local Area Network) or GRE 1038 #: (Generic Routing Encapsulation) could be recognized as this packet type, if they can not be 1039 #: recognized independently as of hardware capability. 1040 TUNNEL_GRENAT = auto() 1041 #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet type. 1042 TUNNEL_GTPC = auto() 1043 #: GTP-U (GPRS Tunnelling Protocol) user data tunneling packet type. 1044 TUNNEL_GTPU = auto() 1045 #: ESP (IP Encapsulating Security Payload) tunneling packet type. 1046 TUNNEL_ESP = auto() 1047 #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type. 1048 TUNNEL_L2TP = auto() 1049 #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling packet type. 1050 TUNNEL_VXLAN_GPE = auto() 1051 #: MPLS-in-UDP tunneling packet type (RFC 7510). 1052 TUNNEL_MPLS_IN_UDP = auto() 1053 #: MPLS-in-GRE tunneling packet type (RFC 4023). 1054 TUNNEL_MPLS_IN_GRE = auto() 1055 #: No tunnel information found on the packet. 1056 TUNNEL_UNKNOWN = auto() 1057 1058 # Inner L2 1059 #: Ethernet packet type. This is used for inner packet type only. 1060 INNER_L2_ETHER = auto() 1061 #: Ethernet packet type with VLAN (Virtual Local Area Network) tag. 1062 INNER_L2_ETHER_VLAN = auto() 1063 #: QinQ packet type. 1064 INNER_L2_ETHER_QINQ = auto() 1065 #: No inner L2 information found on the packet. 1066 INNER_L2_UNKNOWN = auto() 1067 1068 # Inner L3 1069 #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and does 1070 #: not contain any header option. 1071 INNER_L3_IPV4 = auto() 1072 #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and 1073 #: contains header options. 1074 INNER_L3_IPV4_EXT = auto() 1075 #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and does 1076 #: not contain any extension header. 1077 INNER_L3_IPV6 = auto() 1078 #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and may or 1079 #: may not contain header options. 1080 INNER_L3_IPV4_EXT_UNKNOWN = auto() 1081 #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and 1082 #: contains extension headers. 1083 INNER_L3_IPV6_EXT = auto() 1084 #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and may or 1085 #: may not contain extension headers. 1086 INNER_L3_IPV6_EXT_UNKNOWN = auto() 1087 #: No inner L3 information found on the packet. 1088 INNER_L3_UNKNOWN = auto() 1089 1090 # Inner L4 1091 #: TCP (Transmission Control Protocol) packet type. This is used for inner packet only. 1092 INNER_L4_TCP = auto() 1093 #: UDP (User Datagram Protocol) packet type. This is used for inner packet only. 1094 INNER_L4_UDP = auto() 1095 #: Fragmented IP (Internet Protocol) packet type. This is used for inner packet only, and may 1096 #: or maynot have a layer 4 packet. 1097 INNER_L4_FRAG = auto() 1098 #: SCTP (Stream Control Transmission Protocol) packet type. This is used for inner packet only. 1099 INNER_L4_SCTP = auto() 1100 #: ICMP (Internet Control Message Protocol) packet type. This is used for inner packet only. 1101 INNER_L4_ICMP = auto() 1102 #: Non-fragmented IP (Internet Protocol) packet type. It is used for inner packet only, and may 1103 #: or may not have other unknown layer 4 packet types. 1104 INNER_L4_NONFRAG = auto() 1105 #: No inner L4 information found on the packet. 1106 INNER_L4_UNKNOWN = auto() 1107 1108 @classmethod 1109 def from_list_string(cls, names: str) -> Self: 1110 """Makes a flag from a whitespace-separated list of names. 1111 1112 Args: 1113 names: a whitespace-separated list containing the members of this flag. 1114 1115 Returns: 1116 An instance of this flag. 1117 """ 1118 flag = cls(0) 1119 for name in names.split(): 1120 flag |= cls.from_str(name) 1121 return flag 1122 1123 @classmethod 1124 def from_str(cls, name: str) -> Self: 1125 """Makes a flag matching the supplied name. 1126 1127 Args: 1128 name: a valid member of this flag in text 1129 Returns: 1130 An instance of this flag. 1131 """ 1132 member_name = name.strip().replace("-", "_") 1133 return cls[member_name] 1134 1135 @classmethod 1136 def make_parser(cls, hw: bool) -> ParserFn: 1137 """Makes a parser function. 1138 1139 Args: 1140 hw: Whether to make a parser for hardware ptypes or software ptypes. If :data:`True`, 1141 hardware ptypes will be collected, otherwise software pytpes will. 1142 1143 Returns: 1144 ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 1145 parser function that makes an instance of this flag from text. 1146 """ 1147 return TextParser.wrap( 1148 TextParser.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"), 1149 cls.from_list_string, 1150 ) 1151 1152 1153@dataclass 1154class TestPmdVerbosePacket(TextParser): 1155 """Packet information provided by verbose output in Testpmd. 1156 1157 This dataclass expects that packet information be prepended with the starting line of packet 1158 bursts. Specifically, the line that reads "port X/queue Y: sent/received Z packets". 1159 """ 1160 1161 #: ID of the port that handled the packet. 1162 port_id: int = field(metadata=TextParser.find_int(r"port (\d+)/queue \d+")) 1163 #: ID of the queue that handled the packet. 1164 queue_id: int = field(metadata=TextParser.find_int(r"port \d+/queue (\d+)")) 1165 #: Whether the packet was received or sent by the queue/port. 1166 was_received: bool = field(metadata=TextParser.find(r"received \d+ packets")) 1167 #: 1168 src_mac: str = field(metadata=TextParser.find(f"src=({REGEX_FOR_MAC_ADDRESS})")) 1169 #: 1170 dst_mac: str = field(metadata=TextParser.find(f"dst=({REGEX_FOR_MAC_ADDRESS})")) 1171 #: Memory pool the packet was handled on. 1172 pool: str = field(metadata=TextParser.find(r"pool=(\S+)")) 1173 #: Packet type in hex. 1174 p_type: int = field(metadata=TextParser.find_int(r"type=(0x[a-fA-F\d]+)")) 1175 #: 1176 length: int = field(metadata=TextParser.find_int(r"length=(\d+)")) 1177 #: Number of segments in the packet. 1178 nb_segs: int = field(metadata=TextParser.find_int(r"nb_segs=(\d+)")) 1179 #: Hardware packet type. 1180 hw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=True)) 1181 #: Software packet type. 1182 sw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=False)) 1183 #: 1184 l2_len: int = field(metadata=TextParser.find_int(r"l2_len=(\d+)")) 1185 #: 1186 ol_flags: PacketOffloadFlag = field(metadata=PacketOffloadFlag.make_parser()) 1187 #: RSS hash of the packet in hex. 1188 rss_hash: int | None = field( 1189 default=None, metadata=TextParser.find_int(r"RSS hash=(0x[a-fA-F\d]+)") 1190 ) 1191 #: RSS queue that handled the packet in hex. 1192 rss_queue: int | None = field( 1193 default=None, metadata=TextParser.find_int(r"RSS queue=(0x[a-fA-F\d]+)") 1194 ) 1195 #: 1196 l3_len: int | None = field(default=None, metadata=TextParser.find_int(r"l3_len=(\d+)")) 1197 #: 1198 l4_len: int | None = field(default=None, metadata=TextParser.find_int(r"l4_len=(\d+)")) 1199 1200 1201def requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMethod: 1202 """Decorator for :class:`TestPmdShell` commands methods that require stopped ports. 1203 1204 If the decorated method is called while the ports are started, then these are stopped before 1205 continuing. 1206 1207 Args: 1208 func: The :class:`TestPmdShell` method to decorate. 1209 """ 1210 1211 @functools.wraps(func) 1212 def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs): 1213 if self.ports_started: 1214 self._logger.debug("Ports need to be stopped to continue.") 1215 self.stop_all_ports() 1216 1217 return func(self, *args, **kwargs) 1218 1219 return _wrapper 1220 1221 1222def requires_started_ports(func: TestPmdShellMethod) -> TestPmdShellMethod: 1223 """Decorator for :class:`TestPmdShell` commands methods that require started ports. 1224 1225 If the decorated method is called while the ports are stopped, then these are started before 1226 continuing. 1227 1228 Args: 1229 func: The :class:`TestPmdShell` method to decorate. 1230 """ 1231 1232 @functools.wraps(func) 1233 def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs): 1234 if not self.ports_started: 1235 self._logger.debug("Ports need to be started to continue.") 1236 self.start_all_ports() 1237 1238 return func(self, *args, **kwargs) 1239 1240 return _wrapper 1241 1242 1243def add_remove_mtu(mtu: int = 1500) -> Callable[[TestPmdShellMethod], TestPmdShellMethod]: 1244 """Configure MTU to `mtu` on all ports, run the decorated function, then revert. 1245 1246 Args: 1247 mtu: The MTU to configure all ports on. 1248 1249 Returns: 1250 The method decorated with setting and reverting MTU. 1251 """ 1252 1253 def decorator(func: TestPmdShellMethod) -> TestPmdShellMethod: 1254 @functools.wraps(func) 1255 def wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs): 1256 original_mtu = self.ports[0].mtu 1257 self.set_port_mtu_all(mtu=mtu, verify=False) 1258 retval = func(self, *args, **kwargs) 1259 self.set_port_mtu_all(original_mtu if original_mtu else 1500, verify=False) 1260 return retval 1261 1262 return wrapper 1263 1264 return decorator 1265 1266 1267class TestPmdShell(DPDKShell): 1268 """Testpmd interactive shell. 1269 1270 The testpmd shell users should never use 1271 the :meth:`~.interactive_shell.InteractiveShell.send_command` method directly, but rather 1272 call specialized methods. If there isn't one that satisfies a need, it should be added. 1273 1274 Attributes: 1275 ports_started: Indicates whether the ports are started. 1276 """ 1277 1278 _app_params: TestPmdParams 1279 _ports: list[TestPmdPort] | None 1280 1281 #: The path to the testpmd executable. 1282 path: ClassVar[PurePath] = PurePath("app", "dpdk-testpmd") 1283 1284 #: The testpmd's prompt. 1285 _default_prompt: ClassVar[str] = "testpmd>" 1286 1287 #: This forces the prompt to appear after sending a command. 1288 _command_extra_chars: ClassVar[str] = "\n" 1289 1290 ports_started: bool 1291 1292 def __init__( 1293 self, 1294 node: SutNode, 1295 privileged: bool = True, 1296 timeout: float = SETTINGS.timeout, 1297 lcore_filter_specifier: LogicalCoreCount | LogicalCoreList = LogicalCoreCount(), 1298 ascending_cores: bool = True, 1299 append_prefix_timestamp: bool = True, 1300 name: str | None = None, 1301 **app_params: Unpack[TestPmdParamsDict], 1302 ) -> None: 1303 """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes app_params to kwargs.""" 1304 super().__init__( 1305 node, 1306 privileged, 1307 timeout, 1308 lcore_filter_specifier, 1309 ascending_cores, 1310 append_prefix_timestamp, 1311 TestPmdParams(**app_params), 1312 name, 1313 ) 1314 self.ports_started = not self._app_params.disable_device_start 1315 self._ports = None 1316 1317 @property 1318 def ports(self) -> list[TestPmdPort]: 1319 """The ports of the instance. 1320 1321 This caches the ports returned by :meth:`show_port_info_all`. 1322 To force an update of port information, execute :meth:`show_port_info_all` or 1323 :meth:`show_port_info`. 1324 1325 Returns: The list of known testpmd ports. 1326 """ 1327 if self._ports is None: 1328 return self.show_port_info_all() 1329 return self._ports 1330 1331 @requires_started_ports 1332 def start(self, verify: bool = True) -> None: 1333 """Start packet forwarding with the current configuration. 1334 1335 Args: 1336 verify: If :data:`True` , a second start command will be sent in an attempt to verify 1337 packet forwarding started as expected. 1338 1339 Raises: 1340 InteractiveCommandExecutionError: If `verify` is :data:`True` and forwarding fails to 1341 start or ports fail to come up. 1342 """ 1343 self.send_command("start") 1344 if verify: 1345 # If forwarding was already started, sending "start" again should tell us 1346 start_cmd_output = self.send_command("start") 1347 if "Packet forwarding already started" not in start_cmd_output: 1348 self._logger.debug(f"Failed to start packet forwarding: \n{start_cmd_output}") 1349 raise InteractiveCommandExecutionError("Testpmd failed to start packet forwarding.") 1350 1351 number_of_ports = len(self._app_params.ports or []) 1352 for port_id in range(number_of_ports): 1353 if not self.wait_link_status_up(port_id): 1354 raise InteractiveCommandExecutionError( 1355 "Not all ports came up after starting packet forwarding in testpmd." 1356 ) 1357 1358 def stop(self, verify: bool = True) -> str: 1359 """Stop packet forwarding. 1360 1361 Args: 1362 verify: If :data:`True` , the output of the stop command is scanned to verify that 1363 forwarding was stopped successfully or not started. If neither is found, it is 1364 considered an error. 1365 1366 Raises: 1367 InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop 1368 forwarding results in an error. 1369 1370 Returns: 1371 Output gathered from the stop command and all other preceding logs in the buffer. This 1372 output is most often used to view forwarding statistics that are displayed when this 1373 command is sent as well as any verbose packet information that hasn't been consumed 1374 prior to calling this method. 1375 """ 1376 stop_cmd_output = self.send_command("stop") 1377 if verify: 1378 if ( 1379 "Done." not in stop_cmd_output 1380 and "Packet forwarding not started" not in stop_cmd_output 1381 ): 1382 self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}") 1383 raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.") 1384 return stop_cmd_output 1385 1386 def get_devices(self) -> list[TestPmdDevice]: 1387 """Get a list of device names that are known to testpmd. 1388 1389 Uses the device info listed in testpmd and then parses the output. 1390 1391 Returns: 1392 A list of devices. 1393 """ 1394 dev_info: str = self.send_command("show device info all") 1395 dev_list: list[TestPmdDevice] = [] 1396 for line in dev_info.split("\n"): 1397 if "device name:" in line.lower(): 1398 dev_list.append(TestPmdDevice(line)) 1399 return dev_list 1400 1401 def wait_link_status_up(self, port_id: int, timeout=SETTINGS.timeout) -> bool: 1402 """Wait until the link status on the given port is "up". 1403 1404 Arguments: 1405 port_id: Port to check the link status on. 1406 timeout: Time to wait for the link to come up. The default value for this 1407 argument may be modified using the :option:`--timeout` command-line argument 1408 or the :envvar:`DTS_TIMEOUT` environment variable. 1409 1410 Returns: 1411 Whether the link came up in time or not. 1412 """ 1413 time_to_stop = time.time() + timeout 1414 port_info: str = "" 1415 while time.time() < time_to_stop: 1416 port_info = self.send_command(f"show port info {port_id}") 1417 if "Link status: up" in port_info: 1418 break 1419 time.sleep(0.5) 1420 else: 1421 self._logger.error(f"The link for port {port_id} did not come up in the given timeout.") 1422 return "Link status: up" in port_info 1423 1424 def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool = True): 1425 """Set packet forwarding mode. 1426 1427 Args: 1428 mode: The forwarding mode to use. 1429 verify: If :data:`True` the output of the command will be scanned in an attempt to 1430 verify that the forwarding mode was set to `mode` properly. 1431 1432 Raises: 1433 InteractiveCommandExecutionError: If `verify` is :data:`True` and the forwarding mode 1434 fails to update. 1435 """ 1436 set_fwd_output = self.send_command(f"set fwd {mode.value}") 1437 if f"Set {mode.value} packet forwarding mode" not in set_fwd_output: 1438 self._logger.debug(f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}") 1439 raise InteractiveCommandExecutionError( 1440 f"Test pmd failed to set fwd mode to {mode.value}" 1441 ) 1442 1443 def stop_all_ports(self, verify: bool = True) -> None: 1444 """Stops all the ports. 1445 1446 Args: 1447 verify: If :data:`True`, the output of the command will be checked for a successful 1448 execution. 1449 1450 Raises: 1451 InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not 1452 stopped successfully. 1453 """ 1454 self._logger.debug("Stopping all the ports...") 1455 output = self.send_command("port stop all") 1456 if verify and not output.strip().endswith("Done"): 1457 raise InteractiveCommandExecutionError("Ports were not stopped successfully.") 1458 1459 self.ports_started = False 1460 1461 def start_all_ports(self, verify: bool = True) -> None: 1462 """Starts all the ports. 1463 1464 Args: 1465 verify: If :data:`True`, the output of the command will be checked for a successful 1466 execution. 1467 1468 Raises: 1469 InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not 1470 started successfully. 1471 """ 1472 self._logger.debug("Starting all the ports...") 1473 output = self.send_command("port start all") 1474 if verify and not output.strip().endswith("Done"): 1475 raise InteractiveCommandExecutionError("Ports were not started successfully.") 1476 1477 self.ports_started = True 1478 1479 @requires_stopped_ports 1480 def set_ports_queues(self, number_of: int) -> None: 1481 """Sets the number of queues per port. 1482 1483 Args: 1484 number_of: The number of RX/TX queues to create per port. 1485 1486 Raises: 1487 InternalError: If `number_of` is invalid. 1488 """ 1489 if number_of < 1: 1490 raise InternalError("The number of queues must be positive and non-zero.") 1491 1492 self.send_command(f"port config all rxq {number_of}") 1493 self.send_command(f"port config all txq {number_of}") 1494 1495 def show_port_info_all(self) -> list[TestPmdPort]: 1496 """Returns the information of all the ports. 1497 1498 Returns: 1499 list[TestPmdPort]: A list containing all the ports information as `TestPmdPort`. 1500 """ 1501 output = self.send_command("show port info all") 1502 1503 # Sample output of the "all" command looks like: 1504 # 1505 # <start> 1506 # 1507 # ********************* Infos for port 0 ********************* 1508 # Key: value 1509 # 1510 # ********************* Infos for port 1 ********************* 1511 # Key: value 1512 # <end> 1513 # 1514 # Takes advantage of the double new line in between ports as end delimiter. But we need to 1515 # artificially add a new line at the end to pick up the last port. Because commands are 1516 # executed on a pseudo-terminal created by paramiko on the remote node, lines end with CRLF. 1517 # Therefore we also need to take the carriage return into account. 1518 iter = re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.S) 1519 self._ports = [TestPmdPort.parse(block.group(0)) for block in iter] 1520 return self._ports 1521 1522 def show_port_info(self, port_id: int) -> TestPmdPort: 1523 """Returns the given port information. 1524 1525 Args: 1526 port_id: The port ID to gather information for. 1527 1528 Raises: 1529 InteractiveCommandExecutionError: If `port_id` is invalid. 1530 1531 Returns: 1532 TestPmdPort: An instance of `TestPmdPort` containing the given port's information. 1533 """ 1534 output = self.send_command(f"show port info {port_id}", skip_first_line=True) 1535 if output.startswith("Invalid port"): 1536 raise InteractiveCommandExecutionError("invalid port given") 1537 1538 port = TestPmdPort.parse(output) 1539 self._update_port(port) 1540 return port 1541 1542 def _update_port(self, port: TestPmdPort) -> None: 1543 if self._ports: 1544 self._ports = [ 1545 existing_port if port.id != existing_port.id else port 1546 for existing_port in self._ports 1547 ] 1548 1549 def show_port_stats_all(self) -> list[TestPmdPortStats]: 1550 """Returns the statistics of all the ports. 1551 1552 Returns: 1553 list[TestPmdPortStats]: A list containing all the ports stats as `TestPmdPortStats`. 1554 """ 1555 output = self.send_command("show port stats all") 1556 1557 # Sample output of the "all" command looks like: 1558 # 1559 # ########### NIC statistics for port 0 ########### 1560 # values... 1561 # ################################################# 1562 # 1563 # ########### NIC statistics for port 1 ########### 1564 # values... 1565 # ################################################# 1566 # 1567 iter = re.finditer(r"(^ #*.+#*$[^#]+)^ #*\r$", output, re.MULTILINE) 1568 return [TestPmdPortStats.parse(block.group(1)) for block in iter] 1569 1570 def show_port_stats(self, port_id: int) -> TestPmdPortStats: 1571 """Returns the given port statistics. 1572 1573 Args: 1574 port_id: The port ID to gather information for. 1575 1576 Raises: 1577 InteractiveCommandExecutionError: If `port_id` is invalid. 1578 1579 Returns: 1580 TestPmdPortStats: An instance of `TestPmdPortStats` containing the given port's stats. 1581 """ 1582 output = self.send_command(f"show port stats {port_id}", skip_first_line=True) 1583 if output.startswith("Invalid port"): 1584 raise InteractiveCommandExecutionError("invalid port given") 1585 1586 return TestPmdPortStats.parse(output) 1587 1588 @requires_stopped_ports 1589 def set_port_mtu(self, port_id: int, mtu: int, verify: bool = True) -> None: 1590 """Change the MTU of a port using testpmd. 1591 1592 Some PMDs require that the port be stopped before changing the MTU, and it does no harm to 1593 stop the port before configuring in cases where it isn't required, so ports are stopped 1594 prior to changing their MTU. 1595 1596 Args: 1597 port_id: ID of the port to adjust the MTU on. 1598 mtu: Desired value for the MTU to be set to. 1599 verify: If `verify` is :data:`True` then the output will be scanned in an attempt to 1600 verify that the mtu was properly set on the port. Defaults to :data:`True`. 1601 1602 Raises: 1603 InteractiveCommandExecutionError: If `verify` is :data:`True` and the MTU was not 1604 properly updated on the port matching `port_id`. 1605 """ 1606 set_mtu_output = self.send_command(f"port config mtu {port_id} {mtu}") 1607 if verify and (f"MTU: {mtu}" not in self.send_command(f"show port info {port_id}")): 1608 self._logger.debug( 1609 f"Failed to set mtu to {mtu} on port {port_id}." f" Output was:\n{set_mtu_output}" 1610 ) 1611 raise InteractiveCommandExecutionError( 1612 f"Test pmd failed to update mtu of port {port_id} to {mtu}" 1613 ) 1614 1615 def set_port_mtu_all(self, mtu: int, verify: bool = True) -> None: 1616 """Change the MTU of all ports using testpmd. 1617 1618 Runs :meth:`set_port_mtu` for every port that testpmd is aware of. 1619 1620 Args: 1621 mtu: Desired value for the MTU to be set to. 1622 verify: Whether to verify that setting the MTU on each port was successful or not. 1623 Defaults to :data:`True`. 1624 1625 Raises: 1626 InteractiveCommandExecutionError: If `verify` is :data:`True` and the MTU was not 1627 properly updated on at least one port. 1628 """ 1629 for port in self.ports: 1630 self.set_port_mtu(port.id, mtu, verify) 1631 1632 @staticmethod 1633 def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket]: 1634 """Extract the verbose information present in given testpmd output. 1635 1636 This method extracts sections of verbose output that begin with the line 1637 "port X/queue Y: sent/received Z packets" and end with the ol_flags of a packet. 1638 1639 Args: 1640 output: Testpmd output that contains verbose information 1641 1642 Returns: 1643 List of parsed packet information gathered from verbose information in `output`. 1644 """ 1645 out: list[TestPmdVerbosePacket] = [] 1646 prev_header: str = "" 1647 iter = re.finditer( 1648 r"(?P<HEADER>(?:port \d+/queue \d+: (?:received|sent) \d+ packets)?)\s*" 1649 r"(?P<PACKET>src=[\w\s=:-]+?ol_flags: [\w ]+)", 1650 output, 1651 ) 1652 for match in iter: 1653 if match.group("HEADER"): 1654 prev_header = match.group("HEADER") 1655 out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACKET')}")) 1656 return out 1657 1658 def _close(self) -> None: 1659 """Overrides :meth:`~.interactive_shell.close`.""" 1660 self.stop() 1661 self.send_command("quit", "Bye...") 1662 return super()._close() 1663 1664 """ 1665 ====== Capability retrieval methods ====== 1666 """ 1667 1668 @requires_started_ports 1669 def get_capabilities_rxq_info( 1670 self, 1671 supported_capabilities: MutableSet["NicCapability"], 1672 unsupported_capabilities: MutableSet["NicCapability"], 1673 ) -> None: 1674 """Get all rxq capabilities and divide them into supported and unsupported. 1675 1676 Args: 1677 supported_capabilities: Supported capabilities will be added to this set. 1678 unsupported_capabilities: Unsupported capabilities will be added to this set. 1679 """ 1680 self._logger.debug("Getting rxq capabilities.") 1681 command = f"show rxq info {self.ports[0].id} 0" 1682 rxq_info = TestPmdRxqInfo.parse(self.send_command(command)) 1683 if rxq_info.rx_scattered_packets: 1684 supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) 1685 else: 1686 unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) 1687 1688 1689class NicCapability(NoAliasEnum): 1690 """A mapping between capability names and the associated :class:`TestPmdShell` methods. 1691 1692 The :class:`TestPmdShell` capability checking method executes the command that checks 1693 whether the capability is supported. 1694 A decorator may optionally be added to the method that will add and remove configuration 1695 that's necessary to retrieve the capability support status. 1696 The Enum members may be assigned the method itself or a tuple of 1697 (capability_checking_method, decorator_function). 1698 1699 The signature of each :class:`TestPmdShell` capability checking method must be:: 1700 1701 fn(self, supported_capabilities: MutableSet, unsupported_capabilities: MutableSet) -> None 1702 1703 The capability checking method must get whether a capability is supported or not 1704 from a testpmd command. If multiple capabilities can be obtained from a testpmd command, 1705 each should be obtained in the method. These capabilities should then 1706 be added to `supported_capabilities` or `unsupported_capabilities` based on their support. 1707 1708 The two dictionaries are shared across all capability discovery function calls in a given 1709 test run so that we don't call the same function multiple times. For example, when we find 1710 :attr:`SCATTERED_RX_ENABLED` in :meth:`TestPmdShell.get_capabilities_rxq_info`, 1711 we don't go looking for it again if a different test case also needs it. 1712 """ 1713 1714 #: Scattered packets Rx enabled 1715 SCATTERED_RX_ENABLED: TestPmdShellNicCapability = ( 1716 TestPmdShell.get_capabilities_rxq_info, 1717 add_remove_mtu(9000), 1718 ) 1719 1720 def __call__( 1721 self, 1722 testpmd_shell: TestPmdShell, 1723 supported_capabilities: MutableSet[Self], 1724 unsupported_capabilities: MutableSet[Self], 1725 ) -> None: 1726 """Execute the associated capability retrieval function. 1727 1728 Args: 1729 testpmd_shell: :class:`TestPmdShell` object to which the function will be bound to. 1730 supported_capabilities: The dictionary storing the supported capabilities 1731 of a given test run. 1732 unsupported_capabilities: The dictionary storing the unsupported capabilities 1733 of a given test run. 1734 """ 1735 self.value(testpmd_shell, supported_capabilities, unsupported_capabilities) 1736