1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2020 Intel Corporation. */
5 * Some functions in this program are taken from
6 * Linux kernel samples/bpf/xdpsock* and modified
9 * See test_xsk.sh for detailed information on test topology
10 * and prerequisite network setup.
12 * This test program contains two threads, each thread is single socket with
13 * a unique UMEM. It validates in-order packet delivery and packet content
14 * by sending packets to each other.
18 * These selftests test AF_XDP SKB and Native/DRV modes using veth
19 * Virtual Ethernet interfaces.
21 * The following tests are run:
24 * Generic mode XDP is driver independent, used when the driver does
25 * not have support for XDP. Works on any netdevice using sockets and
26 * generic XDP path. XDP hook from netif_receive_skb().
27 * a. nopoll - soft-irq processing
28 * b. poll - using poll() syscall
30 * Create a Tx and a Rx socket, Tx from one socket, Rx on another. Destroy
31 * both sockets, then repeat multiple times. Only nopoll mode is used
32 * d. Bi-directional sockets
33 * Configure sockets as bi-directional tx/rx sockets, sets up fill and
34 * completion rings on each socket, tx/rx in both directions. Only nopoll
37 * 2. AF_XDP DRV/Native mode
38 * Works on any netdevice with XDP_REDIRECT support, driver dependent. Processes
39 * packets before SKB allocation. Provides better performance than SKB. Driver
40 * hook available just after DMA of buffer descriptor.
44 * d. Bi-directional sockets
45 * - Only copy mode is supported because veth does not currently support
52 * - Single process spawns two threads: Tx and Rx
53 * - Each of these two threads attach to a veth interface within their assigned
55 * - Each thread Creates one AF_XDP socket connected to a unique umem for each
57 * - Tx thread Transmits 10k packets from veth<xxxx> to veth<yyyy>
58 * - Rx thread verifies if all 10k packets were received and delivered in-order,
59 * and have the right content
61 * Enable/disable debug mode:
62 * --------------------------
63 * To enable L2 - L4 headers and payload dump of each packet on STDOUT, add
64 * parameter -D to params array in test_xsk.sh, i.e. params=("-S" "-D")
71 #include <asm/barrier.h>
72 typedef __u16 __sum16
;
73 #include <linux/if_link.h>
74 #include <linux/if_ether.h>
76 #include <linux/udp.h>
77 #include <arpa/inet.h>
89 #include <sys/resource.h>
90 #include <sys/types.h>
91 #include <sys/queue.h>
94 #include <stdatomic.h>
96 #include "xdpxceiver.h"
97 #include "../kselftest.h"
99 static void __exit_with_error(int error
, const char *file
, const char *func
, int line
)
101 ksft_test_result_fail
102 ("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file
, func
, line
, error
, strerror(error
));
106 #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
108 #define print_ksft_result(void)\
109 (ksft_test_result_pass("PASS: %s %s %s%s\n", uut ? "DRV" : "SKB", opt_poll ? "POLL" :\
110 "NOPOLL", opt_teardown ? "Socket Teardown" : "",\
111 opt_bidi ? "Bi-directional Sockets" : ""))
113 static void pthread_init_mutex(void)
115 pthread_mutex_init(&sync_mutex
, NULL
);
116 pthread_mutex_init(&sync_mutex_tx
, NULL
);
117 pthread_cond_init(&signal_rx_condition
, NULL
);
118 pthread_cond_init(&signal_tx_condition
, NULL
);
121 static void pthread_destroy_mutex(void)
123 pthread_mutex_destroy(&sync_mutex
);
124 pthread_mutex_destroy(&sync_mutex_tx
);
125 pthread_cond_destroy(&signal_rx_condition
);
126 pthread_cond_destroy(&signal_tx_condition
);
129 static void *memset32_htonl(void *dest
, u32 val
, u32 size
)
131 u32
*ptr
= (u32
*)dest
;
136 for (i
= 0; i
< (size
& (~0x3)); i
+= 4)
139 for (; i
< size
; i
++)
140 ((char *)dest
)[i
] = ((char *)&val
)[i
& 3];
146 * This function code has been taken from
147 * Linux kernel lib/checksum.c
149 static inline unsigned short from32to16(unsigned int x
)
151 /* add up 16-bit and 16-bit for 16+c bit */
152 x
= (x
& 0xffff) + (x
>> 16);
154 x
= (x
& 0xffff) + (x
>> 16);
159 * Fold a partial checksum
160 * This function code has been taken from
161 * Linux kernel include/asm-generic/checksum.h
163 static inline __u16
csum_fold(__u32 csum
)
165 u32 sum
= (__force u32
)csum
;
167 sum
= (sum
& 0xffff) + (sum
>> 16);
168 sum
= (sum
& 0xffff) + (sum
>> 16);
169 return (__force __u16
)~sum
;
173 * This function code has been taken from
174 * Linux kernel lib/checksum.c
176 static inline u32
from64to32(u64 x
)
178 /* add up 32-bit and 32-bit for 32+c bit */
179 x
= (x
& 0xffffffff) + (x
>> 32);
181 x
= (x
& 0xffffffff) + (x
>> 32);
185 __u32
csum_tcpudp_nofold(__be32 saddr
, __be32 daddr
, __u32 len
, __u8 proto
, __u32 sum
);
188 * This function code has been taken from
189 * Linux kernel lib/checksum.c
191 __u32
csum_tcpudp_nofold(__be32 saddr
, __be32 daddr
, __u32 len
, __u8 proto
, __u32 sum
)
193 unsigned long long s
= (__force u32
)sum
;
195 s
+= (__force u32
)saddr
;
196 s
+= (__force u32
)daddr
;
197 #ifdef __BIG_ENDIAN__
200 s
+= (proto
+ len
) << 8;
202 return (__force __u32
)from64to32(s
);
206 * This function has been taken from
207 * Linux kernel include/asm-generic/checksum.h
210 csum_tcpudp_magic(__be32 saddr
, __be32 daddr
, __u32 len
, __u8 proto
, __u32 sum
)
212 return csum_fold(csum_tcpudp_nofold(saddr
, daddr
, len
, proto
, sum
));
215 static inline u16
udp_csum(u32 saddr
, u32 daddr
, u32 len
, u8 proto
, u16
*udp_pkt
)
220 /* udp hdr and data */
221 for (; cnt
< len
; cnt
+= 2)
222 csum
+= udp_pkt
[cnt
>> 1];
224 return csum_tcpudp_magic(saddr
, daddr
, len
, proto
, csum
);
227 static void gen_eth_hdr(void *data
, struct ethhdr
*eth_hdr
)
229 memcpy(eth_hdr
->h_dest
, ((struct ifobject
*)data
)->dst_mac
, ETH_ALEN
);
230 memcpy(eth_hdr
->h_source
, ((struct ifobject
*)data
)->src_mac
, ETH_ALEN
);
231 eth_hdr
->h_proto
= htons(ETH_P_IP
);
234 static void gen_ip_hdr(void *data
, struct iphdr
*ip_hdr
)
236 ip_hdr
->version
= IP_PKT_VER
;
238 ip_hdr
->tos
= IP_PKT_TOS
;
239 ip_hdr
->tot_len
= htons(IP_PKT_SIZE
);
241 ip_hdr
->frag_off
= 0;
242 ip_hdr
->ttl
= IPDEFTTL
;
243 ip_hdr
->protocol
= IPPROTO_UDP
;
244 ip_hdr
->saddr
= ((struct ifobject
*)data
)->src_ip
;
245 ip_hdr
->daddr
= ((struct ifobject
*)data
)->dst_ip
;
249 static void gen_udp_hdr(void *data
, void *arg
, struct udphdr
*udp_hdr
)
251 udp_hdr
->source
= htons(((struct ifobject
*)arg
)->src_port
);
252 udp_hdr
->dest
= htons(((struct ifobject
*)arg
)->dst_port
);
253 udp_hdr
->len
= htons(UDP_PKT_SIZE
);
254 memset32_htonl(pkt_data
+ PKT_HDR_SIZE
,
255 htonl(((struct generic_data
*)data
)->seqnum
), UDP_PKT_DATA_SIZE
);
258 static void gen_udp_csum(struct udphdr
*udp_hdr
, struct iphdr
*ip_hdr
)
262 udp_csum(ip_hdr
->saddr
, ip_hdr
->daddr
, UDP_PKT_SIZE
, IPPROTO_UDP
, (u16
*)udp_hdr
);
265 static void gen_eth_frame(struct xsk_umem_info
*umem
, u64 addr
)
267 memcpy(xsk_umem__get_data(umem
->buffer
, addr
), pkt_data
, PKT_SIZE
);
270 static void xsk_configure_umem(struct ifobject
*data
, void *buffer
, u64 size
)
274 data
->umem
= calloc(1, sizeof(struct xsk_umem_info
));
276 exit_with_error(errno
);
278 ret
= xsk_umem__create(&data
->umem
->umem
, buffer
, size
,
279 &data
->umem
->fq
, &data
->umem
->cq
, NULL
);
281 exit_with_error(ret
);
283 data
->umem
->buffer
= buffer
;
286 static void xsk_populate_fill_ring(struct xsk_umem_info
*umem
)
291 ret
= xsk_ring_prod__reserve(&umem
->fq
, XSK_RING_PROD__DEFAULT_NUM_DESCS
, &idx
);
292 if (ret
!= XSK_RING_PROD__DEFAULT_NUM_DESCS
)
293 exit_with_error(ret
);
294 for (i
= 0; i
< XSK_RING_PROD__DEFAULT_NUM_DESCS
; i
++)
295 *xsk_ring_prod__fill_addr(&umem
->fq
, idx
++) = i
* XSK_UMEM__DEFAULT_FRAME_SIZE
;
296 xsk_ring_prod__submit(&umem
->fq
, XSK_RING_PROD__DEFAULT_NUM_DESCS
);
299 static int xsk_configure_socket(struct ifobject
*ifobject
)
301 struct xsk_socket_config cfg
;
302 struct xsk_ring_cons
*rxr
;
303 struct xsk_ring_prod
*txr
;
306 ifobject
->xsk
= calloc(1, sizeof(struct xsk_socket_info
));
308 exit_with_error(errno
);
310 ifobject
->xsk
->umem
= ifobject
->umem
;
311 cfg
.rx_size
= XSK_RING_CONS__DEFAULT_NUM_DESCS
;
312 cfg
.tx_size
= XSK_RING_PROD__DEFAULT_NUM_DESCS
;
313 cfg
.libbpf_flags
= 0;
314 cfg
.xdp_flags
= opt_xdp_flags
;
315 cfg
.bind_flags
= opt_xdp_bind_flags
;
318 rxr
= (ifobject
->fv
.vector
== rx
) ? &ifobject
->xsk
->rx
: NULL
;
319 txr
= (ifobject
->fv
.vector
== tx
) ? &ifobject
->xsk
->tx
: NULL
;
321 rxr
= &ifobject
->xsk
->rx
;
322 txr
= &ifobject
->xsk
->tx
;
325 ret
= xsk_socket__create(&ifobject
->xsk
->xsk
, ifobject
->ifname
,
326 opt_queue
, ifobject
->umem
->umem
, rxr
, txr
, &cfg
);
334 static struct option long_options
[] = {
335 {"interface", required_argument
, 0, 'i'},
336 {"queue", optional_argument
, 0, 'q'},
337 {"poll", no_argument
, 0, 'p'},
338 {"xdp-skb", no_argument
, 0, 'S'},
339 {"xdp-native", no_argument
, 0, 'N'},
340 {"copy", no_argument
, 0, 'c'},
341 {"tear-down", no_argument
, 0, 'T'},
342 {"bidi", optional_argument
, 0, 'B'},
343 {"debug", optional_argument
, 0, 'D'},
344 {"tx-pkt-count", optional_argument
, 0, 'C'},
348 static void usage(const char *prog
)
351 " Usage: %s [OPTIONS]\n"
353 " -i, --interface Use interface\n"
354 " -q, --queue=n Use queue n (default 0)\n"
355 " -p, --poll Use poll syscall\n"
356 " -S, --xdp-skb=n Use XDP SKB mode\n"
357 " -N, --xdp-native=n Enforce XDP DRV (native) mode\n"
358 " -c, --copy Force copy mode\n"
359 " -T, --tear-down Tear down sockets by repeatedly recreating them\n"
360 " -B, --bidi Bi-directional sockets test\n"
361 " -D, --debug Debug mode - dump packets L2 - L5\n"
362 " -C, --tx-pkt-count=n Number of packets to send\n";
363 ksft_print_msg(str
, prog
);
366 static bool switch_namespace(int idx
)
368 char fqns
[26] = "/var/run/netns/";
371 strncat(fqns
, ifdict
[idx
]->nsname
, sizeof(fqns
) - strlen(fqns
) - 1);
372 nsfd
= open(fqns
, O_RDONLY
);
375 exit_with_error(errno
);
377 if (setns(nsfd
, 0) == -1)
378 exit_with_error(errno
);
383 static void *nsswitchthread(void *args
)
385 if (switch_namespace(((struct targs
*)args
)->idx
)) {
386 ifdict
[((struct targs
*)args
)->idx
]->ifindex
=
387 if_nametoindex(ifdict
[((struct targs
*)args
)->idx
]->ifname
);
388 if (!ifdict
[((struct targs
*)args
)->idx
]->ifindex
) {
389 ksft_test_result_fail
390 ("ERROR: [%s] interface \"%s\" does not exist\n",
391 __func__
, ifdict
[((struct targs
*)args
)->idx
]->ifname
);
392 ((struct targs
*)args
)->retptr
= false;
394 ksft_print_msg("Interface found: %s\n",
395 ifdict
[((struct targs
*)args
)->idx
]->ifname
);
396 ((struct targs
*)args
)->retptr
= true;
399 ((struct targs
*)args
)->retptr
= false;
404 static int validate_interfaces(void)
408 for (int i
= 0; i
< MAX_INTERFACES
; i
++) {
409 if (!strcmp(ifdict
[i
]->ifname
, "")) {
411 ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
413 if (strcmp(ifdict
[i
]->nsname
, "")) {
416 targs
= (struct targs
*)malloc(sizeof(struct targs
));
418 exit_with_error(errno
);
421 if (pthread_create(&ns_thread
, NULL
, nsswitchthread
, (void *)targs
))
422 exit_with_error(errno
);
424 pthread_join(ns_thread
, NULL
);
427 ksft_print_msg("NS switched: %s\n", ifdict
[i
]->nsname
);
431 ifdict
[i
]->ifindex
= if_nametoindex(ifdict
[i
]->ifname
);
432 if (!ifdict
[i
]->ifindex
) {
433 ksft_test_result_fail
434 ("ERROR: interface \"%s\" does not exist\n", ifdict
[i
]->ifname
);
437 ksft_print_msg("Interface found: %s\n", ifdict
[i
]->ifname
);
444 static void parse_command_line(int argc
, char **argv
)
446 int option_index
, interface_index
= 0, c
;
451 c
= getopt_long(argc
, argv
, "i:q:pSNcTBDC:", long_options
, &option_index
);
458 if (interface_index
== MAX_INTERFACES
)
462 sptr
= strndupa(optarg
, strlen(optarg
));
463 memcpy(ifdict
[interface_index
]->ifname
,
464 strsep(&sptr
, ","), MAX_INTERFACE_NAME_CHARS
);
465 token
= strsep(&sptr
, ",");
467 memcpy(ifdict
[interface_index
]->nsname
, token
,
468 MAX_INTERFACES_NAMESPACE_CHARS
);
472 opt_queue
= atoi(optarg
);
478 opt_xdp_flags
|= XDP_FLAGS_SKB_MODE
;
479 opt_xdp_bind_flags
|= XDP_COPY
;
480 uut
= ORDER_CONTENT_VALIDATE_XDP_SKB
;
483 opt_xdp_flags
|= XDP_FLAGS_DRV_MODE
;
484 opt_xdp_bind_flags
|= XDP_COPY
;
485 uut
= ORDER_CONTENT_VALIDATE_XDP_DRV
;
488 opt_xdp_bind_flags
|= XDP_COPY
;
500 opt_pkt_count
= atoi(optarg
);
503 usage(basename(argv
[0]));
508 if (!validate_interfaces()) {
509 usage(basename(argv
[0]));
514 static void kick_tx(struct xsk_socket_info
*xsk
)
518 ret
= sendto(xsk_socket__fd(xsk
->xsk
), NULL
, 0, MSG_DONTWAIT
, NULL
, 0);
519 if (ret
>= 0 || errno
== ENOBUFS
|| errno
== EAGAIN
|| errno
== EBUSY
|| errno
== ENETDOWN
)
521 exit_with_error(errno
);
524 static inline void complete_tx_only(struct xsk_socket_info
*xsk
, int batch_size
)
529 if (!xsk
->outstanding_tx
)
532 if (!NEED_WAKEUP
|| xsk_ring_prod__needs_wakeup(&xsk
->tx
))
535 rcvd
= xsk_ring_cons__peek(&xsk
->umem
->cq
, batch_size
, &idx
);
537 xsk_ring_cons__release(&xsk
->umem
->cq
, rcvd
);
538 xsk
->outstanding_tx
-= rcvd
;
539 xsk
->tx_npkts
+= rcvd
;
543 static void rx_pkt(struct xsk_socket_info
*xsk
, struct pollfd
*fds
)
545 unsigned int rcvd
, i
;
546 u32 idx_rx
= 0, idx_fq
= 0;
549 rcvd
= xsk_ring_cons__peek(&xsk
->rx
, BATCH_SIZE
, &idx_rx
);
551 if (xsk_ring_prod__needs_wakeup(&xsk
->umem
->fq
)) {
552 ret
= poll(fds
, 1, POLL_TMOUT
);
554 exit_with_error(ret
);
559 ret
= xsk_ring_prod__reserve(&xsk
->umem
->fq
, rcvd
, &idx_fq
);
560 while (ret
!= rcvd
) {
562 exit_with_error(ret
);
563 if (xsk_ring_prod__needs_wakeup(&xsk
->umem
->fq
)) {
564 ret
= poll(fds
, 1, POLL_TMOUT
);
566 exit_with_error(ret
);
568 ret
= xsk_ring_prod__reserve(&xsk
->umem
->fq
, rcvd
, &idx_fq
);
571 for (i
= 0; i
< rcvd
; i
++) {
572 u64 addr
= xsk_ring_cons__rx_desc(&xsk
->rx
, idx_rx
)->addr
;
573 (void)xsk_ring_cons__rx_desc(&xsk
->rx
, idx_rx
++)->len
;
574 u64 orig
= xsk_umem__extract_addr(addr
);
576 addr
= xsk_umem__add_offset_to_addr(addr
);
577 pkt_node_rx
= malloc(sizeof(struct pkt
) + PKT_SIZE
);
579 exit_with_error(errno
);
581 pkt_node_rx
->pkt_frame
= (char *)malloc(PKT_SIZE
);
582 if (!pkt_node_rx
->pkt_frame
)
583 exit_with_error(errno
);
585 memcpy(pkt_node_rx
->pkt_frame
, xsk_umem__get_data(xsk
->umem
->buffer
, addr
),
588 TAILQ_INSERT_HEAD(&head
, pkt_node_rx
, pkt_nodes
);
590 *xsk_ring_prod__fill_addr(&xsk
->umem
->fq
, idx_fq
++) = orig
;
593 xsk_ring_prod__submit(&xsk
->umem
->fq
, rcvd
);
594 xsk_ring_cons__release(&xsk
->rx
, rcvd
);
595 xsk
->rx_npkts
+= rcvd
;
598 static void tx_only(struct xsk_socket_info
*xsk
, u32
*frameptr
, int batch_size
)
603 while (xsk_ring_prod__reserve(&xsk
->tx
, batch_size
, &idx
) < batch_size
)
604 complete_tx_only(xsk
, batch_size
);
606 for (i
= 0; i
< batch_size
; i
++) {
607 struct xdp_desc
*tx_desc
= xsk_ring_prod__tx_desc(&xsk
->tx
, idx
+ i
);
609 tx_desc
->addr
= (*frameptr
+ i
) << XSK_UMEM__DEFAULT_FRAME_SHIFT
;
610 tx_desc
->len
= PKT_SIZE
;
613 xsk_ring_prod__submit(&xsk
->tx
, batch_size
);
614 xsk
->outstanding_tx
+= batch_size
;
615 *frameptr
+= batch_size
;
616 *frameptr
%= num_frames
;
617 complete_tx_only(xsk
, batch_size
);
620 static inline int get_batch_size(int pkt_cnt
)
625 if (pkt_cnt
+ BATCH_SIZE
<= opt_pkt_count
)
628 return opt_pkt_count
- pkt_cnt
;
631 static void complete_tx_only_all(void *arg
)
637 if (((struct ifobject
*)arg
)->xsk
->outstanding_tx
) {
638 complete_tx_only(((struct ifobject
*)
639 arg
)->xsk
, BATCH_SIZE
);
640 pending
= !!((struct ifobject
*)arg
)->xsk
->outstanding_tx
;
645 static void tx_only_all(void *arg
)
647 struct pollfd fds
[MAX_SOCKS
] = { };
652 fds
[0].fd
= xsk_socket__fd(((struct ifobject
*)arg
)->xsk
->xsk
);
653 fds
[0].events
= POLLOUT
;
655 while ((opt_pkt_count
&& pkt_cnt
< opt_pkt_count
) || !opt_pkt_count
) {
656 int batch_size
= get_batch_size(pkt_cnt
);
659 ret
= poll(fds
, 1, POLL_TMOUT
);
663 if (!(fds
[0].revents
& POLLOUT
))
667 tx_only(((struct ifobject
*)arg
)->xsk
, &frame_nb
, batch_size
);
668 pkt_cnt
+= batch_size
;
672 complete_tx_only_all(arg
);
675 static void worker_pkt_dump(void)
677 struct in_addr ipaddr
;
679 fprintf(stdout
, "---------------------------------------\n");
680 for (int iter
= 0; iter
< num_frames
- 1; iter
++) {
681 /*extract L2 frame */
682 fprintf(stdout
, "DEBUG>> L2: dst mac: ");
683 for (int i
= 0; i
< ETH_ALEN
; i
++)
684 fprintf(stdout
, "%02X", ((struct ethhdr
*)
685 pkt_buf
[iter
]->payload
)->h_dest
[i
]);
687 fprintf(stdout
, "\nDEBUG>> L2: src mac: ");
688 for (int i
= 0; i
< ETH_ALEN
; i
++)
689 fprintf(stdout
, "%02X", ((struct ethhdr
*)
690 pkt_buf
[iter
]->payload
)->h_source
[i
]);
692 /*extract L3 frame */
693 fprintf(stdout
, "\nDEBUG>> L3: ip_hdr->ihl: %02X\n",
694 ((struct iphdr
*)(pkt_buf
[iter
]->payload
+ sizeof(struct ethhdr
)))->ihl
);
697 ((struct iphdr
*)(pkt_buf
[iter
]->payload
+ sizeof(struct ethhdr
)))->saddr
;
698 fprintf(stdout
, "DEBUG>> L3: ip_hdr->saddr: %s\n", inet_ntoa(ipaddr
));
701 ((struct iphdr
*)(pkt_buf
[iter
]->payload
+ sizeof(struct ethhdr
)))->daddr
;
702 fprintf(stdout
, "DEBUG>> L3: ip_hdr->daddr: %s\n", inet_ntoa(ipaddr
));
704 /*extract L4 frame */
705 fprintf(stdout
, "DEBUG>> L4: udp_hdr->src: %d\n",
706 ntohs(((struct udphdr
*)(pkt_buf
[iter
]->payload
+
707 sizeof(struct ethhdr
) +
708 sizeof(struct iphdr
)))->source
));
710 fprintf(stdout
, "DEBUG>> L4: udp_hdr->dst: %d\n",
711 ntohs(((struct udphdr
*)(pkt_buf
[iter
]->payload
+
712 sizeof(struct ethhdr
) +
713 sizeof(struct iphdr
)))->dest
));
714 /*extract L5 frame */
715 int payload
= *((uint32_t *)(pkt_buf
[iter
]->payload
+ PKT_HDR_SIZE
));
717 if (payload
== EOT
) {
718 ksft_print_msg("End-of-transmission frame received\n");
719 fprintf(stdout
, "---------------------------------------\n");
722 fprintf(stdout
, "DEBUG>> L5: payload: %d\n", payload
);
723 fprintf(stdout
, "---------------------------------------\n");
727 static void worker_pkt_validate(void)
729 u32 payloadseqnum
= -2;
732 pkt_node_rx_q
= malloc(sizeof(struct pkt
));
733 pkt_node_rx_q
= TAILQ_LAST(&head
, head_s
);
736 /*do not increment pktcounter if !(tos=0x9 and ipv4) */
737 if ((((struct iphdr
*)(pkt_node_rx_q
->pkt_frame
+
738 sizeof(struct ethhdr
)))->version
== IP_PKT_VER
)
739 && (((struct iphdr
*)(pkt_node_rx_q
->pkt_frame
+ sizeof(struct ethhdr
)))->tos
==
741 payloadseqnum
= *((uint32_t *) (pkt_node_rx_q
->pkt_frame
+ PKT_HDR_SIZE
));
742 if (debug_pkt_dump
&& payloadseqnum
!= EOT
) {
743 pkt_obj
= (struct pkt_frame
*)malloc(sizeof(struct pkt_frame
));
744 pkt_obj
->payload
= (char *)malloc(PKT_SIZE
);
745 memcpy(pkt_obj
->payload
, pkt_node_rx_q
->pkt_frame
, PKT_SIZE
);
746 pkt_buf
[payloadseqnum
] = pkt_obj
;
749 if (payloadseqnum
== EOT
) {
750 ksft_print_msg("End-of-transmission frame received: PASS\n");
755 if (prev_pkt
+ 1 != payloadseqnum
) {
756 ksft_test_result_fail
757 ("ERROR: [%s] prev_pkt [%d], payloadseqnum [%d]\n",
758 __func__
, prev_pkt
, payloadseqnum
);
762 TAILQ_REMOVE(&head
, pkt_node_rx_q
, pkt_nodes
);
763 free(pkt_node_rx_q
->pkt_frame
);
765 pkt_node_rx_q
= NULL
;
766 prev_pkt
= payloadseqnum
;
769 ksft_print_msg("Invalid frame received: ");
770 ksft_print_msg("[IP_PKT_VER: %02X], [IP_PKT_TOS: %02X]\n",
771 ((struct iphdr
*)(pkt_node_rx_q
->pkt_frame
+
772 sizeof(struct ethhdr
)))->version
,
773 ((struct iphdr
*)(pkt_node_rx_q
->pkt_frame
+
774 sizeof(struct ethhdr
)))->tos
);
775 TAILQ_REMOVE(&head
, pkt_node_rx_q
, pkt_nodes
);
776 free(pkt_node_rx_q
->pkt_frame
);
778 pkt_node_rx_q
= NULL
;
783 static void thread_common_ops(void *arg
, void *bufs
, pthread_mutex_t
*mutexptr
,
784 atomic_int
*spinningptr
)
789 xsk_configure_umem((struct ifobject
*)arg
, bufs
, num_frames
* XSK_UMEM__DEFAULT_FRAME_SIZE
);
790 ret
= xsk_configure_socket((struct ifobject
*)arg
);
792 /* Retry Create Socket if it fails as xsk_socket__create()
795 * Essential to lock Mutex here to prevent Tx thread from
796 * entering before Rx and causing a deadlock
798 pthread_mutex_lock(mutexptr
);
799 while (ret
&& ctr
< SOCK_RECONF_CTR
) {
800 atomic_store(spinningptr
, 1);
801 xsk_configure_umem((struct ifobject
*)arg
,
802 bufs
, num_frames
* XSK_UMEM__DEFAULT_FRAME_SIZE
);
803 ret
= xsk_configure_socket((struct ifobject
*)arg
);
807 atomic_store(spinningptr
, 0);
808 pthread_mutex_unlock(mutexptr
);
810 if (ctr
>= SOCK_RECONF_CTR
)
811 exit_with_error(ret
);
814 static void *worker_testapp_validate(void *arg
)
816 struct udphdr
*udp_hdr
=
817 (struct udphdr
*)(pkt_data
+ sizeof(struct ethhdr
) + sizeof(struct iphdr
));
818 struct generic_data
*data
= (struct generic_data
*)malloc(sizeof(struct generic_data
));
819 struct iphdr
*ip_hdr
= (struct iphdr
*)(pkt_data
+ sizeof(struct ethhdr
));
820 struct ethhdr
*eth_hdr
= (struct ethhdr
*)pkt_data
;
823 pthread_attr_setstacksize(&attr
, THREAD_STACK
);
826 bufs
= mmap(NULL
, num_frames
* XSK_UMEM__DEFAULT_FRAME_SIZE
,
827 PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
828 if (bufs
== MAP_FAILED
)
829 exit_with_error(errno
);
831 if (strcmp(((struct ifobject
*)arg
)->nsname
, ""))
832 switch_namespace(((struct ifobject
*)arg
)->ifdict_index
);
835 if (((struct ifobject
*)arg
)->fv
.vector
== tx
) {
836 int spinningrxctr
= 0;
839 thread_common_ops(arg
, bufs
, &sync_mutex_tx
, &spinning_tx
);
841 while (atomic_load(&spinning_rx
) && spinningrxctr
< SOCK_RECONF_CTR
) {
846 ksft_print_msg("Interface [%s] vector [Tx]\n", ((struct ifobject
*)arg
)->ifname
);
847 for (int i
= 0; i
< num_frames
; i
++) {
849 if (i
== (num_frames
- 1))
853 gen_udp_hdr((void *)data
, (void *)arg
, udp_hdr
);
854 gen_ip_hdr((void *)arg
, ip_hdr
);
855 gen_udp_csum(udp_hdr
, ip_hdr
);
856 gen_eth_hdr((void *)arg
, eth_hdr
);
857 gen_eth_frame(((struct ifobject
*)arg
)->umem
,
858 i
* XSK_UMEM__DEFAULT_FRAME_SIZE
);
862 ksft_print_msg("Sending %d packets on interface %s\n",
863 (opt_pkt_count
- 1), ((struct ifobject
*)arg
)->ifname
);
865 } else if (((struct ifobject
*)arg
)->fv
.vector
== rx
) {
866 struct pollfd fds
[MAX_SOCKS
] = { };
870 thread_common_ops(arg
, bufs
, &sync_mutex_tx
, &spinning_rx
);
872 ksft_print_msg("Interface [%s] vector [Rx]\n", ((struct ifobject
*)arg
)->ifname
);
873 xsk_populate_fill_ring(((struct ifobject
*)arg
)->umem
);
876 if (debug_pkt_dump
) {
877 pkt_buf
= malloc(sizeof(struct pkt_frame
**) * num_frames
);
879 exit_with_error(errno
);
882 fds
[0].fd
= xsk_socket__fd(((struct ifobject
*)arg
)->xsk
->xsk
);
883 fds
[0].events
= POLLIN
;
885 pthread_mutex_lock(&sync_mutex
);
886 pthread_cond_signal(&signal_rx_condition
);
887 pthread_mutex_unlock(&sync_mutex
);
891 ret
= poll(fds
, 1, POLL_TMOUT
);
895 rx_pkt(((struct ifobject
*)arg
)->xsk
, fds
);
896 worker_pkt_validate();
902 ksft_print_msg("Received %d packets on interface %s\n",
903 pkt_counter
, ((struct ifobject
*)arg
)->ifname
);
906 ksft_print_msg("Destroying socket\n");
909 if (!opt_bidi
|| (opt_bidi
&& bidi_pass
)) {
910 xsk_socket__delete(((struct ifobject
*)arg
)->xsk
->xsk
);
911 (void)xsk_umem__delete(((struct ifobject
*)arg
)->umem
->umem
);
916 static void testapp_validate(void)
918 pthread_attr_init(&attr
);
919 pthread_attr_setstacksize(&attr
, THREAD_STACK
);
921 if (opt_bidi
&& bidi_pass
) {
922 pthread_init_mutex();
923 if (!switching_notify
) {
924 ksft_print_msg("Switching Tx/Rx vectors\n");
929 pthread_mutex_lock(&sync_mutex
);
932 if (!opt_bidi
|| (opt_bidi
&& !bidi_pass
)) {
933 if (pthread_create(&t0
, &attr
, worker_testapp_validate
, (void *)ifdict
[1]))
934 exit_with_error(errno
);
935 } else if (opt_bidi
&& bidi_pass
) {
936 /*switch Tx/Rx vectors */
937 ifdict
[0]->fv
.vector
= rx
;
938 if (pthread_create(&t0
, &attr
, worker_testapp_validate
, (void *)ifdict
[0]))
939 exit_with_error(errno
);
942 struct timespec max_wait
= { 0, 0 };
944 if (clock_gettime(CLOCK_REALTIME
, &max_wait
))
945 exit_with_error(errno
);
946 max_wait
.tv_sec
+= TMOUT_SEC
;
948 if (pthread_cond_timedwait(&signal_rx_condition
, &sync_mutex
, &max_wait
) == ETIMEDOUT
)
949 exit_with_error(errno
);
951 pthread_mutex_unlock(&sync_mutex
);
954 if (!opt_bidi
|| (opt_bidi
&& !bidi_pass
)) {
955 if (pthread_create(&t1
, &attr
, worker_testapp_validate
, (void *)ifdict
[0]))
956 exit_with_error(errno
);
957 } else if (opt_bidi
&& bidi_pass
) {
958 /*switch Tx/Rx vectors */
959 ifdict
[1]->fv
.vector
= tx
;
960 if (pthread_create(&t1
, &attr
, worker_testapp_validate
, (void *)ifdict
[1]))
961 exit_with_error(errno
);
964 pthread_join(t1
, NULL
);
965 pthread_join(t0
, NULL
);
967 if (debug_pkt_dump
) {
969 for (int iter
= 0; iter
< num_frames
- 1; iter
++) {
970 free(pkt_buf
[iter
]->payload
);
976 if (!opt_teardown
&& !opt_bidi
)
980 static void testapp_sockets(void)
982 for (int i
= 0; i
< (opt_teardown
? MAX_TEARDOWN_ITER
: MAX_BIDI_ITER
); i
++) {
986 ksft_print_msg("Creating socket\n");
988 opt_bidi
? bidi_pass
++ : bidi_pass
;
994 static void init_iface_config(void *ifaceconfig
)
997 ifdict
[0]->fv
.vector
= tx
;
998 memcpy(ifdict
[0]->dst_mac
, ((struct ifaceconfigobj
*)ifaceconfig
)->dst_mac
, ETH_ALEN
);
999 memcpy(ifdict
[0]->src_mac
, ((struct ifaceconfigobj
*)ifaceconfig
)->src_mac
, ETH_ALEN
);
1000 ifdict
[0]->dst_ip
= ((struct ifaceconfigobj
*)ifaceconfig
)->dst_ip
.s_addr
;
1001 ifdict
[0]->src_ip
= ((struct ifaceconfigobj
*)ifaceconfig
)->src_ip
.s_addr
;
1002 ifdict
[0]->dst_port
= ((struct ifaceconfigobj
*)ifaceconfig
)->dst_port
;
1003 ifdict
[0]->src_port
= ((struct ifaceconfigobj
*)ifaceconfig
)->src_port
;
1005 /*Init interface1 */
1006 ifdict
[1]->fv
.vector
= rx
;
1007 memcpy(ifdict
[1]->dst_mac
, ((struct ifaceconfigobj
*)ifaceconfig
)->src_mac
, ETH_ALEN
);
1008 memcpy(ifdict
[1]->src_mac
, ((struct ifaceconfigobj
*)ifaceconfig
)->dst_mac
, ETH_ALEN
);
1009 ifdict
[1]->dst_ip
= ((struct ifaceconfigobj
*)ifaceconfig
)->src_ip
.s_addr
;
1010 ifdict
[1]->src_ip
= ((struct ifaceconfigobj
*)ifaceconfig
)->dst_ip
.s_addr
;
1011 ifdict
[1]->dst_port
= ((struct ifaceconfigobj
*)ifaceconfig
)->src_port
;
1012 ifdict
[1]->src_port
= ((struct ifaceconfigobj
*)ifaceconfig
)->dst_port
;
1015 int main(int argc
, char **argv
)
1017 struct rlimit _rlim
= { RLIM_INFINITY
, RLIM_INFINITY
};
1019 if (setrlimit(RLIMIT_MEMLOCK
, &_rlim
))
1020 exit_with_error(errno
);
1022 const char *MAC1
= "\x00\x0A\x56\x9E\xEE\x62";
1023 const char *MAC2
= "\x00\x0A\x56\x9E\xEE\x61";
1024 const char *IP1
= "192.168.100.162";
1025 const char *IP2
= "192.168.100.161";
1026 u16 UDP_DST_PORT
= 2020;
1027 u16 UDP_SRC_PORT
= 2121;
1029 ifaceconfig
= (struct ifaceconfigobj
*)malloc(sizeof(struct ifaceconfigobj
));
1030 memcpy(ifaceconfig
->dst_mac
, MAC1
, ETH_ALEN
);
1031 memcpy(ifaceconfig
->src_mac
, MAC2
, ETH_ALEN
);
1032 inet_aton(IP1
, &ifaceconfig
->dst_ip
);
1033 inet_aton(IP2
, &ifaceconfig
->src_ip
);
1034 ifaceconfig
->dst_port
= UDP_DST_PORT
;
1035 ifaceconfig
->src_port
= UDP_SRC_PORT
;
1037 for (int i
= 0; i
< MAX_INTERFACES
; i
++) {
1038 ifdict
[i
] = (struct ifobject
*)malloc(sizeof(struct ifobject
));
1040 exit_with_error(errno
);
1042 ifdict
[i
]->ifdict_index
= i
;
1045 setlocale(LC_ALL
, "");
1047 parse_command_line(argc
, argv
);
1049 num_frames
= ++opt_pkt_count
;
1051 init_iface_config((void *)ifaceconfig
);
1053 pthread_init_mutex();
1057 if (!opt_teardown
&& !opt_bidi
) {
1059 } else if (opt_teardown
&& opt_bidi
) {
1060 ksft_test_result_fail("ERROR: parameters -T and -B cannot be used together\n");
1066 for (int i
= 0; i
< MAX_INTERFACES
; i
++)
1069 pthread_destroy_mutex();