1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Test that sockets listening on a specific address are preferred |
4 | * over sockets listening on addr_any. |
5 | */ |
6 | |
7 | #define _GNU_SOURCE |
8 | |
9 | #include <arpa/inet.h> |
10 | #include <errno.h> |
11 | #include <error.h> |
12 | #include <linux/dccp.h> |
13 | #include <linux/in.h> |
14 | #include <linux/unistd.h> |
15 | #include <stdbool.h> |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | #include <sys/epoll.h> |
20 | #include <sys/types.h> |
21 | #include <sys/socket.h> |
22 | #include <unistd.h> |
23 | |
24 | #ifndef SOL_DCCP |
25 | #define SOL_DCCP 269 |
26 | #endif |
27 | |
28 | static const char *IP4_ADDR = "127.0.0.1" ; |
29 | static const char *IP6_ADDR = "::1" ; |
30 | static const char *IP4_MAPPED6 = "::ffff:127.0.0.1" ; |
31 | |
32 | static const int PORT = 8888; |
33 | |
34 | static void build_rcv_fd(int family, int proto, int *rcv_fds, int count, |
35 | const char *addr_str) |
36 | { |
37 | struct sockaddr_in addr4 = {0}; |
38 | struct sockaddr_in6 addr6 = {0}; |
39 | struct sockaddr *addr; |
40 | int opt, i, sz; |
41 | |
42 | memset(&addr, 0, sizeof(addr)); |
43 | |
44 | switch (family) { |
45 | case AF_INET: |
46 | addr4.sin_family = family; |
47 | if (!addr_str) |
48 | addr4.sin_addr.s_addr = htonl(INADDR_ANY); |
49 | else if (!inet_pton(family, addr_str, &addr4.sin_addr.s_addr)) |
50 | error(1, errno, "inet_pton failed: %s" , addr_str); |
51 | addr4.sin_port = htons(PORT); |
52 | sz = sizeof(addr4); |
53 | addr = (struct sockaddr *)&addr4; |
54 | break; |
55 | case AF_INET6: |
56 | addr6.sin6_family = AF_INET6; |
57 | if (!addr_str) |
58 | addr6.sin6_addr = in6addr_any; |
59 | else if (!inet_pton(family, addr_str, &addr6.sin6_addr)) |
60 | error(1, errno, "inet_pton failed: %s" , addr_str); |
61 | addr6.sin6_port = htons(PORT); |
62 | sz = sizeof(addr6); |
63 | addr = (struct sockaddr *)&addr6; |
64 | break; |
65 | default: |
66 | error(1, 0, "Unsupported family %d" , family); |
67 | /* clang does not recognize error() above as terminating |
68 | * the program, so it complains that saddr, sz are |
69 | * not initialized when this code path is taken. Silence it. |
70 | */ |
71 | return; |
72 | } |
73 | |
74 | for (i = 0; i < count; ++i) { |
75 | rcv_fds[i] = socket(family, proto, 0); |
76 | if (rcv_fds[i] < 0) |
77 | error(1, errno, "failed to create receive socket" ); |
78 | |
79 | opt = 1; |
80 | if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt, |
81 | sizeof(opt))) |
82 | error(1, errno, "failed to set SO_REUSEPORT" ); |
83 | |
84 | if (bind(rcv_fds[i], addr, sz)) |
85 | error(1, errno, "failed to bind receive socket" ); |
86 | |
87 | if (proto == SOCK_STREAM && listen(rcv_fds[i], 10)) |
88 | error(1, errno, "tcp: failed to listen on receive port" ); |
89 | else if (proto == SOCK_DCCP) { |
90 | if (setsockopt(rcv_fds[i], SOL_DCCP, |
91 | DCCP_SOCKOPT_SERVICE, |
92 | &(int) {htonl(42)}, sizeof(int))) |
93 | error(1, errno, "failed to setsockopt" ); |
94 | |
95 | if (listen(rcv_fds[i], 10)) |
96 | error(1, errno, "dccp: failed to listen on receive port" ); |
97 | } |
98 | } |
99 | } |
100 | |
101 | static int connect_and_send(int family, int proto) |
102 | { |
103 | struct sockaddr_in saddr4 = {0}; |
104 | struct sockaddr_in daddr4 = {0}; |
105 | struct sockaddr_in6 saddr6 = {0}; |
106 | struct sockaddr_in6 daddr6 = {0}; |
107 | struct sockaddr *saddr, *daddr; |
108 | int fd, sz; |
109 | |
110 | switch (family) { |
111 | case AF_INET: |
112 | saddr4.sin_family = AF_INET; |
113 | saddr4.sin_addr.s_addr = htonl(INADDR_ANY); |
114 | saddr4.sin_port = 0; |
115 | |
116 | daddr4.sin_family = AF_INET; |
117 | if (!inet_pton(family, IP4_ADDR, &daddr4.sin_addr.s_addr)) |
118 | error(1, errno, "inet_pton failed: %s" , IP4_ADDR); |
119 | daddr4.sin_port = htons(PORT); |
120 | |
121 | sz = sizeof(saddr4); |
122 | saddr = (struct sockaddr *)&saddr4; |
123 | daddr = (struct sockaddr *)&daddr4; |
124 | break; |
125 | case AF_INET6: |
126 | saddr6.sin6_family = AF_INET6; |
127 | saddr6.sin6_addr = in6addr_any; |
128 | |
129 | daddr6.sin6_family = AF_INET6; |
130 | if (!inet_pton(family, IP6_ADDR, &daddr6.sin6_addr)) |
131 | error(1, errno, "inet_pton failed: %s" , IP6_ADDR); |
132 | daddr6.sin6_port = htons(PORT); |
133 | |
134 | sz = sizeof(saddr6); |
135 | saddr = (struct sockaddr *)&saddr6; |
136 | daddr = (struct sockaddr *)&daddr6; |
137 | break; |
138 | default: |
139 | error(1, 0, "Unsupported family %d" , family); |
140 | /* clang does not recognize error() above as terminating |
141 | * the program, so it complains that saddr, daddr, sz are |
142 | * not initialized when this code path is taken. Silence it. |
143 | */ |
144 | return -1; |
145 | } |
146 | |
147 | fd = socket(family, proto, 0); |
148 | if (fd < 0) |
149 | error(1, errno, "failed to create send socket" ); |
150 | |
151 | if (proto == SOCK_DCCP && |
152 | setsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, |
153 | &(int){htonl(42)}, sizeof(int))) |
154 | error(1, errno, "failed to setsockopt" ); |
155 | |
156 | if (bind(fd, saddr, sz)) |
157 | error(1, errno, "failed to bind send socket" ); |
158 | |
159 | if (connect(fd, daddr, sz)) |
160 | error(1, errno, "failed to connect send socket" ); |
161 | |
162 | if (send(fd, "a" , 1, 0) < 0) |
163 | error(1, errno, "failed to send message" ); |
164 | |
165 | return fd; |
166 | } |
167 | |
168 | static int receive_once(int epfd, int proto) |
169 | { |
170 | struct epoll_event ev; |
171 | int i, fd; |
172 | char buf[8]; |
173 | |
174 | i = epoll_wait(epfd, &ev, 1, 3); |
175 | if (i < 0) |
176 | error(1, errno, "epoll_wait failed" ); |
177 | |
178 | if (proto == SOCK_STREAM || proto == SOCK_DCCP) { |
179 | fd = accept(ev.data.fd, NULL, NULL); |
180 | if (fd < 0) |
181 | error(1, errno, "failed to accept" ); |
182 | i = recv(fd, buf, sizeof(buf), 0); |
183 | close(fd); |
184 | } else { |
185 | i = recv(ev.data.fd, buf, sizeof(buf), 0); |
186 | } |
187 | |
188 | if (i < 0) |
189 | error(1, errno, "failed to recv" ); |
190 | |
191 | return ev.data.fd; |
192 | } |
193 | |
194 | static void test(int *rcv_fds, int count, int family, int proto, int fd) |
195 | { |
196 | struct epoll_event ev; |
197 | int epfd, i, send_fd, recv_fd; |
198 | |
199 | epfd = epoll_create(1); |
200 | if (epfd < 0) |
201 | error(1, errno, "failed to create epoll" ); |
202 | |
203 | ev.events = EPOLLIN; |
204 | for (i = 0; i < count; ++i) { |
205 | ev.data.fd = rcv_fds[i]; |
206 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev)) |
207 | error(1, errno, "failed to register sock epoll" ); |
208 | } |
209 | |
210 | send_fd = connect_and_send(family, proto); |
211 | |
212 | recv_fd = receive_once(epfd, proto); |
213 | if (recv_fd != fd) |
214 | error(1, 0, "received on an unexpected socket" ); |
215 | |
216 | close(send_fd); |
217 | close(epfd); |
218 | } |
219 | |
220 | |
221 | static void run_one_test(int fam_send, int fam_rcv, int proto, |
222 | const char *addr_str) |
223 | { |
224 | /* Below we test that a socket listening on a specific address |
225 | * is always selected in preference over a socket listening |
226 | * on addr_any. Bugs where this is not the case often result |
227 | * in sockets created first or last to get picked. So below |
228 | * we make sure that there are always addr_any sockets created |
229 | * before and after a specific socket is created. |
230 | */ |
231 | int rcv_fds[10], i; |
232 | |
233 | build_rcv_fd(AF_INET, proto, rcv_fds, count: 2, NULL); |
234 | build_rcv_fd(AF_INET6, proto, rcv_fds: rcv_fds + 2, count: 2, NULL); |
235 | build_rcv_fd(family: fam_rcv, proto, rcv_fds: rcv_fds + 4, count: 1, addr_str); |
236 | build_rcv_fd(AF_INET, proto, rcv_fds: rcv_fds + 5, count: 2, NULL); |
237 | build_rcv_fd(AF_INET6, proto, rcv_fds: rcv_fds + 7, count: 2, NULL); |
238 | test(rcv_fds, count: 9, family: fam_send, proto, fd: rcv_fds[4]); |
239 | for (i = 0; i < 9; ++i) |
240 | close(rcv_fds[i]); |
241 | fprintf(stderr, "pass\n" ); |
242 | } |
243 | |
244 | static void test_proto(int proto, const char *proto_str) |
245 | { |
246 | if (proto == SOCK_DCCP) { |
247 | int test_fd; |
248 | |
249 | test_fd = socket(AF_INET, proto, 0); |
250 | if (test_fd < 0) { |
251 | if (errno == ESOCKTNOSUPPORT) { |
252 | fprintf(stderr, "DCCP not supported: skipping DCCP tests\n" ); |
253 | return; |
254 | } else |
255 | error(1, errno, "failed to create a DCCP socket" ); |
256 | } |
257 | close(test_fd); |
258 | } |
259 | |
260 | fprintf(stderr, "%s IPv4 ... " , proto_str); |
261 | run_one_test(AF_INET, AF_INET, proto, addr_str: IP4_ADDR); |
262 | |
263 | fprintf(stderr, "%s IPv6 ... " , proto_str); |
264 | run_one_test(AF_INET6, AF_INET6, proto, addr_str: IP6_ADDR); |
265 | |
266 | fprintf(stderr, "%s IPv4 mapped to IPv6 ... " , proto_str); |
267 | run_one_test(AF_INET, AF_INET6, proto, addr_str: IP4_MAPPED6); |
268 | } |
269 | |
270 | int main(void) |
271 | { |
272 | test_proto(proto: SOCK_DGRAM, proto_str: "UDP" ); |
273 | test_proto(proto: SOCK_STREAM, proto_str: "TCP" ); |
274 | test_proto(proto: SOCK_DCCP, proto_str: "DCCP" ); |
275 | |
276 | fprintf(stderr, "SUCCESS\n" ); |
277 | return 0; |
278 | } |
279 | |