1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #define _GNU_SOURCE |
3 | |
4 | #include <errno.h> |
5 | #include <stdbool.h> |
6 | #include <stdio.h> |
7 | #include <string.h> |
8 | #include <unistd.h> |
9 | #include <sched.h> |
10 | |
11 | #include <arpa/inet.h> |
12 | #include <sys/mount.h> |
13 | #include <sys/stat.h> |
14 | #include <sys/un.h> |
15 | |
16 | #include <linux/err.h> |
17 | #include <linux/in.h> |
18 | #include <linux/in6.h> |
19 | #include <linux/limits.h> |
20 | |
21 | #include "bpf_util.h" |
22 | #include "network_helpers.h" |
23 | #include "test_progs.h" |
24 | |
25 | #ifndef IPPROTO_MPTCP |
26 | #define IPPROTO_MPTCP 262 |
27 | #endif |
28 | |
29 | #define clean_errno() (errno == 0 ? "None" : strerror(errno)) |
30 | #define log_err(MSG, ...) ({ \ |
31 | int __save = errno; \ |
32 | fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ |
33 | __FILE__, __LINE__, clean_errno(), \ |
34 | ##__VA_ARGS__); \ |
35 | errno = __save; \ |
36 | }) |
37 | |
38 | struct ipv4_packet pkt_v4 = { |
39 | .eth.h_proto = __bpf_constant_htons(ETH_P_IP), |
40 | .iph.ihl = 5, |
41 | .iph.protocol = IPPROTO_TCP, |
42 | .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), |
43 | .tcp.urg_ptr = 123, |
44 | .tcp.doff = 5, |
45 | }; |
46 | |
47 | struct ipv6_packet pkt_v6 = { |
48 | .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), |
49 | .iph.nexthdr = IPPROTO_TCP, |
50 | .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), |
51 | .tcp.urg_ptr = 123, |
52 | .tcp.doff = 5, |
53 | }; |
54 | |
55 | int settimeo(int fd, int timeout_ms) |
56 | { |
57 | struct timeval timeout = { .tv_sec = 3 }; |
58 | |
59 | if (timeout_ms > 0) { |
60 | timeout.tv_sec = timeout_ms / 1000; |
61 | timeout.tv_usec = (timeout_ms % 1000) * 1000; |
62 | } |
63 | |
64 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, |
65 | sizeof(timeout))) { |
66 | log_err("Failed to set SO_RCVTIMEO" ); |
67 | return -1; |
68 | } |
69 | |
70 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, |
71 | sizeof(timeout))) { |
72 | log_err("Failed to set SO_SNDTIMEO" ); |
73 | return -1; |
74 | } |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | #define save_errno_close(fd) ({ int __save = errno; close(fd); errno = __save; }) |
80 | |
81 | static int __start_server(int type, int protocol, const struct sockaddr *addr, |
82 | socklen_t addrlen, int timeout_ms, bool reuseport) |
83 | { |
84 | int on = 1; |
85 | int fd; |
86 | |
87 | fd = socket(addr->sa_family, type, protocol); |
88 | if (fd < 0) { |
89 | log_err("Failed to create server socket" ); |
90 | return -1; |
91 | } |
92 | |
93 | if (settimeo(fd, timeout_ms)) |
94 | goto error_close; |
95 | |
96 | if (reuseport && |
97 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) { |
98 | log_err("Failed to set SO_REUSEPORT" ); |
99 | goto error_close; |
100 | } |
101 | |
102 | if (bind(fd, addr, addrlen) < 0) { |
103 | log_err("Failed to bind socket" ); |
104 | goto error_close; |
105 | } |
106 | |
107 | if (type == SOCK_STREAM) { |
108 | if (listen(fd, 1) < 0) { |
109 | log_err("Failed to listed on socket" ); |
110 | goto error_close; |
111 | } |
112 | } |
113 | |
114 | return fd; |
115 | |
116 | error_close: |
117 | save_errno_close(fd); |
118 | return -1; |
119 | } |
120 | |
121 | static int start_server_proto(int family, int type, int protocol, |
122 | const char *addr_str, __u16 port, int timeout_ms) |
123 | { |
124 | struct sockaddr_storage addr; |
125 | socklen_t addrlen; |
126 | |
127 | if (make_sockaddr(family, addr_str, port, &addr, &addrlen)) |
128 | return -1; |
129 | |
130 | return __start_server(type, protocol, (struct sockaddr *)&addr, |
131 | addrlen, timeout_ms, false); |
132 | } |
133 | |
134 | int start_server(int family, int type, const char *addr_str, __u16 port, |
135 | int timeout_ms) |
136 | { |
137 | return start_server_proto(family, type, protocol: 0, addr_str, port, timeout_ms); |
138 | } |
139 | |
140 | int start_mptcp_server(int family, const char *addr_str, __u16 port, |
141 | int timeout_ms) |
142 | { |
143 | return start_server_proto(family, type: SOCK_STREAM, IPPROTO_MPTCP, addr_str, |
144 | port, timeout_ms); |
145 | } |
146 | |
147 | int *start_reuseport_server(int family, int type, const char *addr_str, |
148 | __u16 port, int timeout_ms, unsigned int nr_listens) |
149 | { |
150 | struct sockaddr_storage addr; |
151 | unsigned int nr_fds = 0; |
152 | socklen_t addrlen; |
153 | int *fds; |
154 | |
155 | if (!nr_listens) |
156 | return NULL; |
157 | |
158 | if (make_sockaddr(family, addr_str, port, &addr, &addrlen)) |
159 | return NULL; |
160 | |
161 | fds = malloc(sizeof(*fds) * nr_listens); |
162 | if (!fds) |
163 | return NULL; |
164 | |
165 | fds[0] = __start_server(type, 0, (struct sockaddr *)&addr, addrlen, |
166 | timeout_ms, true); |
167 | if (fds[0] == -1) |
168 | goto close_fds; |
169 | nr_fds = 1; |
170 | |
171 | if (getsockname(fds[0], (struct sockaddr *)&addr, &addrlen)) |
172 | goto close_fds; |
173 | |
174 | for (; nr_fds < nr_listens; nr_fds++) { |
175 | fds[nr_fds] = __start_server(type, 0, (struct sockaddr *)&addr, |
176 | addrlen, timeout_ms, true); |
177 | if (fds[nr_fds] == -1) |
178 | goto close_fds; |
179 | } |
180 | |
181 | return fds; |
182 | |
183 | close_fds: |
184 | free_fds(fds, nr_close_fds: nr_fds); |
185 | return NULL; |
186 | } |
187 | |
188 | void free_fds(int *fds, unsigned int nr_close_fds) |
189 | { |
190 | if (fds) { |
191 | while (nr_close_fds) |
192 | close(fds[--nr_close_fds]); |
193 | free(fds); |
194 | } |
195 | } |
196 | |
197 | int fastopen_connect(int server_fd, const char *data, unsigned int data_len, |
198 | int timeout_ms) |
199 | { |
200 | struct sockaddr_storage addr; |
201 | socklen_t addrlen = sizeof(addr); |
202 | struct sockaddr_in *addr_in; |
203 | int fd, ret; |
204 | |
205 | if (getsockname(server_fd, (struct sockaddr *)&addr, &addrlen)) { |
206 | log_err("Failed to get server addr" ); |
207 | return -1; |
208 | } |
209 | |
210 | addr_in = (struct sockaddr_in *)&addr; |
211 | fd = socket(addr_in->sin_family, SOCK_STREAM, 0); |
212 | if (fd < 0) { |
213 | log_err("Failed to create client socket" ); |
214 | return -1; |
215 | } |
216 | |
217 | if (settimeo(fd, timeout_ms)) |
218 | goto error_close; |
219 | |
220 | ret = sendto(fd, data, data_len, MSG_FASTOPEN, (struct sockaddr *)&addr, |
221 | addrlen); |
222 | if (ret != data_len) { |
223 | log_err("sendto(data, %u) != %d\n" , data_len, ret); |
224 | goto error_close; |
225 | } |
226 | |
227 | return fd; |
228 | |
229 | error_close: |
230 | save_errno_close(fd); |
231 | return -1; |
232 | } |
233 | |
234 | static int connect_fd_to_addr(int fd, |
235 | const struct sockaddr_storage *addr, |
236 | socklen_t addrlen, const bool must_fail) |
237 | { |
238 | int ret; |
239 | |
240 | errno = 0; |
241 | ret = connect(fd, (const struct sockaddr *)addr, addrlen); |
242 | if (must_fail) { |
243 | if (!ret) { |
244 | log_err("Unexpected success to connect to server" ); |
245 | return -1; |
246 | } |
247 | if (errno != EPERM) { |
248 | log_err("Unexpected error from connect to server" ); |
249 | return -1; |
250 | } |
251 | } else { |
252 | if (ret) { |
253 | log_err("Failed to connect to server" ); |
254 | return -1; |
255 | } |
256 | } |
257 | |
258 | return 0; |
259 | } |
260 | |
261 | int connect_to_addr(const struct sockaddr_storage *addr, socklen_t addrlen, int type) |
262 | { |
263 | int fd; |
264 | |
265 | fd = socket(addr->ss_family, type, 0); |
266 | if (fd < 0) { |
267 | log_err("Failed to create client socket" ); |
268 | return -1; |
269 | } |
270 | |
271 | if (connect_fd_to_addr(fd, addr, addrlen, false)) |
272 | goto error_close; |
273 | |
274 | return fd; |
275 | |
276 | error_close: |
277 | save_errno_close(fd); |
278 | return -1; |
279 | } |
280 | |
281 | static const struct network_helper_opts default_opts; |
282 | |
283 | int connect_to_fd_opts(int server_fd, const struct network_helper_opts *opts) |
284 | { |
285 | struct sockaddr_storage addr; |
286 | struct sockaddr_in *addr_in; |
287 | socklen_t addrlen, optlen; |
288 | int fd, type, protocol; |
289 | |
290 | if (!opts) |
291 | opts = &default_opts; |
292 | |
293 | optlen = sizeof(type); |
294 | |
295 | if (opts->type) { |
296 | type = opts->type; |
297 | } else { |
298 | if (getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &type, &optlen)) { |
299 | log_err("getsockopt(SOL_TYPE)" ); |
300 | return -1; |
301 | } |
302 | } |
303 | |
304 | if (opts->proto) { |
305 | protocol = opts->proto; |
306 | } else { |
307 | if (getsockopt(server_fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &optlen)) { |
308 | log_err("getsockopt(SOL_PROTOCOL)" ); |
309 | return -1; |
310 | } |
311 | } |
312 | |
313 | addrlen = sizeof(addr); |
314 | if (getsockname(server_fd, (struct sockaddr *)&addr, &addrlen)) { |
315 | log_err("Failed to get server addr" ); |
316 | return -1; |
317 | } |
318 | |
319 | addr_in = (struct sockaddr_in *)&addr; |
320 | fd = socket(addr_in->sin_family, type, protocol); |
321 | if (fd < 0) { |
322 | log_err("Failed to create client socket" ); |
323 | return -1; |
324 | } |
325 | |
326 | if (settimeo(fd, timeout_ms: opts->timeout_ms)) |
327 | goto error_close; |
328 | |
329 | if (opts->cc && opts->cc[0] && |
330 | setsockopt(fd, SOL_TCP, TCP_CONGESTION, opts->cc, |
331 | strlen(opts->cc) + 1)) |
332 | goto error_close; |
333 | |
334 | if (!opts->noconnect) |
335 | if (connect_fd_to_addr(fd, &addr, addrlen, opts->must_fail)) |
336 | goto error_close; |
337 | |
338 | return fd; |
339 | |
340 | error_close: |
341 | save_errno_close(fd); |
342 | return -1; |
343 | } |
344 | |
345 | int connect_to_fd(int server_fd, int timeout_ms) |
346 | { |
347 | struct network_helper_opts opts = { |
348 | .timeout_ms = timeout_ms, |
349 | }; |
350 | |
351 | return connect_to_fd_opts(server_fd, opts: &opts); |
352 | } |
353 | |
354 | int connect_fd_to_fd(int client_fd, int server_fd, int timeout_ms) |
355 | { |
356 | struct sockaddr_storage addr; |
357 | socklen_t len = sizeof(addr); |
358 | |
359 | if (settimeo(fd: client_fd, timeout_ms)) |
360 | return -1; |
361 | |
362 | if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) { |
363 | log_err("Failed to get server addr" ); |
364 | return -1; |
365 | } |
366 | |
367 | if (connect_fd_to_addr(client_fd, &addr, len, false)) |
368 | return -1; |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | int make_sockaddr(int family, const char *addr_str, __u16 port, |
374 | struct sockaddr_storage *addr, socklen_t *len) |
375 | { |
376 | if (family == AF_INET) { |
377 | struct sockaddr_in *sin = (void *)addr; |
378 | |
379 | memset(addr, 0, sizeof(*sin)); |
380 | sin->sin_family = AF_INET; |
381 | sin->sin_port = htons(port); |
382 | if (addr_str && |
383 | inet_pton(AF_INET, addr_str, &sin->sin_addr) != 1) { |
384 | log_err("inet_pton(AF_INET, %s)" , addr_str); |
385 | return -1; |
386 | } |
387 | if (len) |
388 | *len = sizeof(*sin); |
389 | return 0; |
390 | } else if (family == AF_INET6) { |
391 | struct sockaddr_in6 *sin6 = (void *)addr; |
392 | |
393 | memset(addr, 0, sizeof(*sin6)); |
394 | sin6->sin6_family = AF_INET6; |
395 | sin6->sin6_port = htons(port); |
396 | if (addr_str && |
397 | inet_pton(AF_INET6, addr_str, &sin6->sin6_addr) != 1) { |
398 | log_err("inet_pton(AF_INET6, %s)" , addr_str); |
399 | return -1; |
400 | } |
401 | if (len) |
402 | *len = sizeof(*sin6); |
403 | return 0; |
404 | } else if (family == AF_UNIX) { |
405 | /* Note that we always use abstract unix sockets to avoid having |
406 | * to clean up leftover files. |
407 | */ |
408 | struct sockaddr_un *sun = (void *)addr; |
409 | |
410 | memset(addr, 0, sizeof(*sun)); |
411 | sun->sun_family = family; |
412 | sun->sun_path[0] = 0; |
413 | strcpy(sun->sun_path + 1, addr_str); |
414 | if (len) |
415 | *len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(addr_str); |
416 | return 0; |
417 | } |
418 | return -1; |
419 | } |
420 | |
421 | char *ping_command(int family) |
422 | { |
423 | if (family == AF_INET6) { |
424 | /* On some systems 'ping' doesn't support IPv6, so use ping6 if it is present. */ |
425 | if (!system("which ping6 >/dev/null 2>&1" )) |
426 | return "ping6" ; |
427 | else |
428 | return "ping -6" ; |
429 | } |
430 | return "ping" ; |
431 | } |
432 | |
433 | struct nstoken { |
434 | int orig_netns_fd; |
435 | }; |
436 | |
437 | struct nstoken *open_netns(const char *name) |
438 | { |
439 | int nsfd; |
440 | char nspath[PATH_MAX]; |
441 | int err; |
442 | struct nstoken *token; |
443 | |
444 | token = calloc(1, sizeof(struct nstoken)); |
445 | if (!ASSERT_OK_PTR(token, "malloc token" )) |
446 | return NULL; |
447 | |
448 | token->orig_netns_fd = open("/proc/self/ns/net" , O_RDONLY); |
449 | if (!ASSERT_GE(token->orig_netns_fd, 0, "open /proc/self/ns/net" )) |
450 | goto fail; |
451 | |
452 | snprintf(buf: nspath, size: sizeof(nspath), fmt: "%s/%s" , "/var/run/netns" , name); |
453 | nsfd = open(nspath, O_RDONLY | O_CLOEXEC); |
454 | if (!ASSERT_GE(nsfd, 0, "open netns fd" )) |
455 | goto fail; |
456 | |
457 | err = setns(nsfd, CLONE_NEWNET); |
458 | close(nsfd); |
459 | if (!ASSERT_OK(err, "setns" )) |
460 | goto fail; |
461 | |
462 | return token; |
463 | fail: |
464 | free(token); |
465 | return NULL; |
466 | } |
467 | |
468 | void close_netns(struct nstoken *token) |
469 | { |
470 | if (!token) |
471 | return; |
472 | |
473 | ASSERT_OK(setns(token->orig_netns_fd, CLONE_NEWNET), "setns" ); |
474 | close(token->orig_netns_fd); |
475 | free(token); |
476 | } |
477 | |
478 | int get_socket_local_port(int sock_fd) |
479 | { |
480 | struct sockaddr_storage addr; |
481 | socklen_t addrlen = sizeof(addr); |
482 | int err; |
483 | |
484 | err = getsockname(sock_fd, (struct sockaddr *)&addr, &addrlen); |
485 | if (err < 0) |
486 | return err; |
487 | |
488 | if (addr.ss_family == AF_INET) { |
489 | struct sockaddr_in *sin = (struct sockaddr_in *)&addr; |
490 | |
491 | return sin->sin_port; |
492 | } else if (addr.ss_family == AF_INET6) { |
493 | struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&addr; |
494 | |
495 | return sin->sin6_port; |
496 | } |
497 | |
498 | return -1; |
499 | } |
500 | |