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