xref: /linux/tools/testing/selftests/drivers/net/xdp_helper.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <sys/mman.h>
8 #include <sys/socket.h>
9 #include <linux/if_xdp.h>
10 #include <linux/if_link.h>
11 #include <net/if.h>
12 #include <inttypes.h>
13 
14 #define UMEM_SZ (1U << 16)
15 #define NUM_DESC (UMEM_SZ / 2048)
16 
17 /* Move this to a common header when reused! */
ksft_ready(void)18 static void ksft_ready(void)
19 {
20 	const char msg[7] = "ready\n";
21 	char *env_str;
22 	int fd;
23 
24 	env_str = getenv("KSFT_READY_FD");
25 	if (env_str) {
26 		fd = atoi(env_str);
27 		if (!fd) {
28 			fprintf(stderr, "invalid KSFT_READY_FD = '%s'\n",
29 				env_str);
30 			return;
31 		}
32 	} else {
33 		fd = STDOUT_FILENO;
34 	}
35 
36 	write(fd, msg, sizeof(msg));
37 	if (fd != STDOUT_FILENO)
38 		close(fd);
39 }
40 
ksft_wait(void)41 static void ksft_wait(void)
42 {
43 	char *env_str;
44 	char byte;
45 	int fd;
46 
47 	env_str = getenv("KSFT_WAIT_FD");
48 	if (env_str) {
49 		fd = atoi(env_str);
50 		if (!fd) {
51 			fprintf(stderr, "invalid KSFT_WAIT_FD = '%s'\n",
52 				env_str);
53 			return;
54 		}
55 	} else {
56 		/* Not running in KSFT env, wait for input from STDIN instead */
57 		fd = STDIN_FILENO;
58 	}
59 
60 	read(fd, &byte, sizeof(byte));
61 	if (fd != STDIN_FILENO)
62 		close(fd);
63 }
64 
65 /* this is a simple helper program that creates an XDP socket and does the
66  * minimum necessary to get bind() to succeed.
67  *
68  * this test program is not intended to actually process packets, but could be
69  * extended in the future if that is actually needed.
70  *
71  * it is used by queues.py to ensure the xsk netlinux attribute is set
72  * correctly.
73  */
main(int argc,char ** argv)74 int main(int argc, char **argv)
75 {
76 	struct xdp_umem_reg umem_reg = { 0 };
77 	struct sockaddr_xdp sxdp = { 0 };
78 	int num_desc = NUM_DESC;
79 	void *umem_area;
80 	int ifindex;
81 	int sock_fd;
82 	int queue;
83 
84 	if (argc != 3) {
85 		fprintf(stderr, "Usage: %s ifindex queue_id\n", argv[0]);
86 		return 1;
87 	}
88 
89 	sock_fd = socket(AF_XDP, SOCK_RAW, 0);
90 	if (sock_fd < 0) {
91 		perror("socket creation failed");
92 		/* if the kernel doesn't support AF_XDP, let the test program
93 		 * know with -1. All other error paths return 1.
94 		 */
95 		if (errno == EAFNOSUPPORT)
96 			return -1;
97 		return 1;
98 	}
99 
100 	/* "Probing mode", just checking if AF_XDP sockets are supported */
101 	if (!strcmp(argv[1], "-") && !strcmp(argv[2], "-")) {
102 		printf("AF_XDP support detected\n");
103 		close(sock_fd);
104 		return 0;
105 	}
106 
107 	ifindex = atoi(argv[1]);
108 	queue = atoi(argv[2]);
109 
110 	umem_area = mmap(NULL, UMEM_SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE |
111 			MAP_ANONYMOUS, -1, 0);
112 	if (umem_area == MAP_FAILED) {
113 		perror("mmap failed");
114 		return 1;
115 	}
116 
117 	umem_reg.addr = (uintptr_t)umem_area;
118 	umem_reg.len = UMEM_SZ;
119 	umem_reg.chunk_size = 2048;
120 	umem_reg.headroom = 0;
121 
122 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_REG, &umem_reg,
123 		   sizeof(umem_reg));
124 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_FILL_RING, &num_desc,
125 		   sizeof(num_desc));
126 	setsockopt(sock_fd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &num_desc,
127 		   sizeof(num_desc));
128 	setsockopt(sock_fd, SOL_XDP, XDP_RX_RING, &num_desc, sizeof(num_desc));
129 
130 	sxdp.sxdp_family = AF_XDP;
131 	sxdp.sxdp_ifindex = ifindex;
132 	sxdp.sxdp_queue_id = queue;
133 	sxdp.sxdp_flags = 0;
134 
135 	if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) != 0) {
136 		munmap(umem_area, UMEM_SZ);
137 		perror("bind failed");
138 		close(sock_fd);
139 		return 1;
140 	}
141 
142 	ksft_ready();
143 	ksft_wait();
144 
145 	/* parent program will write a byte to stdin when its ready for this
146 	 * helper to exit
147 	 */
148 
149 	close(sock_fd);
150 	return 0;
151 }
152