Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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, KsftSkipEx, 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},shut-none"
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 ",shut-none"
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 test_xdp_native_update_mb_to_sb(cfg):
697 """
698 Test multi-buf to single-buf replacement with jumbo MTU.
699 """
700 obj = cfg.net_lib_dir / "xdp_dummy.bpf.o"
701 mtu = 9000
702
703 ip(f"link set dev {cfg.ifname} mtu {mtu}")
704 defer(ip, f"link set dev {cfg.ifname} mtu {cfg.dev['mtu']} xdpdrv off")
705
706 attach = cmd(f"ip link set dev {cfg.ifname} xdpdrv obj {obj} sec xdp", fail=False)
707 if attach.ret == 0:
708 raise KsftSkipEx(f"device supports single-buffer XDP with mtu {mtu}")
709
710 attach = cmd(f"ip link set dev {cfg.ifname} xdpdrv obj {obj} sec xdp.frags", fail=False)
711 if attach.ret != 0:
712 ksft_pr(attach)
713 raise KsftSkipEx("device does not support multi-buffer XDP")
714
715 # Verify updating mb -> mb program works.
716 cmd(f"ip -force link set dev {cfg.ifname} xdpdrv obj {obj} sec xdp.frags")
717
718 # Verify updating mb -> sb program does not work.
719 update = cmd(f"ip -force link set dev {cfg.ifname} xdpdrv obj {obj} sec xdp", fail=False)
720 if update.ret == 0:
721 raise KsftFailEx("device unexpectedly updates non-multi-buffer XDP")
722
723
724def main():
725 """
726 Main function to execute the XDP tests.
727
728 This function runs a series of tests to validate the XDP support for
729 both the single and multi-buffer. It uses the NetDrvEpEnv context
730 manager to manage the network driver environment and the ksft_run
731 function to execute the tests.
732 """
733 with NetDrvEpEnv(__file__) as cfg:
734 cfg.ethnl = EthtoolFamily()
735 cfg.netnl = NetdevFamily()
736 ksft_run(
737 [
738 test_xdp_native_pass_sb,
739 test_xdp_native_pass_mb,
740 test_xdp_native_drop_sb,
741 test_xdp_native_drop_mb,
742 test_xdp_native_tx_sb,
743 test_xdp_native_tx_mb,
744 test_xdp_native_adjst_tail_grow_data,
745 test_xdp_native_adjst_tail_shrnk_data,
746 test_xdp_native_adjst_head_grow_data,
747 test_xdp_native_adjst_head_shrnk_data,
748 test_xdp_native_qstats,
749 test_xdp_native_update_mb_to_sb,
750 ],
751 args=(cfg,))
752 ksft_exit()
753
754
755if __name__ == "__main__":
756 main()