Commit | Line | Data |
---|---|---|
3ca8e402 CG |
1 | /* |
2 | * Test functionality of BPF filters for SO_REUSEPORT. The tests below will use | |
3 | * a BPF program (both classic and extended) to read the first word from an | |
4 | * incoming packet (expected to be in network byte-order), calculate a modulus | |
5 | * of that number, and then dispatch the packet to the Nth socket using the | |
6 | * result. These tests are run for each supported address family and protocol. | |
7 | * Additionally, a few edge cases in the implementation are tested. | |
8 | */ | |
9 | ||
10 | #include <errno.h> | |
11 | #include <error.h> | |
4b2a6aed | 12 | #include <fcntl.h> |
3ca8e402 CG |
13 | #include <linux/bpf.h> |
14 | #include <linux/filter.h> | |
15 | #include <linux/unistd.h> | |
16 | #include <netinet/in.h> | |
4b2a6aed | 17 | #include <netinet/tcp.h> |
3ca8e402 CG |
18 | #include <stdio.h> |
19 | #include <stdlib.h> | |
20 | #include <string.h> | |
21 | #include <sys/epoll.h> | |
22 | #include <sys/types.h> | |
23 | #include <sys/socket.h> | |
24 | #include <unistd.h> | |
25 | ||
26 | #ifndef ARRAY_SIZE | |
27 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | |
28 | #endif | |
29 | ||
30 | struct test_params { | |
31 | int recv_family; | |
32 | int send_family; | |
33 | int protocol; | |
34 | size_t recv_socks; | |
35 | uint16_t recv_port; | |
36 | uint16_t send_port_min; | |
37 | }; | |
38 | ||
39 | static size_t sockaddr_size(void) | |
40 | { | |
41 | return sizeof(struct sockaddr_storage); | |
42 | } | |
43 | ||
44 | static struct sockaddr *new_any_sockaddr(int family, uint16_t port) | |
45 | { | |
46 | struct sockaddr_storage *addr; | |
47 | struct sockaddr_in *addr4; | |
48 | struct sockaddr_in6 *addr6; | |
49 | ||
50 | addr = malloc(sizeof(struct sockaddr_storage)); | |
51 | memset(addr, 0, sizeof(struct sockaddr_storage)); | |
52 | ||
53 | switch (family) { | |
54 | case AF_INET: | |
55 | addr4 = (struct sockaddr_in *)addr; | |
56 | addr4->sin_family = AF_INET; | |
57 | addr4->sin_addr.s_addr = htonl(INADDR_ANY); | |
58 | addr4->sin_port = htons(port); | |
59 | break; | |
60 | case AF_INET6: | |
61 | addr6 = (struct sockaddr_in6 *)addr; | |
62 | addr6->sin6_family = AF_INET6; | |
63 | addr6->sin6_addr = in6addr_any; | |
64 | addr6->sin6_port = htons(port); | |
65 | break; | |
66 | default: | |
67 | error(1, 0, "Unsupported family %d", family); | |
68 | } | |
69 | return (struct sockaddr *)addr; | |
70 | } | |
71 | ||
72 | static struct sockaddr *new_loopback_sockaddr(int family, uint16_t port) | |
73 | { | |
74 | struct sockaddr *addr = new_any_sockaddr(family, port); | |
75 | struct sockaddr_in *addr4; | |
76 | struct sockaddr_in6 *addr6; | |
77 | ||
78 | switch (family) { | |
79 | case AF_INET: | |
80 | addr4 = (struct sockaddr_in *)addr; | |
81 | addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
82 | break; | |
83 | case AF_INET6: | |
84 | addr6 = (struct sockaddr_in6 *)addr; | |
85 | addr6->sin6_addr = in6addr_loopback; | |
86 | break; | |
87 | default: | |
88 | error(1, 0, "Unsupported family %d", family); | |
89 | } | |
90 | return addr; | |
91 | } | |
92 | ||
93 | static void attach_ebpf(int fd, uint16_t mod) | |
94 | { | |
95 | static char bpf_log_buf[65536]; | |
96 | static const char bpf_license[] = "GPL"; | |
97 | ||
98 | int bpf_fd; | |
99 | const struct bpf_insn prog[] = { | |
100 | /* BPF_MOV64_REG(BPF_REG_6, BPF_REG_1) */ | |
101 | { BPF_ALU64 | BPF_MOV | BPF_X, BPF_REG_6, BPF_REG_1, 0, 0 }, | |
102 | /* BPF_LD_ABS(BPF_W, 0) R0 = (uint32_t)skb[0] */ | |
103 | { BPF_LD | BPF_ABS | BPF_W, 0, 0, 0, 0 }, | |
104 | /* BPF_ALU64_IMM(BPF_MOD, BPF_REG_0, mod) */ | |
105 | { BPF_ALU64 | BPF_MOD | BPF_K, BPF_REG_0, 0, 0, mod }, | |
106 | /* BPF_EXIT_INSN() */ | |
107 | { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } | |
108 | }; | |
109 | union bpf_attr attr; | |
110 | ||
111 | memset(&attr, 0, sizeof(attr)); | |
112 | attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; | |
113 | attr.insn_cnt = ARRAY_SIZE(prog); | |
fc100a7f HD |
114 | attr.insns = (unsigned long) &prog; |
115 | attr.license = (unsigned long) &bpf_license; | |
116 | attr.log_buf = (unsigned long) &bpf_log_buf; | |
3ca8e402 CG |
117 | attr.log_size = sizeof(bpf_log_buf); |
118 | attr.log_level = 1; | |
119 | attr.kern_version = 0; | |
120 | ||
121 | bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); | |
122 | if (bpf_fd < 0) | |
123 | error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf); | |
124 | ||
125 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, | |
126 | sizeof(bpf_fd))) | |
127 | error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF"); | |
1134158b CG |
128 | |
129 | close(bpf_fd); | |
3ca8e402 CG |
130 | } |
131 | ||
132 | static void attach_cbpf(int fd, uint16_t mod) | |
133 | { | |
134 | struct sock_filter code[] = { | |
135 | /* A = (uint32_t)skb[0] */ | |
136 | { BPF_LD | BPF_W | BPF_ABS, 0, 0, 0 }, | |
137 | /* A = A % mod */ | |
138 | { BPF_ALU | BPF_MOD, 0, 0, mod }, | |
139 | /* return A */ | |
140 | { BPF_RET | BPF_A, 0, 0, 0 }, | |
141 | }; | |
142 | struct sock_fprog p = { | |
143 | .len = ARRAY_SIZE(code), | |
144 | .filter = code, | |
145 | }; | |
146 | ||
147 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) | |
148 | error(1, errno, "failed to set SO_ATTACH_REUSEPORT_CBPF"); | |
149 | } | |
150 | ||
151 | static void build_recv_group(const struct test_params p, int fd[], uint16_t mod, | |
152 | void (*attach_bpf)(int, uint16_t)) | |
153 | { | |
154 | struct sockaddr * const addr = | |
155 | new_any_sockaddr(p.recv_family, p.recv_port); | |
156 | int i, opt; | |
157 | ||
158 | for (i = 0; i < p.recv_socks; ++i) { | |
159 | fd[i] = socket(p.recv_family, p.protocol, 0); | |
160 | if (fd[i] < 0) | |
161 | error(1, errno, "failed to create recv %d", i); | |
162 | ||
163 | opt = 1; | |
164 | if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEPORT, &opt, | |
165 | sizeof(opt))) | |
166 | error(1, errno, "failed to set SO_REUSEPORT on %d", i); | |
167 | ||
168 | if (i == 0) | |
169 | attach_bpf(fd[i], mod); | |
170 | ||
171 | if (bind(fd[i], addr, sockaddr_size())) | |
172 | error(1, errno, "failed to bind recv socket %d", i); | |
173 | ||
4b2a6aed CG |
174 | if (p.protocol == SOCK_STREAM) { |
175 | opt = 4; | |
176 | if (setsockopt(fd[i], SOL_TCP, TCP_FASTOPEN, &opt, | |
177 | sizeof(opt))) | |
178 | error(1, errno, | |
179 | "failed to set TCP_FASTOPEN on %d", i); | |
3ca8e402 CG |
180 | if (listen(fd[i], p.recv_socks * 10)) |
181 | error(1, errno, "failed to listen on socket"); | |
4b2a6aed | 182 | } |
3ca8e402 CG |
183 | } |
184 | free(addr); | |
185 | } | |
186 | ||
187 | static void send_from(struct test_params p, uint16_t sport, char *buf, | |
188 | size_t len) | |
189 | { | |
190 | struct sockaddr * const saddr = new_any_sockaddr(p.send_family, sport); | |
191 | struct sockaddr * const daddr = | |
192 | new_loopback_sockaddr(p.send_family, p.recv_port); | |
193 | const int fd = socket(p.send_family, p.protocol, 0); | |
194 | ||
195 | if (fd < 0) | |
196 | error(1, errno, "failed to create send socket"); | |
197 | ||
198 | if (bind(fd, saddr, sockaddr_size())) | |
199 | error(1, errno, "failed to bind send socket"); | |
3ca8e402 | 200 | |
4b2a6aed | 201 | if (sendto(fd, buf, len, MSG_FASTOPEN, daddr, sockaddr_size()) < 0) |
3ca8e402 CG |
202 | error(1, errno, "failed to send message"); |
203 | ||
204 | close(fd); | |
205 | free(saddr); | |
206 | free(daddr); | |
207 | } | |
208 | ||
209 | static void test_recv_order(const struct test_params p, int fd[], int mod) | |
210 | { | |
211 | char recv_buf[8], send_buf[8]; | |
212 | struct msghdr msg; | |
213 | struct iovec recv_io = { recv_buf, 8 }; | |
214 | struct epoll_event ev; | |
215 | int epfd, conn, i, sport, expected; | |
216 | uint32_t data, ndata; | |
217 | ||
218 | epfd = epoll_create(1); | |
219 | if (epfd < 0) | |
220 | error(1, errno, "failed to create epoll"); | |
221 | for (i = 0; i < p.recv_socks; ++i) { | |
222 | ev.events = EPOLLIN; | |
223 | ev.data.fd = fd[i]; | |
224 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], &ev)) | |
225 | error(1, errno, "failed to register sock %d epoll", i); | |
226 | } | |
227 | ||
228 | memset(&msg, 0, sizeof(msg)); | |
229 | msg.msg_iov = &recv_io; | |
230 | msg.msg_iovlen = 1; | |
231 | ||
232 | for (data = 0; data < p.recv_socks * 2; ++data) { | |
233 | sport = p.send_port_min + data; | |
234 | ndata = htonl(data); | |
235 | memcpy(send_buf, &ndata, sizeof(ndata)); | |
236 | send_from(p, sport, send_buf, sizeof(ndata)); | |
237 | ||
238 | i = epoll_wait(epfd, &ev, 1, -1); | |
239 | if (i < 0) | |
240 | error(1, errno, "epoll wait failed"); | |
241 | ||
242 | if (p.protocol == SOCK_STREAM) { | |
243 | conn = accept(ev.data.fd, NULL, NULL); | |
244 | if (conn < 0) | |
245 | error(1, errno, "error accepting"); | |
246 | i = recvmsg(conn, &msg, 0); | |
247 | close(conn); | |
248 | } else { | |
249 | i = recvmsg(ev.data.fd, &msg, 0); | |
250 | } | |
251 | if (i < 0) | |
252 | error(1, errno, "recvmsg error"); | |
253 | if (i != sizeof(ndata)) | |
254 | error(1, 0, "expected size %zd got %d", | |
255 | sizeof(ndata), i); | |
256 | ||
257 | for (i = 0; i < p.recv_socks; ++i) | |
258 | if (ev.data.fd == fd[i]) | |
259 | break; | |
260 | memcpy(&ndata, recv_buf, sizeof(ndata)); | |
261 | fprintf(stderr, "Socket %d: %d\n", i, ntohl(ndata)); | |
262 | ||
263 | expected = (sport % mod); | |
264 | if (i != expected) | |
265 | error(1, 0, "expected socket %d", expected); | |
266 | } | |
267 | } | |
268 | ||
4b2a6aed | 269 | static void test_reuseport_ebpf(struct test_params p) |
3ca8e402 CG |
270 | { |
271 | int i, fd[p.recv_socks]; | |
272 | ||
273 | fprintf(stderr, "Testing EBPF mod %zd...\n", p.recv_socks); | |
274 | build_recv_group(p, fd, p.recv_socks, attach_ebpf); | |
275 | test_recv_order(p, fd, p.recv_socks); | |
276 | ||
4b2a6aed | 277 | p.send_port_min += p.recv_socks * 2; |
3ca8e402 CG |
278 | fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2); |
279 | attach_ebpf(fd[0], p.recv_socks / 2); | |
280 | test_recv_order(p, fd, p.recv_socks / 2); | |
281 | ||
282 | for (i = 0; i < p.recv_socks; ++i) | |
283 | close(fd[i]); | |
284 | } | |
285 | ||
4b2a6aed | 286 | static void test_reuseport_cbpf(struct test_params p) |
3ca8e402 CG |
287 | { |
288 | int i, fd[p.recv_socks]; | |
289 | ||
290 | fprintf(stderr, "Testing CBPF mod %zd...\n", p.recv_socks); | |
291 | build_recv_group(p, fd, p.recv_socks, attach_cbpf); | |
292 | test_recv_order(p, fd, p.recv_socks); | |
293 | ||
4b2a6aed | 294 | p.send_port_min += p.recv_socks * 2; |
3ca8e402 CG |
295 | fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2); |
296 | attach_cbpf(fd[0], p.recv_socks / 2); | |
297 | test_recv_order(p, fd, p.recv_socks / 2); | |
298 | ||
299 | for (i = 0; i < p.recv_socks; ++i) | |
300 | close(fd[i]); | |
301 | } | |
302 | ||
303 | static void test_extra_filter(const struct test_params p) | |
304 | { | |
305 | struct sockaddr * const addr = | |
306 | new_any_sockaddr(p.recv_family, p.recv_port); | |
307 | int fd1, fd2, opt; | |
308 | ||
309 | fprintf(stderr, "Testing too many filters...\n"); | |
310 | fd1 = socket(p.recv_family, p.protocol, 0); | |
311 | if (fd1 < 0) | |
312 | error(1, errno, "failed to create socket 1"); | |
313 | fd2 = socket(p.recv_family, p.protocol, 0); | |
314 | if (fd2 < 0) | |
315 | error(1, errno, "failed to create socket 2"); | |
316 | ||
317 | opt = 1; | |
318 | if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) | |
319 | error(1, errno, "failed to set SO_REUSEPORT on socket 1"); | |
320 | if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) | |
321 | error(1, errno, "failed to set SO_REUSEPORT on socket 2"); | |
322 | ||
323 | attach_ebpf(fd1, 10); | |
324 | attach_ebpf(fd2, 10); | |
325 | ||
326 | if (bind(fd1, addr, sockaddr_size())) | |
327 | error(1, errno, "failed to bind recv socket 1"); | |
328 | ||
329 | if (!bind(fd2, addr, sockaddr_size()) && errno != EADDRINUSE) | |
330 | error(1, errno, "bind socket 2 should fail with EADDRINUSE"); | |
331 | ||
332 | free(addr); | |
333 | } | |
334 | ||
335 | static void test_filter_no_reuseport(const struct test_params p) | |
336 | { | |
337 | struct sockaddr * const addr = | |
338 | new_any_sockaddr(p.recv_family, p.recv_port); | |
339 | const char bpf_license[] = "GPL"; | |
340 | struct bpf_insn ecode[] = { | |
341 | { BPF_ALU64 | BPF_MOV | BPF_K, BPF_REG_0, 0, 0, 10 }, | |
342 | { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } | |
343 | }; | |
344 | struct sock_filter ccode[] = {{ BPF_RET | BPF_A, 0, 0, 0 }}; | |
345 | union bpf_attr eprog; | |
346 | struct sock_fprog cprog; | |
347 | int fd, bpf_fd; | |
348 | ||
349 | fprintf(stderr, "Testing filters on non-SO_REUSEPORT socket...\n"); | |
350 | ||
351 | memset(&eprog, 0, sizeof(eprog)); | |
352 | eprog.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; | |
353 | eprog.insn_cnt = ARRAY_SIZE(ecode); | |
fc100a7f HD |
354 | eprog.insns = (unsigned long) &ecode; |
355 | eprog.license = (unsigned long) &bpf_license; | |
3ca8e402 CG |
356 | eprog.kern_version = 0; |
357 | ||
358 | memset(&cprog, 0, sizeof(cprog)); | |
359 | cprog.len = ARRAY_SIZE(ccode); | |
360 | cprog.filter = ccode; | |
361 | ||
362 | ||
363 | bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &eprog, sizeof(eprog)); | |
364 | if (bpf_fd < 0) | |
365 | error(1, errno, "ebpf error"); | |
366 | fd = socket(p.recv_family, p.protocol, 0); | |
367 | if (fd < 0) | |
368 | error(1, errno, "failed to create socket 1"); | |
369 | ||
370 | if (bind(fd, addr, sockaddr_size())) | |
371 | error(1, errno, "failed to bind recv socket 1"); | |
372 | ||
373 | errno = 0; | |
374 | if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, | |
375 | sizeof(bpf_fd)) || errno != EINVAL) | |
376 | error(1, errno, "setsockopt should have returned EINVAL"); | |
377 | ||
378 | errno = 0; | |
379 | if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &cprog, | |
380 | sizeof(cprog)) || errno != EINVAL) | |
381 | error(1, errno, "setsockopt should have returned EINVAL"); | |
382 | ||
383 | free(addr); | |
384 | } | |
385 | ||
386 | static void test_filter_without_bind(void) | |
387 | { | |
4b2a6aed | 388 | int fd1, fd2, opt = 1; |
3ca8e402 CG |
389 | |
390 | fprintf(stderr, "Testing filter add without bind...\n"); | |
391 | fd1 = socket(AF_INET, SOCK_DGRAM, 0); | |
392 | if (fd1 < 0) | |
393 | error(1, errno, "failed to create socket 1"); | |
394 | fd2 = socket(AF_INET, SOCK_DGRAM, 0); | |
395 | if (fd2 < 0) | |
396 | error(1, errno, "failed to create socket 2"); | |
4b2a6aed CG |
397 | if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) |
398 | error(1, errno, "failed to set SO_REUSEPORT on socket 1"); | |
399 | if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) | |
400 | error(1, errno, "failed to set SO_REUSEPORT on socket 2"); | |
3ca8e402 CG |
401 | |
402 | attach_ebpf(fd1, 10); | |
403 | attach_cbpf(fd2, 10); | |
404 | ||
405 | close(fd1); | |
406 | close(fd2); | |
407 | } | |
408 | ||
4b2a6aed CG |
409 | void enable_fastopen(void) |
410 | { | |
411 | int fd = open("/proc/sys/net/ipv4/tcp_fastopen", 0); | |
412 | int rw_mask = 3; /* bit 1: client side; bit-2 server side */ | |
413 | int val, size; | |
414 | char buf[16]; | |
415 | ||
416 | if (fd < 0) | |
417 | error(1, errno, "Unable to open tcp_fastopen sysctl"); | |
418 | if (read(fd, buf, sizeof(buf)) <= 0) | |
419 | error(1, errno, "Unable to read tcp_fastopen sysctl"); | |
420 | val = atoi(buf); | |
421 | close(fd); | |
422 | ||
423 | if ((val & rw_mask) != rw_mask) { | |
424 | fd = open("/proc/sys/net/ipv4/tcp_fastopen", O_RDWR); | |
425 | if (fd < 0) | |
426 | error(1, errno, | |
427 | "Unable to open tcp_fastopen sysctl for writing"); | |
428 | val |= rw_mask; | |
429 | size = snprintf(buf, 16, "%d", val); | |
430 | if (write(fd, buf, size) <= 0) | |
431 | error(1, errno, "Unable to write tcp_fastopen sysctl"); | |
432 | close(fd); | |
433 | } | |
434 | } | |
3ca8e402 CG |
435 | |
436 | int main(void) | |
437 | { | |
438 | fprintf(stderr, "---- IPv4 UDP ----\n"); | |
1134158b CG |
439 | /* NOTE: UDP socket lookups traverse a different code path when there |
440 | * are > 10 sockets in a group. Run the bpf test through both paths. | |
441 | */ | |
3ca8e402 CG |
442 | test_reuseport_ebpf((struct test_params) { |
443 | .recv_family = AF_INET, | |
444 | .send_family = AF_INET, | |
445 | .protocol = SOCK_DGRAM, | |
446 | .recv_socks = 10, | |
447 | .recv_port = 8000, | |
448 | .send_port_min = 9000}); | |
1134158b CG |
449 | test_reuseport_ebpf((struct test_params) { |
450 | .recv_family = AF_INET, | |
451 | .send_family = AF_INET, | |
452 | .protocol = SOCK_DGRAM, | |
453 | .recv_socks = 20, | |
454 | .recv_port = 8000, | |
455 | .send_port_min = 9000}); | |
3ca8e402 CG |
456 | test_reuseport_cbpf((struct test_params) { |
457 | .recv_family = AF_INET, | |
458 | .send_family = AF_INET, | |
459 | .protocol = SOCK_DGRAM, | |
460 | .recv_socks = 10, | |
461 | .recv_port = 8001, | |
462 | .send_port_min = 9020}); | |
1134158b CG |
463 | test_reuseport_cbpf((struct test_params) { |
464 | .recv_family = AF_INET, | |
465 | .send_family = AF_INET, | |
466 | .protocol = SOCK_DGRAM, | |
467 | .recv_socks = 20, | |
468 | .recv_port = 8001, | |
469 | .send_port_min = 9020}); | |
3ca8e402 CG |
470 | test_extra_filter((struct test_params) { |
471 | .recv_family = AF_INET, | |
472 | .protocol = SOCK_DGRAM, | |
473 | .recv_port = 8002}); | |
474 | test_filter_no_reuseport((struct test_params) { | |
475 | .recv_family = AF_INET, | |
476 | .protocol = SOCK_DGRAM, | |
477 | .recv_port = 8008}); | |
478 | ||
479 | fprintf(stderr, "---- IPv6 UDP ----\n"); | |
480 | test_reuseport_ebpf((struct test_params) { | |
481 | .recv_family = AF_INET6, | |
482 | .send_family = AF_INET6, | |
483 | .protocol = SOCK_DGRAM, | |
484 | .recv_socks = 10, | |
485 | .recv_port = 8003, | |
486 | .send_port_min = 9040}); | |
1134158b CG |
487 | test_reuseport_ebpf((struct test_params) { |
488 | .recv_family = AF_INET6, | |
489 | .send_family = AF_INET6, | |
490 | .protocol = SOCK_DGRAM, | |
491 | .recv_socks = 20, | |
492 | .recv_port = 8003, | |
493 | .send_port_min = 9040}); | |
3ca8e402 CG |
494 | test_reuseport_cbpf((struct test_params) { |
495 | .recv_family = AF_INET6, | |
496 | .send_family = AF_INET6, | |
497 | .protocol = SOCK_DGRAM, | |
498 | .recv_socks = 10, | |
499 | .recv_port = 8004, | |
500 | .send_port_min = 9060}); | |
1134158b CG |
501 | test_reuseport_cbpf((struct test_params) { |
502 | .recv_family = AF_INET6, | |
503 | .send_family = AF_INET6, | |
504 | .protocol = SOCK_DGRAM, | |
505 | .recv_socks = 20, | |
506 | .recv_port = 8004, | |
507 | .send_port_min = 9060}); | |
3ca8e402 CG |
508 | test_extra_filter((struct test_params) { |
509 | .recv_family = AF_INET6, | |
510 | .protocol = SOCK_DGRAM, | |
511 | .recv_port = 8005}); | |
512 | test_filter_no_reuseport((struct test_params) { | |
513 | .recv_family = AF_INET6, | |
514 | .protocol = SOCK_DGRAM, | |
515 | .recv_port = 8009}); | |
516 | ||
517 | fprintf(stderr, "---- IPv6 UDP w/ mapped IPv4 ----\n"); | |
1134158b CG |
518 | test_reuseport_ebpf((struct test_params) { |
519 | .recv_family = AF_INET6, | |
520 | .send_family = AF_INET, | |
521 | .protocol = SOCK_DGRAM, | |
522 | .recv_socks = 20, | |
523 | .recv_port = 8006, | |
524 | .send_port_min = 9080}); | |
3ca8e402 CG |
525 | test_reuseport_ebpf((struct test_params) { |
526 | .recv_family = AF_INET6, | |
527 | .send_family = AF_INET, | |
528 | .protocol = SOCK_DGRAM, | |
529 | .recv_socks = 10, | |
530 | .recv_port = 8006, | |
531 | .send_port_min = 9080}); | |
532 | test_reuseport_cbpf((struct test_params) { | |
533 | .recv_family = AF_INET6, | |
534 | .send_family = AF_INET, | |
535 | .protocol = SOCK_DGRAM, | |
536 | .recv_socks = 10, | |
537 | .recv_port = 8007, | |
538 | .send_port_min = 9100}); | |
1134158b CG |
539 | test_reuseport_cbpf((struct test_params) { |
540 | .recv_family = AF_INET6, | |
541 | .send_family = AF_INET, | |
542 | .protocol = SOCK_DGRAM, | |
543 | .recv_socks = 20, | |
544 | .recv_port = 8007, | |
545 | .send_port_min = 9100}); | |
3ca8e402 | 546 | |
4b2a6aed CG |
547 | /* TCP fastopen is required for the TCP tests */ |
548 | enable_fastopen(); | |
549 | fprintf(stderr, "---- IPv4 TCP ----\n"); | |
550 | test_reuseport_ebpf((struct test_params) { | |
551 | .recv_family = AF_INET, | |
552 | .send_family = AF_INET, | |
553 | .protocol = SOCK_STREAM, | |
554 | .recv_socks = 10, | |
555 | .recv_port = 8008, | |
556 | .send_port_min = 9120}); | |
557 | test_reuseport_cbpf((struct test_params) { | |
558 | .recv_family = AF_INET, | |
559 | .send_family = AF_INET, | |
560 | .protocol = SOCK_STREAM, | |
561 | .recv_socks = 10, | |
562 | .recv_port = 8009, | |
563 | .send_port_min = 9160}); | |
564 | test_extra_filter((struct test_params) { | |
565 | .recv_family = AF_INET, | |
566 | .protocol = SOCK_STREAM, | |
567 | .recv_port = 8010}); | |
568 | test_filter_no_reuseport((struct test_params) { | |
569 | .recv_family = AF_INET, | |
570 | .protocol = SOCK_STREAM, | |
571 | .recv_port = 8011}); | |
572 | ||
573 | fprintf(stderr, "---- IPv6 TCP ----\n"); | |
574 | test_reuseport_ebpf((struct test_params) { | |
575 | .recv_family = AF_INET6, | |
576 | .send_family = AF_INET6, | |
577 | .protocol = SOCK_STREAM, | |
578 | .recv_socks = 10, | |
579 | .recv_port = 8012, | |
580 | .send_port_min = 9200}); | |
581 | test_reuseport_cbpf((struct test_params) { | |
582 | .recv_family = AF_INET6, | |
583 | .send_family = AF_INET6, | |
584 | .protocol = SOCK_STREAM, | |
585 | .recv_socks = 10, | |
586 | .recv_port = 8013, | |
587 | .send_port_min = 9240}); | |
588 | test_extra_filter((struct test_params) { | |
589 | .recv_family = AF_INET6, | |
590 | .protocol = SOCK_STREAM, | |
591 | .recv_port = 8014}); | |
592 | test_filter_no_reuseport((struct test_params) { | |
593 | .recv_family = AF_INET6, | |
594 | .protocol = SOCK_STREAM, | |
595 | .recv_port = 8015}); | |
596 | ||
597 | fprintf(stderr, "---- IPv6 TCP w/ mapped IPv4 ----\n"); | |
598 | test_reuseport_ebpf((struct test_params) { | |
599 | .recv_family = AF_INET6, | |
600 | .send_family = AF_INET, | |
601 | .protocol = SOCK_STREAM, | |
602 | .recv_socks = 10, | |
603 | .recv_port = 8016, | |
604 | .send_port_min = 9320}); | |
605 | test_reuseport_cbpf((struct test_params) { | |
606 | .recv_family = AF_INET6, | |
607 | .send_family = AF_INET, | |
608 | .protocol = SOCK_STREAM, | |
609 | .recv_socks = 10, | |
610 | .recv_port = 8017, | |
611 | .send_port_min = 9360}); | |
3ca8e402 CG |
612 | |
613 | test_filter_without_bind(); | |
614 | ||
615 | fprintf(stderr, "SUCCESS\n"); | |
616 | return 0; | |
617 | } |