hc
2024-01-05 071106ecf68c401173c58808b1cf5f68cc50d390
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
 * Test for the regression introduced by
 *
 * b9470c27607b ("inet: kill smallest_size and smallest_port")
 *
 * If we open an ipv4 socket on a port with reuseaddr we shouldn't reset the tb
 * when we open the ipv6 conterpart, which is what was happening previously.
 */
#include <errno.h>
#include <error.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
 
#define PORT 9999
 
int open_port(int ipv6, int any)
{
   int fd = -1;
   int reuseaddr = 1;
   int v6only = 1;
   int addrlen;
   int ret = -1;
   struct sockaddr *addr;
   int family = ipv6 ? AF_INET6 : AF_INET;
 
   struct sockaddr_in6 addr6 = {
       .sin6_family = AF_INET6,
       .sin6_port = htons(PORT),
       .sin6_addr = in6addr_any
   };
   struct sockaddr_in addr4 = {
       .sin_family = AF_INET,
       .sin_port = htons(PORT),
       .sin_addr.s_addr = any ? htonl(INADDR_ANY) : inet_addr("127.0.0.1"),
   };
 
 
   if (ipv6) {
       addr = (struct sockaddr*)&addr6;
       addrlen = sizeof(addr6);
   } else {
       addr = (struct sockaddr*)&addr4;
       addrlen = sizeof(addr4);
   }
 
   if ((fd = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
       perror("socket");
       goto out;
   }
 
   if (ipv6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only,
                  sizeof(v6only)) < 0) {
       perror("setsockopt IPV6_V6ONLY");
       goto out;
   }
 
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
              sizeof(reuseaddr)) < 0) {
       perror("setsockopt SO_REUSEADDR");
       goto out;
   }
 
   if (bind(fd, addr, addrlen) < 0) {
       perror("bind");
       goto out;
   }
 
   if (any)
       return fd;
 
   if (listen(fd, 1) < 0) {
       perror("listen");
       goto out;
   }
   return fd;
out:
   close(fd);
   return ret;
}
 
int main(void)
{
   int listenfd;
   int fd1, fd2;
 
   fprintf(stderr, "Opening 127.0.0.1:%d\n", PORT);
   listenfd = open_port(0, 0);
   if (listenfd < 0)
       error(1, errno, "Couldn't open listen socket");
   fprintf(stderr, "Opening INADDR_ANY:%d\n", PORT);
   fd1 = open_port(0, 1);
   if (fd1 >= 0)
       error(1, 0, "Was allowed to create an ipv4 reuseport on a already bound non-reuseport socket");
   fprintf(stderr, "Opening in6addr_any:%d\n", PORT);
   fd1 = open_port(1, 1);
   if (fd1 < 0)
       error(1, errno, "Couldn't open ipv6 reuseport");
   fprintf(stderr, "Opening INADDR_ANY:%d\n", PORT);
   fd2 = open_port(0, 1);
   if (fd2 >= 0)
       error(1, 0, "Was allowed to create an ipv4 reuseport on a already bound non-reuseport socket");
   close(fd1);
   fprintf(stderr, "Opening INADDR_ANY:%d after closing ipv6 socket\n", PORT);
   fd1 = open_port(0, 1);
   if (fd1 >= 0)
       error(1, 0, "Was allowed to create an ipv4 reuseport on an already bound non-reuseport socket with no ipv6");
   fprintf(stderr, "Success");
   return 0;
}