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