1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5This file contains tests to verify native XDP support in network drivers. 6The tests utilize the BPF program `xdp_native.bpf.o` from the `selftests.net.lib` 7directory, with each test focusing on a specific aspect of XDP functionality. 8""" 9import random 10import string 11from dataclasses import dataclass 12from enum import Enum 13 14from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge, ksft_ne, ksft_pr 15from lib.py import KsftNamedVariant, ksft_variants 16from lib.py import KsftFailEx, NetDrvEpEnv 17from lib.py import EthtoolFamily, NetdevFamily, NlError 18from lib.py import bkg, cmd, rand_port, wait_port_listen 19from lib.py import ip, defer 20from lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids 21 22 23class TestConfig(Enum): 24 """Enum for XDP configuration options.""" 25 MODE = 0 # Configures the BPF program for a specific test 26 PORT = 1 # Port configuration to communicate with the remote host 27 ADJST_OFFSET = 2 # Tail/Head adjustment offset for extension/shrinking 28 ADJST_TAG = 3 # Adjustment tag to annotate the start and end of extension 29 30 31class XDPAction(Enum): 32 """Enum for XDP actions.""" 33 PASS = 0 # Pass the packet up to the stack 34 DROP = 1 # Drop the packet 35 TX = 2 # Route the packet to the remote host 36 TAIL_ADJST = 3 # Adjust the tail of the packet 37 HEAD_ADJST = 4 # Adjust the head of the packet 38 39 40class XDPStats(Enum): 41 """Enum for XDP statistics.""" 42 RX = 0 # Count of valid packets received for testing 43 PASS = 1 # Count of packets passed up to the stack 44 DROP = 2 # Count of packets dropped 45 TX = 3 # Count of incoming packets routed to the remote host 46 ABORT = 4 # Count of packets that were aborted 47 48 49@dataclass 50class BPFProgInfo: 51 """Data class to store information about a BPF program.""" 52 name: str # Name of the BPF program 53 file: str # BPF program object file 54 xdp_sec: str = "xdp" # XDP section name (e.g., "xdp" or "xdp.frags") 55 mtu: int = 1500 # Maximum Transmission Unit, default is 1500 56 57 58def _exchg_udp(cfg, port, test_string): 59 """ 60 Exchanges UDP packets between a local and remote host using the socat tool. 61 62 Args: 63 cfg: Configuration object containing network settings. 64 port: Port number to use for the UDP communication. 65 test_string: String that the remote host will send. 66 67 Returns: 68 The string received by the test host. 69 """ 70 cfg.require_cmd("socat", remote=True) 71 72 rx_udp_cmd = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT" 73 tx_udp_cmd = f"echo -n {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}" 74 75 with bkg(rx_udp_cmd, exit_wait=True) as nc: 76 wait_port_listen(port, proto="udp") 77 cmd(tx_udp_cmd, host=cfg.remote, shell=True) 78 79 return nc.stdout.strip() 80 81 82def _test_udp(cfg, port, size=256): 83 """ 84 Tests UDP packet exchange between a local and remote host. 85 86 Args: 87 cfg: Configuration object containing network settings. 88 port: Port number to use for the UDP communication. 89 size: The length of the test string to be exchanged, default is 256 characters. 90 91 Returns: 92 bool: True if the received string matches the sent string, False otherwise. 93 """ 94 test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(size)) 95 recvd_str = _exchg_udp(cfg, port, test_str) 96 97 return recvd_str == test_str 98 99 100def _load_xdp_prog(cfg, bpf_info): 101 """ 102 Loads an XDP program onto a network interface. 103 104 Args: 105 cfg: Configuration object containing network settings. 106 bpf_info: BPFProgInfo object containing information about the BPF program. 107 108 Returns: 109 dict: A dictionary containing the XDP program ID, name, and associated map IDs. 110 """ 111 abs_path = cfg.net_lib_dir / bpf_info.file 112 prog_info = {} 113 114 cmd(f"ip link set dev {cfg.remote_ifname} mtu {bpf_info.mtu}", shell=True, host=cfg.remote) 115 defer(ip, f"link set dev {cfg.remote_ifname} mtu 1500", host=cfg.remote) 116 117 cmd( 118 f"ip link set dev {cfg.ifname} mtu {bpf_info.mtu} xdpdrv obj {abs_path} sec {bpf_info.xdp_sec}", 119 shell=True 120 ) 121 defer(ip, f"link set dev {cfg.ifname} mtu 1500 xdpdrv off") 122 123 xdp_info = ip(f"-d link show dev {cfg.ifname}", json=True)[0] 124 prog_info["id"] = xdp_info["xdp"]["prog"]["id"] 125 prog_info["name"] = xdp_info["xdp"]["prog"]["name"] 126 prog_info["maps"] = bpf_prog_map_ids(prog_info["id"]) 127 128 return prog_info 129 130 131def _get_stats(xdp_map_id): 132 """ 133 Retrieves and formats statistics from an XDP map. 134 135 Args: 136 xdp_map_id: The ID of the XDP map from which to retrieve statistics. 137 138 Returns: 139 A dictionary containing formatted packet statistics for various XDP actions. 140 The keys are based on the XDPStats Enum values. 141 142 Raises: 143 KsftFailEx: If the stats retrieval fails. 144 """ 145 stats = bpf_map_dump(xdp_map_id) 146 if not stats: 147 raise KsftFailEx(f"Failed to get stats for map {xdp_map_id}") 148 149 return stats 150 151 152def _test_pass(cfg, bpf_info, msg_sz): 153 """ 154 Tests the XDP_PASS action by exchanging UDP packets. 155 156 Args: 157 cfg: Configuration object containing network settings. 158 bpf_info: BPFProgInfo object containing information about the BPF program. 159 msg_sz: Size of the test message to send. 160 """ 161 162 prog_info = _load_xdp_prog(cfg, bpf_info) 163 port = rand_port() 164 165 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, XDPAction.PASS.value) 166 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 167 168 ksft_eq(_test_udp(cfg, port, msg_sz), True, "UDP packet exchange failed") 169 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 170 171 ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should not be zero") 172 ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.PASS.value], "RX and PASS stats mismatch") 173 174 175def test_xdp_native_pass_sb(cfg): 176 """ 177 Tests the XDP_PASS action for single buffer case. 178 179 Args: 180 cfg: Configuration object containing network settings. 181 """ 182 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 183 184 _test_pass(cfg, bpf_info, 256) 185 186 187def test_xdp_native_pass_mb(cfg): 188 """ 189 Tests the XDP_PASS action for a multi-buff size. 190 191 Args: 192 cfg: Configuration object containing network settings. 193 """ 194 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 195 196 _test_pass(cfg, bpf_info, 8000) 197 198 199def _test_drop(cfg, bpf_info, msg_sz): 200 """ 201 Tests the XDP_DROP action by exchanging UDP packets. 202 203 Args: 204 cfg: Configuration object containing network settings. 205 bpf_info: BPFProgInfo object containing information about the BPF program. 206 msg_sz: Size of the test message to send. 207 """ 208 209 prog_info = _load_xdp_prog(cfg, bpf_info) 210 port = rand_port() 211 212 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, XDPAction.DROP.value) 213 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 214 215 ksft_eq(_test_udp(cfg, port, msg_sz), False, "UDP packet exchange should fail") 216 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 217 218 ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should be zero") 219 ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.DROP.value], "RX and DROP stats mismatch") 220 221 222def test_xdp_native_drop_sb(cfg): 223 """ 224 Tests the XDP_DROP action for a signle-buff case. 225 226 Args: 227 cfg: Configuration object containing network settings. 228 """ 229 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 230 231 _test_drop(cfg, bpf_info, 256) 232 233 234def test_xdp_native_drop_mb(cfg): 235 """ 236 Tests the XDP_DROP action for a multi-buff case. 237 238 Args: 239 cfg: Configuration object containing network settings. 240 """ 241 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 242 243 _test_drop(cfg, bpf_info, 8000) 244 245 246def _test_xdp_native_tx(cfg, bpf_info, payload_lens): 247 """ 248 Tests the XDP_TX action. 249 250 Args: 251 cfg: Configuration object containing network settings. 252 bpf_info: BPFProgInfo object containing the BPF program metadata. 253 payload_lens: Array of packet lengths to send. 254 """ 255 cfg.require_cmd("socat", remote=True) 256 prog_info = _load_xdp_prog(cfg, bpf_info) 257 port = rand_port() 258 259 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, XDPAction.TX.value) 260 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 261 262 expected_pkts = 0 263 for payload_len in payload_lens: 264 test_string = "".join( 265 random.choice(string.ascii_lowercase) for _ in range(payload_len) 266 ) 267 268 rx_udp = f"socat -{cfg.addr_ipver} -T 2 " + \ 269 f"-u UDP-RECV:{port},reuseport STDOUT" 270 271 # Writing zero bytes to stdin gets ignored by socat, 272 # but with the shut-null flag socat generates a zero sized packet 273 # when the socket is closed. 274 tx_cmd_suffix = ",shut-null" if payload_len == 0 else "" 275 tx_udp = f"echo -n {test_string} | socat -t 2 " + \ 276 f"-u STDIN UDP:{cfg.baddr}:{port}{tx_cmd_suffix}" 277 278 with bkg(rx_udp, host=cfg.remote, exit_wait=True) as rnc: 279 wait_port_listen(port, proto="udp", host=cfg.remote) 280 cmd(tx_udp, host=cfg.remote, shell=True) 281 282 ksft_eq(rnc.stdout.strip(), test_string, "UDP packet exchange failed") 283 284 expected_pkts += 1 285 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 286 ksft_eq(stats[XDPStats.RX.value], expected_pkts, "RX stats mismatch") 287 ksft_eq(stats[XDPStats.TX.value], expected_pkts, "TX stats mismatch") 288 289 290def test_xdp_native_tx_sb(cfg): 291 """ 292 Tests the XDP_TX action for a single-buff case. 293 294 Args: 295 cfg: Configuration object containing network settings. 296 """ 297 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 298 299 # Ensure there's enough room for an ETH / IP / UDP header 300 pkt_hdr_len = 42 if cfg.addr_ipver == "4" else 62 301 302 _test_xdp_native_tx(cfg, bpf_info, [0, 1500 // 2, 1500 - pkt_hdr_len]) 303 304 305def test_xdp_native_tx_mb(cfg): 306 """ 307 Tests the XDP_TX action for a multi-buff case. 308 309 Args: 310 cfg: Configuration object containing network settings. 311 """ 312 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", 313 "xdp.frags", 9000) 314 # The first packet ensures we exercise the fragmented code path. 315 # And the subsequent 0-sized packet ensures the driver 316 # reinitializes xdp_buff correctly. 317 _test_xdp_native_tx(cfg, bpf_info, [8000, 0]) 318 319 320def _validate_res(res, offset_lst, pkt_sz_lst): 321 """ 322 Validates the result of a test. 323 324 Args: 325 res: The result of the test, which should be a dictionary with a "status" key. 326 327 Raises: 328 KsftFailEx: If the test fails to pass any combination of offset and packet size. 329 """ 330 if "status" not in res: 331 raise KsftFailEx("Missing 'status' key in result dictionary") 332 333 # Validate that not a single case was successful 334 if res["status"] == "fail": 335 if res["offset"] == offset_lst[0] and res["pkt_sz"] == pkt_sz_lst[0]: 336 raise KsftFailEx(f"{res['reason']}") 337 338 # Get the previous offset and packet size to report the successful run 339 tmp_idx = offset_lst.index(res["offset"]) 340 prev_offset = offset_lst[tmp_idx - 1] 341 if tmp_idx == 0: 342 tmp_idx = pkt_sz_lst.index(res["pkt_sz"]) 343 prev_pkt_sz = pkt_sz_lst[tmp_idx - 1] 344 else: 345 prev_pkt_sz = res["pkt_sz"] 346 347 # Use these values for error reporting 348 ksft_pr( 349 f"Failed run: pkt_sz {res['pkt_sz']}, offset {res['offset']}. " 350 f"Last successful run: pkt_sz {prev_pkt_sz}, offset {prev_offset}. " 351 f"Reason: {res['reason']}" 352 ) 353 354 355def _check_for_failures(recvd_str, stats): 356 """ 357 Checks for common failures while adjusting headroom or tailroom. 358 359 Args: 360 recvd_str: The string received from the remote host after sending a test string. 361 stats: A dictionary containing formatted packet statistics for various XDP actions. 362 363 Returns: 364 str: A string describing the failure reason if a failure is detected, otherwise None. 365 """ 366 367 # Any adjustment failure result in an abort hence, we track this counter 368 if stats[XDPStats.ABORT.value] != 0: 369 return "Adjustment failed" 370 371 # Since we are using aggregate stats for a single test across all offsets and packet sizes 372 # we can't use RX stats only to track data exchange failure without taking a previous 373 # snapshot. An easier way is to simply check for non-zero length of received string. 374 if len(recvd_str) == 0: 375 return "Data exchange failed" 376 377 # Check for RX and PASS stats mismatch. Ideally, they should be equal for a successful run 378 if stats[XDPStats.RX.value] != stats[XDPStats.PASS.value]: 379 return "RX stats mismatch" 380 381 return None 382 383 384def _test_xdp_native_tail_adjst(cfg, pkt_sz_lst, offset_lst): 385 """ 386 Tests the XDP tail adjustment functionality. 387 388 This function loads the appropriate XDP program based on the provided 389 program name and configures the XDP map for tail adjustment. It then 390 validates the tail adjustment by sending and receiving UDP packets 391 with specified packet sizes and offsets. 392 393 Args: 394 cfg: Configuration object containing network settings. 395 prog: Name of the XDP program to load. 396 pkt_sz_lst: List of packet sizes to test. 397 offset_lst: List of offsets to validate support for tail adjustment. 398 399 Returns: 400 dict: A dictionary with test status and failure details if applicable. 401 """ 402 port = rand_port() 403 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 404 405 prog_info = _load_xdp_prog(cfg, bpf_info) 406 407 # Configure the XDP map for tail adjustment 408 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, XDPAction.TAIL_ADJST.value) 409 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 410 411 for offset in offset_lst: 412 tag = format(random.randint(65, 90), "02x") 413 414 bpf_map_set("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) 415 if offset > 0: 416 bpf_map_set("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) 417 418 for pkt_sz in pkt_sz_lst: 419 test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) 420 recvd_str = _exchg_udp(cfg, port, test_str) 421 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 422 423 failure = _check_for_failures(recvd_str, stats) 424 if failure is not None: 425 return { 426 "status": "fail", 427 "reason": failure, 428 "offset": offset, 429 "pkt_sz": pkt_sz, 430 } 431 432 # Validate data content based on offset direction 433 expected_data = None 434 if offset > 0: 435 expected_data = test_str + (offset * chr(int(tag, 16))) 436 else: 437 expected_data = test_str[0:pkt_sz + offset] 438 439 if recvd_str != expected_data: 440 return { 441 "status": "fail", 442 "reason": "Data mismatch", 443 "offset": offset, 444 "pkt_sz": pkt_sz, 445 } 446 447 return {"status": "pass"} 448 449 450def test_xdp_native_adjst_tail_grow_data(cfg): 451 """ 452 Tests the XDP tail adjustment by growing packet data. 453 454 Args: 455 cfg: Configuration object containing network settings. 456 """ 457 pkt_sz_lst = [512, 1024, 2048] 458 offset_lst = [1, 16, 32, 64, 128, 256] 459 res = _test_xdp_native_tail_adjst( 460 cfg, 461 pkt_sz_lst, 462 offset_lst, 463 ) 464 465 _validate_res(res, offset_lst, pkt_sz_lst) 466 467 468def test_xdp_native_adjst_tail_shrnk_data(cfg): 469 """ 470 Tests the XDP tail adjustment by shrinking packet data. 471 472 Args: 473 cfg: Configuration object containing network settings. 474 """ 475 pkt_sz_lst = [512, 1024, 2048] 476 offset_lst = [-16, -32, -64, -128, -256] 477 res = _test_xdp_native_tail_adjst( 478 cfg, 479 pkt_sz_lst, 480 offset_lst, 481 ) 482 483 _validate_res(res, offset_lst, pkt_sz_lst) 484 485 486def get_hds_thresh(cfg): 487 """ 488 Retrieves the header data split (HDS) threshold for a network interface. 489 490 Args: 491 cfg: Configuration object containing network settings. 492 493 Returns: 494 The HDS threshold value. If the threshold is not supported or an error occurs, 495 a default value of 1500 is returned. 496 """ 497 ethnl = cfg.ethnl 498 hds_thresh = 1500 499 500 try: 501 rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}}) 502 if 'hds-thresh' not in rings: 503 ksft_pr(f'hds-thresh not supported. Using default: {hds_thresh}') 504 return hds_thresh 505 hds_thresh = rings['hds-thresh'] 506 except NlError as e: 507 ksft_pr(f"Failed to get rings: {e}. Using default: {hds_thresh}") 508 509 return hds_thresh 510 511 512def _test_xdp_native_head_adjst(cfg, prog, pkt_sz_lst, offset_lst): 513 """ 514 Tests the XDP head adjustment action for a multi-buffer case. 515 516 Args: 517 cfg: Configuration object containing network settings. 518 ethnl: Network namespace or link object (not used in this function). 519 520 This function sets up the packet size and offset lists, then performs 521 the head adjustment test by sending and receiving UDP packets. 522 """ 523 cfg.require_cmd("socat", remote=True) 524 525 prog_info = _load_xdp_prog(cfg, BPFProgInfo(prog, "xdp_native.bpf.o", "xdp.frags", 9000)) 526 port = rand_port() 527 528 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, XDPAction.HEAD_ADJST.value) 529 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 530 531 hds_thresh = get_hds_thresh(cfg) 532 for offset in offset_lst: 533 for pkt_sz in pkt_sz_lst: 534 # The "head" buffer must contain at least the Ethernet header 535 # after we eat into it. We send large-enough packets, but if HDS 536 # is enabled head will only contain headers. Don't try to eat 537 # more than 28 bytes (UDPv4 + eth hdr left: (14 + 20 + 8) - 14) 538 l2_cut_off = 28 if cfg.addr_ipver == 4 else 48 539 if pkt_sz > hds_thresh and offset > l2_cut_off: 540 ksft_pr( 541 f"Failed run: pkt_sz ({pkt_sz}) > HDS threshold ({hds_thresh}) and " 542 f"offset {offset} > {l2_cut_off}" 543 ) 544 return {"status": "pass"} 545 546 test_str = ''.join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) 547 tag = format(random.randint(65, 90), '02x') 548 549 bpf_map_set("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) 550 bpf_map_set("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) 551 552 recvd_str = _exchg_udp(cfg, port, test_str) 553 554 # Check for failures around adjustment and data exchange 555 failure = _check_for_failures(recvd_str, _get_stats(prog_info['maps']['map_xdp_stats'])) 556 if failure is not None: 557 return { 558 "status": "fail", 559 "reason": failure, 560 "offset": offset, 561 "pkt_sz": pkt_sz 562 } 563 564 # Validate data content based on offset direction 565 expected_data = None 566 if offset < 0: 567 expected_data = chr(int(tag, 16)) * (0 - offset) + test_str 568 else: 569 expected_data = test_str[offset:] 570 571 if recvd_str != expected_data: 572 return { 573 "status": "fail", 574 "reason": "Data mismatch", 575 "offset": offset, 576 "pkt_sz": pkt_sz 577 } 578 579 return {"status": "pass"} 580 581 582def test_xdp_native_adjst_head_grow_data(cfg): 583 """ 584 Tests the XDP headroom growth support. 585 586 Args: 587 cfg: Configuration object containing network settings. 588 589 This function sets up the packet size and offset lists, then calls the 590 _test_xdp_native_head_adjst_mb function to perform the actual test. The 591 test is passed if the headroom is successfully extended for given packet 592 sizes and offsets. 593 """ 594 pkt_sz_lst = [512, 1024, 2048] 595 596 # Negative values result in headroom shrinking, resulting in growing of payload 597 offset_lst = [-16, -32, -64, -128, -256] 598 res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) 599 600 _validate_res(res, offset_lst, pkt_sz_lst) 601 602 603def test_xdp_native_adjst_head_shrnk_data(cfg): 604 """ 605 Tests the XDP headroom shrinking support. 606 607 Args: 608 cfg: Configuration object containing network settings. 609 610 This function sets up the packet size and offset lists, then calls the 611 _test_xdp_native_head_adjst_mb function to perform the actual test. The 612 test is passed if the headroom is successfully shrunk for given packet 613 sizes and offsets. 614 """ 615 pkt_sz_lst = [512, 1024, 2048] 616 617 # Positive values result in headroom growing, resulting in shrinking of payload 618 offset_lst = [16, 32, 64, 128, 256] 619 res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) 620 621 _validate_res(res, offset_lst, pkt_sz_lst) 622 623 624@ksft_variants([ 625 KsftNamedVariant("pass", XDPAction.PASS), 626 KsftNamedVariant("drop", XDPAction.DROP), 627 KsftNamedVariant("tx", XDPAction.TX), 628]) 629def test_xdp_native_qstats(cfg, act): 630 """ 631 Send 1000 messages. Expect XDP action specified in @act. 632 Make sure the packets were counted to interface level qstats 633 (Rx, and Tx if act is TX). 634 """ 635 636 cfg.require_cmd("socat") 637 638 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 639 prog_info = _load_xdp_prog(cfg, bpf_info) 640 port = rand_port() 641 642 bpf_map_set("map_xdp_setup", TestConfig.MODE.value, act.value) 643 bpf_map_set("map_xdp_setup", TestConfig.PORT.value, port) 644 645 # Discard the input, but we need a listener to avoid ICMP errors 646 rx_udp = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport " + \ 647 "/dev/null" 648 # Listener runs on "remote" in case of XDP_TX 649 rx_host = cfg.remote if act == XDPAction.TX else None 650 # We want to spew 1000 packets quickly, bash seems to do a good enough job 651 # Each reopening of the socket gives us a differenot local port (for RSS) 652 tx_udp = "for _ in `seq 20`; do " \ 653 f"exec 5<>/dev/udp/{cfg.addr}/{port}; " \ 654 "for i in `seq 50`; do echo a >&5; done; " \ 655 "exec 5>&-; done" 656 657 cfg.wait_hw_stats_settle() 658 # Qstats have more clearly defined semantics than rtnetlink. 659 # XDP is the "first layer of the stack" so XDP packets should be counted 660 # as received and sent as if the decision was made in the routing layer. 661 before = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 662 663 with bkg(rx_udp, host=rx_host, exit_wait=True): 664 wait_port_listen(port, proto="udp", host=rx_host) 665 cmd(tx_udp, host=cfg.remote, shell=True) 666 667 cfg.wait_hw_stats_settle() 668 after = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 669 670 expected_pkts = 1000 671 ksft_ge(after['rx-packets'] - before['rx-packets'], expected_pkts) 672 if act == XDPAction.TX: 673 ksft_ge(after['tx-packets'] - before['tx-packets'], expected_pkts) 674 675 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 676 ksft_eq(stats[XDPStats.RX.value], expected_pkts, "XDP RX stats mismatch") 677 if act == XDPAction.TX: 678 ksft_eq(stats[XDPStats.TX.value], expected_pkts, "XDP TX stats mismatch") 679 680 # Flip the ring count back and forth to make sure the stats from XDP rings 681 # don't get lost. 682 chans = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}}) 683 if chans.get('combined-count', 0) > 1: 684 cfg.ethnl.channels_set({'header': {'dev-index': cfg.ifindex}, 685 'combined-count': 1}) 686 cfg.ethnl.channels_set({'header': {'dev-index': cfg.ifindex}, 687 'combined-count': chans['combined-count']}) 688 before = after 689 after = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 690 691 ksft_ge(after['rx-packets'], before['rx-packets']) 692 if act == XDPAction.TX: 693 ksft_ge(after['tx-packets'], before['tx-packets']) 694 695 696def main(): 697 """ 698 Main function to execute the XDP tests. 699 700 This function runs a series of tests to validate the XDP support for 701 both the single and multi-buffer. It uses the NetDrvEpEnv context 702 manager to manage the network driver environment and the ksft_run 703 function to execute the tests. 704 """ 705 with NetDrvEpEnv(__file__) as cfg: 706 cfg.ethnl = EthtoolFamily() 707 cfg.netnl = NetdevFamily() 708 ksft_run( 709 [ 710 test_xdp_native_pass_sb, 711 test_xdp_native_pass_mb, 712 test_xdp_native_drop_sb, 713 test_xdp_native_drop_mb, 714 test_xdp_native_tx_sb, 715 test_xdp_native_tx_mb, 716 test_xdp_native_adjst_tail_grow_data, 717 test_xdp_native_adjst_tail_shrnk_data, 718 test_xdp_native_adjst_head_grow_data, 719 test_xdp_native_adjst_head_shrnk_data, 720 test_xdp_native_qstats, 721 ], 722 args=(cfg,)) 723 ksft_exit() 724 725 726if __name__ == "__main__": 727 main() 728