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! */ 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 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 */ 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