diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 04062a7910..a23ff5b790 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -26,10 +26,33 @@ #include "missing.h" #include "netlink-util.h" -static int start_loopback(sd_netlink *rtnl) { +struct state { + unsigned n_messages; + int rcode; +}; + +static int start_loopback_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + struct state *s = userdata; + + assert(s); + assert(s->n_messages > 0); + s->n_messages--; + + errno = 0; + log_debug_errno(sd_netlink_message_get_errno(m), "Got start error code: %m"); + + s->rcode = sd_netlink_message_get_errno(m); + + return 0; +} + +static int start_loopback(sd_netlink *rtnl, struct state *s) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; + assert(rtnl); + assert(s); + r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); if (r < 0) return r; @@ -38,10 +61,95 @@ static int start_loopback(sd_netlink *rtnl) { if (r < 0) return r; - r = sd_netlink_call(rtnl, req, 0, NULL); + r = sd_netlink_call_async(rtnl, req, start_loopback_handler, s, USEC_INFINITY, NULL); if (r < 0) return r; + s->n_messages ++; + return 0; +} + +static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + struct state *s = userdata; + + assert(s); + assert(s->n_messages > 0); + s->n_messages--; + + /* Note that we don't really care whether the addresses could be added or not */ + errno = 0; + log_debug_errno(sd_netlink_message_get_errno(m), "Got address error code: %m"); + + return 0; +} + +static int add_ipv4_address(sd_netlink *rtnl, struct state *s) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(rtnl); + assert(s); + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_prefixlen(req, 8); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); + if (r < 0) + return r; + + r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } ); + if (r < 0) + return r; + + r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); + if (r < 0) + return r; + + s->n_messages ++; + return 0; +} + +static int add_ipv6_address(sd_netlink *rtnl, struct state *s) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(rtnl); + assert(s); + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_prefixlen(req, 128); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); + if (r < 0) + return r; + + r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback); + if (r < 0) + return r; + + r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); + if (r < 0) + return r; + + s->n_messages ++; return 0; } @@ -54,7 +162,7 @@ static bool check_loopback(sd_netlink *rtnl) { if (r < 0) return false; - r = sd_netlink_call(rtnl, req, 0, &reply); + r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply); if (r < 0) return false; @@ -67,23 +175,51 @@ static bool check_loopback(sd_netlink *rtnl) { int loopback_setup(void) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + struct state state = {}; int r; r = sd_netlink_open(&rtnl); if (r < 0) - return r; + return log_error_errno(r, "Failed to open netlink: %m"); - r = start_loopback(rtnl); - if (r < 0) { + /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when + * setting up the loopback device. The reason we do this here a second time (and possibly race against the + * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see + * + * https://github.com/systemd/systemd/issues/5641 */ + + r = add_ipv4_address(rtnl, &state); + if (r < 0) + return log_error_errno(r, "Failed to enqeue IPv4 loopback address add request: %m"); + + r = add_ipv6_address(rtnl, &state); + if (r < 0) + return log_error_errno(r, "Failed to enqeue IPv4 loopback address add request: %m"); + + r = start_loopback(rtnl, &state); + if (r < 0) + return log_error_errno(r, "Failed to enqeue loopback interface start request: %m"); + + while (state.n_messages > 0) { + r = sd_netlink_wait(rtnl, USEC_INFINITY); + if (r < 0) + return log_error_errno(r, "Failed to wait for netlink event: %m"); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to process netlink event: %m"); + } + + if (state.rcode != 0) { /* If we lack the permissions to configure the * loopback device, but we find it to be already * configured, let's exit cleanly, in order to * supported unprivileged containers. */ - if (r == -EPERM && check_loopback(rtnl)) + if (state.rcode == -EPERM && check_loopback(rtnl)) return 0; - return log_warning_errno(r, "Failed to configure loopback device: %m"); + return log_warning_errno(state.rcode, "Failed to configure loopback device: %m"); } return 0; diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c index 8ebd0e0e01..c70633a0f3 100644 --- a/src/test/test-loopback.c +++ b/src/test/test-loopback.c @@ -27,6 +27,7 @@ int main(int argc, char* argv[]) { int r; log_open(); + log_set_max_level(LOG_DEBUG); log_parse_environment(); r = loopback_setup();