1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ |
4 | |
5 | /* Test listening on the same port 443 with multiple VIPS. |
6 | * Each VIP:443 will have multiple sk listening on by using |
7 | * SO_REUSEPORT. |
8 | */ |
9 | |
10 | #include <unistd.h> |
11 | #include <stdio.h> |
12 | #include <stdlib.h> |
13 | #include <error.h> |
14 | #include <errno.h> |
15 | #include <time.h> |
16 | #include <arpa/inet.h> |
17 | |
18 | #define IP6_LADDR_START "2401:dead::1" |
19 | #define IP6_LPORT 443 |
20 | #define NSEC_PER_SEC 1000000000L |
21 | #define NSEC_PER_USEC 1000L |
22 | |
23 | static unsigned int nr_socks_per_vip; |
24 | static unsigned int nr_vips; |
25 | |
26 | static int *bind_reuseport_sock6(void) |
27 | { |
28 | int *lfds, *cur_fd, err, optvalue = 1; |
29 | struct sockaddr_in6 sa6 = {}; |
30 | unsigned int i, j; |
31 | |
32 | sa6.sin6_family = AF_INET6; |
33 | sa6.sin6_port = htons(IP6_LPORT); |
34 | err = inet_pton(AF_INET6, IP6_LADDR_START, &sa6.sin6_addr); |
35 | if (err != 1) |
36 | error(1, err, "inet_pton(%s)" , IP6_LADDR_START); |
37 | |
38 | lfds = malloc(nr_vips * nr_socks_per_vip * sizeof(lfds[0])); |
39 | if (!lfds) |
40 | error(1, errno, "cannot alloc array of lfds" ); |
41 | |
42 | cur_fd = lfds; |
43 | for (i = 0; i < nr_vips; i++) { |
44 | for (j = 0; j < nr_socks_per_vip; j++) { |
45 | *cur_fd = socket(AF_INET6, SOCK_STREAM, 0); |
46 | if (*cur_fd == -1) |
47 | error(1, errno, |
48 | "lfds[%u,%u] = socket(AF_INET6)" , i, j); |
49 | |
50 | err = setsockopt(*cur_fd, SOL_SOCKET, SO_REUSEPORT, |
51 | &optvalue, sizeof(optvalue)); |
52 | if (err) |
53 | error(1, errno, |
54 | "setsockopt(lfds[%u,%u], SO_REUSEPORT)" , |
55 | i, j); |
56 | |
57 | err = bind(*cur_fd, (struct sockaddr *)&sa6, |
58 | sizeof(sa6)); |
59 | if (err) |
60 | error(1, errno, "bind(lfds[%u,%u])" , i, j); |
61 | cur_fd++; |
62 | } |
63 | sa6.sin6_addr.s6_addr32[3]++; |
64 | } |
65 | |
66 | return lfds; |
67 | } |
68 | |
69 | int main(int argc, const char *argv[]) |
70 | { |
71 | struct timespec start_ts, end_ts; |
72 | unsigned long start_ns, end_ns; |
73 | unsigned int nr_lsocks; |
74 | int *lfds, i, err; |
75 | |
76 | if (argc != 3 || atoi(argv[1]) <= 0 || atoi(argv[2]) <= 0) |
77 | error(1, 0, "Usage: %s <nr_vips> <nr_socks_per_vip>\n" , |
78 | argv[0]); |
79 | |
80 | nr_vips = atoi(argv[1]); |
81 | nr_socks_per_vip = atoi(argv[2]); |
82 | nr_lsocks = nr_vips * nr_socks_per_vip; |
83 | lfds = bind_reuseport_sock6(); |
84 | |
85 | clock_gettime(CLOCK_MONOTONIC, &start_ts); |
86 | for (i = 0; i < nr_lsocks; i++) { |
87 | err = listen(lfds[i], 0); |
88 | if (err) |
89 | error(1, errno, "listen(lfds[%d])" , i); |
90 | } |
91 | clock_gettime(CLOCK_MONOTONIC, &end_ts); |
92 | |
93 | start_ns = start_ts.tv_sec * NSEC_PER_SEC + start_ts.tv_nsec; |
94 | end_ns = end_ts.tv_sec * NSEC_PER_SEC + end_ts.tv_nsec; |
95 | |
96 | printf("listen %d socks took %lu.%lu\n" , nr_lsocks, |
97 | (end_ns - start_ns) / NSEC_PER_SEC, |
98 | (end_ns - start_ns) / NSEC_PER_USEC); |
99 | |
100 | for (i = 0; i < nr_lsocks; i++) |
101 | close(lfds[i]); |
102 | |
103 | free(lfds); |
104 | return 0; |
105 | } |
106 | |