Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2026 Stephen Hemminger
3 : : */
4 : :
5 : : /*
6 : : * Basic test of TAP device functionality
7 : : * based off of PMD ring test.
8 : : */
9 : :
10 : : #include "test.h"
11 : :
12 : : #include <stdio.h>
13 : :
14 : : #ifndef RTE_EXEC_ENV_LINUX
15 : :
16 : : /* TAP PMD is only available on Linux */
17 : : static int
18 : : test_pmd_tap(void)
19 : : {
20 : : printf("TAP PMD not supported on this platform, skipping test\n");
21 : : return TEST_SKIPPED;
22 : : }
23 : :
24 : : #else /* RTE_EXEC_ENV_LINUX */
25 : :
26 : : #include <string.h>
27 : : #include <unistd.h>
28 : : #include <limits.h>
29 : : #include <net/if.h>
30 : : #include <sys/socket.h>
31 : : #include <linux/if_packet.h>
32 : :
33 : : #include <rte_ethdev.h>
34 : : #include <rte_bus_vdev.h>
35 : : #include <rte_mbuf.h>
36 : : #include <rte_ether.h>
37 : : #include <rte_ip.h>
38 : :
39 : : #define SOCKET0 0
40 : : #define RING_SIZE 256
41 : : #define NB_MBUF 4096
42 : : #define MAX_PKT_BURST 32
43 : : #define PKT_LEN 64
44 : :
45 : : static struct rte_mempool *mp;
46 : : static int tap_port0 = -1;
47 : : static int tap_port1 = -1;
48 : :
49 : : static int
50 : 2 : test_tap_ethdev_configure(int port)
51 : : {
52 : : struct rte_eth_conf port_conf;
53 : : struct rte_eth_link link;
54 : : int ret;
55 : :
56 : : memset(&port_conf, 0, sizeof(struct rte_eth_conf));
57 : :
58 : 2 : ret = rte_eth_dev_configure(port, 1, 1, &port_conf);
59 [ - + ]: 2 : if (ret < 0) {
60 : 0 : printf("Configure failed for port %d: %s\n",
61 : : port, rte_strerror(-ret));
62 : 0 : return -1;
63 : : }
64 : :
65 : 2 : ret = rte_eth_tx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL);
66 [ - + ]: 2 : if (ret < 0) {
67 : 0 : printf("TX queue setup failed for port %d: %s\n",
68 : : port, rte_strerror(-ret));
69 : 0 : return -1;
70 : : }
71 : :
72 : 2 : ret = rte_eth_rx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL, mp);
73 [ - + ]: 2 : if (ret < 0) {
74 : 0 : printf("RX queue setup failed for port %d: %s\n",
75 : : port, rte_strerror(-ret));
76 : 0 : return -1;
77 : : }
78 : :
79 : 2 : ret = rte_eth_dev_start(port);
80 [ - + ]: 2 : if (ret < 0) {
81 : 0 : printf("Error starting port %d: %s\n",
82 : : port, rte_strerror(-ret));
83 : 0 : return -1;
84 : : }
85 : :
86 : 2 : ret = rte_eth_link_get(port, &link);
87 [ - + ]: 2 : if (ret < 0) {
88 : 0 : printf("Link get failed for port %d: %s\n",
89 : : port, rte_strerror(-ret));
90 : 0 : return -1;
91 : : }
92 : :
93 : 2 : printf("Port %d: link status %s, speed %u Mbps\n",
94 : : port,
95 [ - + ]: 2 : link.link_status ? "up" : "down",
96 : : link.link_speed);
97 : :
98 : 2 : return 0;
99 : : }
100 : :
101 : : static struct rte_mbuf *
102 : 16 : create_test_packet(struct rte_mempool *pool, uint16_t pkt_len)
103 : : {
104 : : struct rte_mbuf *mbuf;
105 : : struct rte_ether_hdr *eth_hdr;
106 : : struct rte_ipv4_hdr *ip_hdr;
107 : : uint8_t *payload;
108 : : uint16_t i;
109 : :
110 : 16 : mbuf = rte_pktmbuf_alloc(pool);
111 [ - + ]: 16 : if (mbuf == NULL) {
112 : : printf("%s(): mbuf alloc failed\n", __func__);
113 : 0 : return NULL;
114 : : }
115 : :
116 : : /* Ensure minimum packet size for Ethernet */
117 : : if (pkt_len < RTE_ETHER_MIN_LEN)
118 : : pkt_len = RTE_ETHER_MIN_LEN;
119 : :
120 : 16 : eth_hdr = (struct rte_ether_hdr *)rte_pktmbuf_append(mbuf, pkt_len);
121 [ - + ]: 16 : if (eth_hdr == NULL) {
122 : : printf("%s(): append %u bytes failed\n", __func__, pkt_len);
123 : 0 : rte_pktmbuf_free(mbuf);
124 : 0 : return NULL;
125 : : }
126 : :
127 : : /* Create Ethernet header */
128 : 16 : eth_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
129 [ + - ]: 16 : memset(ð_hdr->dst_addr, 0xFF, RTE_ETHER_ADDR_LEN); /* broadcast */
130 : 16 : memset(ð_hdr->src_addr, 0x02, RTE_ETHER_ADDR_LEN);
131 : 16 : eth_hdr->src_addr.addr_bytes[5] = 0x01;
132 : 16 : eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
133 : :
134 : : /* Create simple IPv4 header */
135 : 16 : ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
136 : : memset(ip_hdr, 0, sizeof(*ip_hdr));
137 : 16 : ip_hdr->version_ihl = 0x45; /* IPv4, 20 byte header */
138 [ + - ]: 16 : ip_hdr->total_length = rte_cpu_to_be_16(pkt_len - sizeof(*eth_hdr));
139 : 16 : ip_hdr->time_to_live = 64;
140 : 16 : ip_hdr->next_proto_id = IPPROTO_UDP;
141 : 16 : ip_hdr->src_addr = rte_cpu_to_be_32(0x0A000001); /* 10.0.0.1 */
142 : 16 : ip_hdr->dst_addr = rte_cpu_to_be_32(0x0A000002); /* 10.0.0.2 */
143 : :
144 : : /* Fill payload with pattern */
145 : 16 : payload = (uint8_t *)(ip_hdr + 1);
146 [ + + ]: 496 : for (i = 0; i < pkt_len - sizeof(*eth_hdr) - sizeof(*ip_hdr); i++)
147 : 480 : payload[i] = (uint8_t)(i & 0xFF);
148 : :
149 : : return mbuf;
150 : : }
151 : :
152 : : static int
153 : 1 : test_tap_send_receive(void)
154 : : {
155 : : struct rte_mbuf *tx_mbufs[MAX_PKT_BURST];
156 : : struct rte_mbuf *rx_mbufs[MAX_PKT_BURST];
157 : : uint16_t nb_tx, nb_rx;
158 : : int i;
159 : :
160 : : printf("Testing TAP packet send and receive\n");
161 : :
162 : : /* Create test packets */
163 [ + + ]: 17 : for (i = 0; i < MAX_PKT_BURST / 2; i++) {
164 : 16 : tx_mbufs[i] = create_test_packet(mp, PKT_LEN);
165 [ - + ]: 16 : if (tx_mbufs[i] == NULL) {
166 : : printf("Failed to create test packet %d\n", i);
167 : : /* Free already allocated packets */
168 [ # # ]: 0 : while (--i >= 0)
169 : 0 : rte_pktmbuf_free(tx_mbufs[i]);
170 : : return TEST_FAILED;
171 : : }
172 : : }
173 : :
174 : : /* Send packets */
175 : 1 : nb_tx = rte_eth_tx_burst(tap_port0, 0, tx_mbufs, MAX_PKT_BURST / 2);
176 : 1 : printf("Transmitted %u packets on port %d\n", nb_tx, tap_port0);
177 : :
178 : : /* Free any unsent packets */
179 [ - + ]: 1 : for (i = nb_tx; i < MAX_PKT_BURST / 2; i++)
180 : 0 : rte_pktmbuf_free(tx_mbufs[i]);
181 : :
182 [ - + ]: 1 : if (nb_tx == 0) {
183 : : printf("Warning: No packets transmitted (this may be expected if interface is not up)\n");
184 : 0 : return TEST_SUCCESS;
185 : : }
186 : :
187 : : /* Small delay to allow packets to be processed */
188 : 1 : usleep(10000);
189 : :
190 : : /* Try to receive packets (note: TAP loopback depends on kernel config) */
191 : 1 : nb_rx = rte_eth_rx_burst(tap_port0, 0, rx_mbufs, MAX_PKT_BURST);
192 : 1 : printf("Received %u packets on port %d\n", nb_rx, tap_port0);
193 : :
194 : : /* Free received packets */
195 [ - + ]: 1 : for (i = 0; i < nb_rx; i++)
196 : 0 : rte_pktmbuf_free(rx_mbufs[i]);
197 : :
198 : : return TEST_SUCCESS;
199 : : }
200 : :
201 : : static int
202 : 2 : test_tap_stats_get(int port)
203 : : {
204 : : struct rte_eth_stats stats;
205 : : int ret;
206 : :
207 : : printf("Testing TAP PMD stats_get for port %d\n", port);
208 : :
209 : 2 : ret = rte_eth_stats_get(port, &stats);
210 [ - + ]: 2 : if (ret != 0) {
211 : 0 : printf("Error: failed to get stats for port %d: %s\n",
212 : : port, rte_strerror(-ret));
213 : 0 : return -1;
214 : : }
215 : :
216 : : printf("Port %d stats:\n", port);
217 : 2 : printf(" ipackets: %"PRIu64"\n", stats.ipackets);
218 : 2 : printf(" opackets: %"PRIu64"\n", stats.opackets);
219 : 2 : printf(" ibytes: %"PRIu64"\n", stats.ibytes);
220 : 2 : printf(" obytes: %"PRIu64"\n", stats.obytes);
221 : 2 : printf(" ierrors: %"PRIu64"\n", stats.ierrors);
222 : 2 : printf(" oerrors: %"PRIu64"\n", stats.oerrors);
223 : :
224 : 2 : return 0;
225 : : }
226 : :
227 : : static int
228 : 2 : test_tap_stats_reset(int port)
229 : : {
230 : : struct rte_eth_stats stats;
231 : : int ret;
232 : :
233 : : printf("Testing TAP PMD stats_reset for port %d\n", port);
234 : :
235 : 2 : ret = rte_eth_stats_reset(port);
236 [ - + ]: 2 : if (ret != 0) {
237 : 0 : printf("Error: failed to reset stats for port %d: %s\n",
238 : : port, rte_strerror(-ret));
239 : 0 : return -1;
240 : : }
241 : :
242 : 2 : ret = rte_eth_stats_get(port, &stats);
243 [ - + ]: 2 : if (ret != 0) {
244 : : printf("Error: failed to get stats after reset for port %d\n",
245 : : port);
246 : 0 : return -1;
247 : : }
248 : :
249 : : /* After reset, all stats should be zero */
250 [ + - + - ]: 2 : if (stats.ipackets != 0 || stats.opackets != 0 ||
251 [ + - + - ]: 2 : stats.ibytes != 0 || stats.obytes != 0 ||
252 [ + - - + ]: 2 : stats.ierrors != 0 || stats.oerrors != 0) {
253 : : printf("Error: port %d stats are not zero after reset\n", port);
254 : 0 : return -1;
255 : : }
256 : :
257 : : printf("Stats reset successful for port %d\n", port);
258 : 2 : return 0;
259 : : }
260 : :
261 : : static int
262 : 2 : test_tap_link_status(int port)
263 : : {
264 : : struct rte_eth_link link;
265 : : int ret;
266 : :
267 : : printf("Testing TAP PMD link status for port %d\n", port);
268 : :
269 : 2 : ret = rte_eth_link_get_nowait(port, &link);
270 [ - + ]: 2 : if (ret < 0) {
271 : 0 : printf("Error: failed to get link status for port %d: %s\n",
272 : : port, rte_strerror(-ret));
273 : 0 : return -1;
274 : : }
275 : :
276 : 4 : printf("Port %d link: status=%s speed=%u duplex=%s\n",
277 : : port,
278 [ - + ]: 2 : link.link_status ? "up" : "down",
279 : : link.link_speed,
280 [ - + ]: 2 : link.link_duplex ? "full" : "half");
281 : :
282 : 2 : return 0;
283 : : }
284 : :
285 : : static int
286 : 2 : test_tap_dev_info(int port)
287 : : {
288 : : struct rte_eth_dev_info dev_info;
289 : : int ret;
290 : :
291 : : printf("Testing TAP PMD dev_info for port %d\n", port);
292 : :
293 : 2 : ret = rte_eth_dev_info_get(port, &dev_info);
294 [ - + ]: 2 : if (ret != 0) {
295 : 0 : printf("Error: failed to get dev info for port %d: %s\n",
296 : : port, rte_strerror(-ret));
297 : 0 : return -1;
298 : : }
299 : :
300 : : printf("Port %d device info:\n", port);
301 : 2 : printf(" driver_name: %s\n", dev_info.driver_name);
302 : 2 : printf(" if_index: %u\n", dev_info.if_index);
303 : 2 : printf(" max_rx_queues: %u\n", dev_info.max_rx_queues);
304 : 2 : printf(" max_tx_queues: %u\n", dev_info.max_tx_queues);
305 : 2 : printf(" max_rx_pktlen: %u\n", dev_info.max_rx_pktlen);
306 : :
307 : : /* Verify this is indeed a TAP device */
308 [ - + ]: 2 : if (strcmp(dev_info.driver_name, "net_tap") != 0 &&
309 [ # # ]: 0 : strcmp(dev_info.driver_name, "net_tun") != 0) {
310 : : printf("Warning: unexpected driver name: %s\n",
311 : : dev_info.driver_name);
312 : : }
313 : :
314 : : return 0;
315 : : }
316 : :
317 : : static int
318 : 1 : test_tap_mtu(int port)
319 : : {
320 : : uint16_t mtu;
321 : : int ret;
322 : :
323 : : printf("Testing TAP PMD MTU operations for port %d\n", port);
324 : :
325 : : /* Get current MTU */
326 : 1 : ret = rte_eth_dev_get_mtu(port, &mtu);
327 [ - + ]: 1 : if (ret != 0) {
328 : 0 : printf("Error: failed to get MTU for port %d: %s\n",
329 : : port, rte_strerror(-ret));
330 : 0 : return -1;
331 : : }
332 : 1 : printf("Current MTU for port %d: %u\n", port, mtu);
333 : :
334 : : /* Try to set a new MTU */
335 : 1 : ret = rte_eth_dev_set_mtu(port, 1400);
336 [ - + ]: 1 : if (ret != 0) {
337 : 0 : printf("Warning: failed to set MTU to 1400 for port %d: %s\n",
338 : : port, rte_strerror(-ret));
339 : : /* Not a fatal error - may require privileges */
340 : : } else {
341 : : printf("MTU set to 1400 for port %d\n", port);
342 : :
343 : : /* Restore original MTU */
344 : 1 : ret = rte_eth_dev_set_mtu(port, mtu);
345 [ - + ]: 1 : if (ret != 0)
346 : : printf("Warning: failed to restore MTU for port %d\n", port);
347 : : }
348 : :
349 : : return 0;
350 : : }
351 : :
352 : : static int
353 : 2 : test_tap_mac_addr(int port)
354 : : {
355 : : struct rte_ether_addr mac_addr;
356 : : int ret;
357 : :
358 : : printf("Testing TAP PMD MAC address for port %d\n", port);
359 : :
360 : 2 : ret = rte_eth_macaddr_get(port, &mac_addr);
361 [ - + ]: 2 : if (ret != 0) {
362 : 0 : printf("Error: failed to get MAC address for port %d: %s\n",
363 : : port, rte_strerror(-ret));
364 : 0 : return -1;
365 : : }
366 : :
367 : 2 : printf("Port %d MAC address: " RTE_ETHER_ADDR_PRT_FMT "\n",
368 : 2 : port, RTE_ETHER_ADDR_BYTES(&mac_addr));
369 : :
370 : 2 : return 0;
371 : : }
372 : :
373 : : static int
374 : 1 : test_tap_promiscuous(int port)
375 : : {
376 : : int ret;
377 : : int promisc_enabled;
378 : :
379 : : printf("Testing TAP PMD promiscuous mode for port %d\n", port);
380 : :
381 : : /* Get current promiscuous state */
382 : 1 : promisc_enabled = rte_eth_promiscuous_get(port);
383 [ + - ]: 1 : printf("Promiscuous mode initially %s for port %d\n",
384 : : promisc_enabled ? "enabled" : "disabled", port);
385 : :
386 : : /* Enable promiscuous mode */
387 : 1 : ret = rte_eth_promiscuous_enable(port);
388 [ - + ]: 1 : if (ret != 0) {
389 : 0 : printf("Warning: failed to enable promiscuous mode for port %d: %s\n",
390 : : port, rte_strerror(-ret));
391 : : } else {
392 [ - + ]: 1 : if (rte_eth_promiscuous_get(port) != 1) {
393 : : printf("Error: promiscuous mode not enabled after enable call\n");
394 : 0 : return -1;
395 : : }
396 : : printf("Promiscuous mode enabled for port %d\n", port);
397 : : }
398 : :
399 : : /* Disable promiscuous mode */
400 : 1 : ret = rte_eth_promiscuous_disable(port);
401 [ - + ]: 1 : if (ret != 0) {
402 : 0 : printf("Warning: failed to disable promiscuous mode for port %d: %s\n",
403 : : port, rte_strerror(-ret));
404 : : } else {
405 [ - + ]: 1 : if (rte_eth_promiscuous_get(port) != 0) {
406 : : printf("Error: promiscuous mode not disabled after disable call\n");
407 : 0 : return -1;
408 : : }
409 : : printf("Promiscuous mode disabled for port %d\n", port);
410 : : }
411 : :
412 : : return 0;
413 : : }
414 : :
415 : : static int
416 : 1 : test_tap_allmulti(int port)
417 : : {
418 : : int ret;
419 : : int allmulti_enabled;
420 : :
421 : : printf("Testing TAP PMD allmulticast mode for port %d\n", port);
422 : :
423 : : /* Get current allmulticast state */
424 : 1 : allmulti_enabled = rte_eth_allmulticast_get(port);
425 [ + - ]: 1 : printf("Allmulticast mode initially %s for port %d\n",
426 : : allmulti_enabled ? "enabled" : "disabled", port);
427 : :
428 : : /* Enable allmulticast mode */
429 : 1 : ret = rte_eth_allmulticast_enable(port);
430 [ - + ]: 1 : if (ret != 0) {
431 : 0 : printf("Warning: failed to enable allmulticast mode for port %d: %s\n",
432 : : port, rte_strerror(-ret));
433 : : } else {
434 [ - + ]: 1 : if (rte_eth_allmulticast_get(port) != 1) {
435 : : printf("Error: allmulticast mode not enabled after enable call\n");
436 : 0 : return -1;
437 : : }
438 : : printf("Allmulticast mode enabled for port %d\n", port);
439 : : }
440 : :
441 : : /* Disable allmulticast mode */
442 : 1 : ret = rte_eth_allmulticast_disable(port);
443 [ - + ]: 1 : if (ret != 0) {
444 : 0 : printf("Warning: failed to disable allmulticast mode for port %d: %s\n",
445 : : port, rte_strerror(-ret));
446 : : } else {
447 [ - + ]: 1 : if (rte_eth_allmulticast_get(port) != 0) {
448 : : printf("Error: allmulticast mode not disabled after disable call\n");
449 : 0 : return -1;
450 : : }
451 : : printf("Allmulticast mode disabled for port %d\n", port);
452 : : }
453 : :
454 : : return 0;
455 : : }
456 : :
457 : : static int
458 : 1 : test_tap_queue_start_stop(int port)
459 : : {
460 : : int ret;
461 : :
462 : : printf("Testing TAP PMD queue start/stop for port %d\n", port);
463 : :
464 : : /* Stop RX queue */
465 : 1 : ret = rte_eth_dev_rx_queue_stop(port, 0);
466 [ - + ]: 1 : if (ret != 0 && ret != -ENOTSUP) {
467 : 0 : printf("Error: failed to stop RX queue for port %d: %s\n",
468 : : port, rte_strerror(-ret));
469 : 0 : return -1;
470 : : }
471 : : printf("RX queue stopped for port %d\n", port);
472 : :
473 : : /* Stop TX queue */
474 : 1 : ret = rte_eth_dev_tx_queue_stop(port, 0);
475 [ - + ]: 1 : if (ret != 0 && ret != -ENOTSUP) {
476 : 0 : printf("Error: failed to stop TX queue for port %d: %s\n",
477 : : port, rte_strerror(-ret));
478 : 0 : return -1;
479 : : }
480 : : printf("TX queue stopped for port %d\n", port);
481 : :
482 : : /* Start RX queue */
483 : 1 : ret = rte_eth_dev_rx_queue_start(port, 0);
484 [ - + ]: 1 : if (ret != 0 && ret != -ENOTSUP) {
485 : 0 : printf("Error: failed to start RX queue for port %d: %s\n",
486 : : port, rte_strerror(-ret));
487 : 0 : return -1;
488 : : }
489 : : printf("RX queue started for port %d\n", port);
490 : :
491 : : /* Start TX queue */
492 : 1 : ret = rte_eth_dev_tx_queue_start(port, 0);
493 [ - + ]: 1 : if (ret != 0 && ret != -ENOTSUP) {
494 : 0 : printf("Error: failed to start TX queue for port %d: %s\n",
495 : : port, rte_strerror(-ret));
496 : 0 : return -1;
497 : : }
498 : : printf("TX queue started for port %d\n", port);
499 : :
500 : 1 : return 0;
501 : : }
502 : :
503 : : static int
504 : 1 : test_tap_link_up_down(int port)
505 : : {
506 : : struct rte_eth_link link;
507 : : int ret;
508 : :
509 : : printf("Testing TAP PMD link up/down for port %d\n", port);
510 : :
511 : : /* Set link down */
512 : 1 : ret = rte_eth_dev_set_link_down(port);
513 [ - + ]: 1 : if (ret != 0) {
514 : 0 : printf("Warning: failed to set link down for port %d: %s\n",
515 : : port, rte_strerror(-ret));
516 : : } else {
517 : 1 : ret = rte_eth_link_get_nowait(port, &link);
518 [ + - ]: 1 : if (ret == 0)
519 : 1 : printf("Link status after set_link_down: %s\n",
520 [ + - ]: 1 : link.link_status ? "up" : "down");
521 : : }
522 : :
523 : : /* Set link up */
524 : 1 : ret = rte_eth_dev_set_link_up(port);
525 [ - + ]: 1 : if (ret != 0) {
526 : 0 : printf("Warning: failed to set link up for port %d: %s\n",
527 : : port, rte_strerror(-ret));
528 : : } else {
529 : 1 : ret = rte_eth_link_get_nowait(port, &link);
530 [ + - ]: 1 : if (ret == 0)
531 : 1 : printf("Link status after set_link_up: %s\n",
532 [ - + ]: 1 : link.link_status ? "up" : "down");
533 : : }
534 : :
535 : 1 : return 0;
536 : : }
537 : :
538 : : static int
539 : 1 : test_tap_dev_stop_start(int port)
540 : : {
541 : : int ret;
542 : :
543 : : printf("Testing TAP PMD device stop/start for port %d\n", port);
544 : :
545 : : /* Stop the device */
546 : 1 : ret = rte_eth_dev_stop(port);
547 [ - + ]: 1 : if (ret != 0) {
548 : 0 : printf("Error: failed to stop port %d: %s\n",
549 : : port, rte_strerror(-ret));
550 : 0 : return -1;
551 : : }
552 : : printf("Device stopped for port %d\n", port);
553 : :
554 : : /* Start the device again */
555 : 1 : ret = rte_eth_dev_start(port);
556 [ - + ]: 1 : if (ret != 0) {
557 : 0 : printf("Error: failed to start port %d: %s\n",
558 : : port, rte_strerror(-ret));
559 : 0 : return -1;
560 : : }
561 : : printf("Device started for port %d\n", port);
562 : :
563 : 1 : return 0;
564 : : }
565 : :
566 : : static int
567 : 1 : test_tap_multi_queue(void)
568 : : {
569 : 1 : struct rte_eth_conf port_conf = { 0 };
570 : : const uint16_t nb_queues = 2;
571 : :
572 : : printf("Testing TAP PMD multi-queue configuration\n");
573 : :
574 : : /* Create a separate mempool for multi-queue test */
575 : : struct rte_mempool *mq_mp
576 : 1 : = rte_pktmbuf_pool_create("tap_mq_pool", NB_MBUF, 32, 0,
577 : 1 : RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
578 [ - + ]: 1 : if (mq_mp == NULL) {
579 : 0 : printf("Warning: failed to create mempool for multi-queue test: %s\n",
580 : : rte_strerror(rte_errno));
581 : 0 : return TEST_SKIPPED;
582 : : }
583 : :
584 : : /* Create a new TAP device for multi-queue test */
585 : 1 : int ret = rte_vdev_init("net_tap_mq", "iface=dtap_mq");
586 [ - + ]: 1 : if (ret < 0) {
587 : 0 : printf("Warning: failed to create multi-queue TAP device: %s\n",
588 : : rte_strerror(-ret));
589 : 0 : rte_mempool_free(mq_mp);
590 : 0 : return TEST_SKIPPED;
591 : : }
592 : :
593 : : /* Find the port */
594 : : uint16_t port;
595 [ + - ]: 3 : RTE_ETH_FOREACH_DEV(port) {
596 : : char name[RTE_ETH_NAME_MAX_LEN];
597 [ + - ]: 3 : if (rte_eth_dev_get_name_by_port(port, name) == 0 &&
598 [ + + ]: 3 : strstr(name, "net_tap_mq"))
599 : 1 : goto found;
600 : : }
601 : :
602 : : printf("Error: could not find multi-queue TAP device\n");
603 : 0 : rte_vdev_uninit("net_tap_mq");
604 : 0 : rte_mempool_free(mq_mp);
605 : 0 : return TEST_FAILED;
606 : :
607 : : found:
608 : : /* Configure with multiple queues */
609 : 1 : ret = rte_eth_dev_configure(port, nb_queues, nb_queues, &port_conf);
610 [ - + ]: 1 : if (ret < 0) {
611 : 0 : printf("Warning: multi-queue configure failed: %s\n",
612 : : rte_strerror(-ret));
613 : 0 : rte_vdev_uninit("net_tap_mq");
614 : 0 : rte_mempool_free(mq_mp);
615 : 0 : return TEST_SKIPPED;
616 : : }
617 : :
618 : : /* Setup TX queues */
619 [ + + ]: 3 : for (uint16_t q = 0; q < nb_queues; q++) {
620 : 2 : ret = rte_eth_tx_queue_setup(port, q, RING_SIZE, SOCKET0, NULL);
621 [ - + ]: 2 : if (ret < 0) {
622 : 0 : printf("Error: TX queue %u setup failed: %s\n",
623 : : q, rte_strerror(-ret));
624 : 0 : rte_vdev_uninit("net_tap_mq");
625 : 0 : rte_mempool_free(mq_mp);
626 : 0 : return TEST_FAILED;
627 : : }
628 : : }
629 : :
630 : : /* Setup RX queues */
631 [ + + ]: 3 : for (uint16_t q = 0; q < nb_queues; q++) {
632 : 2 : ret = rte_eth_rx_queue_setup(port, q, RING_SIZE, SOCKET0, NULL, mq_mp);
633 [ - + ]: 2 : if (ret < 0) {
634 : 0 : printf("Error: RX queue %u setup failed: %s\n",
635 : : q, rte_strerror(-ret));
636 : 0 : rte_vdev_uninit("net_tap_mq");
637 : 0 : rte_mempool_free(mq_mp);
638 : 0 : return TEST_FAILED;
639 : : }
640 : : }
641 : :
642 : 1 : ret = rte_eth_dev_start(port);
643 [ - + ]: 1 : if (ret < 0) {
644 : 0 : printf("Error: failed to start multi-queue port: %s\n",
645 : : rte_strerror(-ret));
646 : 0 : rte_vdev_uninit("net_tap_mq");
647 : 0 : rte_mempool_free(mq_mp);
648 : 0 : return TEST_FAILED;
649 : : }
650 : :
651 : : printf("Multi-queue TAP device configured with %u queues\n", nb_queues);
652 : :
653 : : /* Cleanup */
654 : 1 : ret = rte_eth_dev_stop(port);
655 [ - + ]: 1 : if (ret != 0) {
656 : : printf("Error: rte_eth_dev_stop failed\n");
657 : 0 : return TEST_FAILED;
658 : : }
659 : 1 : rte_eth_dev_close(port);
660 : 1 : rte_vdev_uninit("net_tap_mq");
661 : 1 : rte_mempool_free(mq_mp);
662 : :
663 : 1 : return TEST_SUCCESS;
664 : : }
665 : :
666 : : static void
667 : 1 : test_tap_cleanup(void)
668 : : {
669 : : int ret;
670 : :
671 : : printf("Cleaning up TAP PMD test resources\n");
672 : :
673 [ + - ]: 1 : if (tap_port0 >= 0) {
674 : 1 : ret = rte_eth_dev_stop(tap_port0);
675 [ - + ]: 1 : if (ret != 0)
676 : 0 : printf("Warning: failed to stop port %d: %s\n",
677 : : tap_port0, rte_strerror(-ret));
678 : 1 : rte_eth_dev_close(tap_port0);
679 : : }
680 : :
681 [ + - ]: 1 : if (tap_port1 >= 0) {
682 : 1 : ret = rte_eth_dev_stop(tap_port1);
683 [ - + ]: 1 : if (ret != 0)
684 : 0 : printf("Warning: failed to stop port %d: %s\n",
685 : : tap_port1, rte_strerror(-ret));
686 : 1 : rte_eth_dev_close(tap_port1);
687 : : }
688 : :
689 : 1 : rte_vdev_uninit("net_tap0");
690 : 1 : rte_vdev_uninit("net_tap1");
691 : :
692 [ + - ]: 1 : if (mp != NULL) {
693 : 1 : rte_mempool_free(mp);
694 : 1 : mp = NULL;
695 : : }
696 : :
697 : 1 : tap_port0 = -1;
698 : 1 : tap_port1 = -1;
699 : 1 : }
700 : :
701 : : static int
702 : 1 : test_tap_setup(void)
703 : : {
704 : : int ret;
705 : : uint16_t port_id;
706 : :
707 : : printf("Setting up TAP PMD test\n");
708 : :
709 : : /* Create mempool */
710 : 1 : mp = rte_pktmbuf_pool_create("tap_test_pool", NB_MBUF, 32, 0,
711 : 1 : RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
712 [ - + ]: 1 : if (mp == NULL) {
713 : 0 : printf("Error: failed to create mempool: %s\n",
714 : : rte_strerror(rte_errno));
715 : 0 : return -1;
716 : : }
717 : :
718 : : /* Create first TAP device */
719 : 1 : ret = rte_vdev_init("net_tap0", "iface=dtap_test0");
720 [ - + ]: 1 : if (ret < 0) {
721 : 0 : printf("Error: failed to create TAP device net_tap0: %s\n",
722 : : rte_strerror(-ret));
723 : 0 : rte_mempool_free(mp);
724 : 0 : mp = NULL;
725 : 0 : return TEST_SKIPPED;
726 : : }
727 : :
728 : : /* Create second TAP device */
729 : 1 : ret = rte_vdev_init("net_tap1", "iface=dtap_test1");
730 [ - + ]: 1 : if (ret < 0) {
731 : 0 : printf("Error: failed to create TAP device net_tap1: %s\n",
732 : : rte_strerror(-ret));
733 : 0 : rte_vdev_uninit("net_tap0");
734 : 0 : rte_mempool_free(mp);
735 : 0 : mp = NULL;
736 : 0 : return -1;
737 : : }
738 : :
739 : : /* Find the port IDs */
740 [ + + ]: 3 : RTE_ETH_FOREACH_DEV(port_id) {
741 : : char name[RTE_ETH_NAME_MAX_LEN];
742 [ - + ]: 2 : if (rte_eth_dev_get_name_by_port(port_id, name) != 0)
743 : 0 : continue;
744 : :
745 [ + + ]: 2 : if (strstr(name, "net_tap0"))
746 : 1 : tap_port0 = port_id;
747 [ + - ]: 1 : else if (strstr(name, "net_tap1"))
748 : 1 : tap_port1 = port_id;
749 : : }
750 : :
751 [ + - - + ]: 1 : if (tap_port0 < 0 || tap_port1 < 0) {
752 : : printf("Error: failed to find TAP port IDs\n");
753 : 0 : test_tap_cleanup();
754 : 0 : return -1;
755 : : }
756 : :
757 : : printf("Created TAP devices: tap_port0=%d, tap_port1=%d\n",
758 : : tap_port0, tap_port1);
759 : :
760 : 1 : return 0;
761 : : }
762 : :
763 : : /* Individual test case wrappers */
764 : :
765 : : static int
766 : 1 : test_tap_rx_queue_setup(void)
767 : : {
768 : 1 : struct rte_eth_conf port_conf = { 0 };
769 : : struct rte_mempool *tiny_mp = NULL;
770 : : int port, ret;
771 : : int result = TEST_FAILED;
772 : :
773 : : printf("Testing TAP RX queue setup parameter validation\n");
774 : :
775 : : /* Create a dedicated TAP device for negative tests */
776 : 1 : ret = rte_vdev_init("net_tap_neg", "iface=dtap_neg");
777 [ - + ]: 1 : if (ret < 0) {
778 : 0 : printf("Warning: failed to create TAP device for negative test: %s\n",
779 : : rte_strerror(-ret));
780 : 0 : return TEST_SKIPPED;
781 : : }
782 : :
783 : : /* Find the port */
784 : : uint16_t port_id;
785 : : port = -1;
786 [ + - ]: 3 : RTE_ETH_FOREACH_DEV(port_id) {
787 : : char name[RTE_ETH_NAME_MAX_LEN];
788 [ + - ]: 3 : if (rte_eth_dev_get_name_by_port(port_id, name) == 0 &&
789 [ + + ]: 3 : strstr(name, "net_tap_neg")) {
790 : : port = port_id;
791 : 1 : break;
792 : : }
793 : : }
794 : :
795 [ - + ]: 1 : if (port < 0) {
796 : : printf("Error: could not find negative test TAP device\n");
797 : 0 : goto cleanup;
798 : : }
799 : :
800 : 1 : ret = rte_eth_dev_configure(port, 1, 1, &port_conf);
801 [ - + ]: 1 : if (ret < 0) {
802 : 0 : printf("Error: configure failed for negative test port: %s\n",
803 : : rte_strerror(-ret));
804 : 0 : goto cleanup;
805 : : }
806 : :
807 : : /* TX queue is needed since TAP requires nb_rx == nb_tx */
808 : 1 : ret = rte_eth_tx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL);
809 [ - + ]: 1 : if (ret < 0) {
810 : 0 : printf("Error: TX queue setup failed for negative test port: %s\n",
811 : : rte_strerror(-ret));
812 : 0 : goto cleanup;
813 : : }
814 : :
815 : : /* Test 1: NULL mempool should fail */
816 : : printf(" Test: NULL mempool\n");
817 : 1 : ret = rte_eth_rx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL, NULL);
818 [ - + ]: 1 : if (ret == 0) {
819 : : printf("Error: RX queue setup with NULL mempool should have failed\n");
820 : 0 : goto cleanup;
821 : : }
822 : : printf(" Correctly rejected NULL mempool (ret=%d)\n", ret);
823 : :
824 : : /* Test 2: Invalid queue ID should fail */
825 : : printf(" Test: invalid queue ID\n");
826 : 1 : ret = rte_eth_rx_queue_setup(port, 99, RING_SIZE, SOCKET0, NULL, mp);
827 [ - + ]: 1 : if (ret == 0) {
828 : : printf("Error: RX queue setup with invalid queue ID should have failed\n");
829 : 0 : goto cleanup;
830 : : }
831 : : printf(" Correctly rejected invalid queue ID (ret=%d)\n", ret);
832 : :
833 : : /* Test 3: Mempool with data room too small for headroom should fail.
834 : : * Create a pool where data_room_size equals headroom, leaving
835 : : * zero usable space in the first segment.
836 : : */
837 : : printf(" Test: mempool with no usable data room\n");
838 : 1 : tiny_mp = rte_pktmbuf_pool_create("tap_tiny_pool", 64, 0, 0,
839 : 1 : RTE_PKTMBUF_HEADROOM, rte_socket_id());
840 [ - + ]: 1 : if (tiny_mp == NULL) {
841 : 0 : printf("Warning: could not create tiny mempool: %s\n",
842 : : rte_strerror(rte_errno));
843 : : /* Can still pass on the tests above */
844 : : printf(" Skipping tiny mempool test\n");
845 : : } else {
846 : 1 : ret = rte_eth_rx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL, tiny_mp);
847 [ - + ]: 1 : if (ret == 0) {
848 : : printf("Error: RX queue setup with tiny mempool should have failed\n");
849 : 0 : goto cleanup;
850 : : }
851 : : printf(" Correctly rejected tiny mempool (ret=%d)\n", ret);
852 : : }
853 : :
854 : : /* Test 4: Valid setup should succeed after all the negative tests */
855 : : printf(" Test: valid setup after negative tests\n");
856 : 1 : ret = rte_eth_rx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL, mp);
857 [ - + ]: 1 : if (ret < 0) {
858 : 0 : printf("Error: valid RX queue setup failed after negative tests: %s\n",
859 : : rte_strerror(-ret));
860 : 0 : goto cleanup;
861 : : }
862 : : printf(" Valid setup succeeded\n");
863 : :
864 : : result = TEST_SUCCESS;
865 : :
866 : 1 : cleanup:
867 : 1 : rte_eth_dev_close(port);
868 : 1 : rte_vdev_uninit("net_tap_neg");
869 : 1 : rte_mempool_free(tiny_mp);
870 : :
871 : 1 : return result;
872 : : }
873 : :
874 : : static int
875 : 1 : test_tap_configure_port0(void)
876 : : {
877 : 1 : return test_tap_ethdev_configure(tap_port0) == 0 ?
878 [ - + ]: 1 : TEST_SUCCESS : TEST_FAILED;
879 : : }
880 : :
881 : : static int
882 : 1 : test_tap_configure_port1(void)
883 : : {
884 : 1 : return test_tap_ethdev_configure(tap_port1) == 0 ?
885 [ - + ]: 1 : TEST_SUCCESS : TEST_FAILED;
886 : : }
887 : :
888 : : static int
889 : 1 : test_tap_packet_send_receive(void)
890 : : {
891 : 1 : return test_tap_send_receive();
892 : : }
893 : :
894 : : static int
895 : 1 : test_tap_get_stats(void)
896 : : {
897 [ + - ]: 1 : if (test_tap_stats_get(tap_port0) != 0)
898 : : return TEST_FAILED;
899 [ - + ]: 1 : if (test_tap_stats_get(tap_port1) != 0)
900 : 0 : return TEST_FAILED;
901 : : return TEST_SUCCESS;
902 : : }
903 : :
904 : : static int
905 : 1 : test_tap_reset_stats(void)
906 : : {
907 [ + - ]: 1 : if (test_tap_stats_reset(tap_port0) != 0)
908 : : return TEST_FAILED;
909 [ - + ]: 1 : if (test_tap_stats_reset(tap_port1) != 0)
910 : 0 : return TEST_FAILED;
911 : : return TEST_SUCCESS;
912 : : }
913 : :
914 : : static int
915 : 1 : test_tap_get_link_status(void)
916 : : {
917 [ + - ]: 1 : if (test_tap_link_status(tap_port0) != 0)
918 : : return TEST_FAILED;
919 [ - + ]: 1 : if (test_tap_link_status(tap_port1) != 0)
920 : 0 : return TEST_FAILED;
921 : : return TEST_SUCCESS;
922 : : }
923 : :
924 : : static int
925 : 1 : test_tap_get_dev_info(void)
926 : : {
927 [ + - ]: 1 : if (test_tap_dev_info(tap_port0) != 0)
928 : : return TEST_FAILED;
929 [ - + ]: 1 : if (test_tap_dev_info(tap_port1) != 0)
930 : 0 : return TEST_FAILED;
931 : : return TEST_SUCCESS;
932 : : }
933 : :
934 : : static int
935 : 1 : test_tap_mtu_ops(void)
936 : : {
937 [ - + ]: 1 : return test_tap_mtu(tap_port0) == 0 ? TEST_SUCCESS : TEST_FAILED;
938 : : }
939 : :
940 : : static int
941 : 1 : test_tap_mac_addr_get(void)
942 : : {
943 [ + - ]: 1 : if (test_tap_mac_addr(tap_port0) != 0)
944 : : return TEST_FAILED;
945 [ - + ]: 1 : if (test_tap_mac_addr(tap_port1) != 0)
946 : 0 : return TEST_FAILED;
947 : : return TEST_SUCCESS;
948 : : }
949 : :
950 : : static int
951 : 1 : test_tap_promisc_mode(void)
952 : : {
953 [ - + ]: 1 : return test_tap_promiscuous(tap_port0) == 0 ? TEST_SUCCESS : TEST_FAILED;
954 : : }
955 : :
956 : : static int
957 : 1 : test_tap_allmulti_mode(void)
958 : : {
959 [ - + ]: 1 : return test_tap_allmulti(tap_port0) == 0 ? TEST_SUCCESS : TEST_FAILED;
960 : : }
961 : :
962 : : static int
963 : 1 : test_tap_queue_ops(void)
964 : : {
965 : 1 : return test_tap_queue_start_stop(tap_port0) == 0 ?
966 [ - + ]: 1 : TEST_SUCCESS : TEST_FAILED;
967 : : }
968 : :
969 : : static int
970 : 1 : test_tap_link_ops(void)
971 : : {
972 [ - + ]: 1 : return test_tap_link_up_down(tap_port0) == 0 ? TEST_SUCCESS : TEST_FAILED;
973 : : }
974 : :
975 : : static int
976 : 1 : test_tap_stop_start(void)
977 : : {
978 : 1 : return test_tap_dev_stop_start(tap_port0) == 0 ?
979 [ - + ]: 1 : TEST_SUCCESS : TEST_FAILED;
980 : : }
981 : :
982 : : static int
983 : 1 : test_tap_multiqueue(void)
984 : : {
985 : 1 : return test_tap_multi_queue();
986 : : }
987 : :
988 : : static int
989 : 1 : test_tap_tx_burst(void)
990 : : {
991 : : struct rte_eth_stats stats_before, stats_after;
992 : : struct rte_mbuf *mbuf;
993 : : uint16_t nb_tx;
994 : : int ret;
995 : :
996 : : printf("Testing TAP TX burst with invalid mbuf chains\n");
997 : :
998 : 1 : ret = rte_eth_stats_reset(tap_port0);
999 [ - + ]: 1 : if (ret != 0) {
1000 : 0 : printf("Error: stats reset failed: %s\n", rte_strerror(-ret));
1001 : 0 : return TEST_FAILED;
1002 : : }
1003 : :
1004 : : /* Test 1: Oversized packet should be rejected.
1005 : : * Create a single-segment packet larger than MTU + L2 overhead.
1006 : : */
1007 : : printf(" Test: oversized packet\n");
1008 : 1 : mbuf = rte_pktmbuf_alloc(mp);
1009 [ - + ]: 1 : if (mbuf == NULL) {
1010 : : printf("Error: mbuf alloc failed\n");
1011 : 0 : return TEST_FAILED;
1012 : : }
1013 : :
1014 : : /* Fill with data exceeding default MTU (1500) + headers */
1015 [ - + ]: 1 : if (rte_pktmbuf_append(mbuf, 1600) == NULL) {
1016 : : printf("Error: pktmbuf_append failed\n");
1017 : 0 : rte_pktmbuf_free(mbuf);
1018 : 0 : return TEST_FAILED;
1019 : : }
1020 : 1 : memset(rte_pktmbuf_mtod(mbuf, void *), 0, 1600);
1021 : :
1022 : 1 : rte_eth_stats_get(tap_port0, &stats_before);
1023 : 1 : nb_tx = rte_eth_tx_burst(tap_port0, 0, &mbuf, 1);
1024 : 1 : rte_eth_stats_get(tap_port0, &stats_after);
1025 : :
1026 [ - + ]: 1 : if (nb_tx != 0) {
1027 : 0 : printf("Error: oversized packet was accepted (nb_tx=%u)\n", nb_tx);
1028 : 0 : return TEST_FAILED;
1029 : : }
1030 : 1 : rte_pktmbuf_free(mbuf);
1031 : :
1032 [ - + ]: 1 : if (stats_after.oerrors <= stats_before.oerrors) {
1033 : : printf("Error: oerrors not incremented for oversized packet\n");
1034 : 0 : return TEST_FAILED;
1035 : : }
1036 : : printf(" Correctly rejected oversized packet\n");
1037 : :
1038 : : /* Test 2: mbuf chain with nb_segs >= IOV_MAX.
1039 : : * The tun_pi header takes one iovec slot, so a chain with
1040 : : * IOV_MAX segments requires IOV_MAX + 1 iovecs total,
1041 : : * which must be rejected by tap_write_mbufs.
1042 : : */
1043 : : printf(" Test: mbuf chain with nb_segs >= IOV_MAX (%d)\n", IOV_MAX);
1044 : :
1045 : 1 : struct rte_mbuf *head = rte_pktmbuf_alloc(mp);
1046 [ - + ]: 1 : if (head == NULL) {
1047 : : printf("Error: head mbuf alloc failed\n");
1048 : 0 : return TEST_FAILED;
1049 : : }
1050 : :
1051 : : struct rte_ether_hdr *eth = (struct rte_ether_hdr *)
1052 : : rte_pktmbuf_append(head, RTE_ETHER_MIN_LEN);
1053 [ - + ]: 1 : if (eth == NULL) {
1054 : : printf("Error: append to head failed\n");
1055 : 0 : rte_pktmbuf_free(head);
1056 : 0 : return TEST_FAILED;
1057 : : }
1058 : : memset(eth, 0, RTE_ETHER_MIN_LEN);
1059 : 1 : eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
1060 : :
1061 : : int nsegs;
1062 [ + + ]: 1024 : for (nsegs = 1; nsegs < IOV_MAX; nsegs++) {
1063 : 1023 : struct rte_mbuf *seg = rte_pktmbuf_alloc(mp);
1064 [ - + ]: 1023 : if (seg == NULL) {
1065 : : printf("Warning: could only allocate %d of %d segments\n",
1066 : : nsegs, IOV_MAX);
1067 : : break;
1068 : : }
1069 [ - + ]: 1023 : if (rte_pktmbuf_append(seg, 1) == NULL) {
1070 : 0 : rte_pktmbuf_free(seg);
1071 : 0 : break;
1072 : : }
1073 : :
1074 [ + - ]: 1023 : ret = rte_pktmbuf_chain(head, seg);
1075 : : if (ret != 0) {
1076 : 0 : rte_pktmbuf_free(seg);
1077 : 0 : break;
1078 : : }
1079 : : }
1080 : :
1081 [ - + ]: 1 : if (head->nb_segs < IOV_MAX) {
1082 : 0 : printf("Warning: only built %u segments, need %d to test IOV_MAX\n",
1083 : : head->nb_segs, IOV_MAX);
1084 : 0 : rte_pktmbuf_free(head);
1085 : : printf(" Skipping IOV_MAX test (insufficient mbufs)\n");
1086 : 0 : return TEST_SUCCESS;
1087 : : }
1088 : :
1089 : 1 : printf(" Built chain with %u segments\n", head->nb_segs);
1090 : :
1091 : 1 : rte_eth_stats_get(tap_port0, &stats_before);
1092 : 1 : nb_tx = rte_eth_tx_burst(tap_port0, 0, &head, 1);
1093 : 1 : rte_eth_stats_get(tap_port0, &stats_after);
1094 : :
1095 [ - + ]: 1 : if (nb_tx != 0) {
1096 : 0 : printf("Error: chain with %u segments (>= IOV_MAX) should be rejected\n",
1097 : 0 : head->nb_segs);
1098 : 0 : return TEST_FAILED;
1099 : : }
1100 : 1 : rte_pktmbuf_free(head);
1101 : :
1102 [ - + ]: 1 : if (stats_after.oerrors <= stats_before.oerrors) {
1103 : : printf("Error: oerrors not incremented for IOV_MAX chain\n");
1104 : 0 : return TEST_FAILED;
1105 : : }
1106 : : printf(" Correctly rejected chain with nb_segs >= IOV_MAX\n");
1107 : :
1108 : : printf(" TX validation tests passed\n");
1109 : 1 : return TEST_SUCCESS;
1110 : : }
1111 : :
1112 : : /*
1113 : : * Inject a raw Ethernet frame via an AF_PACKET socket bound to the TAP's
1114 : : * Linux interface and attempt to receive it with rte_eth_rx_burst.
1115 : : */
1116 : : static int
1117 : 9 : tap_inject_packet(const char *ifname, const struct rte_ether_addr *dst)
1118 : : {
1119 : : uint8_t frame[RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN];
1120 : : union {
1121 : : struct sockaddr_ll sll;
1122 : : struct sockaddr sock;
1123 : : } addr;
1124 : : struct rte_ether_hdr *eth;
1125 : : unsigned int ifindex;
1126 : : int fd, ret;
1127 : :
1128 : 9 : ifindex = if_nametoindex(ifname);
1129 [ - + ]: 9 : TEST_ASSERT(ifindex != 0, "if_nametoindex(%s) failed", ifname);
1130 : :
1131 : 9 : fd = socket(AF_PACKET, SOCK_RAW, RTE_BE16(RTE_ETHER_TYPE_IPV4));
1132 [ - + ]: 9 : TEST_ASSERT(fd >= 0, "AF_PACKET socket failed: %s", strerror(errno));
1133 : :
1134 : : memset(&addr, 0, sizeof(addr));
1135 : 9 : addr.sll.sll_family = AF_PACKET;
1136 : 9 : addr.sll.sll_ifindex = ifindex;
1137 : 9 : addr.sll.sll_protocol = RTE_BE16(RTE_ETHER_TYPE_IPV4);
1138 : :
1139 : 9 : ret = bind(fd, &addr.sock, sizeof(addr.sll));
1140 [ - + ]: 9 : TEST_ASSERT(ret == 0, "bind to %s failed: %s", ifname, strerror(errno));
1141 : :
1142 : : memset(frame, 0, sizeof(frame));
1143 : : eth = (struct rte_ether_hdr *)frame;
1144 : 9 : eth->dst_addr = *dst;
1145 : 9 : eth->src_addr = (struct rte_ether_addr){{0x02, 0, 0, 0xbb, 0, 0xaa}};
1146 : 9 : eth->ether_type = RTE_BE16(RTE_ETHER_TYPE_IPV4);
1147 : :
1148 : 9 : ret = send(fd, frame, sizeof(frame), 0);
1149 [ - + ]: 9 : TEST_ASSERT(ret == sizeof(frame), "send failed: %s", strerror(errno));
1150 : :
1151 : 9 : close(fd);
1152 : 9 : return 0;
1153 : : }
1154 : :
1155 : : static uint16_t
1156 : 10 : tap_drain_rx(int port, const struct rte_ether_addr *dst)
1157 : : {
1158 : : struct rte_mbuf *mbufs[MAX_PKT_BURST];
1159 : : struct rte_ether_hdr *eth;
1160 : : uint16_t total = 0;
1161 : :
1162 : : /* drain the rxq 10 times to ensure the kernel has sent the packet */
1163 [ + + ]: 110 : for (uint16_t i = 0; i < 10; i++) {
1164 : 100 : uint16_t nb_rx = rte_eth_rx_burst(port, 0, mbufs, MAX_PKT_BURST);
1165 [ + + ]: 106 : for (uint16_t j = 0; j < nb_rx; j++) {
1166 : 6 : eth = rte_pktmbuf_mtod(mbufs[j], struct rte_ether_hdr *);
1167 [ + - + - ]: 6 : if (dst != NULL && rte_is_same_ether_addr(ð->dst_addr, dst))
1168 : 6 : total += 1;
1169 : 6 : rte_pktmbuf_free(mbufs[j]);
1170 : : }
1171 : 100 : usleep(1000);
1172 : : }
1173 : :
1174 : 10 : return total;
1175 : : }
1176 : :
1177 : : static int
1178 : 1 : test_tap_mac_filter(void)
1179 : : {
1180 : 1 : struct rte_ether_addr bcast_mac = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
1181 : 1 : struct rte_ether_addr mcast_mac = {{0x01, 0, 0x5e, 0, 0, 0x01}};
1182 : 1 : struct rte_ether_addr foreign_mac = {{0x02, 0, 0, 0, 0, 0xaa}};
1183 : : static const char *ifname = "dtap_test0";
1184 : : struct rte_ether_addr port_mac;
1185 : : uint16_t nb_rx;
1186 : : int ret;
1187 : :
1188 : : printf("Testing TAP MAC address filtering\n");
1189 : :
1190 : 1 : ret = rte_eth_macaddr_get(tap_port0, &port_mac);
1191 [ - + ]: 1 : TEST_ASSERT(ret == 0, "failed to get MAC for port %d", tap_port0);
1192 : :
1193 : : /* Disable promisc so MAC filter is active */
1194 : 1 : rte_eth_promiscuous_disable(tap_port0);
1195 : 1 : rte_eth_allmulticast_disable(tap_port0);
1196 : :
1197 : : /* Drain any stale packets */
1198 : 1 : tap_drain_rx(tap_port0, NULL);
1199 : :
1200 : : /* Test 1: packet to port's own MAC should be received */
1201 : : printf(" Test: unicast to own MAC\n");
1202 : 1 : ret = tap_inject_packet(ifname, &port_mac);
1203 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1204 : 1 : nb_rx = tap_drain_rx(tap_port0, &port_mac);
1205 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "packet to own MAC was not received");
1206 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1207 : :
1208 : : /* Test 2: packet to foreign unicast MAC should be dropped */
1209 : : printf(" Test: unicast to foreign MAC\n");
1210 : 1 : ret = tap_inject_packet(ifname, &foreign_mac);
1211 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1212 : 1 : nb_rx = tap_drain_rx(tap_port0, &foreign_mac);
1213 [ - + ]: 1 : TEST_ASSERT(nb_rx == 0, "packet to foreign MAC was not dropped");
1214 : : printf(" Dropped - OK\n");
1215 : :
1216 : : /* Test 3: broadcast should always be received */
1217 : : printf(" Test: broadcast\n");
1218 : 1 : ret = tap_inject_packet(ifname, &bcast_mac);
1219 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1220 : 1 : nb_rx = tap_drain_rx(tap_port0, &bcast_mac);
1221 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "broadcast packet was not received");
1222 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1223 : :
1224 : : /* Test 4: promisc mode should bypass the filter */
1225 : : printf(" Test: promisc receives foreign MAC\n");
1226 : 1 : rte_eth_promiscuous_enable(tap_port0);
1227 : 1 : ret = tap_inject_packet(ifname, &foreign_mac);
1228 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1229 : 1 : nb_rx = tap_drain_rx(tap_port0, &foreign_mac);
1230 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "promisc mode did not receive foreign MAC");
1231 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1232 : 1 : rte_eth_promiscuous_disable(tap_port0);
1233 : :
1234 : : /* Test 5: multicast without allmulti and without mc list should drop */
1235 : : printf(" Test: multicast dropped without mc list\n");
1236 : 1 : ret = tap_inject_packet(ifname, &mcast_mac);
1237 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1238 : 1 : nb_rx = tap_drain_rx(tap_port0, &mcast_mac);
1239 [ - + ]: 1 : TEST_ASSERT(nb_rx == 0, "multicast was not dropped");
1240 : : printf(" Dropped - OK\n");
1241 : :
1242 : : /* Test 6: multicast with matching mc list should be received */
1243 : : printf(" Test: multicast received with mc list\n");
1244 : 1 : ret = rte_eth_dev_set_mc_addr_list(tap_port0, &mcast_mac, 1);
1245 [ - + ]: 1 : TEST_ASSERT(ret == 0, "set_mc_addr_list failed: %s", rte_strerror(-ret));
1246 : 1 : ret = tap_inject_packet(ifname, &mcast_mac);
1247 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1248 : 1 : nb_rx = tap_drain_rx(tap_port0, &mcast_mac);
1249 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "multicast with matching mc list was not received");
1250 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1251 : 1 : rte_eth_dev_set_mc_addr_list(tap_port0, NULL, 0);
1252 : :
1253 : : /* Test 7: allmulti should receive any multicast */
1254 : : printf(" Test: allmulti receives multicast\n");
1255 : 1 : rte_eth_allmulticast_enable(tap_port0);
1256 : 1 : ret = tap_inject_packet(ifname, &mcast_mac);
1257 [ + - ]: 1 : if (ret < 0)
1258 : : return TEST_FAILED;
1259 : 1 : nb_rx = tap_drain_rx(tap_port0, &mcast_mac);
1260 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "allmulti did not receive multicast");
1261 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1262 : 1 : rte_eth_allmulticast_disable(tap_port0);
1263 : :
1264 : : /* Test 8: secondary unicast MAC via mac_addr_add */
1265 : : printf(" Test: secondary unicast MAC\n");
1266 : 1 : ret = rte_eth_dev_mac_addr_add(tap_port0, &foreign_mac, 0);
1267 [ - + ]: 1 : TEST_ASSERT(ret == 0, "mac_addr_add failed: %s", rte_strerror(-ret));
1268 : 1 : ret = tap_inject_packet(ifname, &foreign_mac);
1269 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1270 : 1 : nb_rx = tap_drain_rx(tap_port0, &foreign_mac);
1271 [ - + ]: 1 : TEST_ASSERT(nb_rx > 0, "packet to added MAC was not received");
1272 : 1 : printf(" Received %u packet(s) - OK\n", nb_rx);
1273 : :
1274 : : /* Remove and verify it's dropped again */
1275 : 1 : rte_eth_dev_mac_addr_remove(tap_port0, &foreign_mac);
1276 : 1 : ret = tap_inject_packet(ifname, &foreign_mac);
1277 [ - + ]: 1 : TEST_ASSERT(ret == 0, "packet inject failed");
1278 : 1 : nb_rx = tap_drain_rx(tap_port0, &foreign_mac);
1279 [ - + ]: 1 : TEST_ASSERT(nb_rx == 0, "packet to removed MAC was not dropped");
1280 : : printf(" Dropped after remove - OK\n");
1281 : :
1282 : : /* Restore promisc (default state) */
1283 : 1 : rte_eth_promiscuous_enable(tap_port0);
1284 : :
1285 : 1 : return TEST_SUCCESS;
1286 : : }
1287 : :
1288 : : static struct unit_test_suite test_pmd_tap_suite = {
1289 : : .setup = test_tap_setup,
1290 : : .teardown = test_tap_cleanup,
1291 : : .suite_name = "TAP PMD Unit Test Suite",
1292 : : .unit_test_cases = {
1293 : : TEST_CASE(test_tap_configure_port0),
1294 : : TEST_CASE(test_tap_configure_port1),
1295 : : TEST_CASE(test_tap_get_dev_info),
1296 : : TEST_CASE(test_tap_get_link_status),
1297 : : TEST_CASE(test_tap_mac_addr_get),
1298 : : TEST_CASE(test_tap_get_stats),
1299 : : TEST_CASE(test_tap_reset_stats),
1300 : : TEST_CASE(test_tap_packet_send_receive),
1301 : : TEST_CASE(test_tap_promisc_mode),
1302 : : TEST_CASE(test_tap_allmulti_mode),
1303 : : TEST_CASE(test_tap_mtu_ops),
1304 : : TEST_CASE(test_tap_queue_ops),
1305 : : TEST_CASE(test_tap_link_ops),
1306 : : TEST_CASE(test_tap_stop_start),
1307 : : TEST_CASE(test_tap_multiqueue),
1308 : : TEST_CASE(test_tap_rx_queue_setup),
1309 : : TEST_CASE(test_tap_tx_burst),
1310 : : TEST_CASE(test_tap_mac_filter),
1311 : : TEST_CASES_END()
1312 : : }
1313 : : };
1314 : :
1315 : : static int
1316 : 1 : test_pmd_tap(void)
1317 : : {
1318 : 1 : return unit_test_suite_runner(&test_pmd_tap_suite);
1319 : : }
1320 : :
1321 : : #endif /* RTE_EXEC_ENV_LINUX */
1322 : :
1323 : 276 : REGISTER_FAST_TEST(tap_pmd_autotest, NOHUGE_OK, ASAN_OK, test_pmd_tap);
|