diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2021-05-12 12:12:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-12 12:12:22 +0200 |
commit | 7a7e58ce443221ed09a9bb15dcef84fdbc5574a0 (patch) | |
tree | cc1e4df7b702d7130fd824310f7be58b4dc0bb2e | |
parent | 8f7123731d2a269ee9985cc265b6e69af63c1b6b (diff) | |
parent | 932e157b5e0936a5f531b8bb559997830eb14739 (diff) | |
download | systemd-7a7e58ce443221ed09a9bb15dcef84fdbc5574a0.tar.gz |
Merge pull request #19533 from yuwata/network-queue
network: introduce queue to configure address, route, etc
30 files changed, 2124 insertions, 1156 deletions
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index c20ee8eb4b..c855f39b1d 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -371,28 +371,26 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { return _hashmap_first_key(HASHMAP_BASE(h), false); } -#define hashmap_clear_with_destructor(_s, _f) \ +#define hashmap_clear_with_destructor(h, f) \ ({ \ + Hashmap *_h = (h); \ void *_item; \ - while ((_item = hashmap_steal_first(_s))) \ - _f(_item); \ + while ((_item = hashmap_steal_first(_h))) \ + f(_item); \ + _h; \ }) -#define hashmap_free_with_destructor(_s, _f) \ - ({ \ - hashmap_clear_with_destructor(_s, _f); \ - hashmap_free(_s); \ - }) -#define ordered_hashmap_clear_with_destructor(_s, _f) \ +#define hashmap_free_with_destructor(h, f) \ + hashmap_free(hashmap_clear_with_destructor(h, f)) +#define ordered_hashmap_clear_with_destructor(h, f) \ ({ \ + OrderedHashmap *_h = (h); \ void *_item; \ - while ((_item = ordered_hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define ordered_hashmap_free_with_destructor(_s, _f) \ - ({ \ - ordered_hashmap_clear_with_destructor(_s, _f); \ - ordered_hashmap_free(_s); \ + while ((_item = ordered_hashmap_steal_first(_h))) \ + f(_item); \ + _h; \ }) +#define ordered_hashmap_free_with_destructor(h, f) \ + ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f)) /* no hashmap_next */ void* ordered_hashmap_next(OrderedHashmap *h, const void *key); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index d5caf662ab..906b3fe97e 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -73,7 +73,13 @@ int in_addr_prefix_range( union in_addr_union *ret_start, union in_addr_union *ret_end); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) { + return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret); +} int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); +static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) { + return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, ret); +} int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret); @@ -120,3 +126,10 @@ extern const struct hash_ops in_addr_data_hash_ops; extern const struct hash_ops in_addr_prefix_hash_ops; extern const struct hash_ops in_addr_prefix_hash_ops_free; extern const struct hash_ops in6_addr_hash_ops; + +#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" +#define IPV4_ADDRESS_FMT_VAL(address) \ + be32toh((address).s_addr) >> 24, \ + (be32toh((address).s_addr) >> 16) & 0xFFu, \ + (be32toh((address).s_addr) >> 8) & 0xFFu, \ + be32toh((address).s_addr) & 0xFFu diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index a377f20b1f..64df41766f 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -75,6 +75,17 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s); #define ORDERED_SET_FOREACH(e, s) \ _ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) +#define ordered_set_clear_with_destructor(s, f) \ + ({ \ + OrderedSet *_s = (s); \ + void *_item; \ + while ((_item = ordered_set_steal_first(_s))) \ + f(_item); \ + _s; \ + }) +#define ordered_set_free_with_destructor(s, f) \ + ordered_set_free(ordered_set_clear_with_destructor(s, f)) + DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); diff --git a/src/basic/set.h b/src/basic/set.h index 52b6f4984c..0f8673934f 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -95,17 +95,16 @@ static inline void *set_steal_first(Set *s) { return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } -#define set_clear_with_destructor(_s, _f) \ +#define set_clear_with_destructor(s, f) \ ({ \ + Set *_s = (s); \ void *_item; \ while ((_item = set_steal_first(_s))) \ - _f(_item); \ - }) -#define set_free_with_destructor(_s, _f) \ - ({ \ - set_clear_with_destructor(_s, _f); \ - set_free(_s); \ + f(_item); \ + _s; \ }) +#define set_free_with_destructor(s, f) \ + set_free(set_clear_with_destructor(s, f)) /* no set_steal_first_key */ /* no set_first_key */ diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 448c50d4ab..90a184319a 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -409,6 +409,44 @@ int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void return 0; } +MultipathRoute *multipath_route_free(MultipathRoute *m) { + if (!m) + return NULL; + + free(m->ifname); + + return mfree(m); +} + +int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) { + _cleanup_(multipath_route_freep) MultipathRoute *n = NULL; + _cleanup_free_ char *ifname = NULL; + + assert(m); + assert(ret); + + if (m->ifname) { + ifname = strdup(m->ifname); + if (!ifname) + return -ENOMEM; + } + + n = new(MultipathRoute, 1); + if (!n) + return -ENOMEM; + + *n = (MultipathRoute) { + .gateway = m->gateway, + .weight = m->weight, + .ifindex = m->ifindex, + .ifname = TAKE_PTR(ifname), + }; + + *ret = TAKE_PTR(n); + + return 0; +} + int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) { _cleanup_ordered_set_free_free_ OrderedSet *set = NULL; int r; @@ -420,7 +458,7 @@ int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, O return -EBADMSG; for (; size >= sizeof(struct rtnexthop); ) { - _cleanup_free_ MultipathRoute *m = NULL; + _cleanup_(multipath_route_freep) MultipathRoute *m = NULL; if (NLMSG_ALIGN(rtnh->rtnh_len) > size) return -EBADMSG; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index a3a3951ff7..98bbfa4513 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -19,10 +19,16 @@ typedef struct RouteVia { typedef struct MultipathRoute { RouteVia gateway; - int ifindex; uint32_t weight; + int ifindex; + char *ifname; } MultipathRoute; +MultipathRoute *multipath_route_free(MultipathRoute *m); +DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free); + +int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret); + int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret); uint32_t rtnl_message_get_serial(sd_netlink_message *m); void rtnl_message_seal(sd_netlink_message *m); diff --git a/src/network/meson.build b/src/network/meson.build index 4fca3106dc..a8b9232e64 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -105,6 +105,8 @@ sources = files(''' networkd-network.h networkd-nexthop.c networkd-nexthop.h + networkd-queue.c + networkd-queue.h networkd-route.c networkd-route.h networkd-routing-policy-rule.c diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 724b0f95d3..5a3388c3c4 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -9,9 +9,9 @@ #include "netlink-util.h" #include "networkd-address-pool.h" #include "networkd-address.h" -#include "networkd-ipv6-proxy-ndp.h" #include "networkd-manager.h" #include "networkd-network.h" +#include "networkd-queue.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" @@ -150,7 +150,7 @@ static bool address_may_have_broadcast(const Address *a) { * See https://tools.ietf.org/html/rfc3021 */ return a->family == AF_INET && - in_addr_is_null(AF_INET, &a->in_addr_peer) && + in4_addr_is_null(&a->in_addr_peer.in) && a->prefixlen <= 30; } @@ -406,7 +406,7 @@ static int address_update(Address *address, const Address *src) { } if (address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + in6_addr_is_link_local(&address->in_addr.in6) > 0 && in6_addr_is_null(&address->link->ipv6ll_address)) { r = link_ipv6ll_gained(address->link, &address->in_addr.in6); @@ -485,6 +485,63 @@ int link_has_ipv6_address(Link *link, const struct in6_addr *address) { return address_get(link, a, NULL) >= 0; } +static int link_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) { + Address *a; + + assert(address); + + SET_FOREACH(a, addresses) { + if (a->family != AF_INET) + continue; + + if (!in4_addr_equal(&a->in_addr.in, address)) + continue; + + if (ret) + *ret = a; + + return 0; + } + + return -ENOENT; +} + +int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { + Link *link; + int r; + + assert(manager); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + if (family == AF_INET) + HASHMAP_FOREACH(link, manager->links) { + Address *a; + + if (link_get_ipv4_address(link->addresses, &address->in, &a) >= 0) + return !check_ready || address_is_ready(a); + if (link_get_ipv4_address(link->addresses_foreign, &address->in, &a) >= 0) + return !check_ready || address_is_ready(a); + } + else { + _cleanup_(address_freep) Address *tmp = NULL; + Address *a; + + r = address_new(&tmp); + if (r < 0) + return r; + + tmp->family = family; + tmp->in_addr = *address; + + HASHMAP_FOREACH(link, manager->links) + if (address_get(link, tmp, &a) >= 0) + return !check_ready || address_is_ready(a); + } + + return false; +} + static void log_address_debug(const Address *address, const char *str, const Link *link) { _cleanup_free_ char *addr = NULL, *peer = NULL; char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX]; @@ -520,25 +577,6 @@ static void log_address_debug(const Address *address, const char *str, const Lin preferred_str ? "for " : "forever", strempty(preferred_str)); } -static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->ifname); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EADDRNOTAVAIL) - log_link_message_warning_errno(link, m, r, "Could not drop address"); - else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - return 1; -} - static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) { int r; @@ -570,6 +608,33 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag return 0; } +int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->address_remove_messages > 0); + assert(error_msg); + + link->address_remove_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EADDRNOTAVAIL) + log_link_message_warning_errno(link, m, r, error_msg); + else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + return 1; +} + +static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + return address_remove_handler_internal(rtnl, m, link, "Could not drop address"); +} + int address_remove( const Address *address, Link *link, @@ -603,6 +668,7 @@ int address_remove( return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); + link->address_remove_messages++; return 0; } @@ -706,7 +772,7 @@ int link_drop_foreign_addresses(Link *link) { SET_FOREACH(address, link->addresses_foreign) { /* we consider IPv6LL addresses to be managed by the kernel */ - if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) + if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link)) continue; if (link_address_is_dynamic(link, address)) { @@ -732,35 +798,6 @@ int link_drop_foreign_addresses(Link *link) { return r; } -static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->ifname); - assert(link->address_remove_messages > 0); - - link->address_remove_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EADDRNOTAVAIL) - log_link_message_warning_errno(link, m, r, "Could not drop address"); - else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->address_remove_messages == 0 && link->request_static_addresses) { - link_set_state(link, LINK_STATE_CONFIGURING); - r = link_set_addresses(link); - if (r < 0) - link_enter_failed(link); - } - - return 1; -} - int link_drop_addresses(Link *link) { Address *address, *pool_address; int k, r = 0; @@ -769,17 +806,15 @@ int link_drop_addresses(Link *link) { SET_FOREACH(address, link->addresses) { /* we consider IPv6LL addresses to be managed by the kernel */ - if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) + if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link)) continue; - k = address_remove(address, link, remove_static_address_handler); + k = address_remove(address, link, NULL); if (k < 0 && r >= 0) { r = k; continue; } - link->address_remove_messages++; - SET_FOREACH(pool_address, link->pool_addresses) if (address_equal(address, pool_address)) address_free(set_remove(link->pool_addresses, pool_address)); @@ -845,9 +880,31 @@ static int address_acquire(Link *link, const Address *original, Address **ret) { return 1; } +int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(error_msg); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, error_msg); + link_enter_failed(link); + return 0; + } else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + return 1; +} + static int ipv4_dad_configure(Address *address); -int address_configure( +static int address_configure( const Address *address, Link *link, link_netlink_message_handler_t callback, @@ -951,78 +1008,24 @@ int address_configure( return k; } -static int static_address_ready_callback(Address *address) { - Address *a; - Link *link; - int r; - - assert(address); - assert(address->link); - - link = address->link; - - if (!link->addresses_configured) - return 0; - - SET_FOREACH(a, link->static_addresses) - if (!address_is_ready(a)) { - _cleanup_free_ char *str = NULL; - - (void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str); - log_link_debug(link, "an address %s is not ready", strnull(str)); - return 0; - } - - /* This should not be called again */ - SET_FOREACH(a, link->static_addresses) - a->callback = NULL; - - link->addresses_ready = true; - - r = link_set_ipv6_proxy_ndp_addresses(link); - if (r < 0) - return r; - - return link_set_routes(link); -} - -static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; - assert(rtnl); - assert(m); assert(link); - assert(link->ifname); - assert(link->address_messages > 0); - - link->address_messages--; + assert(link->static_address_messages > 0); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + link->static_address_messages--; - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->address_messages == 0) { - Address *a; + r = address_configure_handler_internal(rtnl, m, link, "Failed to set static address"); + if (r <= 0) + return r; + if (link->static_address_messages == 0) { log_link_debug(link, "Addresses set"); - link->addresses_configured = true; - - /* When all static addresses are already ready, then static_address_ready_callback() - * will not be called automatically. So, call it here. */ - a = set_first(link->static_addresses); - if (!a) { - log_link_debug(link, "No static address is stored. Already removed?"); - return 1; - } + link->static_addresses_configured = true; + link_check_ready(link); - r = static_address_ready_callback(a); + r = dhcp4_server_configure(link); if (r < 0) link_enter_failed(link); } @@ -1030,55 +1033,65 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) return 1; } -static int static_address_configure(const Address *address, Link *link) { - Address *ret; +static int static_address_after_configure(Request *req, void *object) { + Address *address = object; + Link *link; int r; + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ADDRESS); assert(address); - assert(link); - - r = address_configure(address, link, address_handler, &ret); - if (r < 0) - return log_link_warning_errno(link, r, "Could not configure static address: %m"); - link->address_messages++; + link = req->link; - r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret); + r = set_ensure_put(&link->static_addresses, &address_hash_ops, address); if (r < 0) return log_link_warning_errno(link, r, "Failed to store static address: %m"); - ret->callback = static_address_ready_callback; - return 0; } -int link_set_addresses(Link *link) { - Address *ad; +int link_request_address( + Link *link, + Address *address, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { + + assert(link); + assert(address); + + log_address_debug(address, "Requesting", link); + return link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object, + message_counter, netlink_handler, ret); +} + +int link_request_static_addresses(Link *link) { + Address *a; Prefix *p; int r; assert(link); assert(link->network); - if (link->address_remove_messages != 0) { - log_link_debug(link, "Removing old addresses, new addresses will be configured later."); - link->request_static_addresses = true; - return 0; - } + link->static_addresses_configured = false; - if (link->address_messages != 0) { - log_link_debug(link, "Static addresses are configuring."); - return 0; - } + ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { + Request *req; - ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) { - r = static_address_configure(ad, link); + r = link_request_address(link, a, false, &link->static_address_messages, + static_address_handler, &req); if (r < 0) return r; + + req->after_configure = static_address_after_configure; } HASHMAP_FOREACH(p, link->network->prefixes_by_section) { _cleanup_(address_freep) Address *address = NULL; + Request *req; if (!p->assign) continue; @@ -1097,22 +1110,18 @@ int link_set_addresses(Link *link) { address->family = AF_INET6; address->route_metric = p->route_metric; - r = static_address_configure(address, link); - if (r < 0) - return r; - } - - if (link->address_messages == 0) { - link->addresses_configured = true; - link->addresses_ready = true; - r = link_set_ipv6_proxy_ndp_addresses(link); + r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages, + static_address_handler, &req); if (r < 0) return r; - r = link_set_routes(link); - if (r < 0) - return r; + req->after_configure = static_address_after_configure; + } + + if (link->static_address_messages == 0) { + link->static_addresses_configured = true; + link_check_ready(link); } else { log_link_debug(link, "Setting addresses"); link_set_state(link, LINK_STATE_CONFIGURING); @@ -1121,6 +1130,34 @@ int link_set_addresses(Link *link) { return 0; } +int request_process_address(Request *req) { + Address *ret = NULL; /* avoid false maybe-uninitialized warning */ + int r; + + assert(req); + assert(req->link); + assert(req->address); + assert(req->type == REQUEST_TYPE_ADDRESS); + + if (!link_is_ready_to_configure(req->link, false)) + return 0; + + if (req->link->address_remove_messages > 0) + return 0; + + r = address_configure(req->address, req->link, req->netlink_handler, &ret); + if (r < 0) + return r; + + if (req->after_configure) { + r = req->after_configure(req, ret); + if (r < 0) + return r; + } + + return 1; +} + int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(address_freep) Address *tmp = NULL; Link *link = NULL; @@ -1304,7 +1341,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, } static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - _cleanup_free_ char *pretty = NULL; Address *address; Link *link; int r; @@ -1315,22 +1351,26 @@ static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { address = (Address *) userdata; link = address->link; - (void) in_addr_to_string(address->family, &address->in_addr, &pretty); + assert(address->family == AF_INET); + switch (event) { case SD_IPV4ACD_EVENT_STOP: log_link_debug(link, "Stopping ACD client..."); return; case SD_IPV4ACD_EVENT_BIND: - log_link_debug(link, "Successfully claimed address %s", strna(pretty)); + log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); link_check_ready(link); break; case SD_IPV4ACD_EVENT_CONFLICT: - log_link_warning(link, "DAD conflict. Dropping address %s", strna(pretty)); + log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); r = address_remove(address, link, NULL); if (r < 0) - log_link_error_errno(link, r, "Failed to drop DAD conflicted address %s", strna(pretty));; + log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); link_check_ready(link); break; @@ -1353,12 +1393,7 @@ static int ipv4_dad_configure(Address *address) { if (address->family != AF_INET) return 0; - if (DEBUG_LOGGING) { - _cleanup_free_ char *pretty = NULL; - - (void) in_addr_to_string(address->family, &address->in_addr, &pretty); - log_link_debug(address->link, "Starting IPv4ACD client. Probing address %s", strna(pretty)); - } + log_address_debug(address, "Starting IPv4ACD client. Probing", address->link); if (!address->acd) { r = sd_ipv4acd_new(&address->acd); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 58f44cd4cd..e9fddddf9f 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -16,6 +16,7 @@ typedef struct Manager Manager; typedef struct Network Network; +typedef struct Request Request; typedef int (*address_ready_callback_t)(Address *address); typedef struct Address { @@ -51,7 +52,8 @@ typedef struct Address { int address_new(Address **ret); Address *address_free(Address *address); int address_get(Link *link, const Address *in, Address **ret); -int address_configure(const Address *address, Link *link, link_netlink_message_handler_t callback, Address **ret); +int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); +int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int address_remove(const Address *address, Link *link, link_netlink_message_handler_t callback); bool address_equal(const Address *a1, const Address *a2); bool address_is_ready(const Address *a); @@ -60,16 +62,26 @@ int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret); DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); -int link_set_addresses(Link *link); int link_drop_addresses(Link *link); int link_drop_foreign_addresses(Link *link); bool link_address_is_dynamic(const Link *link, const Address *address); int link_has_ipv6_address(Link *link, const struct in6_addr *address); +int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); void ipv4_dad_unref(Link *link); int ipv4_dad_stop(Link *link); int ipv4_dad_update_mac(Link *link); +int link_request_address( + Link *link, + Address *address, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret); +int link_request_static_addresses(Link *link); +int request_process_address(Request *req); + int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m); void network_drop_invalid_addresses(Network *network); @@ -86,10 +98,3 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_flags); CONFIG_PARSER_PROTOTYPE(config_parse_address_scope); CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric); CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection); - -#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" -#define IPV4_ADDRESS_FMT_VAL(address) \ - be32toh((address).s_addr) >> 24, \ - (be32toh((address).s_addr) >> 16) & 0xFFu, \ - (be32toh((address).s_addr) >> 8) & 0xFFu, \ - be32toh((address).s_addr) & 0xFFu diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index f20264be9e..abb37a3d26 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -17,13 +17,16 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" +#include "networkd-nexthop.h" +#include "networkd-queue.h" +#include "networkd-route.h" #include "networkd-state-file.h" #include "string-table.h" #include "strv.h" #include "sysctl-util.h" #include "web-util.h" -static int dhcp4_update_address(Link *link, bool announce); +static int dhcp4_request_address_and_routes(Link *link, bool announce); static int dhcp4_remove_all(Link *link); void network_adjust_dhcp4(Network *network) { @@ -83,8 +86,20 @@ static void dhcp4_check_ready(Link *link) { if (link->network->dhcp_send_decline && !link->dhcp4_address_bind) return; - if (link->dhcp4_messages > 0) + if (link->dhcp4_messages > 0) { + log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__); return; + } + + if (!link->dhcp_address) { + log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__); + return; + } + + if (!address_is_ready(link->dhcp_address)) { + log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__); + return; + } link->dhcp4_configured = true; @@ -102,6 +117,27 @@ static void dhcp4_check_ready(Link *link) { link_check_ready(link); } +static int dhcp4_after_route_configure(Request *req, void *object) { + Route *route = object; + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ROUTE); + assert(route); + + link = req->link; + + r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m"); + + set_remove(link->dhcp_routes_old, route); + + return 0; +} + static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -135,6 +171,15 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li r = dhcp4_remove_all(link); if (r < 0) link_enter_failed(link); + + r = link_request_static_nexthops(link, true); + if (r < 0) + link_enter_failed(link); + + r = link_request_static_routes(link, true); + if (r < 0) + link_enter_failed(link); + return 1; } @@ -143,24 +188,26 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } -static int dhcp_route_configure(Route *route, Link *link) { - Route *ret; +static int dhcp4_request_route(Route *in, Link *link) { + _cleanup_(route_freep) Route *route = in; + Request *req; int r; assert(route); assert(link); - r = route_configure(route, link, dhcp4_route_handler, &ret); + r = link_has_route(link, route); if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m"); - - link->dhcp4_messages++; + return r; + if (r == 0) + link->dhcp4_configured = false; - r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages, + dhcp4_route_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m"); + return r; - (void) set_remove(link->dhcp_routes_old, ret); + req->after_configure = dhcp4_after_route_configure; return 0; } @@ -171,7 +218,7 @@ static bool link_prefixroute(Link *link) { link->manager->dhcp4_prefix_root_cannot_set_table; } -static int link_set_dhcp_prefix_route(Link *link) { +static int dhcp4_request_prefix_route(Link *link) { _cleanup_(route_freep) Route *route = NULL; struct in_addr address, netmask; int r; @@ -204,10 +251,10 @@ static int link_set_dhcp_prefix_route(Link *link) { route->table = link_get_dhcp_route_table(link); route->mtu = link->network->dhcp_route_mtu; - return dhcp_route_configure(route, link); + return dhcp4_request_route(TAKE_PTR(route), link); } -static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw) { +static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw) { _cleanup_(route_freep) Route *route = NULL; struct in_addr address; int r; @@ -234,14 +281,15 @@ static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw) route->table = link_get_dhcp_route_table(link); route->mtu = link->network->dhcp_route_mtu; - return dhcp_route_configure(route, link); + return dhcp4_request_route(TAKE_PTR(route), link); } -static int dhcp_route_configure_auto( - Route *route, +static int dhcp4_request_route_auto( + Route *in, Link *link, const struct in_addr *gw) { + _cleanup_(route_freep) Route *route = in; struct in_addr address, netmask, prefix; uint8_t prefixlen; int r; @@ -251,8 +299,6 @@ static int dhcp_route_configure_auto( assert(link->dhcp_lease); assert(gw); - /* The route object may be reused in an iteration. All elements must be set or cleared. */ - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) return r; @@ -309,7 +355,7 @@ static int dhcp_route_configure_auto( return 0; } - r = link_set_dhcp_route_to_gateway(link, gw); + r = dhcp4_request_route_to_gateway(link, gw); if (r < 0) return r; @@ -319,13 +365,12 @@ static int dhcp_route_configure_auto( route->prefsrc.in = address; } - return dhcp_route_configure(route, link); + return dhcp4_request_route(TAKE_PTR(route), link); } -static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_gw) { +static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_gw) { _cleanup_free_ sd_dhcp_route **static_routes = NULL; bool classless_route = false, static_route = false; - _cleanup_(route_freep) Route *route = NULL; struct in_addr default_gw = {}; int n, r; @@ -360,24 +405,25 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g if (classless_route && static_route) log_link_debug(link, "Classless static routes received from DHCP server: ignoring static-route option"); - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET; - route->gw_family = AF_INET; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = link_get_dhcp_route_table(link); - route->mtu = link->network->dhcp_route_mtu; - for (int i = 0; i < n; i++) { + _cleanup_(route_freep) Route *route = NULL; struct in_addr gw; if (sd_dhcp_route_get_option(static_routes[i]) != (classless_route ? SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE : SD_DHCP_OPTION_STATIC_ROUTE)) continue; + r = route_new(&route); + if (r < 0) + return r; + + route->family = AF_INET; + route->gw_family = AF_INET; + route->protocol = RTPROT_DHCP; + route->priority = link->network->dhcp_route_metric; + route->table = link_get_dhcp_route_table(link); + route->mtu = link->network->dhcp_route_mtu; + r = sd_dhcp_route_get_gateway(static_routes[i], &gw); if (r < 0) return r; @@ -398,7 +444,7 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g in4_addr_is_null(&default_gw)) default_gw = gw; - r = dhcp_route_configure_auto(route, link, &gw); + r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw); if (r < 0) return r; } @@ -407,7 +453,7 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g return classless_route; } -static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) { +static int dhcp4_request_gateway(Link *link, struct in_addr *ret_gw) { _cleanup_(route_freep) Route *route = NULL; const struct in_addr *router; struct in_addr address; @@ -439,7 +485,7 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) { /* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host * so that we can route no matter the netmask or existing kernel route tables. */ - r = link_set_dhcp_route_to_gateway(link, &router[0]); + r = dhcp4_request_route_to_gateway(link, &router[0]); if (r < 0) return r; @@ -457,7 +503,7 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) { route->table = link_get_dhcp_route_table(link); route->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure(route, link); + r = dhcp4_request_route(TAKE_PTR(route), link); if (r < 0) return r; @@ -468,17 +514,21 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) { if (rt->gw_family != AF_INET) continue; - rt->gw.in = router[0]; - if (!rt->protocol_set) - rt->protocol = RTPROT_DHCP; - if (!rt->priority_set) - rt->priority = link->network->dhcp_route_metric; - if (!rt->table_set) - rt->table = link_get_dhcp_route_table(link); - if (rt->mtu == 0) - rt->mtu = link->network->dhcp_route_mtu; - - r = dhcp_route_configure(rt, link); + r = route_dup(rt, &route); + if (r < 0) + return r; + + route->gw.in = router[0]; + if (!route->protocol_set) + route->protocol = RTPROT_DHCP; + if (!route->priority_set) + route->priority = link->network->dhcp_route_metric; + if (!route->table_set) + route->table = link_get_dhcp_route_table(link); + if (route->mtu == 0) + route->mtu = link->network->dhcp_route_mtu; + + r = dhcp4_request_route(TAKE_PTR(route), link); if (r < 0) return r; } @@ -487,13 +537,12 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) { return 0; } -static int link_set_routes_to_servers( +static int dhcp4_request_routes_to_servers( Link *link, const struct in_addr *servers, size_t n_servers, const struct in_addr *gw) { - _cleanup_(route_freep) Route *route = NULL; int r; assert(link); @@ -502,24 +551,25 @@ static int link_set_routes_to_servers( assert(servers || n_servers == 0); assert(gw); - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET; - route->dst_prefixlen = 32; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = link_get_dhcp_route_table(link); - route->mtu = link->network->dhcp_route_mtu; - for (size_t i = 0; i < n_servers; i++) { + _cleanup_(route_freep) Route *route = NULL; + if (in4_addr_is_null(&servers[i])) continue; + r = route_new(&route); + if (r < 0) + return r; + + route->family = AF_INET; route->dst.in = servers[i]; + route->dst_prefixlen = 32; + route->protocol = RTPROT_DHCP; + route->priority = link->network->dhcp_route_metric; + route->table = link_get_dhcp_route_table(link); + route->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure_auto(route, link, gw); + r = dhcp4_request_route_auto(TAKE_PTR(route), link, gw); if (r < 0) return r; } @@ -527,7 +577,7 @@ static int link_set_routes_to_servers( return 0; } -static int link_set_routes_to_dns(Link *link, const struct in_addr *gw) { +static int dhcp4_request_routes_to_dns(Link *link, const struct in_addr *gw) { const struct in_addr *dns; int r; @@ -546,10 +596,10 @@ static int link_set_routes_to_dns(Link *link, const struct in_addr *gw) { if (r < 0) return r; - return link_set_routes_to_servers(link, dns, r, gw); + return dhcp4_request_routes_to_servers(link, dns, r, gw); } -static int link_set_routes_to_ntp(Link *link, const struct in_addr *gw) { +static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) { const struct in_addr *ntp; int r; @@ -568,25 +618,17 @@ static int link_set_routes_to_ntp(Link *link, const struct in_addr *gw) { if (r < 0) return r; - return link_set_routes_to_servers(link, ntp, r, gw); + return dhcp4_request_routes_to_servers(link, ntp, r, gw); } -static int link_set_dhcp_routes(Link *link) { +static int dhcp4_request_routes(Link *link) { struct in_addr gw = {}; Route *rt; int r; assert(link); - if (!link->dhcp_lease) /* link went down while we configured the IP addresses? */ - return 0; - - if (!link->network) /* link went down while we configured the IP addresses? */ - return 0; - - if (!link_has_carrier(link) && !link->network->configure_without_carrier) - /* During configuring addresses, the link lost its carrier. As networkd is dropping - * the addresses now, let's not configure the routes either. */ + if (!link->dhcp_lease) return 0; while ((rt = set_steal_first(link->dhcp_routes))) { @@ -595,28 +637,28 @@ static int link_set_dhcp_routes(Link *link) { return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m"); } - r = link_set_dhcp_prefix_route(link); + r = dhcp4_request_prefix_route(link); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: Could not set prefix route: %m"); + return log_link_error_errno(link, r, "DHCP error: Could not request prefix route: %m"); - r = link_set_dhcp_static_routes(link, &gw); + r = dhcp4_request_static_routes(link, &gw); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: Could not set static routes: %m"); + return log_link_error_errno(link, r, "DHCP error: Could not request static routes: %m"); if (r == 0) { /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and * a Router option, the DHCP client MUST ignore the Router option. */ - r = link_set_dhcp_gateway(link, &gw); + r = dhcp4_request_gateway(link, &gw); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: Could not set gateway: %m"); + return log_link_error_errno(link, r, "DHCP error: Could not request gateway: %m"); } - r = link_set_routes_to_dns(link, &gw); + r = dhcp4_request_routes_to_dns(link, &gw); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: Could not set routes to DNS servers: %m"); + return log_link_error_errno(link, r, "DHCP error: Could not request routes to DNS servers: %m"); - r = link_set_routes_to_ntp(link, &gw); + r = dhcp4_request_routes_to_ntp(link, &gw); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: Could not set routes to NTP servers: %m"); + return log_link_error_errno(link, r, "DHCP error: Could not request routes to NTP servers: %m"); return 0; } @@ -670,24 +712,20 @@ static int dhcp_reset_hostname(Link *link) { return 0; } -static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp4_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; - assert(m); assert(link); assert(link->dhcp4_remove_messages > 0); link->dhcp4_remove_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -ESRCH) - log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 route, ignoring"); + r = link_route_remove_handler_internal(rtnl, m, link, "Failed to remove DHCPv4 route, ignoring"); + if (r <= 0) + return r; if (link->dhcp4_remove_messages == 0) { - r = dhcp4_update_address(link, false); + r = dhcp4_request_address_and_routes(link, false); if (r < 0) link_enter_failed(link); } @@ -695,26 +733,20 @@ static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, L return 1; } -static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp4_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; - assert(m); assert(link); assert(link->dhcp4_remove_messages > 0); link->dhcp4_remove_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EADDRNOTAVAIL) - log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring"); - else - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_remove_handler_internal(rtnl, m, link, "Failed to remove DHCPv4 address, ignoring"); + if (r <= 0) + return r; if (link->dhcp4_remove_messages == 0) { - r = dhcp4_update_address(link, false); + r = dhcp4_request_address_and_routes(link, false); if (r < 0) link_enter_failed(link); } @@ -729,7 +761,7 @@ static int dhcp4_remove_all(Link *link) { assert(link); SET_FOREACH(route, link->dhcp_routes) { - k = route_remove(route, NULL, link, dhcp4_remove_route_handler); + k = route_remove(route, NULL, link, dhcp4_route_remove_handler); if (k < 0) r = k; else @@ -737,7 +769,7 @@ static int dhcp4_remove_all(Link *link) { } if (link->dhcp_address) { - k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler); + k = address_remove(link->dhcp_address, link, dhcp4_address_remove_handler); if (k < 0) r = k; else @@ -779,12 +811,18 @@ static int dhcp_lease_lost(Link *link) { (void) sd_ipv4acd_stop(link->dhcp_acd); - return r; + if (r < 0) + return r; + + r = link_request_static_nexthops(link, true); + if (r < 0) + return r; + + return link_request_static_routes(link, true); } static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - _cleanup_free_ char *pretty = NULL; - union in_addr_union address = {}; + struct in_addr address; Link *link; int r; @@ -795,23 +833,21 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { switch (event) { case SD_IPV4ACD_EVENT_STOP: - log_link_debug(link, "Stopping ACD client for DHCP4..."); + log_link_debug(link, "Stopping ACD client for DHCPv4 address."); return; case SD_IPV4ACD_EVENT_BIND: if (DEBUG_LOGGING) { - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in); - (void) in_addr_to_string(AF_INET, &address, &pretty); - log_link_debug(link, "Successfully claimed DHCP4 address %s", strna(pretty)); + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + log_link_debug(link, "Successfully claimed DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address)); } link->dhcp4_address_bind = true; dhcp4_check_ready(link); break; case SD_IPV4ACD_EVENT_CONFLICT: - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in); - (void) in_addr_to_string(AF_INET, &address, &pretty); - log_link_warning(link, "DAD conflict. Dropping DHCP4 address %s", strna(pretty)); + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + log_link_warning(link, "DAD conflict. Dropping DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address)); r = sd_dhcp_client_send_decline(link->dhcp_client); if (r < 0) @@ -893,8 +929,7 @@ static int dhcp4_dad_update_mac(Link *link) { } static int dhcp4_start_acd(Link *link) { - union in_addr_union addr; - struct in_addr old; + struct in_addr addr, old; int r; if (!link->network->dhcp_send_decline) @@ -907,7 +942,7 @@ static int dhcp4_start_acd(Link *link) { link->dhcp4_address_bind = false; - r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in); + r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); if (r < 0) return r; @@ -915,7 +950,7 @@ static int dhcp4_start_acd(Link *link) { if (r < 0) return r; - r = sd_ipv4acd_set_address(link->dhcp_acd, &addr.in); + r = sd_ipv4acd_set_address(link->dhcp_acd, &addr); if (r < 0) return r; @@ -923,45 +958,51 @@ static int dhcp4_start_acd(Link *link) { if (r < 0) return r; - if (DEBUG_LOGGING) { - _cleanup_free_ char *pretty = NULL; + log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(addr)); - (void) in_addr_to_string(AF_INET, &addr, &pretty); - log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty)); - } + return sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr, &old)); +} - r = sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr.in, &old)); - if (r < 0) - return r; +static int dhcp4_address_ready_callback(Address *address) { + assert(address); - return 1; + /* Do not call this again. */ + address->callback = NULL; + + dhcp4_check_ready(address->link); + return 0; } -static int dhcp4_address_ready_callback(Address *address) { +static int dhcp4_after_address_configure(Request *req, void *object) { + Address *address = object; Link *link; int r; + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ADDRESS); assert(address); - link = address->link; + link = req->link; - /* Do not call this again. */ - address->callback = NULL; - - r = link_set_dhcp_routes(link); - if (r < 0) - return r; + if (!address_equal(link->dhcp_address, address)) { + if (link->dhcp_address_old && + !address_equal(link->dhcp_address_old, link->dhcp_address)) { + /* Still too old address exists? Let's remove it immediately. */ + r = address_remove(link->dhcp_address_old, link, NULL); + if (r < 0) + return r; + } + link->dhcp_address_old = link->dhcp_address; + } - /* Reconfigure static routes as kernel may remove some routes when lease expires. */ - r = link_set_routes(link); - if (r < 0) - return r; + link->dhcp_address = address; r = dhcp4_start_acd(link); if (r < 0) - return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCP4 address: %m"); + return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCPv4 address: %m"); - dhcp4_check_ready(link); return 0; } @@ -969,36 +1010,30 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * int r; assert(link); + assert(link->dhcp4_messages > 0); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + link->dhcp4_messages--; - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv4 address"); + if (r <= 0) + return r; if (address_is_ready(link->dhcp_address)) { r = dhcp4_address_ready_callback(link->dhcp_address); - if (r < 0) { + if (r < 0) link_enter_failed(link); - return 1; - } } else link->dhcp_address->callback = dhcp4_address_ready_callback; return 1; } -static int dhcp4_update_address(Link *link, bool announce) { +static int dhcp4_request_address(Link *link, bool announce) { _cleanup_(address_freep) Address *addr = NULL; uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; struct in_addr address, netmask; unsigned prefixlen; - Address *ret; + Request *req; int r; assert(link); @@ -1007,15 +1042,6 @@ static int dhcp4_update_address(Link *link, bool announce) { if (!link->dhcp_lease) return 0; - link_set_state(link, LINK_STATE_CONFIGURING); - link->dhcp4_configured = false; - - /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are called, the - * related flags must be cleared. Otherwise, the link becomes configured state before routes - * are configured. */ - link->static_routes_configured = false; - link->static_nexthops_configured = false; - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) return log_link_warning_errno(link, r, "DHCP error: no address: %m"); @@ -1073,15 +1099,34 @@ static int dhcp4_update_address(Link *link, bool announce) { SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link)); addr->route_metric = link->network->dhcp_route_metric; - /* allow reusing an existing address and simply update its lifetime - * in case it already exists */ - r = address_configure(addr, link, dhcp4_address_handler, &ret); + if (address_get(link, addr, NULL) < 0) + link->dhcp4_configured = false; + + r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages, + dhcp4_address_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m"); + return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m"); - if (!address_equal(link->dhcp_address, ret)) - link->dhcp_address_old = link->dhcp_address; - link->dhcp_address = ret; + req->after_configure = dhcp4_after_address_configure; + + return 0; +} + +static int dhcp4_request_address_and_routes(Link *link, bool announce) { + int r; + + assert(link); + + r = dhcp4_request_address(link, announce); + if (r < 0) + return r; + + r = dhcp4_request_routes(link); + if (r < 0) + return r; + + link_set_state(link, LINK_STATE_CONFIGURING); + link_check_ready(link); return 0; } @@ -1101,7 +1146,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { link->dhcp_lease = sd_dhcp_lease_ref(lease); link_dirty(link); - return dhcp4_update_address(link, false); + return dhcp4_request_address_and_routes(link, false); } static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { @@ -1166,16 +1211,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { } } - if (link->dhcp4_remove_messages == 0) { - r = dhcp4_update_address(link, true); - if (r < 0) - return r; - } else - log_link_debug(link, - "The link has previously assigned DHCPv4 address or routes. " - "The newly assigned address and routes will set up after old ones are removed."); - - return 0; + return dhcp4_request_address_and_routes(link, true); } static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) { diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index b13a635a78..7061afe0a2 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -18,6 +18,7 @@ #include "networkd-dhcp6.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "networkd-radv.h" #include "siphash24.h" #include "string-table.h" @@ -43,7 +44,7 @@ bool link_dhcp6_pd_is_enabled(Link *link) { static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { uint32_t lifetime_preferred, lifetime_valid; - union in_addr_union pd_prefix; + struct in6_addr pd_prefix; uint8_t pd_prefix_len; if (!lease) @@ -51,7 +52,7 @@ static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { sd_dhcp6_lease_reset_pd_prefix_iter(lease); - return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; + return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; } DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) { @@ -86,21 +87,21 @@ static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6Deleg DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func); -static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) { +static Link *dhcp6_pd_get_link_by_prefix(Link *link, const struct in6_addr *prefix) { DHCP6DelegatedPrefix *pd; assert(link); assert(link->manager); assert(prefix); - pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6); + pd = hashmap_get(link->manager->dhcp6_prefixes, prefix); if (!pd) return NULL; return pd->link; } -static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) { +static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, struct in6_addr *ret_prefix) { DHCP6DelegatedPrefix *pd, in; assert(link); @@ -109,7 +110,7 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *p assert(ret_prefix); in = (DHCP6DelegatedPrefix) { - .pd_prefix = pd_prefix->in6, + .pd_prefix = *pd_prefix, .link = link, }; @@ -117,7 +118,7 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *p if (!pd) return -ENOENT; - ret_prefix->in6 = pd->prefix; + *ret_prefix = pd->prefix; return 0; } @@ -229,7 +230,7 @@ int dhcp6_pd_remove(Link *link) { return r; } -static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { +static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); @@ -237,15 +238,9 @@ static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *l link->dhcp6_pd_route_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route"); - link_enter_failed(link); - return 1; - } + r = route_configure_handler_internal(rtnl, m, link, "Failed to add DHCPv6 Prefix Delegation route"); + if (r <= 0) + return r; if (link->dhcp6_pd_route_messages == 0) { log_link_debug(link, "DHCPv6 prefix delegation routes set"); @@ -264,11 +259,32 @@ static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *l return 1; } -static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) { +static int dhcp6_pd_after_route_configure(Request *req, void *object) { + Route *route = object; + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ROUTE); + assert(route); + + link = req->link; + + r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m"); + + set_remove(link->dhcp6_pd_routes_old, route); + + return 0; +} + +static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, const struct in6_addr *pd_prefix) { _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL; _cleanup_(route_freep) Route *route = NULL; Link *assigned_link; - Route *ret; + Request *req; int r; assert(link); @@ -281,24 +297,23 @@ static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, con return r; route->family = AF_INET6; - route->dst = *prefix; + route->dst.in6 = *prefix; route->dst_prefixlen = 64; route->protocol = RTPROT_DHCP; route->priority = link->network->dhcp6_pd_route_metric; - r = route_configure(route, link, dhcp6_pd_route_handler, &ret); + r = link_has_route(link, route); if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m"); - if (r > 0) + return r; + if (r == 0) link->dhcp6_pd_route_configured = false; - link->dhcp6_pd_route_messages++; - - r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_route_messages, + dhcp6_pd_route_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m"); + return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m"); - (void) set_remove(link->dhcp6_pd_routes_old, ret); + req->after_configure = dhcp6_pd_after_route_configure; assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix); if (assigned_link) { @@ -311,8 +326,8 @@ static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, con return log_oom(); *pd = (DHCP6DelegatedPrefix) { - .prefix = prefix->in6, - .pd_prefix = pd_prefix->in6, + .prefix = *prefix, + .pd_prefix = *pd_prefix, .link = link_ref(link), }; @@ -338,16 +353,9 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin link->dhcp6_pd_address_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 delegated prefix address"); + if (r <= 0) + return r; if (link->dhcp6_pd_address_messages == 0) { log_link_debug(link, "DHCPv6 delegated prefix addresses set"); @@ -370,12 +378,15 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) { _cleanup_free_ char *buffer = NULL; int log_level; + assert(address); + assert(address->family == AF_INET6); + log_level = address_get(link, address, NULL) >= 0 ? LOG_DEBUG : LOG_INFO; if (log_level < log_get_max_level()) return; - (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buffer); + (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer); if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, address->cinfo.ifa_valid * USEC_PER_SEC, @@ -391,14 +402,35 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) { preferred_str ? "for " : "forever", strempty(preferred_str)); } -static int dhcp6_set_pd_address( +static int dhcp6_pd_after_address_configure(Request *req, void *object) { + Address *address = object; + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ADDRESS); + assert(address); + + link = req->link; + + r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m"); + + set_remove(link->dhcp6_pd_addresses_old, address); + + return 0; +} + +static int dhcp6_pd_request_address( Link *link, - const union in_addr_union *prefix, + const struct in6_addr *prefix, uint32_t lifetime_preferred, uint32_t lifetime_valid) { _cleanup_(address_freep) Address *address = NULL; - Address *ret; + Request *req; int r; assert(link); @@ -412,10 +444,10 @@ static int dhcp6_set_pd_address( if (r < 0) return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m"); - address->in_addr = *prefix; + address->in_addr.in6 = *prefix; - if (in_addr_is_set(AF_INET6, &link->network->dhcp6_pd_token)) - memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8); + if (in6_addr_is_set(&link->network->dhcp6_pd_token)) + memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.s6_addr + 8, 8); else { r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); if (r < 0) @@ -430,27 +462,24 @@ static int dhcp6_set_pd_address( address->route_metric = link->network->dhcp6_pd_route_metric; log_dhcp6_pd_address(link, address); - r = address_configure(address, link, dhcp6_pd_address_handler, &ret); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m"); - if (r > 0) - link->dhcp6_pd_address_configured = false; - link->dhcp6_pd_address_messages++; + if (address_get(link, address, NULL) < 0) + link->dhcp6_pd_address_configured = false; - r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret); + r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_address_messages, + dhcp6_pd_address_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m"); + return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m"); - (void) set_remove(link->dhcp6_pd_addresses_old, ret); + req->after_configure = dhcp6_pd_after_address_configure; return 0; } static int dhcp6_pd_assign_prefix( Link *link, - const union in_addr_union *prefix, - const union in_addr_union *pd_prefix, + const struct in6_addr *prefix, + const struct in6_addr *pd_prefix, uint32_t lifetime_preferred, uint32_t lifetime_valid) { @@ -461,16 +490,16 @@ static int dhcp6_pd_assign_prefix( assert(prefix); if (link->network->dhcp6_pd_announce) { - r = radv_add_prefix(link, &prefix->in6, 64, lifetime_preferred, lifetime_valid); + r = radv_add_prefix(link, prefix, 64, lifetime_preferred, lifetime_valid); if (r < 0) return r; } - r = dhcp6_set_pd_route(link, prefix, pd_prefix); + r = dhcp6_pd_request_route(link, prefix, pd_prefix); if (r < 0) return r; - r = dhcp6_set_pd_address(link, prefix, lifetime_preferred, lifetime_valid); + r = dhcp6_pd_request_address(link, prefix, lifetime_preferred, lifetime_valid); if (r < 0) return r; @@ -486,9 +515,9 @@ static bool link_has_preferred_subnet_id(Link *link) { static int dhcp6_get_preferred_delegated_prefix( Link *link, - const union in_addr_union *masked_pd_prefix, + const struct in6_addr *masked_pd_prefix, uint8_t pd_prefix_len, - union in_addr_union *ret) { + struct in6_addr *ret) { /* We start off with the original PD prefix we have been assigned and iterate from there */ union in_addr_union prefix; @@ -502,7 +531,7 @@ static int dhcp6_get_preferred_delegated_prefix( assert(pd_prefix_len <= 64); n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); - prefix = *masked_pd_prefix; + prefix.in6 = *masked_pd_prefix; if (link_has_preferred_subnet_id(link)) { uint64_t subnet_id = link->network->dhcp6_pd_subnet_id; @@ -521,28 +550,28 @@ static int dhcp6_get_preferred_delegated_prefix( /* Verify that the prefix we did calculate fits in the pd prefix. * This should not fail as we checked the prefix size beforehand */ - assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0); + assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) masked_pd_prefix, pd_prefix_len, &prefix) > 0); - assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6); if (assigned_link && assigned_link != link) { _cleanup_free_ char *assigned_buf = NULL; - (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); + (void) in6_addr_to_string(&prefix.in6, &assigned_buf); return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN), "The requested prefix %s is already assigned to another link.", strna(assigned_buf)); } - *ret = prefix; + *ret = prefix.in6; return 0; } for (uint64_t n = 0; n < n_prefixes; n++) { /* If we do not have an allocation preference just iterate * through the address space and return the first free prefix. */ - assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6); if (!assigned_link || assigned_link == link) { - *ret = prefix; + *ret = prefix.in6; return 0; } @@ -555,7 +584,7 @@ static int dhcp6_get_preferred_delegated_prefix( } static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, - const union in_addr_union *masked_pd_prefix, + const struct in6_addr *masked_pd_prefix, uint8_t pd_prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid, @@ -571,7 +600,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, HASHMAP_FOREACH(link, dhcp6_link->manager->links) { _cleanup_free_ char *assigned_buf = NULL; - union in_addr_union assigned_prefix; + struct in6_addr assigned_prefix; if (link == dhcp6_link) continue; @@ -594,7 +623,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, } } - (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf); + (void) in6_addr_to_string(&assigned_prefix, &assigned_buf); r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix, lifetime_preferred, lifetime_valid); if (r < 0) { @@ -659,11 +688,10 @@ static int dhcp6_pd_finalize(Link *link) { if (r < 0) return r; - if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) - link_check_ready(link); - else + if (!link->dhcp6_pd_address_configured || !link->dhcp6_pd_route_configured) link_set_state(link, LINK_STATE_CONFIGURING); + link_check_ready(link); return 0; } @@ -781,7 +809,7 @@ static int dhcp6_remove(Link *link) { return r; } -static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { +static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); @@ -789,15 +817,9 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link link->dhcp6_route_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet"); - link_enter_failed(link); - return 1; - } + r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated subnet"); + if (r <= 0) + return r; if (link->dhcp6_route_messages == 0) { log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set"); @@ -815,16 +837,37 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link return 1; } -static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) { +static int dhcp6_after_route_configure(Request *req, void *object) { + Route *route = object; + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ROUTE); + assert(route); + + link = req->link; + + r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet: %m"); + + set_remove(link->dhcp6_routes_old, route); + + return 0; +} + +static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen) { _cleanup_(route_freep) Route *route = NULL; _cleanup_free_ char *buf = NULL; - Route *ret; + Request *req; int r; assert(link); assert(addr); - (void) in_addr_prefix_to_string(AF_INET6, addr, prefixlen, &buf); + (void) in6_addr_prefix_to_string(addr, prefixlen, &buf); if (prefixlen == 64) { log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s since distributed prefix is 64", @@ -837,32 +880,30 @@ static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *ad return log_oom(); route->family = AF_INET6; - route->dst = *addr; + route->dst.in6 = *addr; route->dst_prefixlen = prefixlen; route->table = link_get_dhcp_route_table(link); route->type = RTN_UNREACHABLE; route->protocol = RTPROT_DHCP; - r = route_configure(route, link, dhcp6_route_handler, &ret); + r = link_has_route(link, route); if (r < 0) - return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s: %m", - strna(buf)); - if (r > 0) + return r; + if (r == 0) link->dhcp6_route_configured = false; - link->dhcp6_route_messages++; - - r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_route_messages, + dhcp6_route_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s: %m", + return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m", strna(buf)); - (void) set_remove(link->dhcp6_routes_old, ret); + req->after_configure = dhcp6_after_route_configure; return 0; } -static int dhcp6_pd_prefix_add(Link *link, const union in_addr_union *prefix, uint8_t prefixlen) { +static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) { _cleanup_free_ struct in_addr_prefix *p = NULL; _cleanup_free_ char *buf = NULL; int r; @@ -877,10 +918,10 @@ static int dhcp6_pd_prefix_add(Link *link, const union in_addr_union *prefix, ui *p = (struct in_addr_prefix) { .family = AF_INET6, .prefixlen = prefixlen, - .address = *prefix, + .address.in6 = *prefix, }; - (void) in_addr_prefix_to_string(p->family, &p->address, p->prefixlen, &buf); + (void) in6_addr_prefix_to_string(prefix, prefixlen, &buf); log_link_full(link, set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG : @@ -918,10 +959,11 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) { uint32_t lifetime_preferred, lifetime_valid; - union in_addr_union pd_prefix, prefix; + struct in6_addr pd_prefix; + union in_addr_union prefix; uint8_t pd_prefix_len; - r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len, + r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid); if (r < 0) break; @@ -932,7 +974,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { if (r == 0) continue; - r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len); + r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len); if (r < 0) return r; @@ -947,7 +989,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { assert(pd_prefix_len <= 64); - prefix = pd_prefix; + prefix.in6 = pd_prefix; r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); if (r < 0) return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m"); @@ -956,20 +998,20 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); _cleanup_free_ char *buf = NULL; - (void) in_addr_prefix_to_string(AF_INET6, &prefix, pd_prefix_len, &buf); + (void) in6_addr_prefix_to_string(&prefix.in6, pd_prefix_len, &buf); log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s", n_prefixes, strna(buf)); } dhcp6_pd_prefix_distribute(dhcp6_link, - &prefix, + &prefix.in6, pd_prefix_len, lifetime_preferred, lifetime_valid, true); dhcp6_pd_prefix_distribute(dhcp6_link, - &prefix, + &prefix.in6, pd_prefix_len, lifetime_preferred, lifetime_valid, @@ -996,16 +1038,9 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * link->dhcp6_address_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address"); + if (r <= 0) + return r; if (link->dhcp6_address_messages == 0) { log_link_debug(link, "DHCPv6 addresses set"); @@ -1032,8 +1067,9 @@ static void log_dhcp6_address(Link *link, const Address *address, char **ret) { assert(link); assert(address); + assert(address->family == AF_INET6); - (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buffer); + (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer); if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, address->cinfo.ifa_valid * USEC_PER_SEC, @@ -1085,7 +1121,28 @@ finalize: *ret = TAKE_PTR(buffer); } -static int dhcp6_update_address( +static int dhcp6_after_address_configure(Request *req, void *object) { + Address *address = object; + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ADDRESS); + assert(address); + + link = req->link; + + r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 address: %m"); + + set_remove(link->dhcp6_addresses_old, address); + + return 0; +} + +static int dhcp6_request_address( Link *link, const struct in6_addr *ip6_addr, uint32_t lifetime_preferred, @@ -1093,7 +1150,7 @@ static int dhcp6_update_address( _cleanup_(address_freep) Address *addr = NULL; _cleanup_free_ char *buffer = NULL; - Address *ret; + Request *req; int r; r = address_new(&addr); @@ -1109,19 +1166,15 @@ static int dhcp6_update_address( log_dhcp6_address(link, addr, &buffer); - r = address_configure(addr, link, dhcp6_address_handler, &ret); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s: %m", strna(buffer)); - if (r > 0) + if (address_get(link, addr, NULL) < 0) link->dhcp6_address_configured = false; - link->dhcp6_address_messages++; - - r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret); + r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_address_messages, + dhcp6_address_handler, &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s: %m", strna(buffer)); + return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s: %m", strna(buffer)); - (void) set_remove(link->dhcp6_addresses_old, ret); + req->after_configure = dhcp6_after_address_configure; return 0; } @@ -1144,7 +1197,7 @@ static int dhcp6_address_acquired(Link *link) { if (r < 0) break; - r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); + r = dhcp6_request_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) return r; } @@ -1224,11 +1277,10 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { if (r < 0) return r; - if (link->dhcp6_address_configured && link->dhcp6_route_configured) - link_check_ready(link); - else + if (!link->dhcp6_address_configured || !link->dhcp6_route_configured) link_set_state(link, LINK_STATE_CONFIGURING); + link_check_ready(link); return 0; } @@ -1298,7 +1350,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { } } -int dhcp6_request_address(Link *link, int ir) { +int dhcp6_request_information(Link *link, int ir) { int r, inf_req, pd; bool running; @@ -1376,7 +1428,7 @@ int dhcp6_start(Link *link) { log_link_debug(link, "Acquiring DHCPv6 lease"); - return dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); + return dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); } int dhcp6_request_prefix_delegation(Link *link) { @@ -1849,7 +1901,8 @@ int config_parse_dhcp6_pd_token( void *data, void *userdata) { - union in_addr_union *addr = data, tmp; + struct in6_addr *addr = data; + union in_addr_union tmp; int r; assert(filename); @@ -1858,7 +1911,7 @@ int config_parse_dhcp6_pd_token( assert(data); if (isempty(rvalue)) { - *addr = IN_ADDR_NULL; + *addr = (struct in6_addr) {}; return 0; } @@ -1875,7 +1928,7 @@ int config_parse_dhcp6_pd_token( return 0; } - *addr = tmp; + *addr = tmp.in6; return 0; } diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 025bbb6188..f24d6eea5a 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -32,7 +32,7 @@ int dhcp6_pd_remove(Link *link); int dhcp6_configure(Link *link); int dhcp6_update_mac(Link *link); int dhcp6_start(Link *link); -int dhcp6_request_address(Link *link, int ir); +int dhcp6_request_information(Link *link, int ir); int dhcp6_request_prefix_delegation(Link *link); int link_serialize_dhcp6_client(Link *link, FILE *f); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 5390c53799..8e2d761cf9 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -8,39 +8,55 @@ #include "networkd-ipv4ll.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "parse-util.h" -static int ipv4ll_address_lost(Link *link) { +static int address_new_from_ipv4ll(Link *link, Address **ret) { _cleanup_(address_freep) Address *address = NULL; struct in_addr addr; int r; assert(link); - - link->ipv4ll_address_configured = false; + assert(link->ipv4ll); + assert(ret); r = sd_ipv4ll_get_address(link->ipv4ll, &addr); if (r < 0) - return 0; - - log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(addr)); + return r; r = address_new(&address); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate address: %m"); + return -ENOMEM; address->family = AF_INET; address->in_addr.in = addr; address->prefixlen = 16; + address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> address->prefixlen); address->scope = RT_SCOPE_LINK; + address->route_metric = IPV4LL_ROUTE_METRIC; + + *ret = TAKE_PTR(address); + return 0; +} + +static int ipv4ll_address_lost(Link *link) { + _cleanup_(address_freep) Address *address = NULL; + int r; + + assert(link); + + link->ipv4ll_address_configured = false; - r = address_remove(address, link, NULL); + r = address_new_from_ipv4ll(link, &address); + if (r == -ENOENT) + return 0; if (r < 0) return r; - link_check_ready(link); + log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - return 0; + return address_remove(address, link, NULL); } static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -49,13 +65,9 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link assert(link); assert(!link->ipv4ll_address_configured); - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "could not set ipv4ll address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_configure_handler_internal(rtnl, m, link, "Could not set ipv4ll address"); + if (r <= 0) + return r; link->ipv4ll_address_configured = true; link_check_ready(link); @@ -64,8 +76,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link } static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { - _cleanup_(address_freep) Address *ll_addr = NULL; - struct in_addr address; + _cleanup_(address_freep) Address *address = NULL; int r; assert(ll); @@ -73,31 +84,16 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { link->ipv4ll_address_configured = false; - r = sd_ipv4ll_get_address(ll, &address); + r = address_new_from_ipv4ll(link, &address); if (r == -ENOENT) return 0; - else if (r < 0) - return r; - - log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(address)); - - r = address_new(&ll_addr); if (r < 0) return r; - ll_addr->family = AF_INET; - ll_addr->in_addr.in = address; - ll_addr->prefixlen = 16; - ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); - ll_addr->scope = RT_SCOPE_LINK; - ll_addr->route_metric = IPV4LL_ROUTE_METRIC; - - r = address_configure(ll_addr, link, ipv4ll_address_handler, NULL); - if (r < 0) - return r; + log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR, + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - return 0; + return link_request_address(link, TAKE_PTR(address), true, NULL, ipv4ll_address_handler, NULL); } static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ffaebbbf6d..152ac05e7c 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -33,6 +33,7 @@ #include "networkd-dhcp6.h" #include "networkd-fdb.h" #include "networkd-ipv4ll.h" +#include "networkd-ipv6-proxy-ndp.h" #include "networkd-link-bus.h" #include "networkd-link.h" #include "networkd-lldp-tx.h" @@ -41,11 +42,12 @@ #include "networkd-ndisc.h" #include "networkd-neighbor.h" #include "networkd-nexthop.h" -#include "networkd-sriov.h" -#include "networkd-sysctl.h" +#include "networkd-queue.h" #include "networkd-radv.h" #include "networkd-routing-policy-rule.h" +#include "networkd-sriov.h" #include "networkd-state-file.h" +#include "networkd-sysctl.h" #include "networkd-wifi.h" #include "set.h" #include "socket-util.h" @@ -133,6 +135,25 @@ bool link_ipv6_enabled(Link *link) { return false; } +bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) { + assert(link); + + if (!link->network || link->network->unmanaged) { + if (!allow_unmanaged) + return false; + + return link_has_carrier(link); + } + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + if (!link_has_carrier(link) && !link->network->configure_without_carrier) + return false; + + return true; +} + static bool link_is_enslaved(Link *link) { if (link->flags & IFF_SLAVE) /* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */ @@ -773,12 +794,9 @@ void link_check_ready(Link *link) { return; } - if (!link->addresses_configured) + if (!link->static_addresses_configured) return (void) log_link_debug(link, "%s(): static addresses are not configured.", __func__); - if (!link->neighbors_configured) - return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__); - SET_FOREACH(a, link->addresses) if (!address_is_ready(a)) { _cleanup_free_ char *str = NULL; @@ -787,13 +805,16 @@ void link_check_ready(Link *link) { return (void) log_link_debug(link, "%s(): an address %s is not ready.", __func__, strna(str)); } - if (!link->static_routes_configured) - return (void) log_link_debug(link, "%s(): static routes are not configured.", __func__); + if (!link->static_neighbors_configured) + return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__); if (!link->static_nexthops_configured) return (void) log_link_debug(link, "%s(): static nexthops are not configured.", __func__); - if (!link->routing_policy_rules_configured) + if (!link->static_routes_configured) + return (void) log_link_debug(link, "%s(): static routes are not configured.", __func__); + + if (!link->static_routing_policy_rules_configured) return (void) log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__); if (!link->tc_configured) @@ -857,15 +878,6 @@ static int link_set_static_configs(Link *link) { assert(link->network); assert(link->state != _LINK_STATE_INVALID); - /* Reset all *_configured flags we are configuring. */ - link->request_static_addresses = false; - link->addresses_configured = false; - link->addresses_ready = false; - link->neighbors_configured = false; - link->static_routes_configured = false; - link->static_nexthops_configured = false; - link->routing_policy_rules_configured = false; - r = link_set_bridge_fdb(link); if (r < 0) return r; @@ -874,20 +886,31 @@ static int link_set_static_configs(Link *link) { if (r < 0) return r; - r = link_set_neighbors(link); + r = link_set_ipv6_proxy_ndp_addresses(link); + if (r < 0) + return r; + + r = link_set_address_labels(link); if (r < 0) return r; - r = link_set_addresses(link); + r = link_request_static_addresses(link); if (r < 0) return r; - r = link_set_address_labels(link); + r = link_request_static_neighbors(link); + if (r < 0) + return r; + + r = link_request_static_nexthops(link, false); if (r < 0) return r; - /* now that we can figure out a default address for the dhcp server, start it */ - r = dhcp4_server_configure(link); + r = link_request_static_routes(link, false); + if (r < 0) + return r; + + r = link_request_static_routing_policy_rules(link); if (r < 0) return r; @@ -2028,17 +2051,17 @@ static int link_drop_foreign_config(Link *link) { assert(link); assert(link->manager); - r = link_drop_foreign_addresses(link); + r = link_drop_foreign_routes(link); - k = link_drop_foreign_neighbors(link); + k = link_drop_foreign_nexthops(link); if (k < 0 && r >= 0) r = k; - k = link_drop_foreign_routes(link); + k = link_drop_foreign_addresses(link); if (k < 0 && r >= 0) r = k; - k = link_drop_foreign_nexthops(link); + k = link_drop_foreign_neighbors(link); if (k < 0 && r >= 0) r = k; @@ -2055,17 +2078,17 @@ static int link_drop_config(Link *link) { assert(link); assert(link->manager); - r = link_drop_addresses(link); + r = link_drop_routes(link); - k = link_drop_neighbors(link); + k = link_drop_nexthops(link); if (k < 0 && r >= 0) r = k; - k = link_drop_routes(link); + k = link_drop_addresses(link); if (k < 0 && r >= 0) r = k; - k = link_drop_nexthops(link); + k = link_drop_neighbors(link); if (k < 0 && r >= 0) r = k; @@ -2078,6 +2101,17 @@ static int link_drop_config(Link *link) { return r; } +static void link_drop_requests(Link *link) { + Request *req; + + assert(link); + assert(link->manager); + + ORDERED_SET_FOREACH(req, link->manager->request_queue) + if (req->link == link) + request_drop(req); +} + int link_configure(Link *link) { int r; @@ -2232,6 +2266,8 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for if (r < 0) return r; + link_drop_requests(link); + r = link_drop_config(link); if (r < 0) return r; @@ -2664,6 +2700,8 @@ static int link_carrier_lost(Link *link) { return r; } + link_drop_requests(link); + r = link_drop_config(link); if (r < 0) return r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index b560dde0dc..fbc593f124 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -78,13 +78,16 @@ typedef struct Link { LinkAddressState ipv4_address_state; LinkAddressState ipv6_address_state; - unsigned address_messages; - unsigned address_remove_messages; unsigned address_label_messages; - unsigned neighbor_messages; - unsigned route_messages; - unsigned nexthop_messages; - unsigned routing_policy_rule_messages; + unsigned static_address_messages; + unsigned static_neighbor_messages; + unsigned static_nexthop_messages; + unsigned static_route_messages; + unsigned static_routing_policy_rule_messages; + unsigned address_remove_messages; + unsigned neighbor_remove_messages; + unsigned nexthop_remove_messages; + unsigned route_remove_messages; unsigned tc_messages; unsigned sr_iov_messages; unsigned enslaving; @@ -118,13 +121,11 @@ typedef struct Link { sd_ipv4ll *ipv4ll; bool ipv4ll_address_configured:1; - bool request_static_addresses:1; - bool addresses_configured:1; - bool addresses_ready:1; - bool neighbors_configured:1; - bool static_routes_configured:1; + bool static_addresses_configured:1; + bool static_neighbors_configured:1; bool static_nexthops_configured:1; - bool routing_policy_rules_configured:1; + bool static_routes_configured:1; + bool static_routing_policy_rules_configured:1; bool tc_configured:1; bool sr_iov_configured:1; bool setting_mtu:1; @@ -199,6 +200,8 @@ typedef struct Link { typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*); +bool link_is_ready_to_configure(Link *link, bool allow_unmanaged); + void link_ntp_settings_clear(Link *link); void link_dns_settings_clear(Link *link); Link *link_unref(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 562ce5ca54..fd576169a9 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -34,6 +34,7 @@ #include "networkd-neighbor.h" #include "networkd-network-bus.h" #include "networkd-nexthop.h" +#include "networkd-queue.h" #include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" #include "networkd-state-file.h" @@ -406,6 +407,10 @@ int manager_new(Manager **ret) { if (r < 0) return r; + r = sd_event_add_post(m->event, NULL, manager_process_requests, m); + if (r < 0) + return r; + r = manager_connect_rtnl(m); if (r < 0) return r; @@ -446,6 +451,8 @@ Manager* manager_free(Manager *m) { HASHMAP_FOREACH(link, m->links) (void) link_stop_engines(link, true); + m->request_queue = ordered_set_free_with_destructor(m->request_queue, request_free); + m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free); m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 301b97c1a1..3f8f81b865 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -62,6 +62,7 @@ struct Manager { char* dynamic_hostname; char* dynamic_timezone; + unsigned routing_policy_rule_remove_messages; Set *rules; Set *rules_foreign; @@ -69,10 +70,12 @@ struct Manager { Hashmap *nexthops_by_id; /* Manager stores nexthops without RTA_OIF attribute. */ + unsigned nexthop_remove_messages; Set *nexthops; Set *nexthops_foreign; /* Manager stores routes without RTA_OIF attribute. */ + unsigned route_remove_messages; Set *routes; Set *routes_foreign; @@ -91,6 +94,8 @@ struct Manager { bool bridge_mdb_on_master_not_supported; FirewallContext *fw_ctx; + + OrderedSet *request_queue; }; int manager_new(Manager **ret); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 988c673255..cfcf4cc36a 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -15,6 +15,7 @@ #include "networkd-dhcp6.h" #include "networkd-manager.h" #include "networkd-ndisc.h" +#include "networkd-queue.h" #include "networkd-state-file.h" #include "string-table.h" #include "string-util.h" @@ -89,6 +90,7 @@ static int ndisc_address_callback(Address *address) { assert(address); assert(address->link); + assert(address->family == AF_INET6); SET_FOREACH(n, address->link->ndisc_addresses) if (n->address == address) { @@ -99,7 +101,7 @@ static int ndisc_address_callback(Address *address) { if (in6_addr_is_null(&router)) { _cleanup_free_ char *buf = NULL; - (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buf); + (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buf); log_link_debug(address->link, "%s is called for %s, but it is already removed, ignoring.", __func__, strna(buf)); return 0; @@ -107,7 +109,7 @@ static int ndisc_address_callback(Address *address) { /* Make this called only once */ SET_FOREACH(n, address->link->ndisc_addresses) - if (IN6_ARE_ADDR_EQUAL(&n->router, &router)) + if (in6_addr_equal(&n->router, &router)) n->address->callback = NULL; return ndisc_remove_old_one(address->link, &router, true); @@ -131,7 +133,7 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool return 0; SET_FOREACH(na, link->ndisc_addresses) - if (!na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router)) { + if (!na->marked && in6_addr_equal(&na->router, router)) { set_callback = true; break; } @@ -145,13 +147,13 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool if (set_callback) { SET_FOREACH(na, link->ndisc_addresses) - if (!na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router)) + if (!na->marked && in6_addr_equal(&na->router, router)) na->address->callback = ndisc_address_callback; if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) router, &buf); + (void) in6_addr_to_string(router, &buf); log_link_debug(link, "No SLAAC address obtained from %s is ready. " "The old NDisc information will be removed later.", strna(buf)); @@ -163,32 +165,32 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) router, &buf); + (void) in6_addr_to_string(router, &buf); log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf)); } SET_FOREACH(na, link->ndisc_addresses) - if (na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router)) { + if (na->marked && in6_addr_equal(&na->router, router)) { k = address_remove(na->address, link, NULL); if (k < 0) r = k; } SET_FOREACH(nr, link->ndisc_routes) - if (nr->marked && IN6_ARE_ADDR_EQUAL(&nr->router, router)) { + if (nr->marked && in6_addr_equal(&nr->router, router)) { k = route_remove(nr->route, NULL, link, NULL); if (k < 0) r = k; } SET_FOREACH(rdnss, link->ndisc_rdnss) - if (rdnss->marked && IN6_ARE_ADDR_EQUAL(&rdnss->router, router)) { + if (rdnss->marked && in6_addr_equal(&rdnss->router, router)) { free(set_remove(link->ndisc_rdnss, rdnss)); updated = true; } SET_FOREACH(dnssl, link->ndisc_dnssl) - if (dnssl->marked && IN6_ARE_ADDR_EQUAL(&dnssl->router, router)) { + if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) { free(set_remove(link->ndisc_dnssl, dnssl)); updated = true; } @@ -304,15 +306,9 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li link->ndisc_routes_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_error_errno(link, m, r, "Could not set NDisc route"); - link_enter_failed(link); - return 1; - } + r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route"); + if (r <= 0) + return r; if (link->ndisc_routes_messages == 0) { log_link_debug(link, "NDisc routes set."); @@ -330,24 +326,23 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } -static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt) { +static int ndisc_after_route_configure(Request *req, void *object) { _cleanup_free_ NDiscRoute *nr = NULL; NDiscRoute *nr_exist; struct in6_addr router; - Route *ret; + Route *route = object; + sd_ndisc_router *rt; + Link *link; int r; + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ROUTE); + assert(req->userdata); assert(route); - assert(link); - assert(rt); - - r = route_configure(route, link, ndisc_route_handler, &ret); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set NDisc route: %m"); - if (r > 0) - link->ndisc_routes_configured = false; - link->ndisc_routes_messages++; + link = req->link; + rt = req->userdata; r = sd_ndisc_router_get_address(rt, &router); if (r < 0) @@ -359,7 +354,7 @@ static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt) *nr = (NDiscRoute) { .router = router, - .route = ret, + .route = route, }; nr_exist = set_get(link->ndisc_routes, nr); @@ -378,6 +373,39 @@ static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt) return 0; } +static void ndisc_request_on_free(Request *req) { + assert(req); + + sd_ndisc_router_unref(req->userdata); +} + +static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) { + _cleanup_(route_freep) Route *route = in; + Request *req; + int r; + + assert(route); + assert(link); + assert(rt); + + r = link_has_route(link, route); + if (r < 0) + return r; + if (r == 0) + link->ndisc_routes_configured = false; + + r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages, + ndisc_route_handler, &req); + if (r < 0) + return r; + + req->userdata = sd_ndisc_router_ref(rt); + req->after_configure = ndisc_after_route_configure; + req->on_free = ndisc_request_on_free; + + return 0; +} + static void ndisc_address_hash_func(const NDiscAddress *x, struct siphash *state) { address_hash_func(x->address, state); } @@ -401,16 +429,9 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * link->ndisc_addresses_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_error_errno(link, m, r, "Could not set NDisc address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); + r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address"); + if (r <= 0) + return r; if (link->ndisc_addresses_messages == 0) { log_link_debug(link, "NDisc SLAAC addresses set."); @@ -426,24 +447,23 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } -static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router *rt) { +static int ndisc_after_address_configure(Request *req, void *object) { _cleanup_free_ NDiscAddress *na = NULL; NDiscAddress *na_exist; struct in6_addr router; - Address *ret; + sd_ndisc_router *rt; + Address *address = object; + Link *link; int r; + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_ADDRESS); + assert(req->userdata); assert(address); - assert(link); - assert(rt); - - r = address_configure(address, link, ndisc_address_handler, &ret); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m"); - if (r > 0) - link->ndisc_addresses_configured = false; - link->ndisc_addresses_messages++; + link = req->link; + rt = req->userdata; r = sd_ndisc_router_get_address(rt, &router); if (r < 0) @@ -455,7 +475,7 @@ static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router *na = (NDiscAddress) { .router = router, - .address = ret, + .address = address, }; na_exist = set_get(link->ndisc_addresses, na); @@ -474,9 +494,33 @@ static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router return 0; } +static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { + _cleanup_(address_freep) Address *address = in; + Request *req; + int r; + + assert(address); + assert(link); + assert(rt); + + if (address_get(link, address, NULL) < 0) + link->ndisc_addresses_configured = false; + + r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages, + ndisc_address_handler, &req); + if (r < 0) + return r; + + req->userdata = sd_ndisc_router_ref(rt); + req->after_configure = ndisc_after_address_configure; + req->on_free = ndisc_request_on_free; + + return 0; +} + static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; - union in_addr_union gateway; + struct in6_addr gateway; uint16_t lifetime; unsigned preference; uint32_t table, mtu; @@ -493,17 +537,17 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (lifetime == 0) /* not a default router */ return 0; - r = sd_ndisc_router_get_address(rt, &gateway.in6); + r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); - if (link_has_ipv6_address(link, &gateway.in6) > 0) { + if (link_has_ipv6_address(link, &gateway) > 0) { if (DEBUG_LOGGING) { _cleanup_free_ char *buffer = NULL; - (void) in_addr_to_string(AF_INET6, &gateway, &buffer); + (void) in6_addr_to_string(&gateway, &buffer); log_link_debug(link, "No NDisc route added, gateway %s matches local address", - strnull(buffer)); + strna(buffer)); } return 0; } @@ -534,13 +578,13 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->protocol = RTPROT_RA; route->pref = preference; route->gw_family = AF_INET6; - route->gw = gateway; + route->gw.in6 = gateway; route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); route->mtu = mtu; - r = ndisc_route_configure(route, link, rt); + r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not set default route: %m"); + return log_link_error_errno(link, r, "Could not request default route: %m"); Route *route_gw; HASHMAP_FOREACH(route_gw, link->network->routes_by_section) { @@ -550,22 +594,26 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (route_gw->gw_family != AF_INET6) continue; - route_gw->gw = gateway; - if (!route_gw->table_set) - route_gw->table = table; - if (!route_gw->priority_set) - route_gw->priority = link->network->ipv6_accept_ra_route_metric; - if (!route_gw->protocol_set) - route_gw->protocol = RTPROT_RA; - if (!route_gw->pref_set) - route_gw->pref = preference; - route_gw->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); - if (route_gw->mtu == 0) - route_gw->mtu = mtu; - - r = ndisc_route_configure(route_gw, link, rt); + r = route_dup(route_gw, &route); if (r < 0) - return log_link_error_errno(link, r, "Could not set gateway: %m"); + return r; + + route->gw.in6 = gateway; + if (!route->table_set) + route->table = table; + if (!route->priority_set) + route->priority = link->network->ipv6_accept_ra_route_metric; + if (!route->protocol_set) + route->protocol = RTPROT_RA; + if (!route->pref_set) + route->pref = preference; + route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); + if (route->mtu == 0) + route->mtu = mtu; + + r = ndisc_request_route(TAKE_PTR(route), link, rt); + if (r < 0) + return log_link_error_errno(link, r, "Could not request gateway: %m"); } return 0; @@ -650,7 +698,7 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, _cleanup_free_ struct in6_addr *new_address = NULL; if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE - && (in6_addr_is_null(&j->prefix) || IN6_ARE_ADDR_EQUAL(&j->prefix, address))) { + && (in6_addr_is_null(&j->prefix) || in6_addr_equal(&j->prefix, address))) { /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop * does not actually attempt Duplicate Address Detection; the counter will be incremented * only when the address generation algorithm produces an invalid address, and the loop @@ -709,7 +757,6 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining; _cleanup_set_free_free_ Set *addresses = NULL; - _cleanup_(address_freep) Address *address = NULL; struct in6_addr addr, *a; unsigned prefixlen; usec_t time_now; @@ -746,18 +793,18 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (r < 0) return r; - r = address_new(&address); - if (r < 0) - return log_oom(); - - address->family = AF_INET6; - address->prefixlen = prefixlen; - address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; - address->cinfo.ifa_prefered = lifetime_preferred; - SET_FOREACH(a, addresses) { + _cleanup_(address_freep) Address *address = NULL; Address *existing_address; + r = address_new(&address); + if (r < 0) + return log_oom(); + + address->family = AF_INET6; + address->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; + address->cinfo.ifa_prefered = lifetime_preferred; address->in_addr.in6 = *a; /* see RFC4862 section 5.5.3.e */ @@ -778,9 +825,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (address->cinfo.ifa_valid == 0) continue; - r = ndisc_address_configure(address, link, rt); + r = ndisc_request_address(TAKE_PTR(address), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); + return log_link_error_errno(link, r, "Could not request SLAAC address: %m"); } return 0; @@ -824,16 +871,16 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = ndisc_route_configure(route, link, rt); + r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not set prefix route: %m");; + return log_link_error_errno(link, r, "Could not request prefix route: %m");; return 0; } static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; - union in_addr_union gateway, dst; + struct in6_addr gateway, dst; uint32_t lifetime; unsigned preference, prefixlen; usec_t time_now; @@ -848,35 +895,35 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (lifetime == 0) return 0; - r = sd_ndisc_router_route_get_address(rt, &dst.in6); + r = sd_ndisc_router_route_get_address(rt, &dst); if (r < 0) return log_link_error_errno(link, r, "Failed to get route address: %m"); if ((!set_isempty(link->network->ndisc_allow_listed_route_prefix) && - !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst.in6)) || - set_contains(link->network->ndisc_deny_listed_route_prefix, &dst.in6)) { + !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst)) || + set_contains(link->network->ndisc_deny_listed_route_prefix, &dst)) { if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; - (void) in_addr_to_string(AF_INET6, &dst, &buf); + (void) in6_addr_to_string(&dst, &buf); if (!set_isempty(link->network->ndisc_allow_listed_route_prefix)) - log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strnull(buf)); + log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strna(buf)); else - log_link_debug(link, "Route prefix '%s' is in deny list, ignoring", strnull(buf)); + log_link_debug(link, "Route prefix '%s' is in deny list, ignoring", strna(buf)); } return 0; } - r = sd_ndisc_router_get_address(rt, &gateway.in6); + r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); - if (link_has_ipv6_address(link, &gateway.in6) > 0) { + if (link_has_ipv6_address(link, &gateway) > 0) { if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; - (void) in_addr_to_string(AF_INET6, &gateway, &buf); - log_link_debug(link, "Advertised route gateway, %s, is local to the link, ignoring route", strnull(buf)); + (void) in6_addr_to_string(&gateway, &buf); + log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route", strna(buf)); } return 0; } @@ -902,15 +949,15 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { route->priority = link->network->ipv6_accept_ra_route_metric; route->protocol = RTPROT_RA; route->pref = preference; - route->gw = gateway; + route->gw.in6 = gateway; route->gw_family = AF_INET6; - route->dst = dst; + route->dst.in6 = dst; route->dst_prefixlen = prefixlen; route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); - r = ndisc_route_configure(route, link, rt); + r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not set additional route: %m"); + return log_link_error_errno(link, r, "Could not request additional route: %m"); return 0; } @@ -959,7 +1006,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); SET_FOREACH(rdnss, link->ndisc_rdnss) - if (IN6_ARE_ADDR_EQUAL(&rdnss->router, &router)) + if (in6_addr_equal(&rdnss->router, &router)) rdnss->marked = true; if (lifetime == 0) @@ -1053,7 +1100,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); SET_FOREACH(dnssl, link->ndisc_dnssl) - if (IN6_ARE_ADDR_EQUAL(&dnssl->router, &router)) + if (in6_addr_equal(&dnssl->router, &router)) dnssl->marked = true; if (lifetime == 0) @@ -1119,20 +1166,20 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { switch (type) { case SD_NDISC_OPTION_PREFIX_INFORMATION: { - union in_addr_union a; + struct in6_addr a; uint8_t flags; - r = sd_ndisc_router_prefix_get_address(rt, &a.in6); + r = sd_ndisc_router_prefix_get_address(rt, &a); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); if ((!set_isempty(link->network->ndisc_allow_listed_prefix) && - !set_contains(link->network->ndisc_allow_listed_prefix, &a.in6)) || - set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) { + !set_contains(link->network->ndisc_allow_listed_prefix, &a)) || + set_contains(link->network->ndisc_deny_listed_prefix, &a)) { if (DEBUG_LOGGING) { _cleanup_free_ char *b = NULL; - (void) in_addr_to_string(AF_INET6, &a, &b); + (void) in6_addr_to_string(&a, &b); if (!set_isempty(link->network->ndisc_allow_listed_prefix)) log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b)); else @@ -1187,7 +1234,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { - union in_addr_union router; + struct in6_addr router; uint64_t flags; NDiscAddress *na; NDiscRoute *nr; @@ -1198,17 +1245,17 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { assert(link->manager); assert(rt); - r = sd_ndisc_router_get_address(rt, &router.in6); + r = sd_ndisc_router_get_address(rt, &router); if (r < 0) return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); if ((!set_isempty(link->network->ndisc_allow_listed_router) && - !set_contains(link->network->ndisc_allow_listed_router, &router.in6)) || - set_contains(link->network->ndisc_deny_listed_router, &router.in6)) { + !set_contains(link->network->ndisc_allow_listed_router, &router)) || + set_contains(link->network->ndisc_deny_listed_router, &router)) { if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; - (void) in_addr_to_string(AF_INET6, &router, &buf); + (void) in6_addr_to_string(&router, &buf); if (!set_isempty(link->network->ndisc_allow_listed_router)) log_link_debug(link, "Router '%s' is not in allow list, ignoring", strna(buf)); else @@ -1218,11 +1265,11 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { } SET_FOREACH(na, link->ndisc_addresses) - if (IN6_ARE_ADDR_EQUAL(&na->router, &router.in6)) + if (in6_addr_equal(&na->router, &router)) na->marked = true; SET_FOREACH(nr, link->ndisc_routes) - if (IN6_ARE_ADDR_EQUAL(&nr->router, &router.in6)) + if (in6_addr_equal(&nr->router, &router)) nr->marked = true; r = sd_ndisc_router_get_flags(rt, &flags); @@ -1235,11 +1282,11 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ - r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); + r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED)); else /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode * even if router does not have M or O flag. */ - r = dhcp6_request_address(link, false); + r = dhcp6_request_information(link, false); if (r < 0 && r != -EBUSY) return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); else @@ -1267,11 +1314,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - if (link->ndisc_addresses_configured && link->ndisc_routes_configured) - link_check_ready(link); - else + if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) link_set_state(link, LINK_STATE_CONFIGURING); + link_check_ready(link); return 0; } diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index b33f560b79..2afe71c5f8 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -7,6 +7,7 @@ #include "networkd-manager.h" #include "networkd-neighbor.h" #include "networkd-network.h" +#include "networkd-queue.h" #include "set.h" Neighbor *neighbor_free(Neighbor *neighbor) { @@ -235,34 +236,12 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const "%s neighbor: lladdr: %s, dst: %s", str, strna(lladdr), strna(dst)); } +static int neighbor_configure( + const Neighbor *neighbor, + Link *link, + link_netlink_message_handler_t callback, + Neighbor **ret) { -static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->neighbor_messages > 0); - - link->neighbor_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - /* Neighbor may not exist yet. So, do not enter failed state here. */ - log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring"); - - if (link->neighbor_messages == 0) { - log_link_debug(link, "Neighbors set"); - link->neighbors_configured = true; - link_check_ready(link); - } - - return 1; -} - -static int neighbor_configure(Neighbor *neighbor, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -271,6 +250,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link) { assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); + assert(callback); log_neighbor_debug(neighbor, "Configuring", link); @@ -291,22 +271,65 @@ static int neighbor_configure(Neighbor *neighbor, Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_configure_handler, + r = neighbor_add(link, neighbor, ret); + if (r < 0) + return log_link_error_errno(link, r, "Could not add neighbor: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - link->neighbor_messages++; link_ref(link); - r = neighbor_add(link, neighbor, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not add neighbor: %m"); - return r; } -int link_set_neighbors(Link *link) { +static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->static_neighbor_messages > 0); + + link->static_neighbor_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set neighbor"); + link_enter_failed(link); + return 1; + } + + if (link->static_neighbor_messages == 0) { + log_link_debug(link, "Neighbors set"); + link->static_neighbors_configured = true; + link_check_ready(link); + } + + return 1; +} + +static int link_request_neighbor( + Link *link, + Neighbor *neighbor, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { + + assert(link); + assert(neighbor); + + log_neighbor_debug(neighbor, "Requesting", link); + return link_queue_request(link, REQUEST_TYPE_NEIGHBOR, neighbor, consume_object, + message_counter, netlink_handler, ret); +} + +int link_request_static_neighbors(Link *link) { Neighbor *neighbor; int r; @@ -314,24 +337,20 @@ int link_set_neighbors(Link *link) { assert(link->network); assert(link->state != _LINK_STATE_INVALID); - if (link->neighbor_messages != 0) { - log_link_debug(link, "Neighbors are configuring."); - return 0; - } - - link->neighbors_configured = false; + link->static_neighbors_configured = false; HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { - r = neighbor_configure(neighbor, link); + r = link_request_neighbor(link, neighbor, false, &link->static_neighbor_messages, + static_neighbor_configure_handler, NULL); if (r < 0) - return log_link_warning_errno(link, r, "Could not set neighbor: %m"); + return log_link_warning_errno(link, r, "Could not request neighbor: %m"); } - if (link->neighbor_messages == 0) { - link->neighbors_configured = true; + if (link->static_neighbor_messages == 0) { + link->static_neighbors_configured = true; link_check_ready(link); } else { - log_link_debug(link, "Setting neighbors"); + log_link_debug(link, "Requesting neighbors"); link_set_state(link, LINK_STATE_CONFIGURING); } @@ -343,6 +362,9 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link assert(m); assert(link); + assert(link->neighbor_remove_messages > 0); + + link->neighbor_remove_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -382,6 +404,7 @@ static int neighbor_remove(Neighbor *neighbor, Link *link) { return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); + link->neighbor_remove_messages++; return 0; } @@ -437,6 +460,34 @@ int link_drop_neighbors(Link *link) { return r; } +int request_process_neighbor(Request *req) { + Neighbor *ret; + int r; + + assert(req); + assert(req->link); + assert(req->neighbor); + assert(req->type == REQUEST_TYPE_NEIGHBOR); + + if (!link_is_ready_to_configure(req->link, false)) + return 0; + + if (req->link->neighbor_remove_messages > 0) + return 0; + + r = neighbor_configure(req->neighbor, req->link, req->netlink_handler, &ret); + if (r < 0) + return r; + + if (req->after_configure) { + r = req->after_configure(req, ret); + if (r < 0) + return r; + } + + return 1; +} + int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(neighbor_freep) Neighbor *tmp = NULL; _cleanup_free_ void *lladdr = NULL; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 8ad790be2d..7ac2f90eb3 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -10,9 +10,10 @@ #include "in-addr-util.h" #include "networkd-util.h" -typedef Manager Manager; -typedef Network Network; -typedef Link Link; +typedef struct Link Link; +typedef struct Manager Manager; +typedef struct Network Network; +typedef struct Request Request; union lladdr_union { struct ether_addr mac; @@ -34,10 +35,12 @@ Neighbor *neighbor_free(Neighbor *neighbor); void network_drop_invalid_neighbors(Network *network); -int link_set_neighbors(Link *link); int link_drop_neighbors(Link *link); int link_drop_foreign_neighbors(Link *link); +int link_request_static_neighbors(Link *link); +int request_process_neighbor(Request *req); + int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index d7c216af6e..c094065a83 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -228,7 +228,7 @@ struct Network { bool dhcp6_pd_manage_temporary_address; int64_t dhcp6_pd_subnet_id; uint32_t dhcp6_pd_route_metric; - union in_addr_union dhcp6_pd_token; + struct in6_addr dhcp6_pd_token; /* Bridge Support */ int use_bpdu; diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index f3dbd98ed1..5c6c58356c 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -10,6 +10,8 @@ #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-nexthop.h" +#include "networkd-queue.h" +#include "networkd-route.h" #include "parse-util.h" #include "set.h" #include "string-util.h" @@ -356,13 +358,16 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole)); } -static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(m); + assert(link); + assert(link->nexthop_remove_messages > 0); + + link->nexthop_remove_messages--; - /* Note that link may be NULL. */ - if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; r = sd_netlink_message_get_errno(m); @@ -372,6 +377,22 @@ static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } +static int manager_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) { + int r; + + assert(m); + assert(manager); + assert(manager->nexthop_remove_messages > 0); + + manager->nexthop_remove_messages--; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -ENOENT) + log_message_warning_errno(m, r, "Could not drop nexthop, ignoring"); + + return 1; +} + static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -396,55 +417,40 @@ static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) if (r < 0) return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); - r = netlink_call_async(manager->rtnl, NULL, req, nexthop_remove_handler, - link_netlink_destroy_callback, link); + if (link) + r = netlink_call_async(manager->rtnl, NULL, req, link_nexthop_remove_handler, + link_netlink_destroy_callback, link); + else + r = netlink_call_async(manager->rtnl, NULL, req, manager_nexthop_remove_handler, + NULL, manager); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); /* link may be NULL, link_ref() is OK with that */ + if (link) + link->nexthop_remove_messages++; + else + manager->nexthop_remove_messages++; + return 0; } -static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->nexthop_messages > 0); - - link->nexthop_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set nexthop"); - link_enter_failed(link); - return 1; - } - - if (link->nexthop_messages == 0) { - log_link_debug(link, "Nexthops set"); - link->static_nexthops_configured = true; - /* Now all nexthops are configured. Let's configure remaining routes. */ - r = link_set_routes_with_gateway(link); - if (r < 0) - link_enter_failed(link); - } +static int nexthop_configure( + const NextHop *nexthop, + Link *link, + link_netlink_message_handler_t callback, + NextHop **ret) { - return 1; -} - -static int nexthop_configure(const NextHop *nexthop, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; + int r, k; assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); assert(IN_SET(nexthop->family, AF_INET, AF_INET6)); + assert(callback); log_nexthop_debug(nexthop, nexthop->id, "Configuring", link); @@ -482,60 +488,90 @@ static int nexthop_configure(const NextHop *nexthop, Link *link) { } } - r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler, + k = nexthop_add(link, nexthop, ret); + if (k < 0) + return log_link_error_errno(link, k, "Could not add nexthop: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - r = nexthop_add(link, nexthop, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not add nexthop: %m"); + return k; +} - return r; +static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->static_nexthop_messages > 0); + + link->static_nexthop_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set nexthop"); + link_enter_failed(link); + return 1; + } + + if (link->static_nexthop_messages == 0) { + log_link_debug(link, "Nexthops set"); + link->static_nexthops_configured = true; + link_check_ready(link); + } + + return 1; } -int link_set_nexthops(Link *link) { - enum { - PHASE_ID, /* First phase: Nexthops with ID */ - PHASE_WITHOUT_ID, /* Second phase: Nexthops without ID */ - _PHASE_MAX, - } phase; +static int link_request_nexthop( + Link *link, + NextHop *nexthop, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { + + assert(link); + assert(nexthop); + + log_nexthop_debug(nexthop, nexthop->id, "Requesting", link); + return link_queue_request(link, REQUEST_TYPE_NEXTHOP, nexthop, consume_object, + message_counter, netlink_handler, ret); +} + +int link_request_static_nexthops(Link *link, bool only_ipv4) { NextHop *nh; int r; assert(link); assert(link->network); - if (link->nexthop_messages != 0) { - log_link_debug(link, "Nexthops are configuring."); - return 0; - } - link->static_nexthops_configured = false; - for (phase = PHASE_ID; phase < _PHASE_MAX; phase++) - HASHMAP_FOREACH(nh, link->network->nexthops_by_section) { - if ((nh->id > 0) != (phase == PHASE_ID)) - continue; - - r = nexthop_configure(nh, link); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set nexthop: %m"); + HASHMAP_FOREACH(nh, link->network->nexthops_by_section) { + if (only_ipv4 && nh->family != AF_INET) + continue; - link->nexthop_messages++; - } + r = link_request_nexthop(link, nh, false, &link->static_nexthop_messages, + static_nexthop_handler, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Could not request nexthop: %m"); + } - if (link->nexthop_messages == 0) { + if (link->static_nexthop_messages == 0) { link->static_nexthops_configured = true; - /* Finally, configure routes with gateways. */ - return link_set_routes_with_gateway(link); + link_check_ready(link); + } else { + log_link_debug(link, "Requesting nexthops"); + link_set_state(link, LINK_STATE_CONFIGURING); } - log_link_debug(link, "Setting nexthops"); - link_set_state(link, LINK_STATE_CONFIGURING); - return 0; } @@ -657,6 +693,73 @@ int link_drop_nexthops(Link *link) { return r; } +static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { + assert(link); + assert(nexthop); + + if (nexthop->blackhole) { + if (link->manager->nexthop_remove_messages > 0) + return false; + } else { + Link *l; + + HASHMAP_FOREACH(l, link->manager->links) { + if (l->address_remove_messages > 0) + return false; + if (l->nexthop_remove_messages > 0) + return false; + if (l->route_remove_messages > 0) + return false; + } + } + + if (nexthop->id == 0) { + Request *req; + + ORDERED_SET_FOREACH(req, link->manager->request_queue) { + if (req->type != REQUEST_TYPE_NEXTHOP) + continue; + if (req->nexthop->id != 0) + return false; /* first configure nexthop with id. */ + } + } + + if (nexthop->onlink <= 0 && + in_addr_is_set(nexthop->family, &nexthop->gw) && + !manager_address_is_reachable(link->manager, nexthop->family, &nexthop->gw)) + return false; + + return true; +} + +int request_process_nexthop(Request *req) { + NextHop *ret; + int r; + + assert(req); + assert(req->link); + assert(req->nexthop); + assert(req->type == REQUEST_TYPE_NEXTHOP); + + if (!link_is_ready_to_configure(req->link, false)) + return 0; + + if (!nexthop_is_ready_to_configure(req->link, req->nexthop)) + return 0; + + r = nexthop_configure(req->nexthop, req->link, req->netlink_handler, &ret); + if (r < 0) + return r; + + if (req->after_configure) { + r = req->after_configure(req, ret); + if (r < 0) + return r; + } + + return 1; +} + int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(nexthop_freep) NextHop *tmp = NULL; NextHop *nexthop = NULL; diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index cf06b7e86b..b2e7b45366 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -15,6 +15,7 @@ typedef struct Link Link; typedef struct Manager Manager; typedef struct Network Network; +typedef struct Request Request; typedef struct NextHop { Network *network; @@ -36,10 +37,12 @@ NextHop *nexthop_free(NextHop *nexthop); void network_drop_invalid_nexthops(Network *network); -int link_set_nexthops(Link *link); int link_drop_nexthops(Link *link); int link_drop_foreign_nexthops(Link *link); +int link_request_static_nexthops(Link *link, bool only_ipv4); +int request_process_nexthop(Request *req); + int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret); int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c new file mode 100644 index 0000000000..3b9d17651e --- /dev/null +++ b/src/network/networkd-queue.c @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "networkd-address.h" +#include "networkd-manager.h" +#include "networkd-neighbor.h" +#include "networkd-nexthop.h" +#include "networkd-route.h" +#include "networkd-routing-policy-rule.h" +#include "networkd-queue.h" + +static void request_free_object(RequestType type, void *object) { + switch(type) { + case REQUEST_TYPE_ADDRESS: + address_free(object); + break; + case REQUEST_TYPE_NEIGHBOR: + neighbor_free(object); + break; + case REQUEST_TYPE_NEXTHOP: + nexthop_free(object); + break; + case REQUEST_TYPE_ROUTE: + route_free(object); + break; + case REQUEST_TYPE_ROUTING_POLICY_RULE: + routing_policy_rule_free(object); + break; + default: + assert_not_reached("invalid request type."); + } +} + +Request *request_free(Request *req) { + if (!req) + return NULL; + + if (req->on_free) + req->on_free(req); + if (req->consume_object) + request_free_object(req->type, req->object); + if (req->link && req->link->manager) + ordered_set_remove(req->link->manager->request_queue, req); + link_unref(req->link); + + return mfree(req); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free); + +void request_drop(Request *req) { + if (req->message_counter) + (*req->message_counter)--; + + request_free(req); +} + +int link_queue_request( + Link *link, + RequestType type, + void *object, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { + + _cleanup_(request_freep) Request *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(type >= 0 && type < _REQUEST_TYPE_MAX); + assert(object); + assert(netlink_handler); + + req = new(Request, 1); + if (!req) { + if (consume_object) + request_free_object(type, object); + return -ENOMEM; + } + + *req = (Request) { + .link = link, + .type = type, + .object = object, + .consume_object = consume_object, + .message_counter = message_counter, + .netlink_handler = netlink_handler, + }; + + link_ref(link); + + r = ordered_set_ensure_put(&link->manager->request_queue, NULL, req); + if (r < 0) + return r; + + if (req->message_counter) + (*req->message_counter)++; + + if (ret) + *ret = req; + + TAKE_PTR(req); + return 0; +} + +int manager_process_requests(sd_event_source *s, void *userdata) { + Manager *manager = userdata; + int r; + + assert(manager); + + for (;;) { + bool processed = false; + Request *req; + + ORDERED_SET_FOREACH(req, manager->request_queue) { + switch(req->type) { + case REQUEST_TYPE_ADDRESS: + r = request_process_address(req); + break; + case REQUEST_TYPE_NEIGHBOR: + r = request_process_neighbor(req); + break; + case REQUEST_TYPE_NEXTHOP: + r = request_process_nexthop(req); + break; + case REQUEST_TYPE_ROUTE: + r = request_process_route(req); + break; + case REQUEST_TYPE_ROUTING_POLICY_RULE: + r = request_process_routing_policy_rule(req); + break; + default: + return -EINVAL; + } + if (r < 0) + link_enter_failed(req->link); + if (r > 0) { + ordered_set_remove(manager->request_queue, req); + request_free(req); + processed = true; + } + } + + if (!processed) + break; + } + + return 0; +} diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h new file mode 100644 index 0000000000..f8664d087c --- /dev/null +++ b/src/network/networkd-queue.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-event.h" + +#include "networkd-link.h" + +typedef struct Address Address; +typedef struct Neighbor Neighbor; +typedef struct NextHop NextHop; +typedef struct Route Route; +typedef struct RoutingPolicyRule RoutingPolicyRule; + +typedef struct Request Request; + +typedef int (*request_after_configure_handler_t)(Request*, void*); +typedef void (*request_on_free_handler_t)(Request*); + +typedef enum RequestType { + REQUEST_TYPE_ADDRESS, + REQUEST_TYPE_NEIGHBOR, + REQUEST_TYPE_NEXTHOP, + REQUEST_TYPE_ROUTE, + REQUEST_TYPE_ROUTING_POLICY_RULE, + _REQUEST_TYPE_MAX, + _REQUEST_TYPE_INVALID = -EINVAL, +} RequestType; + +typedef struct Request { + Link *link; + RequestType type; + bool consume_object; + union { + Address *address; + Neighbor *neighbor; + NextHop *nexthop; + Route *route; + RoutingPolicyRule *rule; + void *object; + }; + void *userdata; + unsigned *message_counter; + link_netlink_message_handler_t netlink_handler; + request_after_configure_handler_t after_configure; + request_on_free_handler_t on_free; +} Request; + +Request *request_free(Request *req); +void request_drop(Request *req); + +int link_queue_request( + Link *link, + RequestType type, + void *object, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret); + +int manager_process_requests(sd_event_source *s, void *userdata); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 8f3d84129a..8707e5c089 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -9,8 +9,8 @@ #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-nexthop.h" +#include "networkd-queue.h" #include "networkd-route.h" -#include "networkd-routing-policy-rule.h" #include "parse-util.h" #include "socket-netlink.h" #include "string-table.h" @@ -288,7 +288,7 @@ Route *route_free(Route *route) { set_remove(route->manager->routes_foreign, route); } - ordered_set_free_free(route->multipath_routes); + ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free); sd_event_source_unref(route->expire); @@ -510,6 +510,44 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c } } +int route_dup(const Route *src, Route **ret) { + _cleanup_(route_freep) Route *dest = NULL; + MultipathRoute *m; + int r; + + assert(src); + assert(ret); + + dest = newdup(Route, src, 1); + if (!dest) + return -ENOMEM; + + /* Unset all pointers */ + dest->network = NULL; + dest->section = NULL; + dest->link = NULL; + dest->manager = NULL; + dest->multipath_routes = NULL; + dest->expire = NULL; + + ORDERED_SET_FOREACH(m, src->multipath_routes) { + _cleanup_(multipath_route_freep) MultipathRoute *n = NULL; + + r = multipath_route_dup(m, &n); + if (r < 0) + return r; + + r = ordered_set_ensure_put(&dest->multipath_routes, NULL, n); + if (r < 0) + return r; + + TAKE_PTR(n); + } + + *ret = TAKE_PTR(dest); + return 0; +} + static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) { _cleanup_(route_freep) Route *route = NULL; int r; @@ -604,13 +642,115 @@ static bool route_type_is_reject(const Route *route) { return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW); } -static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *m) { - _cleanup_free_ char *dst = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL, +int link_has_route(Link *link, const Route *route) { + MultipathRoute *m; + int r; + + assert(link); + assert(route); + + if (route->nexthop_id > 0) { + _cleanup_(route_freep) Route *tmp = NULL; + NextHop *nh; + + if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) + return false; + + r = route_new(&tmp); + if (r < 0) + return r; + + route_copy(tmp, route, NULL, nh); + + if (route_type_is_reject(route) || (nh && nh->blackhole)) + return route_get(link->manager, NULL, tmp, NULL) >= 0; + else + return route_get(NULL, link, tmp, NULL) >= 0; + } + + if (ordered_set_isempty(route->multipath_routes)) { + if (route_type_is_reject(route)) + return route_get(link->manager, NULL, route, NULL) >= 0; + else + return route_get(NULL, link, route, NULL) >= 0; + } + + ORDERED_SET_FOREACH(m, route->multipath_routes) { + _cleanup_(route_freep) Route *tmp = NULL; + Link *l; + + if (m->ifname) { + r = resolve_interface(&link->manager->rtnl, m->ifname); + if (r < 0) + return false; + m->ifindex = r; + + if (link_get(link->manager, m->ifindex, &l) < 0) + return false; + } else + l = link; + + r = route_new(&tmp); + if (r < 0) + return r; + + route_copy(tmp, route, m, NULL); + + if (route_get(NULL, l, tmp, NULL) < 0) + return false; + } + + return true; +} + +static bool route_address_is_reachable(const Route *route, int family, const union in_addr_union *address) { + assert(route); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + if (route->family != family) + return false; + + if (!in_addr_is_set(route->family, &route->dst)) + return false; + + return in_addr_prefix_intersect( + route->family, + &route->dst, + route->dst_prefixlen, + address, + FAMILY_ADDRESS_SIZE(family) * 8) > 0; +} + +bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address) { + Link *link; + + assert(manager); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + HASHMAP_FOREACH(link, manager->links) { + Route *route; + + SET_FOREACH(route, link->routes) + if (route_address_is_reachable(route, family, address)) + return true; + SET_FOREACH(route, link->routes_foreign) + if (route_address_is_reachable(route, family, address)) + return true; + } + + return false; +} + +static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) { + _cleanup_free_ char *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL, *table = NULL, *scope = NULL, *proto = NULL; + const char *gw = NULL; assert(route); assert(str); - assert(m); + assert(manager); /* link may be NULL. */ @@ -621,12 +761,32 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin (void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst); if (in_addr_is_set(route->family, &route->src)) (void) in_addr_to_string(route->family, &route->src, &src); - if (in_addr_is_set(route->gw_family, &route->gw)) - (void) in_addr_to_string(route->gw_family, &route->gw, &gw); + if (in_addr_is_set(route->gw_family, &route->gw)) { + (void) in_addr_to_string(route->gw_family, &route->gw, &gw_alloc); + gw = gw_alloc; + } else if (route->gateway_from_dhcp_or_ra) { + if (route->gw_family == AF_INET) + gw = "_dhcp4"; + else if (route->gw_family == AF_INET6) + gw = "_ipv6ra"; + } else { + MultipathRoute *m; + + ORDERED_SET_FOREACH(m, route->multipath_routes) { + _cleanup_free_ char *buf = NULL, *joined = NULL; + union in_addr_union a = m->gateway.address; + + (void) in_addr_to_string(m->gateway.family, &a, &buf); + joined = strjoin(gw_alloc, gw_alloc ? "," : "", strna(buf), m->ifname ? "@" : "", strempty(m->ifname)); + if (joined) + free_and_replace(gw_alloc, joined); + } + gw = gw_alloc; + } if (in_addr_is_set(route->family, &route->prefsrc)) (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc); (void) route_scope_to_string_alloc(route->scope, &scope); - (void) manager_get_route_table_to_string(m, route->table, &table); + (void) manager_get_route_table_to_string(manager, route->table, &table); (void) route_protocol_full_to_string_alloc(route->protocol, &proto); log_link_debug(link, @@ -743,18 +903,42 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req return 0; } -static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +int link_route_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { int r; assert(m); + assert(link); + assert(link->route_remove_messages > 0); + assert(error_msg); - /* Note that link may be NULL. */ - if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + link->route_remove_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -ESRCH) + log_link_message_warning_errno(link, m, r, error_msg); + + return 1; +} + +static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + return link_route_remove_handler_internal(rtnl, m, link, "Could not drop route, ignoring"); +} + +static int manager_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) { + int r; + + assert(m); + assert(manager); + assert(manager->route_remove_messages > 0); + + manager->route_remove_messages--; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -ESRCH) - log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring"); + log_message_warning_errno(m, r, "Could not drop route, ignoring"); return 1; } @@ -774,7 +958,6 @@ int route_remove( if (!manager) manager = link->manager; - /* link may be NULL! */ log_route_debug(route, "Removing", link, manager); @@ -803,18 +986,29 @@ int route_remove( if (r < 0) return r; - r = netlink_call_async(manager->rtnl, NULL, req, - callback ?: route_remove_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + if (link) { + r = netlink_call_async(manager->rtnl, NULL, req, + callback ?: link_route_remove_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + link->route_remove_messages++; + } else { + r = netlink_call_async(manager->rtnl, NULL, req, + manager_route_remove_handler, + NULL, manager); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); - link_ref(link); /* link may be NULL, link_ref() is OK with that */ + manager->route_remove_messages++; + } return 0; } -static bool link_has_route(const Link *link, const Route *route) { +static bool link_has_static_route(const Link *link, const Route *route) { Route *net_route; assert(link); @@ -830,7 +1024,7 @@ static bool link_has_route(const Link *link, const Route *route) { return false; } -static bool links_have_route(const Manager *manager, const Route *route, const Link *except) { +static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) { Link *link; assert(manager); @@ -839,7 +1033,7 @@ static bool links_have_route(const Manager *manager, const Route *route, const L if (link == except) continue; - if (link_has_route(link, route)) + if (link_has_static_route(link, route)) return true; } @@ -863,7 +1057,7 @@ static int manager_drop_routes_internal(Manager *manager, bool foreign, const Li continue; /* The route will be configured later, or already configured by a link. */ - if (links_have_route(manager, route, except)) + if (links_have_static_route(manager, route, except)) continue; /* The existing links do not have the route. Let's drop this now. It may be @@ -915,7 +1109,7 @@ int link_drop_foreign_routes(Link *link) { FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) continue; - if (link_has_route(link, route)) + if (link_has_static_route(link, route)) k = route_add(NULL, link, route, NULL, NULL, NULL); else k = route_remove(route, NULL, link, NULL); @@ -1094,14 +1288,36 @@ static int append_nexthops(const Route *route, sd_netlink_message *req) { return 0; } -int route_configure( +int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { + int r; + + assert(m); + assert(link); + assert(error_msg); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set route with gateway"); + link_enter_failed(link); + return 0; + } + + return 1; +} + +static int route_configure( const Route *route, Link *link, link_netlink_message_handler_t callback, - Route **ret) { + unsigned *ret_n_routes, + Route ***ret_routes) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - Route *nr; + _cleanup_free_ Route **routes = NULL; + unsigned n_routes = 0; /* avoid false maybe-uninitialized warning */ int r, k; assert(link); @@ -1110,6 +1326,7 @@ int route_configure( assert(link->ifindex > 0); assert(IN_SET(route->family, AF_INET, AF_INET6)); assert(callback); + assert(!!ret_n_routes == !!ret_routes); if (route_get(link->manager, link, route, NULL) <= 0 && set_size(link->routes) >= routes_max()) @@ -1192,21 +1409,36 @@ int route_configure( if (route->nexthop_id != 0 || in_addr_is_set(route->gw_family, &route->gw) || ordered_set_isempty(route->multipath_routes)) { - k = route_add_and_setup_timer(link, route, NULL, &nr); + + if (ret_routes) { + n_routes = 1; + routes = new(Route*, n_routes); + if (!routes) + return log_oom(); + } + + k = route_add_and_setup_timer(link, route, NULL, routes); if (k < 0) return k; } else { MultipathRoute *m; + Route **p; r = append_nexthops(route, req); if (r < 0) return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m"); - assert(!ret); + if (ret_routes) { + n_routes = ordered_set_size(route->multipath_routes); + routes = new(Route*, n_routes); + if (!routes) + return log_oom(); + } k = 0; + p = routes; ORDERED_SET_FOREACH(m, route->multipath_routes) { - r = route_add_and_setup_timer(link, route, m, NULL); + r = route_add_and_setup_timer(link, route, m, ret_routes ? p++ : NULL); if (r < 0) return r; if (r > 0) @@ -1221,32 +1453,28 @@ int route_configure( link_ref(link); - if (ret) - *ret = nr; + if (ret_routes) { + *ret_n_routes = n_routes; + *ret_routes = TAKE_PTR(routes); + } return k; } -static int route_handler_with_gateway(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->route_messages > 0); + assert(link->static_route_messages > 0); - link->route_messages--; + link->static_route_messages--; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set route with gateway"); - link_enter_failed(link); - return 1; - } + r = route_configure_handler_internal(rtnl, m, link, "Could not set route"); + if (r <= 0) + return r; - if (link->route_messages == 0) { - log_link_debug(link, "Routes with gateway set"); + if (link->static_route_messages == 0) { + log_link_debug(link, "Routes set"); link->static_routes_configured = true; link_check_ready(link); } @@ -1254,140 +1482,157 @@ static int route_handler_with_gateway(sd_netlink *rtnl, sd_netlink_message *m, L return 1; } -static int route_handler_without_gateway(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; +int link_request_route( + Link *link, + Route *route, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { assert(link); - assert(link->route_messages > 0); - - link->route_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set route without gateway"); - link_enter_failed(link); - return 1; - } - - if (link->route_messages == 0) { - log_link_debug(link, "Routes set without gateway"); - /* Now, we can talk to gateways, let's configure nexthops. */ - r = link_set_nexthops(link); - if (r < 0) - link_enter_failed(link); - } - - return 1; -} - -static bool route_has_gateway(const Route *route) { + assert(link->manager); assert(route); - if (in_addr_is_set(route->gw_family, &route->gw)) - return true; - - if (!ordered_set_isempty(route->multipath_routes)) - return true; - - if (route->nexthop_id > 0) - return true; - - return false; + log_route_debug(route, "Requesting", link, link->manager); + return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object, + message_counter, netlink_handler, ret); } -static int link_set_routes_internal(Link *link, bool with_gateway) { - Route *rt; +int link_request_static_routes(Link *link, bool only_ipv4) { + Route *route; int r; assert(link); assert(link->network); - HASHMAP_FOREACH(rt, link->network->routes_by_section) { - if (rt->gateway_from_dhcp_or_ra) + link->static_routes_configured = false; + + HASHMAP_FOREACH(route, link->network->routes_by_section) { + if (route->gateway_from_dhcp_or_ra) continue; - if (route_has_gateway(rt) != with_gateway) + if (only_ipv4 && route->family != AF_INET) continue; - r = route_configure(rt, link, with_gateway ? route_handler_with_gateway : route_handler_without_gateway, NULL); + r = link_request_route(link, route, false, &link->static_route_messages, + static_route_handler, NULL); if (r < 0) - return log_link_warning_errno(link, r, "Could not set routes: %m"); + return r; + } - link->route_messages++; + if (link->static_route_messages == 0) { + link->static_routes_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Requesting routes"); + link_set_state(link, LINK_STATE_CONFIGURING); } return 0; } -int link_set_routes_with_gateway(Link *link) { +static int route_is_ready_to_configure(const Route *route, Link *link) { + MultipathRoute *m; + NextHop *nh = NULL; int r; + assert(route); assert(link); - assert(link->network); - - if (!link_has_carrier(link) && !link->network->configure_without_carrier) - /* During configuring addresses, the link lost its carrier. As networkd is dropping - * the addresses now, let's not configure the routes either. */ - return 0; - /* Finally, add routes that needs a gateway. */ - r = link_set_routes_internal(link, true); - if (r < 0) - return r; + if (route->nexthop_id > 0 && + manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) + return false; - if (link->route_messages == 0) { - link->static_routes_configured = true; - link_check_ready(link); + if (route_type_is_reject(route) || (nh && nh->blackhole)) { + if (nh && link->manager->nexthop_remove_messages > 0) + return false; + if (link->manager->route_remove_messages > 0) + return false; } else { - log_link_debug(link, "Setting routes with gateway"); - link_set_state(link, LINK_STATE_CONFIGURING); + Link *l; + + HASHMAP_FOREACH(l, link->manager->links) { + if (l->address_remove_messages > 0) + return false; + if (l->nexthop_remove_messages > 0) + return false; + if (l->route_remove_messages > 0) + return false; + } } - return 0; -} + if (in_addr_is_set(route->family, &route->prefsrc) > 0) { + r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6); + if (r <= 0) + return r; + } -int link_set_routes(Link *link) { - int r; + if (route->gateway_onlink <= 0 && + in_addr_is_set(route->gw_family, &route->gw) > 0 && + !manager_address_is_reachable(link->manager, route->gw_family, &route->gw)) + return false; - assert(link); - assert(link->network); - assert(link->state != _LINK_STATE_INVALID); + ORDERED_SET_FOREACH(m, route->multipath_routes) { + union in_addr_union a = m->gateway.address; - link->static_routes_configured = false; + if (route->gateway_onlink <= 0 && + !manager_address_is_reachable(link->manager, m->gateway.family, &a)) + return false; - if (!link->addresses_ready) - return 0; + if (m->ifname) { + Link *l; - if (!link_has_carrier(link) && !link->network->configure_without_carrier) - /* During configuring addresses, the link lost its carrier. As networkd is dropping - * the addresses now, let's not configure the routes either. */ - return 0; + r = resolve_interface(&link->manager->rtnl, m->ifname); + if (r < 0) + return false; + m->ifindex = r; - if (link->route_messages != 0) { - log_link_debug(link, "Static routes are configuring."); - return 0; + if (link_get(link->manager, m->ifindex, &l) < 0) + return false; + + if (!link_is_ready_to_configure(l, true)) + return false; + } } - r = link_set_routing_policy_rules(link); - if (r < 0) + return true; +} + +int request_process_route(Request *req) { + _cleanup_free_ Route **routes = NULL; + unsigned n_routes; + int r; + + assert(req); + assert(req->link); + assert(req->route); + assert(req->type == REQUEST_TYPE_ROUTE); + + if (!link_is_ready_to_configure(req->link, false)) + return 0; + + r = route_is_ready_to_configure(req->route, req->link); + if (r <= 0) return r; - /* First, add the routes that enable us to talk to gateways. */ - r = link_set_routes_internal(link, false); + r = route_configure(req->route, req->link, req->netlink_handler, + req->after_configure ? &n_routes : NULL, + req->after_configure ? &routes : NULL); if (r < 0) return r; - if (link->route_messages == 0) - /* If no route is configured, then configure nexthops. */ - return link_set_nexthops(link); + if (req->after_configure) { + assert(n_routes > 0); - log_link_debug(link, "Setting routes without gateway"); - link_set_state(link, LINK_STATE_CONFIGURING); + for (unsigned i = 0; i < n_routes; i++) { + r = req->after_configure(req, routes[i]); + if (r < 0) + return r; + } + } - return 0; + return 1; } static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) { @@ -2460,13 +2705,14 @@ int config_parse_multipath_route( void *data, void *userdata) { + _cleanup_(multipath_route_freep) MultipathRoute *m = NULL; _cleanup_(route_free_or_set_invalidp) Route *n = NULL; - _cleanup_free_ char *word = NULL, *buf = NULL; - _cleanup_free_ MultipathRoute *m = NULL; + _cleanup_free_ char *word = NULL; Network *network = userdata; - const char *p, *ip, *dev; union in_addr_union a; int family, r; + const char *p; + char *dev; assert(filename); assert(section); @@ -2484,7 +2730,7 @@ int config_parse_multipath_route( } if (isempty(rvalue)) { - n->multipath_routes = ordered_set_free_free(n->multipath_routes); + n->multipath_routes = ordered_set_free_with_destructor(n->multipath_routes, multipath_route_free); return 0; } @@ -2504,15 +2750,14 @@ int config_parse_multipath_route( dev = strchr(word, '@'); if (dev) { - buf = strndup(word, dev - word); - if (!buf) + *dev++ = '\0'; + + m->ifname = strdup(dev); + if (!m->ifname) return log_oom(); - ip = buf; - dev++; - } else - ip = word; + } - r = in_addr_from_string_auto(ip, &family, &a); + r = in_addr_from_string_auto(word, &family, &a); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue); @@ -2521,16 +2766,6 @@ int config_parse_multipath_route( m->gateway.address = a; m->gateway.family = family; - if (dev) { - r = resolve_interface(NULL, dev); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid interface name or index, ignoring assignment: %s", dev); - return 0; - } - m->ifindex = r; - } - if (!isempty(p)) { r = safe_atou32(p, &m->weight); if (r < 0) { diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index ddb433817a..bb007dd74a 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -3,7 +3,6 @@ #include <inttypes.h> #include <stdbool.h> -#include <stdio.h> #include "sd-netlink.h" @@ -14,6 +13,7 @@ typedef struct Manager Manager; typedef struct Network Network; +typedef struct Request Request; typedef struct Route { Network *network; @@ -71,18 +71,31 @@ extern const struct hash_ops route_hash_ops; int route_new(Route **ret); Route *route_free(Route *route); DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); +int route_dup(const Route *src, Route **ret); -int route_configure(const Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret); +int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); +int link_route_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int route_remove(const Route *route, Manager *manager, Link *link, link_netlink_message_handler_t callback); -int link_set_routes(Link *link); -int link_set_routes_with_gateway(Link *link); +int link_has_route(Link *link, const Route *route); +bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address); + int link_drop_routes(Link *link); int link_drop_foreign_routes(Link *link); uint32_t link_get_dhcp_route_table(const Link *link); uint32_t link_get_ipv6_accept_ra_route_table(const Link *link); +int link_request_route( + Link *link, + Route *route, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret); +int link_request_static_routes(Link *link, bool only_ipv4); +int request_process_route(Request *req); + int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); int network_add_ipv4ll_route(Network *network); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 14e079eed3..b44ee8b627 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -12,6 +12,8 @@ #include "ip-protocol-list.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-queue.h" +#include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-util.h" #include "parse-util.h" @@ -109,45 +111,35 @@ static int routing_policy_rule_new_static(Network *network, const char *filename return 0; } -static int routing_policy_rule_copy(RoutingPolicyRule *dest, const RoutingPolicyRule *src) { - _cleanup_free_ char *iif = NULL, *oif = NULL; +static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRule **ret) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *dest = NULL; - assert(dest); assert(src); + assert(ret); + + dest = newdup(RoutingPolicyRule, src, 1); + if (!dest) + return -ENOMEM; + + /* Unset all pointers */ + dest->manager = NULL; + dest->network = NULL; + dest->section = NULL; + dest->iif = dest->oif = NULL; if (src->iif) { - iif = strdup(src->iif); - if (!iif) + dest->iif = strdup(src->iif); + if (!dest->iif) return -ENOMEM; } if (src->oif) { - oif = strdup(src->oif); - if (!oif) + dest->oif = strdup(src->oif); + if (!dest->oif) return -ENOMEM; } - dest->family = src->family; - dest->from = src->from; - dest->from_prefixlen = src->from_prefixlen; - dest->to = src->to; - dest->to_prefixlen = src->to_prefixlen; - dest->invert_rule = src->invert_rule; - dest->tos = src->tos; - dest->type = src->type; - dest->fwmark = src->fwmark; - dest->fwmask = src->fwmask; - dest->priority = src->priority; - dest->table = src->table; - dest->iif = TAKE_PTR(iif); - dest->oif = TAKE_PTR(oif); - dest->ipproto = src->ipproto; - dest->protocol = src->protocol; - dest->sport = src->sport; - dest->dport = src->dport; - dest->uid_range = src->uid_range; - dest->suppress_prefixlen = src->suppress_prefixlen; - + *ret = TAKE_PTR(dest); return 0; } @@ -323,7 +315,7 @@ static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, Ro return -ENOENT; } -static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, int family, RoutingPolicyRule **ret) { +static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) { _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; RoutingPolicyRule *existing; bool is_new = false; @@ -331,19 +323,12 @@ static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, int assert(m); assert(in); - assert(IN_SET(family, AF_INET, AF_INET6)); - assert(in->family == AF_UNSPEC || in->family == family); + assert(IN_SET(in->family, AF_INET, AF_INET6)); - r = routing_policy_rule_new(&rule); + r = routing_policy_rule_dup(in, &rule); if (r < 0) return r; - r = routing_policy_rule_copy(rule, in); - if (r < 0) - return r; - - rule->family = family; - r = routing_policy_rule_get(m, rule, &existing); if (r == -ENOENT) { /* Rule does not exist, use a new one. */ @@ -390,11 +375,11 @@ static int routing_policy_rule_consume_foreign(Manager *m, RoutingPolicyRule *ru return 1; } -static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, int family, const char *str, const Link *link, const Manager *m) { +static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) { _cleanup_free_ char *from = NULL, *to = NULL, *table = NULL; assert(rule); - assert(IN_SET(family, AF_INET, AF_INET6)); + assert(IN_SET(rule->family, AF_INET, AF_INET6)); assert(str); assert(m); @@ -403,8 +388,8 @@ static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, int fam if (!DEBUG_LOGGING) return; - (void) in_addr_prefix_to_string(family, &rule->from, rule->from_prefixlen, &from); - (void) in_addr_prefix_to_string(family, &rule->to, rule->to_prefixlen, &to); + (void) in_addr_prefix_to_string(rule->family, &rule->from, rule->from_prefixlen, &from); + (void) in_addr_prefix_to_string(rule->family, &rule->to, rule->to_prefixlen, &to); (void) manager_get_route_table_to_string(m, rule->table, &table); log_link_debug(link, @@ -534,10 +519,14 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule return 0; } -static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { +static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) { int r; assert(m); + assert(manager); + assert(manager->routing_policy_rule_remove_messages > 0); + + manager->routing_policy_rule_remove_messages--; r = sd_netlink_message_get_errno(m); if (r < 0) @@ -555,7 +544,7 @@ static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *ma assert(manager->rtnl); assert(IN_SET(rule->family, AF_INET, AF_INET6)); - log_routing_policy_rule_debug(rule, rule->family, "Removing", NULL, manager); + log_routing_policy_rule_debug(rule, "Removing", NULL, manager); r = sd_rtnl_message_new_routing_policy_rule(manager->rtnl, &m, RTM_DELRULE, rule->family); if (r < 0) @@ -565,58 +554,37 @@ static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *ma if (r < 0) return r; - r = sd_netlink_call_async(manager->rtnl, NULL, m, - routing_policy_rule_remove_handler, - NULL, NULL, 0, __func__); + r = netlink_call_async(manager->rtnl, NULL, m, + routing_policy_rule_remove_handler, + NULL, manager); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); + manager->routing_policy_rule_remove_messages++; + return 0; } -static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(rtnl); - assert(m); - assert(link); - assert(link->ifname); - assert(link->routing_policy_rule_messages > 0); - - link->routing_policy_rule_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not add routing policy rule"); - link_enter_failed(link); - return 1; - } - - if (link->routing_policy_rule_messages == 0) { - log_link_debug(link, "Routing policy rule configured"); - link->routing_policy_rules_configured = true; - link_check_ready(link); - } - - return 1; -} +static int routing_policy_rule_configure( + const RoutingPolicyRule *rule, + Link *link, + link_netlink_message_handler_t callback, + RoutingPolicyRule **ret) { -static int routing_policy_rule_configure_internal(const RoutingPolicyRule *rule, int family, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(rule); + assert(IN_SET(rule->family, AF_INET, AF_INET6)); assert(link); assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); + assert(callback); - log_routing_policy_rule_debug(rule, family, "Configuring", link, link->manager); + log_routing_policy_rule_debug(rule, "Configuring", link, link->manager); - r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, family); + r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family); if (r < 0) return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m"); @@ -624,43 +592,20 @@ static int routing_policy_rule_configure_internal(const RoutingPolicyRule *rule, if (r < 0) return r; - r = netlink_call_async(link->manager->rtnl, NULL, m, - routing_policy_rule_handler, + r = routing_policy_rule_add(link->manager, rule, ret); + if (r < 0) + return log_link_error_errno(link, r, "Could not add rule: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, m, callback, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - link->routing_policy_rule_messages++; - - r = routing_policy_rule_add(link->manager, rule, family, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not add rule: %m"); return r; } -static int routing_policy_rule_configure(const RoutingPolicyRule *rule, Link *link) { - int r; - - if (IN_SET(rule->family, AF_INET, AF_INET6)) - return routing_policy_rule_configure_internal(rule, rule->family, link); - - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) { - r = routing_policy_rule_configure_internal(rule, AF_INET, link); - if (r < 0) - return r; - } - - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) { - r = routing_policy_rule_configure_internal(rule, AF_INET6, link); - if (r < 0) - return r; - } - - return 0; -} - static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyRule *rule, const Link *except) { Link *link; int r; @@ -685,11 +630,7 @@ static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyR /* The case Family=both. */ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; - r = routing_policy_rule_new(&tmp); - if (r < 0) - return r; - - r = routing_policy_rule_copy(tmp, link_rule); + r = routing_policy_rule_dup(link_rule, &tmp); if (r < 0) return r; @@ -735,36 +676,153 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L return r; } -int link_set_routing_policy_rules(Link *link) { - RoutingPolicyRule *rule; +static int static_routing_policy_rule_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; + assert(rtnl); + assert(m); assert(link); - assert(link->network); + assert(link->ifname); + assert(link->static_routing_policy_rule_messages > 0); - if (link->routing_policy_rule_messages != 0) { - log_link_debug(link, "Routing policy rules are configuring."); - return 0; + link->static_routing_policy_rule_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not add routing policy rule"); + link_enter_failed(link); + return 1; + } + + if (link->static_routing_policy_rule_messages == 0) { + log_link_debug(link, "Routing policy rule configured"); + link->static_routing_policy_rules_configured = true; + link_check_ready(link); } - link->routing_policy_rules_configured = false; + return 1; +} + +static int link_request_routing_policy_rule( + Link *link, + RoutingPolicyRule *rule, + bool consume_object, + unsigned *message_counter, + link_netlink_message_handler_t netlink_handler, + Request **ret) { + + assert(link); + assert(link->manager); + assert(rule); + + log_routing_policy_rule_debug(rule, "Requesting", link, link->manager); + return link_queue_request(link, REQUEST_TYPE_ROUTING_POLICY_RULE, rule, consume_object, + message_counter, netlink_handler, ret); +} + +static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule *rule) { + int r; + + if (IN_SET(rule->family, AF_INET, AF_INET6)) + return link_request_routing_policy_rule(link, rule, false, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); + + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + + r = routing_policy_rule_dup(rule, &tmp); + if (r < 0) + return r; + + tmp->family = AF_INET; + + r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); + if (r < 0) + return r; + } + + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + + r = routing_policy_rule_dup(rule, &tmp); + if (r < 0) + return r; + + tmp->family = AF_INET6; + + r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); + if (r < 0) + return r; + } + + return 0; +} + +int link_request_static_routing_policy_rules(Link *link) { + RoutingPolicyRule *rule; + int r; + + assert(link); + assert(link->network); + + link->static_routing_policy_rules_configured = false; HASHMAP_FOREACH(rule, link->network->rules_by_section) { - r = routing_policy_rule_configure(rule, link); + r = link_request_static_routing_policy_rule(link, rule); if (r < 0) - return log_link_warning_errno(link, r, "Could not set routing policy rule: %m"); + return log_link_warning_errno(link, r, "Could not request routing policy rule: %m"); } - if (link->routing_policy_rule_messages == 0) - link->routing_policy_rules_configured = true; - else { - log_link_debug(link, "Setting routing policy rules"); + if (link->static_routing_policy_rule_messages == 0) { + link->static_routing_policy_rules_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Requesting routing policy rules"); link_set_state(link, LINK_STATE_CONFIGURING); } return 0; } +int request_process_routing_policy_rule(Request *req) { + RoutingPolicyRule *ret = NULL; /* avoid false maybe-uninitialized warning */ + int r; + + assert(req); + assert(req->link); + assert(req->rule); + assert(req->type == REQUEST_TYPE_ROUTING_POLICY_RULE); + + if (!link_is_ready_to_configure(req->link, false)) + return 0; + + if (req->link->manager->routing_policy_rule_remove_messages > 0) + return 0; + + r = routing_policy_rule_configure(req->rule, req->link, req->netlink_handler, &ret); + if (r < 0) + return r; + + if (req->after_configure) { + r = req->after_configure(req, ret); + if (r < 0) + return r; + } + + return 1; +} + static const RoutingPolicyRule kernel_rules[] = { { .family = AF_INET, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, }, { .family = AF_INET, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, }, @@ -977,11 +1035,11 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man switch (type) { case RTM_NEWRULE: if (rule) - log_routing_policy_rule_debug(tmp, tmp->family, "Received remembered", NULL, m); + log_routing_policy_rule_debug(tmp, "Received remembered", NULL, m); else if (!m->manage_foreign_routes) - log_routing_policy_rule_debug(tmp, tmp->family, "Ignoring received foreign", NULL, m); + log_routing_policy_rule_debug(tmp, "Ignoring received foreign", NULL, m); else { - log_routing_policy_rule_debug(tmp, tmp->family, "Remembering foreign", NULL, m); + log_routing_policy_rule_debug(tmp, "Remembering foreign", NULL, m); r = routing_policy_rule_consume_foreign(m, TAKE_PTR(tmp)); if (r < 0) log_warning_errno(r, "Could not remember foreign rule, ignoring: %m"); @@ -989,10 +1047,10 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man break; case RTM_DELRULE: if (rule) { - log_routing_policy_rule_debug(tmp, tmp->family, "Forgetting", NULL, m); + log_routing_policy_rule_debug(tmp, "Forgetting", NULL, m); routing_policy_rule_free(rule); } else - log_routing_policy_rule_debug(tmp, tmp->family, "Kernel removed unknown", NULL, m); + log_routing_policy_rule_debug(tmp, "Kernel removed unknown", NULL, m); break; default: diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index dbda8c51b0..69b0535659 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -4,16 +4,15 @@ #include <inttypes.h> #include <linux/fib_rules.h> #include <stdbool.h> -#include <stdio.h> #include "conf-parser.h" #include "in-addr-util.h" #include "networkd-util.h" -#include "set.h" -typedef struct Network Network; typedef struct Link Link; typedef struct Manager Manager; +typedef struct Network Network; +typedef struct Request Request; typedef struct RoutingPolicyRule { Manager *manager; @@ -55,7 +54,8 @@ RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule); void network_drop_invalid_routing_policy_rules(Network *network); -int link_set_routing_policy_rules(Link *link); +int link_request_static_routing_policy_rules(Link *link); +int request_process_routing_policy_rule(Request *req); int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except); diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 1e50a75980..97581d2e59 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2784,20 +2784,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): expect_up = initial_up next_up = not expect_up - # if initial expected state is down, must wait for setup_state to reach configuring - # so systemd-networkd considers it 'activated' - setup_state = None if initial_up else 'configuring' - for iteration in range(4): with self.subTest(iteration=iteration, expect_up=expect_up): operstate = 'routable' if expect_up else 'off' + setup_state = 'configured' if expect_up else None self.wait_operstate('test1', operstate, setup_state=setup_state, setup_timeout=20) - setup_state = None if expect_up: self.assertIn('UP', check_output('ip link show test1')) self.assertIn('192.168.10.30/24', check_output('ip address show test1')) - self.assertIn('default via 192.168.10.1', check_output('ip route show')) + self.assertIn('default via 192.168.10.1', check_output('ip route show dev test1')) else: self.assertIn('DOWN', check_output('ip link show test1')) @@ -2900,6 +2896,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(rc, 0) time.sleep(1) + self.wait_online(['veth99:routable', 'veth-peer:routable']) + output = check_output('ip nexthop list dev veth99') print(output) self.assertEqual(output, '') @@ -2913,6 +2911,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(rc, 0) time.sleep(1) + self.wait_online(['veth99:routable', 'veth-peer:routable']) + rc = call('ip link del veth99') self.assertEqual(rc, 0) time.sleep(2) |