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(fd: 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_id: node, family, proto); |
217 | receive_on_node(rcv_fd, len, epfd, node_id: 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_id: node, family, proto); |
225 | receive_on_node(rcv_fd, len, epfd, node_id: 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(msg: "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, len: nodes, AF_INET, proto: SOCK_DGRAM); |
248 | |
249 | fprintf(stderr, "---- IPv6 UDP ----\n" ); |
250 | test(rcv_fd, len: nodes, AF_INET6, proto: SOCK_DGRAM); |
251 | |
252 | fprintf(stderr, "---- IPv4 TCP ----\n" ); |
253 | test(rcv_fd, len: nodes, AF_INET, proto: SOCK_STREAM); |
254 | |
255 | fprintf(stderr, "---- IPv6 TCP ----\n" ); |
256 | test(rcv_fd, len: nodes, AF_INET6, proto: SOCK_STREAM); |
257 | |
258 | free(rcv_fd); |
259 | |
260 | fprintf(stderr, "SUCCESS\n" ); |
261 | return 0; |
262 | } |
263 | |