Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2026 Stephen Hemminger
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <fcntl.h>
7 : : #include <inttypes.h>
8 : : #include <sys/ioctl.h>
9 : : #include <sys/socket.h>
10 : : #include <linux/if.h>
11 : : #include <linux/if_tun.h>
12 : : #include <linux/sockios.h>
13 : : #include <pthread.h>
14 : : #include <stdbool.h>
15 : : #include <stdio.h>
16 : : #include <string.h>
17 : : #include <time.h>
18 : : #include <unistd.h>
19 : :
20 : : #include <rte_bus_vdev.h>
21 : : #include <rte_config.h>
22 : : #include <rte_cycles.h>
23 : : #include <rte_ethdev.h>
24 : : #include <rte_ether.h>
25 : : #include <rte_lcore.h>
26 : : #include <rte_mbuf.h>
27 : : #include <rte_mempool.h>
28 : : #include <rte_stdatomic.h>
29 : :
30 : : #include "test.h"
31 : :
32 : : #ifndef RTE_EXEC_ENV_LINUX
33 : : static int
34 : : test_pmd_af_packet(void)
35 : : {
36 : : printf("af_packet only supported on Linux, skipping test\n");
37 : : return TEST_SKIPPED;
38 : : }
39 : :
40 : : #else
41 : :
42 : : #define NUM_MBUFS 512
43 : : #define MBUF_CACHE_SIZE 32
44 : : #define BURST_SIZE 32
45 : : #define RING_SIZE 256
46 : :
47 : : /* Polling and timeout constants */
48 : : #define STATS_POLL_INTERVAL_US 100
49 : : #define STATS_POLL_TIMEOUT_US 100000 /* 100ms overall timeout */
50 : : #define LOOPBACK_TIMEOUT_US 500000 /* 500ms for loopback test */
51 : :
52 : : /* Multi-threaded test constants */
53 : : #define MT_NUM_PACKETS 100
54 : : #define MT_TEST_DURATION_SEC 2
55 : :
56 : : /* Test device names */
57 : : #define AF_PACKET_DEV_NAME "net_af_packet_test"
58 : : #define TAP_DEV_NAME "dpdkafptest"
59 : :
60 : : static struct rte_mempool *mp;
61 : : static uint16_t port_id = RTE_MAX_ETHPORTS;
62 : : static int tap_fd = -1;
63 : : static bool tap_created;
64 : : static bool port_created;
65 : : static bool port_started;
66 : :
67 : : /*
68 : : * Create a TAP interface for testing.
69 : : * Returns fd on success, -1 on failure.
70 : : */
71 : : static int
72 : 1 : create_tap_interface(const char *name)
73 : : {
74 : : struct ifreq ifr;
75 : : int fd, ret;
76 : :
77 : : fd = open("/dev/net/tun", O_RDWR);
78 [ - + ]: 1 : if (fd < 0) {
79 : 0 : printf("Cannot open /dev/net/tun: %s\n", strerror(errno));
80 : : printf("(Are you running as root?)\n");
81 : 0 : return -1;
82 : : }
83 : :
84 : : memset(&ifr, 0, sizeof(ifr));
85 : 1 : ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
86 : : snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
87 : :
88 : 1 : ret = ioctl(fd, TUNSETIFF, &ifr);
89 [ - + ]: 1 : if (ret < 0) {
90 : 0 : printf("Cannot create TAP interface '%s': %s\n",
91 : 0 : name, strerror(errno));
92 : 0 : close(fd);
93 : 0 : return -1;
94 : : }
95 : :
96 : : /* Bring the interface up */
97 : 1 : int sock = socket(AF_INET, SOCK_DGRAM, 0);
98 [ + - ]: 1 : if (sock >= 0) {
99 : : memset(&ifr, 0, sizeof(ifr));
100 : : snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
101 : :
102 : : /* Get current flags */
103 [ + - ]: 1 : if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
104 : 1 : ifr.ifr_flags |= IFF_UP;
105 : 1 : ioctl(sock, SIOCSIFFLAGS, &ifr);
106 : : }
107 : 1 : close(sock);
108 : : }
109 : :
110 : : printf("Created TAP interface '%s'\n", name);
111 : 1 : return fd;
112 : : }
113 : :
114 : : static void
115 : : destroy_tap_interface(int fd)
116 : : {
117 : 1 : if (fd >= 0)
118 : 1 : close(fd);
119 : : }
120 : :
121 : : static int
122 : 1 : create_af_packet_port(const char *name, const char *args, uint16_t *out_port_id)
123 : : {
124 : : int ret;
125 : :
126 : 1 : ret = rte_vdev_init(name, args);
127 [ - + ]: 1 : if (ret != 0) {
128 : : printf("Failed to create af_packet device '%s': %d\n", name, ret);
129 : 0 : return ret;
130 : : }
131 : :
132 : 1 : ret = rte_eth_dev_get_port_by_name(name, out_port_id);
133 [ - + ]: 1 : if (ret != 0) {
134 : : printf("Failed to get port id for '%s': %d\n", name, ret);
135 : 0 : rte_vdev_uninit(name);
136 : 0 : return ret;
137 : : }
138 : :
139 : : return 0;
140 : : }
141 : :
142 : : static int
143 : 1 : configure_af_packet_port(uint16_t pid, uint16_t nb_rx_q, uint16_t nb_tx_q)
144 : : {
145 : 1 : struct rte_eth_conf port_conf = {0};
146 : : struct rte_eth_dev_info dev_info;
147 : : int ret;
148 : : uint16_t q;
149 : :
150 : 1 : ret = rte_eth_dev_info_get(pid, &dev_info);
151 [ - + ]: 1 : if (ret != 0) {
152 : : printf("Failed to get device info for port %u: %d\n", pid, ret);
153 : 0 : return ret;
154 : : }
155 : :
156 : 1 : ret = rte_eth_dev_configure(pid, nb_rx_q, nb_tx_q, &port_conf);
157 [ - + ]: 1 : if (ret != 0) {
158 : : printf("Failed to configure port %u: %d\n", pid, ret);
159 : 0 : return ret;
160 : : }
161 : :
162 [ + + ]: 2 : for (q = 0; q < nb_rx_q; q++) {
163 : 1 : ret = rte_eth_rx_queue_setup(pid, q, RING_SIZE,
164 : 1 : rte_eth_dev_socket_id(pid),
165 : : NULL, mp);
166 [ - + ]: 1 : if (ret != 0) {
167 : : printf("Failed to setup RX queue %u for port %u: %d\n",
168 : : q, pid, ret);
169 : 0 : return ret;
170 : : }
171 : : }
172 : :
173 [ + + ]: 2 : for (q = 0; q < nb_tx_q; q++) {
174 : 1 : ret = rte_eth_tx_queue_setup(pid, q, RING_SIZE,
175 : 1 : rte_eth_dev_socket_id(pid),
176 : : NULL);
177 [ - + ]: 1 : if (ret != 0) {
178 : : printf("Failed to setup TX queue %u for port %u: %d\n",
179 : : q, pid, ret);
180 : 0 : return ret;
181 : : }
182 : : }
183 : :
184 : 1 : ret = rte_eth_dev_start(pid);
185 [ - + ]: 1 : if (ret != 0) {
186 : : printf("Failed to start port %u: %d\n", pid, ret);
187 : 0 : return ret;
188 : : }
189 : :
190 : : return 0;
191 : : }
192 : :
193 : : /*
194 : : * Helper: Allocate and prepare a burst of TX mbufs with minimal Ethernet frames.
195 : : * Returns the number of successfully allocated mbufs.
196 : : */
197 : : static unsigned int
198 : 18 : alloc_tx_mbufs(struct rte_mbuf **bufs, unsigned int count)
199 : : {
200 : : unsigned int i;
201 : : int ret;
202 : :
203 : 18 : ret = rte_pktmbuf_alloc_bulk(mp, bufs, count);
204 [ + - ]: 18 : if (ret != 0)
205 : : return 0;
206 : :
207 [ + + ]: 234 : for (i = 0; i < count; i++) {
208 : : struct rte_ether_hdr *eth_hdr;
209 : 216 : eth_hdr = (struct rte_ether_hdr *)rte_pktmbuf_append(bufs[i],
210 : : sizeof(struct rte_ether_hdr) + 46);
211 [ - + ]: 216 : if (eth_hdr == NULL) {
212 : : /* Free all allocated mbufs on failure */
213 : 0 : rte_pktmbuf_free_bulk(bufs, count);
214 : 0 : return 0;
215 : : }
216 : :
217 : : /* Set broadcast destination and zero source MAC */
218 : 216 : memset(ð_hdr->dst_addr, 0xFF, RTE_ETHER_ADDR_LEN);
219 : 216 : memset(ð_hdr->src_addr, 0x00, RTE_ETHER_ADDR_LEN);
220 : 216 : eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
221 : : }
222 : :
223 : : return count;
224 : : }
225 : :
226 : : /*
227 : : * Helper: Transmit packets and free unsent mbufs.
228 : : * Returns the number of packets successfully transmitted.
229 : : */
230 : : static uint16_t
231 : 18 : do_tx_burst(uint16_t pid, uint16_t queue_id, struct rte_mbuf **bufs,
232 : : unsigned int count)
233 : : {
234 : : uint16_t nb_tx;
235 : :
236 : 18 : nb_tx = rte_eth_tx_burst(pid, queue_id, bufs, count);
237 : :
238 : : /* Free any unsent mbufs */
239 : 18 : rte_pktmbuf_free_bulk(&bufs[nb_tx], count - nb_tx);
240 : :
241 : 18 : return nb_tx;
242 : : }
243 : :
244 : : /*
245 : : * Helper: Receive packets and free received mbufs.
246 : : * Returns the number of packets received.
247 : : */
248 : : static uint16_t
249 : : do_rx_burst(uint16_t pid, uint16_t queue_id, struct rte_mbuf **bufs,
250 : : unsigned int count)
251 : : {
252 : : uint16_t nb_rx;
253 : :
254 : 3 : nb_rx = rte_eth_rx_burst(pid, queue_id, bufs, count);
255 : 3 : rte_pktmbuf_free_bulk(bufs, nb_rx);
256 : :
257 : : return nb_rx;
258 : : }
259 : :
260 : : /*
261 : : * Helper: Wait for stats to update by polling at intervals.
262 : : * Returns 0 on success (stats updated), -1 on timeout.
263 : : */
264 : : static int
265 : 2 : wait_for_stats_update(uint16_t pid, uint64_t expected_opackets,
266 : : uint64_t timeout_us)
267 : : {
268 : : struct rte_eth_stats stats;
269 : : uint64_t elapsed = 0;
270 : :
271 [ + - ]: 2 : while (elapsed < timeout_us) {
272 [ + - ]: 2 : if (rte_eth_stats_get(pid, &stats) == 0) {
273 [ - + ]: 2 : if (stats.opackets >= expected_opackets)
274 : : return 0;
275 : : }
276 : 0 : rte_delay_us_block(STATS_POLL_INTERVAL_US);
277 : 0 : elapsed += STATS_POLL_INTERVAL_US;
278 : : }
279 : :
280 : : return -1;
281 : : }
282 : :
283 : : static int
284 : 1 : test_af_packet_setup(void)
285 : : {
286 : : char devargs[256];
287 : : int ret;
288 : :
289 : : /* Create mempool for mbufs */
290 : 1 : mp = rte_pktmbuf_pool_create("af_packet_test_pool", NUM_MBUFS,
291 : : MBUF_CACHE_SIZE, 0,
292 : : RTE_MBUF_DEFAULT_BUF_SIZE,
293 : 1 : rte_socket_id());
294 [ - + ]: 1 : if (mp == NULL) {
295 : : printf("Failed to create mempool\n");
296 : 0 : return -1;
297 : : }
298 : :
299 : : /* Create TAP interface for testing */
300 : 1 : tap_fd = create_tap_interface(TAP_DEV_NAME);
301 [ + - ]: 1 : if (tap_fd >= 0)
302 : 1 : tap_created = true;
303 : : else {
304 : : printf("TAP interface creation failed - tests will be skipped\n");
305 : 0 : return 0; /* Return success to allow skipped tests */
306 : : }
307 : :
308 : : /* Create and configure af_packet port */
309 : : snprintf(devargs, sizeof(devargs), "iface=%s", TAP_DEV_NAME);
310 : 1 : ret = create_af_packet_port(AF_PACKET_DEV_NAME, devargs, &port_id);
311 [ - + ]: 1 : if (ret != 0) {
312 : : printf("Failed to create af_packet port\n");
313 : 0 : return -1;
314 : : }
315 : 1 : port_created = true;
316 : :
317 : 1 : ret = configure_af_packet_port(port_id, 1, 1);
318 [ - + ]: 1 : if (ret != 0) {
319 : : printf("Failed to configure af_packet port\n");
320 : 0 : return -1;
321 : : }
322 : 1 : port_started = true;
323 : :
324 : 1 : return 0;
325 : : }
326 : :
327 : : static void
328 : 1 : test_af_packet_teardown(void)
329 : : {
330 : : /* Stop and close test port */
331 [ + - ]: 1 : if (port_started) {
332 : 1 : rte_eth_dev_stop(port_id);
333 : 1 : port_started = false;
334 : : }
335 : :
336 [ + - ]: 1 : if (port_created) {
337 : 1 : rte_eth_dev_close(port_id);
338 : 1 : rte_vdev_uninit(AF_PACKET_DEV_NAME);
339 : 1 : port_id = RTE_MAX_ETHPORTS;
340 : 1 : port_created = false;
341 : : }
342 : :
343 : : /* Destroy TAP interface */
344 [ + - ]: 1 : if (tap_created) {
345 [ + - ]: 1 : destroy_tap_interface(tap_fd);
346 : 1 : tap_fd = -1;
347 : 1 : tap_created = false;
348 : : }
349 : :
350 [ + - ]: 1 : if (mp != NULL) {
351 : 1 : rte_mempool_free(mp);
352 : 1 : mp = NULL;
353 : : }
354 : 1 : }
355 : :
356 : : /*
357 : : * Test: Device info verification
358 : : */
359 : : static int
360 : 1 : test_af_packet_dev_info(void)
361 : : {
362 : : struct rte_eth_dev_info dev_info;
363 : : int ret;
364 : :
365 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
366 : : printf("SKIPPED: Port not available (need root)\n");
367 : 0 : return TEST_SKIPPED;
368 : : }
369 : :
370 : 1 : ret = rte_eth_dev_info_get(port_id, &dev_info);
371 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get device info");
372 : :
373 : : /* Verify expected device info values */
374 [ - + ]: 1 : TEST_ASSERT(dev_info.max_mac_addrs == 1,
375 : : "Expected max_mac_addrs=1, got %u", dev_info.max_mac_addrs);
376 : :
377 : : /* The driver has default internal frame size of 2K */
378 [ - + ]: 1 : TEST_ASSERT(dev_info.max_rx_pktlen >= RTE_ETHER_MAX_LEN,
379 : : "max_rx_pktlen %u < %u",
380 : : dev_info.max_rx_pktlen, RTE_ETHER_MAX_LEN);
381 [ - + ]: 1 : TEST_ASSERT(dev_info.max_mtu >= RTE_ETHER_MTU,
382 : : "max_mtu %u < %u", dev_info.max_mtu, RTE_ETHER_MTU);
383 [ - + ]: 1 : TEST_ASSERT(dev_info.min_rx_bufsize == 0,
384 : : "Expected min_rx_bufsize=0, got %u", dev_info.min_rx_bufsize);
385 [ - + ]: 1 : TEST_ASSERT(dev_info.max_rx_queues >= 1, "No RX queues available");
386 [ - + ]: 1 : TEST_ASSERT(dev_info.max_tx_queues >= 1, "No TX queues available");
387 [ - + ]: 1 : TEST_ASSERT(dev_info.if_index > 0, "Invalid interface index");
388 : :
389 : : /* Check TX offload capabilities */
390 [ - + ]: 1 : TEST_ASSERT(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MULTI_SEGS,
391 : : "Expected MULTI_SEGS TX offload capability");
392 [ - + ]: 1 : TEST_ASSERT(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_VLAN_INSERT,
393 : : "Expected VLAN_INSERT TX offload capability");
394 : :
395 : : /* Check RX offload capabilities */
396 [ - + ]: 1 : TEST_ASSERT(dev_info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP,
397 : : "Expected VLAN_STRIP RX offload capability");
398 [ - + ]: 1 : TEST_ASSERT(dev_info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_TIMESTAMP,
399 : : "Expected TIMESTAMP RX offload capability");
400 : :
401 : : return TEST_SUCCESS;
402 : : }
403 : :
404 : : /*
405 : : * Test: Link status
406 : : * Note: af_packet PMD link status reflects the underlying interface state,
407 : : * not the DPDK device start/stop state.
408 : : */
409 : : static int
410 : 1 : test_af_packet_link_status(void)
411 : : {
412 : : struct rte_eth_link link;
413 : : int ret;
414 : :
415 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
416 : : printf("SKIPPED: Port not available (need root)\n");
417 : 0 : return TEST_SKIPPED;
418 : : }
419 : :
420 : 1 : ret = rte_eth_link_get_nowait(port_id, &link);
421 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get link status");
422 : :
423 : : /* TAP interface was brought up during setup, so link should be UP */
424 [ - + ]: 1 : TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
425 : : "Expected link UP (TAP interface is up)");
426 [ - + ]: 1 : TEST_ASSERT(link.link_speed == RTE_ETH_SPEED_NUM_10G,
427 : : "Expected 10G link speed");
428 [ - + ]: 1 : TEST_ASSERT(link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX,
429 : : "Expected full duplex");
430 : :
431 : : return TEST_SUCCESS;
432 : : }
433 : :
434 : : /*
435 : : * Test: Statistics initial state
436 : : */
437 : : static int
438 : 1 : test_af_packet_stats_init(void)
439 : : {
440 : : struct rte_eth_stats stats;
441 : : int ret;
442 : :
443 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
444 : : printf("SKIPPED: Port not available (need root)\n");
445 : 0 : return TEST_SKIPPED;
446 : : }
447 : :
448 : : /* Reset stats */
449 : 1 : ret = rte_eth_stats_reset(port_id);
450 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to reset stats");
451 : :
452 : : /* Get initial stats */
453 : 1 : ret = rte_eth_stats_get(port_id, &stats);
454 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get stats");
455 : :
456 : : /* After reset, stats should be zero */
457 [ - + ]: 1 : TEST_ASSERT(stats.ipackets == 0,
458 : : "Expected ipackets=0 after reset, got %"PRIu64,
459 : : stats.ipackets);
460 [ - + ]: 1 : TEST_ASSERT(stats.opackets == 0,
461 : : "Expected opackets=0 after reset, got %"PRIu64,
462 : : stats.opackets);
463 [ - + ]: 1 : TEST_ASSERT(stats.ibytes == 0,
464 : : "Expected ibytes=0 after reset, got %"PRIu64,
465 : : stats.ibytes);
466 [ - + ]: 1 : TEST_ASSERT(stats.obytes == 0,
467 : : "Expected obytes=0 after reset, got %"PRIu64,
468 : : stats.obytes);
469 : :
470 : : return TEST_SUCCESS;
471 : : }
472 : :
473 : : /*
474 : : * Test: TX packets (packets will be sent to TAP interface)
475 : : */
476 : : static int
477 : 1 : test_af_packet_tx(void)
478 : : {
479 : : struct rte_mbuf *bufs[BURST_SIZE];
480 : : struct rte_eth_stats stats;
481 : : uint16_t nb_tx;
482 : : unsigned int allocated;
483 : : int ret;
484 : :
485 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
486 : : printf("SKIPPED: Port not available (need root)\n");
487 : 0 : return TEST_SKIPPED;
488 : : }
489 : :
490 : : /* Reset stats */
491 : 1 : ret = rte_eth_stats_reset(port_id);
492 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to reset stats");
493 : :
494 : : /* Allocate and prepare mbufs for TX using helper */
495 : 1 : allocated = alloc_tx_mbufs(bufs, BURST_SIZE);
496 [ - + ]: 1 : TEST_ASSERT(allocated == BURST_SIZE, "Failed to allocate all mbufs");
497 : :
498 : : /* TX burst using helper */
499 : 1 : nb_tx = do_tx_burst(port_id, 0, bufs, BURST_SIZE);
500 : :
501 : : /* Poll for stats update instead of fixed delay */
502 [ + - ]: 1 : if (nb_tx > 0) {
503 : 1 : ret = wait_for_stats_update(port_id, nb_tx, STATS_POLL_TIMEOUT_US);
504 [ + - ]: 1 : if (ret == 0) {
505 : 1 : ret = rte_eth_stats_get(port_id, &stats);
506 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get stats");
507 [ - + ]: 1 : TEST_ASSERT(stats.opackets > 0,
508 : : "Expected opackets > 0 after TX");
509 : : }
510 : : /* Timeout is acceptable - stats may not update immediately */
511 : : }
512 : :
513 : : return TEST_SUCCESS;
514 : : }
515 : :
516 : : /*
517 : : * Test: RX packets (non-blocking, may not receive anything)
518 : : */
519 : : static int
520 : 1 : test_af_packet_rx(void)
521 : : {
522 : : struct rte_mbuf *bufs[BURST_SIZE];
523 : :
524 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
525 : : printf("SKIPPED: Port not available (need root)\n");
526 : 0 : return TEST_SKIPPED;
527 : : }
528 : :
529 : : /* Try to receive packets (non-blocking) using helper */
530 : 1 : do_rx_burst(port_id, 0, bufs, BURST_SIZE);
531 : :
532 : : /* RX from tap interface without external traffic will return 0 */
533 : : /* This test just verifies the RX path doesn't crash */
534 : :
535 : 1 : return TEST_SUCCESS;
536 : : }
537 : :
538 : : /*
539 : : * Test: Loopback - TX packets and receive them back via TAP interface.
540 : : * This exercises both the TX and RX paths in a round-trip.
541 : : */
542 : : static int
543 : 1 : test_af_packet_loopback(void)
544 : : {
545 : : struct rte_mbuf *tx_bufs[BURST_SIZE];
546 : : struct rte_mbuf *rx_bufs[BURST_SIZE];
547 : : uint16_t nb_tx, total_rx = 0;
548 : : uint64_t elapsed = 0;
549 : : unsigned int allocated;
550 : : char tap_buf[2048];
551 : :
552 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
553 : : printf("SKIPPED: Port not available (need root)\n");
554 : 0 : return TEST_SKIPPED;
555 : : }
556 : :
557 : : /* Set TAP fd to non-blocking for reading */
558 : 1 : int flags = fcntl(tap_fd, F_GETFL, 0);
559 : 1 : fcntl(tap_fd, F_SETFL, flags | O_NONBLOCK);
560 : :
561 : : /* Allocate and send packets */
562 : 1 : allocated = alloc_tx_mbufs(tx_bufs, BURST_SIZE);
563 [ - + ]: 1 : if (allocated == 0) {
564 : : printf("SKIPPED: Could not allocate mbufs\n");
565 : 0 : return TEST_SKIPPED;
566 : : }
567 : :
568 : 1 : nb_tx = do_tx_burst(port_id, 0, tx_bufs, allocated);
569 [ - + ]: 1 : if (nb_tx == 0) {
570 : : printf("SKIPPED: No packets transmitted\n");
571 : 0 : return TEST_SKIPPED;
572 : : }
573 : :
574 : : /*
575 : : * Read packets from TAP interface (they were sent there by af_packet).
576 : : * Then write them back to TAP so af_packet can receive them.
577 : : */
578 [ + - ]: 32 : while (elapsed < LOOPBACK_TIMEOUT_US) {
579 : : ssize_t bytes_read, bytes_written;
580 : :
581 : : /* Read from TAP (what af_packet sent) */
582 : 32 : bytes_read = read(tap_fd, tap_buf, sizeof(tap_buf));
583 [ + - ]: 32 : if (bytes_read > 0) {
584 : : /* Write back to TAP for af_packet to receive */
585 : 32 : bytes_written = write(tap_fd, tap_buf, bytes_read);
586 [ - + ]: 32 : TEST_ASSERT(bytes_read == bytes_written, "TAP write %zd != %zd",
587 : : bytes_read, bytes_written);
588 : : }
589 : :
590 : : /* Try to receive via af_packet RX */
591 : 32 : uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, rx_bufs, BURST_SIZE);
592 [ + - ]: 32 : if (nb_rx > 0) {
593 : 32 : total_rx += nb_rx;
594 : 32 : rte_pktmbuf_free_bulk(rx_bufs, nb_rx);
595 : : }
596 : :
597 : : /* Check if we've received enough */
598 [ + + ]: 32 : if (total_rx >= nb_tx)
599 : : break;
600 : :
601 : 31 : rte_delay_us_block(STATS_POLL_INTERVAL_US);
602 : 31 : elapsed += STATS_POLL_INTERVAL_US;
603 : : }
604 : :
605 : : /* Restore TAP fd flags */
606 : 1 : fcntl(tap_fd, F_SETFL, flags);
607 : :
608 : : /*
609 : : * Note: We may not receive all packets due to timing, but receiving
610 : : * any packets proves the loopback path works.
611 : : */
612 : 1 : printf("Loopback: TX=%u, RX=%u\n", nb_tx, total_rx);
613 : :
614 : 1 : return TEST_SUCCESS;
615 : : }
616 : :
617 : : /*
618 : : * Test: Promiscuous mode
619 : : */
620 : : static int
621 : 1 : test_af_packet_promiscuous(void)
622 : : {
623 : : int ret;
624 : :
625 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
626 : : printf("SKIPPED: Port not available (need root)\n");
627 : 0 : return TEST_SKIPPED;
628 : : }
629 : :
630 : : /* Enable promiscuous mode */
631 : 1 : ret = rte_eth_promiscuous_enable(port_id);
632 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to enable promiscuous mode");
633 : :
634 : 1 : ret = rte_eth_promiscuous_get(port_id);
635 [ - + ]: 1 : TEST_ASSERT(ret == 1, "Expected promiscuous mode enabled");
636 : :
637 : : /* Disable promiscuous mode */
638 : 1 : ret = rte_eth_promiscuous_disable(port_id);
639 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to disable promiscuous mode");
640 : :
641 : 1 : ret = rte_eth_promiscuous_get(port_id);
642 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Expected promiscuous mode disabled");
643 : :
644 : : return TEST_SUCCESS;
645 : : }
646 : :
647 : : /*
648 : : * Test: MAC address operations
649 : : */
650 : : static int
651 : 1 : test_af_packet_mac_addr(void)
652 : : {
653 : : struct rte_ether_addr mac_addr;
654 : 1 : struct rte_ether_addr new_mac = {
655 : : .addr_bytes = {0x02, 0x11, 0x22, 0x33, 0x44, 0x55}
656 : : };
657 : : int ret;
658 : :
659 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
660 : : printf("SKIPPED: Port not available (need root)\n");
661 : 0 : return TEST_SKIPPED;
662 : : }
663 : :
664 : : /* Get current MAC address */
665 : 1 : ret = rte_eth_macaddr_get(port_id, &mac_addr);
666 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get MAC address");
667 : :
668 : : /* Set new MAC address (use locally administered address) */
669 : 1 : ret = rte_eth_dev_default_mac_addr_set(port_id, &new_mac);
670 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to set MAC address");
671 : :
672 : : /* Verify MAC was set */
673 : 1 : ret = rte_eth_macaddr_get(port_id, &mac_addr);
674 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get MAC address after set");
675 : :
676 [ - + ]: 1 : TEST_ASSERT(memcmp(&mac_addr, &new_mac, sizeof(mac_addr)) == 0,
677 : : "MAC address mismatch after set");
678 : :
679 : : return TEST_SUCCESS;
680 : : }
681 : :
682 : : /*
683 : : * Test: MTU operations
684 : : * Get min/max supported MTU from device info and verify setting works.
685 : : */
686 : : static int
687 : 1 : test_af_packet_mtu(void)
688 : : {
689 : : struct rte_eth_dev_info dev_info;
690 : : uint16_t mtu, original_mtu;
691 : : int ret;
692 : :
693 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
694 : : printf("SKIPPED: Port not available (need root)\n");
695 : 0 : return TEST_SKIPPED;
696 : : }
697 : :
698 : : /* Get device info for min/max MTU */
699 : 1 : ret = rte_eth_dev_info_get(port_id, &dev_info);
700 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get device info");
701 : :
702 : : /* Get current MTU */
703 : 1 : ret = rte_eth_dev_get_mtu(port_id, &original_mtu);
704 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get MTU");
705 : :
706 : 1 : printf("MTU: current=%u, min=%u, max=%u\n",
707 : 1 : original_mtu, dev_info.min_mtu, dev_info.max_mtu);
708 : :
709 : : /* Test setting minimum MTU if supported and different from current */
710 [ + - + - ]: 1 : if (dev_info.min_mtu > 0 && dev_info.min_mtu != original_mtu) {
711 : 1 : ret = rte_eth_dev_set_mtu(port_id, dev_info.min_mtu);
712 [ - + ]: 1 : if (ret == 0) {
713 : 0 : ret = rte_eth_dev_get_mtu(port_id, &mtu);
714 [ # # ]: 0 : TEST_ASSERT(ret == 0, "Failed to get MTU after set to min");
715 [ # # ]: 0 : TEST_ASSERT(mtu == dev_info.min_mtu,
716 : : "MTU not set to min: expected %u, got %u",
717 : : dev_info.min_mtu, mtu);
718 : : }
719 : : /* MTU set may fail depending on interface, that's acceptable */
720 : : }
721 : :
722 : : /* Test setting maximum MTU if supported and different from current */
723 [ + - + - ]: 1 : if (dev_info.max_mtu > 0 && dev_info.max_mtu != original_mtu &&
724 [ + - ]: 1 : dev_info.max_mtu != dev_info.min_mtu) {
725 : 1 : ret = rte_eth_dev_set_mtu(port_id, dev_info.max_mtu);
726 [ + - ]: 1 : if (ret == 0) {
727 : 1 : ret = rte_eth_dev_get_mtu(port_id, &mtu);
728 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get MTU after set to max");
729 [ - + ]: 1 : TEST_ASSERT(mtu == dev_info.max_mtu,
730 : : "MTU not set to max: expected %u, got %u",
731 : : dev_info.max_mtu, mtu);
732 : : }
733 : : /* MTU set may fail depending on interface capabilities */
734 : : }
735 : :
736 : : /* Restore original MTU */
737 : 1 : rte_eth_dev_set_mtu(port_id, original_mtu);
738 : :
739 : 1 : return TEST_SUCCESS;
740 : : }
741 : :
742 : : /*
743 : : * Test: Stats reset verification
744 : : */
745 : : static int
746 : 1 : test_af_packet_stats_reset(void)
747 : : {
748 : : struct rte_eth_stats stats;
749 : : struct rte_mbuf *bufs[BURST_SIZE / 2];
750 : : uint16_t nb_tx;
751 : : unsigned int allocated;
752 : : int ret;
753 : :
754 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
755 : : printf("SKIPPED: Port not available (need root)\n");
756 : 0 : return TEST_SKIPPED;
757 : : }
758 : :
759 : : /* Generate some TX traffic using helpers */
760 : 1 : allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 2);
761 : 1 : nb_tx = do_tx_burst(port_id, 0, bufs, allocated);
762 : :
763 : : /* Poll for stats to update */
764 [ + - ]: 1 : if (nb_tx > 0)
765 : 1 : wait_for_stats_update(port_id, nb_tx, STATS_POLL_TIMEOUT_US);
766 : :
767 : : /* Reset stats */
768 : 1 : ret = rte_eth_stats_reset(port_id);
769 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to reset stats");
770 : :
771 : : /* Verify stats are zero */
772 : 1 : ret = rte_eth_stats_get(port_id, &stats);
773 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get stats after reset");
774 : :
775 [ - + ]: 1 : TEST_ASSERT(stats.ipackets == 0,
776 : : "Expected ipackets=0, got %"PRIu64, stats.ipackets);
777 [ - + ]: 1 : TEST_ASSERT(stats.opackets == 0,
778 : : "Expected opackets=0, got %"PRIu64, stats.opackets);
779 [ - + ]: 1 : TEST_ASSERT(stats.ibytes == 0,
780 : : "Expected ibytes=0, got %"PRIu64, stats.ibytes);
781 [ - + ]: 1 : TEST_ASSERT(stats.obytes == 0,
782 : : "Expected obytes=0, got %"PRIu64, stats.obytes);
783 : :
784 : : return TEST_SUCCESS;
785 : : }
786 : :
787 : : /*
788 : : * Test: Invalid configuration handling - missing iface
789 : : */
790 : : static int
791 : 1 : test_af_packet_invalid_no_iface(void)
792 : : {
793 : : int ret;
794 : :
795 : : /* Test without iface argument (should fail) */
796 : 1 : ret = rte_vdev_init("net_af_packet_invalid1", "");
797 [ - + ]: 1 : TEST_ASSERT(ret != 0, "Expected failure without iface argument");
798 : :
799 : : return TEST_SUCCESS;
800 : : }
801 : :
802 : : /*
803 : : * Test: Invalid configuration handling - non-existent interface
804 : : */
805 : : static int
806 : 1 : test_af_packet_invalid_bad_iface(void)
807 : : {
808 : : int ret;
809 : :
810 : : /* Test with non-existent interface (should fail) */
811 : 1 : ret = rte_vdev_init("net_af_packet_invalid2",
812 : : "iface=nonexistent_iface_12345");
813 [ - + ]: 1 : TEST_ASSERT(ret != 0, "Expected failure with non-existent interface");
814 : :
815 : : return TEST_SUCCESS;
816 : : }
817 : :
818 : : /*
819 : : * Test: Invalid configuration handling - invalid qpairs
820 : : */
821 : : static int
822 : 1 : test_af_packet_invalid_qpairs(void)
823 : : {
824 : : int ret;
825 : :
826 [ - + ]: 1 : if (!tap_created) {
827 : : printf("SKIPPED: TAP interface not available (need root)\n");
828 : 0 : return TEST_SKIPPED;
829 : : }
830 : :
831 : : /* Test with invalid qpairs (should fail) */
832 : 1 : ret = rte_vdev_init("net_af_packet_invalid3",
833 : : "iface=" TAP_DEV_NAME ",qpairs=0");
834 [ - + ]: 1 : TEST_ASSERT(ret != 0, "Expected failure with qpairs=0");
835 : :
836 : : return TEST_SUCCESS;
837 : : }
838 : :
839 : : /*
840 : : * Test: Custom frame size configuration
841 : : */
842 : : static int
843 : 1 : test_af_packet_frame_config(void)
844 : : {
845 : : struct rte_eth_dev_info dev_info;
846 : : uint16_t test_port;
847 : : char devargs[256];
848 : : int ret;
849 : :
850 [ - + ]: 1 : if (!tap_created) {
851 : : printf("SKIPPED: TAP interface not available (need root)\n");
852 : 0 : return TEST_SKIPPED;
853 : : }
854 : :
855 : : /* Create with custom frame parameters */
856 : : snprintf(devargs, sizeof(devargs),
857 : : "iface=%s,blocksz=4096,framesz=2048,framecnt=256",
858 : : TAP_DEV_NAME);
859 : :
860 : 1 : ret = rte_vdev_init("net_af_packet_frame_test", devargs);
861 [ - + ]: 1 : if (ret != 0) {
862 : : printf("SKIPPED: Could not create port with custom frame config\n");
863 : 0 : return TEST_SKIPPED;
864 : : }
865 : :
866 : 1 : ret = rte_eth_dev_get_port_by_name("net_af_packet_frame_test",
867 : : &test_port);
868 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get port id");
869 : :
870 : 1 : ret = rte_eth_dev_info_get(test_port, &dev_info);
871 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get device info");
872 : :
873 : : /* Cleanup */
874 : 1 : rte_eth_dev_close(test_port);
875 : 1 : rte_vdev_uninit("net_af_packet_frame_test");
876 : :
877 : 1 : return TEST_SUCCESS;
878 : : }
879 : :
880 : : /*
881 : : * Test: Qdisc bypass configuration
882 : : */
883 : : static int
884 : 1 : test_af_packet_qdisc_bypass(void)
885 : : {
886 : : uint16_t test_port;
887 : : char devargs[256];
888 : : int ret;
889 : :
890 [ - + ]: 1 : if (!tap_created) {
891 : : printf("SKIPPED: TAP interface not available (need root)\n");
892 : 0 : return TEST_SKIPPED;
893 : : }
894 : :
895 : : /* Create with qdisc_bypass enabled */
896 : : snprintf(devargs, sizeof(devargs), "iface=%s,qdisc_bypass=1",
897 : : TAP_DEV_NAME);
898 : :
899 : 1 : ret = rte_vdev_init("net_af_packet_qdisc_test", devargs);
900 [ - + ]: 1 : if (ret != 0) {
901 : : printf("SKIPPED: qdisc_bypass may not be supported\n");
902 : 0 : return TEST_SKIPPED;
903 : : }
904 : :
905 : 1 : ret = rte_eth_dev_get_port_by_name("net_af_packet_qdisc_test",
906 : : &test_port);
907 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get port id");
908 : :
909 : : /* Cleanup */
910 : 1 : rte_eth_dev_close(test_port);
911 : 1 : rte_vdev_uninit("net_af_packet_qdisc_test");
912 : :
913 : 1 : return TEST_SUCCESS;
914 : : }
915 : :
916 : : /*
917 : : * Test: Multiple queue pairs
918 : : */
919 : : static int
920 : 1 : test_af_packet_multi_queue(void)
921 : : {
922 : : struct rte_eth_dev_info dev_info;
923 : 1 : struct rte_eth_conf port_conf = {0};
924 : : struct rte_mbuf *bufs[BURST_SIZE];
925 : : uint16_t test_port;
926 : : char devargs[256];
927 : : const uint16_t num_queues = 2;
928 : : unsigned int allocated, q;
929 : : int ret;
930 : :
931 [ - + ]: 1 : if (!tap_created) {
932 : : printf("SKIPPED: TAP interface not available (need root)\n");
933 : 0 : return TEST_SKIPPED;
934 : : }
935 : :
936 : : snprintf(devargs, sizeof(devargs), "iface=%s,qpairs=%u",
937 : : TAP_DEV_NAME, num_queues);
938 : 1 : ret = rte_vdev_init("net_af_packet_multi_q", devargs);
939 [ - + ]: 1 : if (ret != 0) {
940 : : printf("SKIPPED: Could not create multi-queue port\n");
941 : 0 : return TEST_SKIPPED;
942 : : }
943 : :
944 : 1 : ret = rte_eth_dev_get_port_by_name("net_af_packet_multi_q", &test_port);
945 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get port id for multi-queue port");
946 : :
947 : 1 : ret = rte_eth_dev_info_get(test_port, &dev_info);
948 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to get device info");
949 : :
950 [ - + ]: 1 : TEST_ASSERT(dev_info.max_rx_queues >= num_queues,
951 : : "Expected at least %u RX queues, got %u",
952 : : num_queues, dev_info.max_rx_queues);
953 [ - + ]: 1 : TEST_ASSERT(dev_info.max_tx_queues >= num_queues,
954 : : "Expected at least %u TX queues, got %u",
955 : : num_queues, dev_info.max_tx_queues);
956 : :
957 : : /* Configure the port */
958 : 1 : ret = rte_eth_dev_configure(test_port, num_queues, num_queues, &port_conf);
959 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to configure multi-queue port");
960 : :
961 [ + + ]: 3 : for (q = 0; q < num_queues; q++) {
962 : 2 : ret = rte_eth_rx_queue_setup(test_port, q, RING_SIZE,
963 : 2 : rte_eth_dev_socket_id(test_port),
964 : : NULL, mp);
965 [ - + ]: 2 : TEST_ASSERT(ret == 0, "Failed to setup RX queue %u", q);
966 : :
967 : 2 : ret = rte_eth_tx_queue_setup(test_port, q, RING_SIZE,
968 : 2 : rte_eth_dev_socket_id(test_port),
969 : : NULL);
970 [ - + ]: 2 : TEST_ASSERT(ret == 0, "Failed to setup TX queue %u", q);
971 : : }
972 : :
973 : 1 : ret = rte_eth_dev_start(test_port);
974 [ - + ]: 1 : TEST_ASSERT(ret == 0, "Failed to start multi-queue port");
975 : :
976 : : /* Test TX on different queues using shared helpers */
977 [ + + ]: 3 : for (q = 0; q < num_queues; q++) {
978 : 2 : allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 2);
979 [ + - ]: 2 : if (allocated > 0)
980 : 2 : do_tx_burst(test_port, q, bufs, allocated);
981 : : }
982 : :
983 : : /* Test RX on different queues */
984 [ + + ]: 3 : for (q = 0; q < num_queues; q++)
985 : 2 : do_rx_burst(test_port, q, bufs, BURST_SIZE);
986 : :
987 : : /* Cleanup */
988 : 1 : rte_eth_dev_stop(test_port);
989 : 1 : rte_eth_dev_close(test_port);
990 : 1 : rte_vdev_uninit("net_af_packet_multi_q");
991 : :
992 : 1 : return TEST_SUCCESS;
993 : : }
994 : :
995 : : /*
996 : : * Multi-threaded test context
997 : : */
998 : : struct mt_test_ctx {
999 : : uint16_t port_id;
1000 : : RTE_ATOMIC(uint32_t) stop;
1001 : : uint32_t rx_count;
1002 : : };
1003 : :
1004 : : /*
1005 : : * Reader thread function for multi-threaded test
1006 : : */
1007 : : static void *
1008 : 1 : mt_reader_thread(void *arg)
1009 : : {
1010 : : struct mt_test_ctx *ctx = arg;
1011 : : struct rte_mbuf *bufs[BURST_SIZE];
1012 : : uint16_t nb_rx;
1013 : :
1014 [ - + ]: 1 : while (!rte_atomic_load_explicit(&ctx->stop, rte_memory_order_relaxed)) {
1015 : 0 : nb_rx = rte_eth_rx_burst(ctx->port_id, 0, bufs, BURST_SIZE);
1016 [ # # ]: 0 : if (nb_rx > 0) {
1017 : 0 : ctx->rx_count += nb_rx;
1018 : 0 : rte_pktmbuf_free_bulk(bufs, nb_rx);
1019 : : }
1020 : :
1021 : 0 : rte_delay_us_block(50);
1022 : : }
1023 : :
1024 : 1 : return NULL;
1025 : : }
1026 : :
1027 : : /*
1028 : : * Test: Multi-threaded concurrent TX and RX
1029 : : * Main thread sends packets while a reader thread receives,
1030 : : * exercising concurrent access to the af_packet PMD.
1031 : : */
1032 : : static int
1033 : 1 : test_af_packet_multithread(void)
1034 : : {
1035 : 1 : struct mt_test_ctx ctx = {0};
1036 : : struct rte_mbuf *bufs[BURST_SIZE];
1037 : : pthread_t reader_tid;
1038 : : struct timespec start, now;
1039 : : uint32_t tx_count = 0;
1040 : : unsigned int allocated;
1041 : : uint16_t nb_tx;
1042 : : int ret;
1043 : :
1044 [ + - - + ]: 1 : if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
1045 : : printf("SKIPPED: Port not available (need root)\n");
1046 : 0 : return TEST_SKIPPED;
1047 : : }
1048 : :
1049 : 1 : ctx.port_id = port_id;
1050 : :
1051 : : /* Start reader thread */
1052 : 1 : ret = pthread_create(&reader_tid, NULL, mt_reader_thread, &ctx);
1053 [ - + ]: 1 : if (ret != 0) {
1054 : : printf("SKIPPED: Failed to create reader thread: %d\n", ret);
1055 : 0 : return TEST_SKIPPED;
1056 : : }
1057 : :
1058 : : /* Main thread sends packets for the configured duration */
1059 : 1 : clock_gettime(CLOCK_MONOTONIC, &start);
1060 : : do {
1061 : 13 : allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 4);
1062 [ + - ]: 13 : if (allocated == 0)
1063 : : break;
1064 : :
1065 : 13 : nb_tx = do_tx_burst(port_id, 0, bufs, allocated);
1066 : 13 : tx_count += nb_tx;
1067 : :
1068 : 13 : rte_delay_us_block(100);
1069 : 13 : clock_gettime(CLOCK_MONOTONIC, &now);
1070 [ + + ]: 13 : } while (tx_count < MT_NUM_PACKETS &&
1071 [ + - ]: 12 : (now.tv_sec - start.tv_sec) < MT_TEST_DURATION_SEC);
1072 : :
1073 : : /* Signal reader to stop */
1074 : 1 : rte_atomic_store_explicit(&ctx.stop, 1, rte_memory_order_relaxed);
1075 : 1 : pthread_join(reader_tid, NULL);
1076 : :
1077 : 1 : printf("Multi-threaded test: TX=%u, RX=%u (duration=%ds)\n",
1078 : : tx_count, ctx.rx_count, MT_TEST_DURATION_SEC);
1079 : :
1080 [ - + ]: 1 : TEST_ASSERT(tx_count > 0,
1081 : : "Expected some packets to be transmitted");
1082 : :
1083 : : return TEST_SUCCESS;
1084 : : }
1085 : :
1086 : : static struct unit_test_suite af_packet_test_suite = {
1087 : : .suite_name = "AF_PACKET PMD Unit Test Suite",
1088 : : .setup = test_af_packet_setup,
1089 : : .teardown = test_af_packet_teardown,
1090 : : .unit_test_cases = {
1091 : : /* Tests that don't modify device state */
1092 : : TEST_CASE(test_af_packet_dev_info),
1093 : : TEST_CASE(test_af_packet_link_status),
1094 : : TEST_CASE(test_af_packet_stats_init),
1095 : : TEST_CASE(test_af_packet_tx),
1096 : : TEST_CASE(test_af_packet_rx),
1097 : : TEST_CASE(test_af_packet_loopback),
1098 : : TEST_CASE(test_af_packet_promiscuous),
1099 : : TEST_CASE(test_af_packet_mac_addr),
1100 : : TEST_CASE(test_af_packet_mtu),
1101 : : TEST_CASE(test_af_packet_stats_reset),
1102 : : TEST_CASE(test_af_packet_multithread),
1103 : :
1104 : : /* Tests that create their own devices */
1105 : : TEST_CASE(test_af_packet_invalid_no_iface),
1106 : : TEST_CASE(test_af_packet_invalid_bad_iface),
1107 : : TEST_CASE(test_af_packet_invalid_qpairs),
1108 : : TEST_CASE(test_af_packet_frame_config),
1109 : : TEST_CASE(test_af_packet_qdisc_bypass),
1110 : : TEST_CASE(test_af_packet_multi_queue),
1111 : :
1112 : : TEST_CASES_END() /**< NULL terminate unit test array */
1113 : : }
1114 : : };
1115 : :
1116 : : static int
1117 : 1 : test_pmd_af_packet(void)
1118 : : {
1119 : 1 : return unit_test_suite_runner(&af_packet_test_suite);
1120 : : }
1121 : : #endif
1122 : :
1123 : 286 : REGISTER_FAST_TEST(af_packet_pmd_autotest, NOHUGE_OK, ASAN_OK, test_pmd_af_packet);
|