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