X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fcore%2Fsocket.c;h=836e513d49d9b09aed899e81aa26fb22ba88d3ff;hb=4ff9bc2ea6e5daf08b5c9de17c0dcf16c3b7379e;hp=af95e9027e187de575d40490581f5b42ab5af693;hpb=88a56c767044b06529dd80180fb492d65aae121e;p=thirdparty%2Fsystemd.git diff --git a/src/core/socket.c b/src/core/socket.c index af95e9027e1..836e513d49d 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1473,6 +1473,25 @@ static int socket_address_listen_do( log_unit_error_errno(u, error, fmt, strna(_t)); \ }) +static int fork_needed(const SocketAddress *address, const ExecContext *context) { + int r; + + assert(address); + assert(context); + + /* Check if we need to do the cgroup or netns stuff. If not we can do things much simpler. */ + + if (IN_SET(address->sockaddr.sa.sa_family, AF_INET, AF_INET6)) { + r = bpf_firewall_supported(); + if (r < 0) + return r; + if (r != BPF_FIREWALL_UNSUPPORTED) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */ + return true; + } + + return context->private_network || context->network_namespace_path; +} + static int socket_address_listen_in_cgroup( Socket *s, const SocketAddress *address, @@ -1485,18 +1504,34 @@ static int socket_address_listen_in_cgroup( assert(s); assert(address); - /* This is a wrapper around socket_address_listen(), that forks off a helper process inside the socket's cgroup - * in which the socket is actually created. This way we ensure the socket is actually properly attached to the - * unit's cgroup for the purpose of BPF filtering and such. */ - - if (!IN_SET(address->sockaddr.sa.sa_family, AF_INET, AF_INET6)) - goto shortcut; /* BPF filtering only applies to IPv4 + IPv6, shortcut things for other protocols */ + /* This is a wrapper around socket_address_listen(), that forks off a helper process inside the + * socket's cgroup and network namespace in which the socket is actually created. This way we ensure + * the socket is actually properly attached to the unit's cgroup for the purpose of BPF filtering and + * such. */ - r = bpf_firewall_supported(); + r = fork_needed(address, &s->exec_context); if (r < 0) return r; - if (r == BPF_FIREWALL_UNSUPPORTED) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */ - goto shortcut; + if (r == 0) { + /* Shortcut things... */ + fd = socket_address_listen_do(s, address, label); + if (fd < 0) + return log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); + + return fd; + } + + r = unit_setup_exec_runtime(UNIT(s)); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed acquire runtime: %m"); + + if (s->exec_context.network_namespace_path && + s->exec_runtime && + s->exec_runtime->netns_storage_socket[0] >= 0) { + r = open_netns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path); + } if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m"); @@ -1509,6 +1544,23 @@ static int socket_address_listen_in_cgroup( pair[0] = safe_close(pair[0]); + if ((s->exec_context.private_network || s->exec_context.network_namespace_path) && + s->exec_runtime && + s->exec_runtime->netns_storage_socket[0] >= 0) { + + if (ns_type_supported(NAMESPACE_NET)) { + r = setup_netns(s->exec_runtime->netns_storage_socket); + if (r < 0) { + log_unit_error_errno(UNIT(s), r, "Failed to join network namespace: %m"); + _exit(EXIT_NETWORK); + } + } else if (s->exec_context.network_namespace_path) { + log_unit_error(UNIT(s), "Network namespace path configured but network namespaces not supported."); + _exit(EXIT_NETWORK); + } else + log_unit_warning(UNIT(s), "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring."); + } + fd = socket_address_listen_do(s, address, label); if (fd < 0) { log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); @@ -1538,13 +1590,6 @@ static int socket_address_listen_in_cgroup( return log_address_error_errno(UNIT(s), address, fd, "Failed to receive listening socket (%s): %m"); return fd; - -shortcut: - fd = socket_address_listen_do(s, address, label); - if (fd < 0) - return log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); - - return fd; } DEFINE_TRIVIAL_CLEANUP_FUNC(Socket *, socket_close_fds); @@ -1807,7 +1852,7 @@ static int socket_coldplug(Unit *u) { SOCKET_FINAL_SIGTERM, SOCKET_FINAL_SIGKILL)) { - r = unit_watch_pid(UNIT(s), s->control_pid); + r = unit_watch_pid(UNIT(s), s->control_pid, false); if (r < 0) return r; @@ -1893,9 +1938,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, true); if (r < 0) - /* FIXME: we need to do something here */ return r; *_pid = pid; @@ -1964,7 +2008,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { _exit(EXIT_SUCCESS); } - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, true); if (r < 0) goto fail; @@ -2266,7 +2310,7 @@ static void socket_enter_running(Socket *s, int cfd) { goto fail; } - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto fail; } @@ -2341,7 +2385,7 @@ static void socket_enter_running(Socket *s, int cfd) { service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) { /* We failed to activate the new service, but it still exists. Let's make sure the service * closes and forgets the connection fd again, immediately. */ @@ -2442,7 +2486,7 @@ static int socket_start(Unit *u) { assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED)); - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); return r; @@ -2812,17 +2856,10 @@ static int socket_accept_do(Socket *s, int fd) { assert(s); assert(fd >= 0); - for (;;) { - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); - if (cfd < 0) { - if (errno == EINTR) - continue; - - return -errno; - } - - break; - } + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) + /* Convert transient network errors into clean and well-defined EAGAIN */ + return ERRNO_IS_ACCEPT_AGAIN(errno) ? -EAGAIN : -errno; return cfd; } @@ -2860,6 +2897,8 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { pair[0] = safe_close(pair[0]); cfd = socket_accept_do(s, fd); + if (cfd == -EAGAIN) /* spurious accept() */ + _exit(EXIT_SUCCESS); if (cfd < 0) { log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m"); _exit(EXIT_FAILURE); @@ -2884,6 +2923,10 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { return r; } + /* If we received no fd, we got EIO here. If this happens with a process exit code of EXIT_SUCCESS + * this is a spurious accept(), let's convert that back to EAGAIN here. */ + if (cfd == -EIO) + return -EAGAIN; if (cfd < 0) return log_unit_error_errno(UNIT(s), cfd, "Failed to receive connection socket: %m"); @@ -2891,6 +2934,8 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { shortcut: cfd = socket_accept_do(s, fd); + if (cfd == -EAGAIN) /* spurious accept(), skip it silently */ + return -EAGAIN; if (cfd < 0) return log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m"); @@ -2910,7 +2955,6 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, log_unit_debug(UNIT(p->socket), "Incoming traffic"); if (revents != EPOLLIN) { - if (revents & EPOLLHUP) log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); else @@ -2923,6 +2967,8 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, socket_address_can_accept(&p->address)) { cfd = socket_accept_in_cgroup(p->socket, p, fd); + if (cfd == -EAGAIN) /* Spurious accept() */ + return 0; if (cfd < 0) goto fail;