1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test functionality of BPF filters with SO_REUSEPORT. Same test as 4 * in reuseport_bpf_cpu, only as one socket per NUMA node. 5 */ 6 7 #define _GNU_SOURCE 8 9 #include <arpa/inet.h> 10 #include <errno.h> 11 #include <error.h> 12 #include <linux/filter.h> 13 #include <linux/bpf.h> 14 #include <linux/in.h> 15 #include <linux/unistd.h> 16 #include <sched.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <sys/epoll.h> 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <unistd.h> 24 #include <numa.h> 25 26 #include "../kselftest.h" 27 28 static const int PORT = 8888; 29 30 static void build_rcv_group(int *rcv_fd, size_t len, int family, int proto) 31 { 32 struct sockaddr_storage addr; 33 struct sockaddr_in *addr4; 34 struct sockaddr_in6 *addr6; 35 size_t i; 36 int opt; 37 38 switch (family) { 39 case AF_INET: 40 addr4 = (struct sockaddr_in *)&addr; 41 addr4->sin_family = AF_INET; 42 addr4->sin_addr.s_addr = htonl(INADDR_ANY); 43 addr4->sin_port = htons(PORT); 44 break; 45 case AF_INET6: 46 addr6 = (struct sockaddr_in6 *)&addr; 47 addr6->sin6_family = AF_INET6; 48 addr6->sin6_addr = in6addr_any; 49 addr6->sin6_port = htons(PORT); 50 break; 51 default: 52 error(1, 0, "Unsupported family %d", family); 53 } 54 55 for (i = 0; i < len; ++i) { 56 rcv_fd[i] = socket(family, proto, 0); 57 if (rcv_fd[i] < 0) 58 error(1, errno, "failed to create receive socket"); 59 60 opt = 1; 61 if (setsockopt(rcv_fd[i], SOL_SOCKET, SO_REUSEPORT, &opt, 62 sizeof(opt))) 63 error(1, errno, "failed to set SO_REUSEPORT"); 64 65 if (bind(rcv_fd[i], (struct sockaddr *)&addr, sizeof(addr))) 66 error(1, errno, "failed to bind receive socket"); 67 68 if (proto == SOCK_STREAM && listen(rcv_fd[i], len * 10)) 69 error(1, errno, "failed to listen on receive port"); 70 } 71 } 72 73 static void attach_bpf(int fd) 74 { 75 static char bpf_log_buf[65536]; 76 static const char bpf_license[] = ""; 77 78 int bpf_fd; 79 const struct bpf_insn prog[] = { 80 /* R0 = bpf_get_numa_node_id() */ 81 { BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_numa_node_id }, 82 /* return R0 */ 83 { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } 84 }; 85 union bpf_attr attr; 86 87 memset(&attr, 0, sizeof(attr)); 88 attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; 89 attr.insn_cnt = ARRAY_SIZE(prog); 90 attr.insns = (unsigned long) &prog; 91 attr.license = (unsigned long) &bpf_license; 92 attr.log_buf = (unsigned long) &bpf_log_buf; 93 attr.log_size = sizeof(bpf_log_buf); 94 attr.log_level = 1; 95 96 bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); 97 if (bpf_fd < 0) 98 error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf); 99 100 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, 101 sizeof(bpf_fd))) 102 error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF"); 103 104 close(bpf_fd); 105 } 106 107 static void send_from_node(int node_id, int family, int proto) 108 { 109 struct sockaddr_storage saddr, daddr; 110 struct sockaddr_in *saddr4, *daddr4; 111 struct sockaddr_in6 *saddr6, *daddr6; 112 int fd; 113 114 switch (family) { 115 case AF_INET: 116 saddr4 = (struct sockaddr_in *)&saddr; 117 saddr4->sin_family = AF_INET; 118 saddr4->sin_addr.s_addr = htonl(INADDR_ANY); 119 saddr4->sin_port = 0; 120 121 daddr4 = (struct sockaddr_in *)&daddr; 122 daddr4->sin_family = AF_INET; 123 daddr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 124 daddr4->sin_port = htons(PORT); 125 break; 126 case AF_INET6: 127 saddr6 = (struct sockaddr_in6 *)&saddr; 128 saddr6->sin6_family = AF_INET6; 129 saddr6->sin6_addr = in6addr_any; 130 saddr6->sin6_port = 0; 131 132 daddr6 = (struct sockaddr_in6 *)&daddr; 133 daddr6->sin6_family = AF_INET6; 134 daddr6->sin6_addr = in6addr_loopback; 135 daddr6->sin6_port = htons(PORT); 136 break; 137 default: 138 error(1, 0, "Unsupported family %d", family); 139 } 140 141 if (numa_run_on_node(node_id) < 0) 142 error(1, errno, "failed to pin to node"); 143 144 fd = socket(family, proto, 0); 145 if (fd < 0) 146 error(1, errno, "failed to create send socket"); 147 148 if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) 149 error(1, errno, "failed to bind send socket"); 150 151 if (connect(fd, (struct sockaddr *)&daddr, sizeof(daddr))) 152 error(1, errno, "failed to connect send socket"); 153 154 if (send(fd, "a", 1, 0) < 0) 155 error(1, errno, "failed to send message"); 156 157 close(fd); 158 } 159 160 static 161 void receive_on_node(int *rcv_fd, int len, int epfd, int node_id, int proto) 162 { 163 struct epoll_event ev; 164 int i, fd; 165 char buf[8]; 166 167 i = epoll_wait(epfd, &ev, 1, -1); 168 if (i < 0) 169 error(1, errno, "epoll_wait failed"); 170 171 if (proto == SOCK_STREAM) { 172 fd = accept(ev.data.fd, NULL, NULL); 173 if (fd < 0) 174 error(1, errno, "failed to accept"); 175 i = recv(fd, buf, sizeof(buf), 0); 176 close(fd); 177 } else { 178 i = recv(ev.data.fd, buf, sizeof(buf), 0); 179 } 180 181 if (i < 0) 182 error(1, errno, "failed to recv"); 183 184 for (i = 0; i < len; ++i) 185 if (ev.data.fd == rcv_fd[i]) 186 break; 187 if (i == len) 188 error(1, 0, "failed to find socket"); 189 fprintf(stderr, "send node %d, receive socket %d\n", node_id, i); 190 if (node_id != i) 191 error(1, 0, "node id/receive socket mismatch"); 192 } 193 194 static void test(int *rcv_fd, int len, int family, int proto) 195 { 196 struct epoll_event ev; 197 int epfd, node; 198 199 build_rcv_group(rcv_fd, len, family, proto); 200 attach_bpf(rcv_fd[0]); 201 202 epfd = epoll_create(1); 203 if (epfd < 0) 204 error(1, errno, "failed to create epoll"); 205 for (node = 0; node < len; ++node) { 206 ev.events = EPOLLIN; 207 ev.data.fd = rcv_fd[node]; 208 if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fd[node], &ev)) 209 error(1, errno, "failed to register sock epoll"); 210 } 211 212 /* Forward iterate */ 213 for (node = 0; node < len; ++node) { 214 if (!numa_bitmask_isbitset(numa_nodes_ptr, node)) 215 continue; 216 send_from_node(node, family, proto); 217 receive_on_node(rcv_fd, len, epfd, node, proto); 218 } 219 220 /* Reverse iterate */ 221 for (node = len - 1; node >= 0; --node) { 222 if (!numa_bitmask_isbitset(numa_nodes_ptr, node)) 223 continue; 224 send_from_node(node, family, proto); 225 receive_on_node(rcv_fd, len, epfd, node, proto); 226 } 227 228 close(epfd); 229 for (node = 0; node < len; ++node) 230 close(rcv_fd[node]); 231 } 232 233 int main(void) 234 { 235 int *rcv_fd, nodes; 236 237 if (numa_available() < 0) 238 ksft_exit_skip("no numa api support\n"); 239 240 nodes = numa_max_node() + 1; 241 242 rcv_fd = calloc(nodes, sizeof(int)); 243 if (!rcv_fd) 244 error(1, 0, "failed to allocate array"); 245 246 fprintf(stderr, "---- IPv4 UDP ----\n"); 247 test(rcv_fd, nodes, AF_INET, SOCK_DGRAM); 248 249 fprintf(stderr, "---- IPv6 UDP ----\n"); 250 test(rcv_fd, nodes, AF_INET6, SOCK_DGRAM); 251 252 fprintf(stderr, "---- IPv4 TCP ----\n"); 253 test(rcv_fd, nodes, AF_INET, SOCK_STREAM); 254 255 fprintf(stderr, "---- IPv6 TCP ----\n"); 256 test(rcv_fd, nodes, AF_INET6, SOCK_STREAM); 257 258 free(rcv_fd); 259 260 fprintf(stderr, "SUCCESS\n"); 261 return 0; 262 } 263
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.