diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c index a04fa6ac46..cfe54f600c 100644 --- a/src/network/netdev/l2tp-tunnel.c +++ b/src/network/netdev/l2tp-tunnel.c @@ -285,10 +285,6 @@ static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_u if (l2tp_acquire_local_address_one(t, a, ret) >= 0) return 1; - SET_FOREACH(a, link->addresses_foreign) - if (l2tp_acquire_local_address_one(t, a, ret) >= 0) - return 1; - return -ENODATA; } diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index e1cefbefbd..47dedfe6f2 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -106,24 +106,6 @@ static bool address_pool_prefix_is_taken( if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) return true; } - - /* Don't clash with assigned foreign addresses */ - SET_FOREACH(a, l->addresses_foreign) { - if (a->family != p->family) - continue; - - if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) - return true; - } - - /* Don't clash with addresses already pulled from the pool, but not assigned yet */ - SET_FOREACH(a, l->pool_addresses) { - if (a->family != p->family) - continue; - - if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) - return true; - } } /* And don't clash with configured but un-assigned addresses either */ diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index a885697969..37c5c3cc6c 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -9,10 +9,12 @@ #include "netlink-util.h" #include "networkd-address-pool.h" #include "networkd-address.h" +#include "networkd-dhcp-server.h" #include "networkd-ipv4acd.h" #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-queue.h" +#include "networkd-route.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" @@ -131,7 +133,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s address->network = network; address->section = TAKE_PTR(n); - address->is_static = true; + address->source = NETWORK_CONFIG_SOURCE_STATIC; r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address); if (r < 0) @@ -151,23 +153,7 @@ Address *address_free(Address *address) { } if (address->link) { - NDiscAddress *n; - set_remove(address->link->addresses, address); - set_remove(address->link->addresses_foreign, address); - set_remove(address->link->addresses_ipv4acd, address); - set_remove(address->link->static_addresses, address); - if (address->link->dhcp_address == address) - address->link->dhcp_address = NULL; - if (address->link->dhcp_address_old == address) - address->link->dhcp_address_old = NULL; - set_remove(address->link->dhcp6_addresses, address); - set_remove(address->link->dhcp6_addresses_old, address); - set_remove(address->link->dhcp6_pd_addresses, address); - set_remove(address->link->dhcp6_pd_addresses_old, address); - SET_FOREACH(n, address->link->ndisc_addresses) - if (address_equal(n->address, address)) - free(set_remove(address->link->ndisc_addresses, n)); if (address->family == AF_INET6 && in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address)) @@ -181,6 +167,41 @@ Address *address_free(Address *address) { return mfree(address); } +bool address_is_ready(const Address *a) { + assert(a); + + if (FLAGS_SET(a->flags, IFA_F_TENTATIVE)) + return false; + + if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_REMOVING)) + return false; + + if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_PROBING)) + return false; + + if (!FLAGS_SET(a->state, NETWORK_CONFIG_STATE_CONFIGURED)) + return false; + + return true; +} + +void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router) { + Address *a; + + assert(link); + + SET_FOREACH(a, link->addresses) { + if (a->source != source) + continue; + + if (source == NETWORK_CONFIG_SOURCE_NDISC && + router && !in6_addr_equal(router, &a->provider.in6)) + continue; + + address_mark(a); + } +} + static bool address_may_have_broadcast(const Address *a) { assert(a); @@ -295,16 +316,6 @@ int address_compare_func(const Address *a1, const Address *a2) { DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free); -bool address_equal(const Address *a1, const Address *a2) { - if (a1 == a2) - return true; - - if (!a1 || !a2) - return false; - - return address_compare_func(a1, a2) == 0; -} - int address_dup(const Address *src, Address **ret) { _cleanup_(address_freep) Address *dest = NULL; int r; @@ -371,67 +382,19 @@ static int address_set_masquerade(Address *address, bool add) { return 0; } -static int address_add_internal(Link *link, Set **addresses, const Address *in, Address **ret) { - _cleanup_(address_freep) Address *address = NULL; +static int address_add(Link *link, Address *address) { int r; assert(link); - assert(addresses); - assert(in); + assert(address); - r = address_dup(in, &address); - if (r < 0) - return r; - - /* Consider address tentative until we get the real flags from the kernel */ - address->flags |= IFA_F_TENTATIVE; - - r = set_ensure_put(addresses, &address_hash_ops, address); + r = set_ensure_put(&link->addresses, &address_hash_ops, address); if (r < 0) return r; if (r == 0) return -EEXIST; address->link = link; - - if (ret) - *ret = address; - TAKE_PTR(address); - return 0; -} - -static int address_add_foreign(Link *link, const Address *in, Address **ret) { - return address_add_internal(link, &link->addresses_foreign, in, ret); -} - -static int address_add(Link *link, const Address *in, Address **ret) { - Address *address; - int r; - - assert(link); - assert(in); - - r = address_get(link, in, &address); - if (r == -ENOENT) { - /* Address does not exist, create a new one */ - r = address_add_internal(link, &link->addresses, in, &address); - if (r < 0) - return r; - } else if (r == 0) { - /* Take over a foreign address */ - r = set_ensure_put(&link->addresses, &address_hash_ops, address); - if (r < 0) - return r; - - set_remove(link->addresses_foreign, address); - } else if (r == 1) { - /* Already exists, do nothing */ - ; - } else - return r; - - if (ret) - *ret = address; return 0; } @@ -441,13 +404,13 @@ static int address_update(Address *address, const Address *src) { assert(address); assert(address->link); - assert(src); link = address->link; - - address->flags = src->flags; - address->scope = src->scope; - address->cinfo = src->cinfo; + if (src) { + address->flags = src->flags; + address->scope = src->scope; + address->cinfo = src->cinfo; + } if (address_is_ready(address) && address->family == AF_INET6 && @@ -484,8 +447,8 @@ static int address_drop(Address *address) { bool ready; int r; - if (!address) - return 0; + assert(address); + assert(address->link); ready = address_is_ready(address); link = address->link; @@ -494,7 +457,8 @@ static int address_drop(Address *address) { if (r < 0) log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m"); - address_free(address); + if (address->state == 0) + address_free(address); link_update_operstate(link, true); @@ -511,20 +475,12 @@ int address_get(Link *link, const Address *in, Address **ret) { assert(in); existing = set_get(link->addresses, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } + if (!existing) + return -ENOENT; - existing = set_get(link->addresses_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - - return -ENOENT; + if (ret) + *ret = existing; + return 0; } int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) { @@ -546,27 +502,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address ** return address_get(link, a, ret); } -static int addresses_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 link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) { int r; @@ -587,11 +522,24 @@ int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned ch a->prefixlen = prefixlen; return address_get(link, a, ret); - } + } else { + Address *a; - if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0) - return 0; - return addresses_get_ipv4_address(link->addresses_foreign, address, ret); + SET_FOREACH(a, link->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) { @@ -606,7 +554,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union if (family == AF_INET) { HASHMAP_FOREACH(link, manager->links_by_index) if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0) - return !check_ready || address_is_ready(a); + return check_ready ? address_is_ready(a) : address_exists(a); } else { _cleanup_(address_freep) Address *tmp = NULL; @@ -619,7 +567,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union HASHMAP_FOREACH(link, manager->links_by_index) if (address_get(link, tmp, &a) >= 0) - return !check_ready || address_is_ready(a); + return check_ready ? address_is_ready(a) : address_exists(a); } return false; @@ -639,7 +587,7 @@ const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) { } static void log_address_debug(const Address *address, const char *str, const Link *link) { - _cleanup_free_ char *addr = NULL, *peer = NULL, *flags_str = NULL; + _cleanup_free_ char *state = NULL, *addr = NULL, *peer = NULL, *flags_str = NULL; assert(address); assert(str); @@ -648,14 +596,16 @@ static void log_address_debug(const Address *address, const char *str, const Lin if (!DEBUG_LOGGING) return; + (void) network_config_state_to_string_alloc(address->state, &state); (void) in_addr_to_string(address->family, &address->in_addr, &addr); if (in_addr_is_set(address->family, &address->in_addr_peer)) (void) in_addr_to_string(address->family, &address->in_addr_peer, &peer); (void) address_flags_to_string_alloc(address->flags, address->family, &flags_str); - log_link_debug(link, "%s address: %s%s%s/%u (valid %s, preferred %s), flags: %s", - str, strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen, + log_link_debug(link, "%s %s address (%s): %s%s%s/%u (valid %s, preferred %s), flags: %s", + str, strna(network_config_source_to_string(address->source)), strna(state), + strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen, FORMAT_LIFETIME(address->cinfo.ifa_valid), FORMAT_LIFETIME(address->cinfo.ifa_prefered), strna(flags_str)); @@ -698,12 +648,8 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; - assert(rtnl); assert(m); assert(link); - assert(link->address_remove_messages > 0); - - link->address_remove_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 0; @@ -715,16 +661,19 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -int address_remove(const Address *address, Link *link) { +int address_remove(Address *address) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + Link *link; int r; assert(address); assert(IN_SET(address->family, AF_INET, AF_INET6)); - assert(link); - assert(link->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); + assert(address->link); + assert(address->link->ifindex > 0); + assert(address->link->manager); + assert(address->link->manager->rtnl); + + link = address->link; log_address_debug(address, "Removing", link); @@ -744,27 +693,11 @@ int address_remove(const Address *address, Link *link) { return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - link->address_remove_messages++; + address_enter_removing(address); return 0; } -static bool link_is_static_address_configured(const Link *link, const Address *address) { - Address *net_address; - - assert(link); - assert(address); - - if (!link->network) - return false; - - ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section) - if (address_equal(net_address, address)) - return true; - - return false; -} - bool link_address_is_dynamic(const Link *link, const Address *address) { Route *route; @@ -777,7 +710,10 @@ bool link_address_is_dynamic(const Link *link, const Address *address) { /* Even when the address is leased from a DHCP server, networkd assign the address * without lifetime when KeepConfiguration=dhcp. So, let's check that we have * corresponding routes with RTPROT_DHCP. */ - SET_FOREACH(route, link->routes_foreign) { + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + if (route->protocol != RTPROT_DHCP) continue; @@ -821,6 +757,7 @@ int link_drop_ipv6ll_addresses(Link *link) { _cleanup_(address_freep) Address *a = NULL; unsigned char flags, prefixlen; struct in6_addr address; + Address *existing; int ifindex; /* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support @@ -866,7 +803,15 @@ int link_drop_ipv6ll_addresses(Link *link) { a->prefixlen = prefixlen; a->flags = flags; - r = address_remove(a, link); + if (address_get(link, a, &existing) < 0) { + r = address_add(link, a); + if (r < 0) + return r; + + existing = TAKE_PTR(a); + } + + r = address_remove(existing); if (r < 0) return r; } @@ -879,63 +824,80 @@ int link_drop_foreign_addresses(Link *link) { int k, r = 0; assert(link); + assert(link->network); - SET_FOREACH(address, link->addresses_foreign) { + /* First, mark all addresses. */ + SET_FOREACH(address, link->addresses) { /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */ if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) continue; + /* Ignore addresses we configured. */ + if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + /* Ignore addresses not assigned yet or already removing. */ + if (!address_exists(address)) + continue; + if (link_address_is_dynamic(link, address)) { if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) continue; } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) continue; - if (link_is_static_address_configured(link, address)) { - k = address_add(link, address, NULL); - if (k < 0) { - log_link_error_errno(link, k, "Failed to add address: %m"); - if (r >= 0) - r = k; - } - } else { - k = address_remove(address, link); - if (k < 0 && r >= 0) - r = k; - } + address_mark(address); + } + + /* Then, unmark requested addresses. */ + ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) { + Address *existing; + + if (address_get(link, address, &existing) >= 0) + address_unmark(existing); + } + + /* Finally, remove all marked addresses. */ + SET_FOREACH(address, link->addresses) { + if (!address_is_marked(address)) + continue; + + k = address_remove(address); + if (k < 0 && r >= 0) + r = k; } return r; } int link_drop_addresses(Link *link) { - Address *address, *pool_address; + Address *address; int k, r = 0; assert(link); SET_FOREACH(address, link->addresses) { + /* Ignore addresses not assigned yet or already removing. */ + if (!address_exists(address)) + continue; + /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */ if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) continue; - k = address_remove(address, link); + k = address_remove(address); if (k < 0 && r >= 0) { r = k; continue; } - - SET_FOREACH(pool_address, link->pool_addresses) - if (address_equal(address, pool_address)) - address_free(set_remove(link->pool_addresses, pool_address)); } return r; } static int address_acquire(Link *link, const Address *original, Address **ret) { - union in_addr_union in_addr = IN_ADDR_NULL; _cleanup_(address_freep) Address *na = NULL; + union in_addr_union in_addr; int r; assert(link); @@ -969,12 +931,6 @@ static int address_acquire(Link *link, const Address *original, Address **ret) { na->in_addr = in_addr; address_set_broadcast(na); - r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; - *ret = TAKE_PTR(na); return 1; } @@ -1063,6 +1019,25 @@ static int address_configure( return 0; } +void address_cancel_request(Address *address) { + Request req; + + assert(address); + assert(address->link); + + if (!address_is_requesting(address)) + return; + + req = (Request) { + .link = address->link, + .type = REQUEST_TYPE_ADDRESS, + .address = address, + }; + + request_drop(ordered_set_get(address->link->manager->request_queue, &req)); + address_cancel_requesting(address); +} + static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -1084,25 +1059,6 @@ static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -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); - - link = req->link; - - 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"); - - return 0; -} - int link_request_address( Link *link, Address *address, @@ -1111,50 +1067,72 @@ int link_request_address( link_netlink_message_handler_t netlink_handler, Request **ret) { - Address *acquired; + Address *acquired, *existing; int r; assert(link); assert(address); + assert(address->source != NETWORK_CONFIG_SOURCE_FOREIGN); r = address_acquire(link, address, &acquired); if (r < 0) return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m"); if (r > 0) { - if (consume_object) { + if (consume_object) address_free(address); - consume_object = false; /* address from pool is already managed by Link. */ - } + address = acquired; + consume_object = true; } + if (address_get(link, address, &existing) < 0) { + _cleanup_(address_freep) Address *tmp = NULL; + + r = address_dup(address, &tmp); + if (r < 0) + return r; + + /* Consider address tentative until we get the real flags from the kernel */ + tmp->flags |= IFA_F_TENTATIVE; + + r = address_add(link, tmp); + if (r < 0) + return r; + + existing = TAKE_PTR(tmp); + } else { + existing->source = address->source; + existing->provider = address->provider; + } + + r = ipv4acd_configure(existing); + if (r < 0) + return r; + log_address_debug(address, "Requesting", link); r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object, message_counter, netlink_handler, ret); if (r < 0) return log_link_warning_errno(link, r, "Failed to request address: %m"); - return r; + if (r == 0) + return 0; + + address_enter_requesting(existing); + + return 1; } int link_request_static_address(Link *link, Address *address, bool consume) { - Request *req; - int r; - assert(link); assert(address); + assert(address->source == NETWORK_CONFIG_SOURCE_STATIC); - r = link_request_address(link, address, consume, &link->static_address_messages, - static_address_handler, &req); - if (r <= 0) - return r; - - req->after_configure = static_address_after_configure; - return 0; + return link_request_address(link, address, consume, &link->static_address_messages, + static_address_handler, NULL); } int link_request_static_addresses(Link *link) { Address *a; - Prefix *p; int r; assert(link); @@ -1168,52 +1146,13 @@ int link_request_static_addresses(Link *link) { return r; } - HASHMAP_FOREACH(p, link->network->prefixes_by_section) { - _cleanup_(address_freep) Address *address = NULL; + r = link_request_radv_addresses(link); + if (r < 0) + return r; - if (!p->assign) - continue; - - r = address_new(&address); - if (r < 0) - return log_oom(); - - r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); - if (r < 0) - return log_link_warning_errno(link, r, "Could not get RA prefix: %m"); - - r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); - if (r < 0) - return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m"); - - address->family = AF_INET6; - address->route_metric = p->route_metric; - - r = link_request_static_address(link, TAKE_PTR(address), true); - if (r < 0) - return r; - } - - if (in4_addr_is_set(&link->network->dhcp_server_address)) { - _cleanup_(address_freep) Address *address = NULL; - - r = address_new(&address); - if (r < 0) - return log_oom(); - - address->family = AF_INET; - address->in_addr.in = link->network->dhcp_server_address; - address->prefixlen = link->network->dhcp_server_address_prefixlen; - address_set_broadcast(address); - - /* The same address may be explicitly configured in [Address] or [Network] section. - * Configure the DHCP server address only when it is not. */ - if (!link_is_static_address_configured(link, address)) { - r = link_request_static_address(link, TAKE_PTR(address), true); - if (r < 0) - return r; - } - } + r = link_request_dhcp_server_address(link); + if (r < 0) + return r; if (link->static_address_messages == 0) { link->static_addresses_configured = true; @@ -1226,41 +1165,25 @@ int link_request_static_addresses(Link *link) { return 0; } -static int address_is_ready_to_configure(Link *link, const Address *address) { - int r; - +static bool address_is_ready_to_configure(Link *link, const Address *address) { assert(link); assert(address); if (!link_is_ready_to_configure(link, false)) return false; - if (link->address_remove_messages > 0) + if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING)) return false; - if (address_get(link, address, NULL) >= 0) - return true; - - /* If this is a new address, then refuse adding more than the limit */ + /* Refuse adding more than the limit */ if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG), - "Too many addresses are configured, refusing: %m"); - - if (address->family == AF_INET && - address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 && - link->hw_addr.length == ETH_ALEN && - !ether_addr_is_null(&link->hw_addr.ether)) - return ipv4acd_address_is_ready_to_configure(link, address); - - r = address_add(link, address, NULL); - if (r < 0) - return log_link_warning_errno(link, r, "Could not add address: %m");; + return false; return true; } int request_process_address(Request *req) { - Address *a; + Address *existing; Link *link; int r; @@ -1271,26 +1194,18 @@ int request_process_address(Request *req) { link = req->link; - r = address_is_ready_to_configure(link, req->address); - if (r <= 0) - return r; + r = address_get(link, req->address, &existing); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get address: %m"); + + if (!address_is_ready_to_configure(link, existing)) + return 0; r = address_configure(req->address, link, req->netlink_handler); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Failed to configure address: %m"); - /* To prevent a double decrement on failure in after_configure(). */ - req->message_counter = NULL; - - r = address_get(link, req->address, &a); - if (r < 0) - return r; - - if (req->after_configure) { - r = req->after_configure(req, a); - if (r < 0) - return r; - } + address_enter_configuring(existing); return 1; } @@ -1441,10 +1356,14 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWADDR: - log_address_debug(tmp, address ? "Remembering updated" : "Remembering foreign", link); - if (!address) { - /* An address appeared that we did not request */ - r = address_add_foreign(link, tmp, &address); + if (address) { + address_enter_configured(address); + log_address_debug(address, "Remembering updated", link); + } else { + address_enter_configured(tmp); + log_address_debug(tmp, "Received new", link); + + r = address_add(link, tmp); if (r < 0) { _cleanup_free_ char *buf = NULL; @@ -1453,6 +1372,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, strnull(buf)); return 0; } + + address = TAKE_PTR(tmp); } /* address_update() logs internally, so we don't need to here. */ @@ -1463,8 +1384,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, break; case RTM_DELADDR: - log_address_debug(tmp, address ? "Forgetting" : "Kernel removed unknown", link); - (void) address_drop(address); + if (address) { + address_enter_removed(address); + log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link); + (void) address_drop(address); + } else + log_address_debug(tmp, "Kernel removed unknown", link); break; @@ -1922,12 +1847,6 @@ int config_parse_duplicate_address_detection( return 0; } -bool address_is_ready(const Address *a) { - assert(a); - - return !(a->flags & IFA_F_TENTATIVE); -} - static int address_section_verify(Address *address) { if (section_is_invalid(address->section)) return -EINVAL; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 6b5f7ba9c4..67b7580be6 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -21,10 +21,12 @@ typedef struct Request Request; typedef int (*address_ready_callback_t)(Address *address); struct Address { + Link *link; Network *network; NetworkConfigSection *section; - - Link *link; + NetworkConfigSource source; + NetworkConfigState state; + union in_addr_union provider; /* DHCP server or router address */ int family; unsigned char prefixlen; @@ -42,8 +44,6 @@ struct Address { bool scope_set:1; bool ip_masquerade_done:1; - bool is_static:1; /* currently only used by IPv4ACD */ - bool acd_announced:1; AddressFamily duplicate_address_detection; sd_ipv4acd *acd; @@ -62,8 +62,7 @@ int address_new(Address **ret); Address* address_free(Address *address); int address_get(Link *link, const Address *in, Address **ret); int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); -int address_remove(const Address *address, Link *link); -bool address_equal(const Address *a1, const Address *a2); +int address_remove(Address *address); int address_dup(const Address *src, Address **ret); bool address_is_ready(const Address *a); void address_set_broadcast(Address *a); @@ -80,6 +79,7 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address ** int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret); int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); +void address_cancel_request(Address *address); int link_request_address( Link *link, Address *address, @@ -99,6 +99,16 @@ void address_hash_func(const Address *a, struct siphash *state); int address_compare_func(const Address *a1, const Address *a2); extern const struct hash_ops address_hash_ops; +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address); +static inline void address_enter_probing(Address *address) { + address_update_state(address, NETWORK_CONFIG_STATE_PROBING, NETWORK_CONFIG_STATE_PROBING); +} +static inline void address_cancel_probing(Address *address) { + address_update_state(address, NETWORK_CONFIG_STATE_PROBING, 0); +} + +void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router); + CONFIG_PARSER_PROTOTYPE(config_parse_address); CONFIG_PARSER_PROTOTYPE(config_parse_broadcast); CONFIG_PARSER_PROTOTYPE(config_parse_label); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index f190bcf428..45ef1b4903 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -16,6 +16,7 @@ #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-queue.h" +#include "networkd-route.h" #include "parse-util.h" #include "socket-netlink.h" #include "string-table.h" @@ -74,6 +75,40 @@ void network_adjust_dhcp_server(Network *network) { } } +int link_request_dhcp_server_address(Link *link) { + _cleanup_(address_freep) Address *address = NULL; + Address *existing; + int r; + + assert(link); + assert(link->network); + + if (!link_dhcp4_server_enabled(link)) + return 0; + + if (!in4_addr_is_set(&link->network->dhcp_server_address)) + return 0; + + r = address_new(&address); + if (r < 0) + return r; + + address->source = NETWORK_CONFIG_SOURCE_STATIC; + address->family = AF_INET; + address->in_addr.in = link->network->dhcp_server_address; + address->prefixlen = link->network->dhcp_server_address_prefixlen; + address_set_broadcast(address); + + if (address_get(link, address, &existing) >= 0 && + address_exists(existing) && + existing->source == NETWORK_CONFIG_SOURCE_STATIC) + /* The same address seems explicitly configured in [Address] or [Network] section. + * Configure the DHCP server address only when it is not. */ + return 0; + + return link_request_static_address(link, TAKE_PTR(address), true); +} + static int link_find_dhcp_server_address(Link *link, Address **ret) { Address *address; @@ -86,13 +121,21 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) { link->network->dhcp_server_address_prefixlen, ret); /* If not, then select one from static addresses. */ - SET_FOREACH(address, link->static_addresses) - if (address->family == AF_INET && - !in4_addr_is_localhost(&address->in_addr.in) && - in4_addr_is_null(&address->in_addr_peer.in)) { - *ret = address; - return 0; - } + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_STATIC) + continue; + if (!address_exists(address)) + continue; + if (address->family != AF_INET) + continue; + if (in4_addr_is_localhost(&address->in_addr.in)) + continue; + if (in4_addr_is_set(&address->in_addr_peer.in)) + continue; + + *ret = address; + return 0; + } return -ENOENT; } @@ -505,9 +548,6 @@ static bool dhcp_server_is_ready_to_configure(Link *link) { if (!link_has_carrier(link)) return false; - if (link->address_remove_messages > 0) - return false; - if (!link->static_addresses_configured) return false; diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index a02cd995ec..1af1c65c66 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -9,6 +9,7 @@ typedef struct Request Request; void network_adjust_dhcp_server(Network *network); +int link_request_dhcp_server_address(Link *link); int link_request_dhcp_server(Link *link); int request_process_dhcp_server(Request *req); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 247416a750..a8d6498423 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -27,7 +27,7 @@ #include "sysctl-util.h" static int dhcp4_request_address_and_routes(Link *link, bool announce); -static int dhcp4_remove_all(Link *link); +static int dhcp4_check_ready(Link *link); void network_adjust_dhcp4(Network *network) { assert(network); @@ -54,84 +54,106 @@ void network_adjust_dhcp4(Network *network) { network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID; } -static int dhcp4_release_old_lease(Link *link) { +static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) { + Address *address; Route *route; int k, r = 0; assert(link); - if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old)) - return 0; + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_DHCP4) + continue; + if (only_marked && !route_is_marked(route)) + continue; - log_link_debug(link, "Removing old DHCPv4 address and routes."); - - SET_FOREACH(route, link->dhcp_routes_old) { - k = route_remove(route, NULL, link); + k = route_remove(route); if (k < 0) r = k; + + route_cancel_request(route); } - if (link->dhcp_address_old) { - k = address_remove(link->dhcp_address_old, link); + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP4) + continue; + if (only_marked && !address_is_marked(address)) + continue; + + k = address_remove(address); if (k < 0) r = k; + + address_cancel_request(address); } return r; } -static void dhcp4_check_ready(Link *link) { +static int dhcp4_address_get(Link *link, Address **ret) { + Address *address; + + assert(link); + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP4) + continue; + if (address_is_marked(address)) + continue; + + if (ret) + *ret = address; + return 0; + } + + return -ENOENT; +} + +static int dhcp4_address_ready_callback(Address *address) { + assert(address); + assert(address->link); + + /* Do not call this again. */ + address->callback = NULL; + + return dhcp4_check_ready(address->link); +} + +static int dhcp4_check_ready(Link *link) { + Address *address; int r; + assert(link); + if (link->dhcp4_messages > 0) { log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__); - return; + return 0; } - if (!link->dhcp_address) { + if (dhcp4_address_get(link, &address) < 0) { log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__); - return; + return 0; } - if (!address_is_ready(link->dhcp_address)) { + if (!address_is_ready(address)) { log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__); - return; + address->callback = dhcp4_address_ready_callback; + return 0; } link->dhcp4_configured = true; + log_link_debug(link, "DHCPv4 address and routes set."); /* New address and routes are configured now. Let's release old lease. */ - r = dhcp4_release_old_lease(link); - if (r < 0) { - link_enter_failed(link); - return; - } + r = dhcp4_remove_address_and_routes(link, /* only_marked = */ true); + if (r < 0) + return r; r = sd_ipv4ll_stop(link->ipv4ll); if (r < 0) - log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address, ignoring: %m"); + return log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address: %m"); 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; } @@ -140,7 +162,7 @@ static int dhcp4_retry(Link *link) { assert(link); - r = dhcp4_remove_all(link); + r = dhcp4_remove_address_and_routes(link, /* only_marked = */ false); if (r < 0) return r; @@ -192,19 +214,29 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } - dhcp4_check_ready(link); + r = dhcp4_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } static int dhcp4_request_route(Route *in, Link *link) { _cleanup_(route_freep) Route *route = in; - Request *req; + struct in_addr server; + Route *existing; int r; assert(route); assert(link); + assert(link->dhcp_lease); + r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &server); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m"); + + route->source = NETWORK_CONFIG_SOURCE_DHCP4; + route->provider.in = server; route->family = AF_INET; if (!route->protocol_set) route->protocol = RTPROT_DHCP; @@ -215,20 +247,13 @@ static int dhcp4_request_route(Route *in, Link *link) { if (route->mtu == 0) route->mtu = link->network->dhcp_route_mtu; - r = link_has_route(link, route); - if (r < 0) - return r; - if (r == 0) + if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */ link->dhcp4_configured = false; + else + route_unmark(existing); - r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages, - dhcp4_route_handler, &req); - if (r <= 0) - return r; - - req->after_configure = dhcp4_after_route_configure; - - return 0; + return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages, + dhcp4_route_handler, NULL); } static bool link_prefixroute(Link *link) { @@ -675,19 +700,10 @@ static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) { static int dhcp4_request_routes(Link *link) { struct in_addr gw = {}; - Route *rt; int r; assert(link); - - if (!link->dhcp_lease) - return 0; - - while ((rt = set_steal_first(link->dhcp_routes))) { - r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m"); - } + assert(link->dhcp_lease); r = dhcp4_request_prefix_route(link); if (r < 0) @@ -768,27 +784,6 @@ static int dhcp_reset_hostname(Link *link) { return 0; } -static int dhcp4_remove_all(Link *link) { - Route *route; - int k, r = 0; - - assert(link); - - SET_FOREACH(route, link->dhcp_routes) { - k = route_remove(route, NULL, link); - if (k < 0) - r = k; - } - - if (link->dhcp_address) { - k = address_remove(link->dhcp_address, link); - if (k < 0) - r = k; - } - - return r; -} - int dhcp4_lease_lost(Link *link) { int k, r = 0; @@ -799,12 +794,7 @@ int dhcp4_lease_lost(Link *link) { link->dhcp4_configured = false; - /* dhcp4_lease_lost() may be called during renewing IP address. */ - k = dhcp4_release_old_lease(link); - if (k < 0) - r = k; - - k = dhcp4_remove_all(link); + k = dhcp4_remove_address_and_routes(link, /* only_marked = */ false); if (k < 0) r = k; @@ -819,24 +809,7 @@ int dhcp4_lease_lost(Link *link) { link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); - if (link->network->dhcp_send_decline) { - Address *a; - - /* The acquired address may be still ARP probing and not configured. */ - - SET_FOREACH(a, link->addresses_ipv4acd) - if (!a->is_static && address_get(link, a, NULL) < 0) { - Request req = { - .link = link, - .address = a, - }; - - log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(a->in_addr.in)); - request_drop(ordered_set_get(link->manager->request_queue, &req)); - } - } - + /* If one of the above failed. Do not request nexthops and routes. */ if (r < 0) return r; @@ -847,43 +820,6 @@ int dhcp4_lease_lost(Link *link) { return link_request_static_routes(link, true); } -static int dhcp4_address_ready_callback(Address *address) { - assert(address); - - /* Do not call this again. */ - address->callback = NULL; - - dhcp4_check_ready(address->link); - return 0; -} - -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 = req->link; - - 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); - if (r < 0) - return r; - } - link->dhcp_address_old = link->dhcp_address; - } - - link->dhcp_address = address; - return 0; -} - static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -896,12 +832,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * if (r <= 0) return r; - if (address_is_ready(link->dhcp_address)) { - r = dhcp4_address_ready_callback(link->dhcp_address); - if (r < 0) - link_enter_failed(link); - } else - link->dhcp_address->callback = dhcp4_address_ready_callback; + r = dhcp4_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } @@ -911,14 +844,12 @@ static int dhcp4_request_address(Link *link, bool announce) { uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; struct in_addr address, netmask, server; unsigned prefixlen; - Request *req; + Address *existing; int r; assert(link); assert(link->network); - - if (!link->dhcp_lease) - return 0; + assert(link->dhcp_lease); r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) @@ -973,6 +904,8 @@ static int dhcp4_request_address(Link *link, bool announce) { if (r < 0) return log_oom(); + addr->source = NETWORK_CONFIG_SOURCE_DHCP4; + addr->provider.in = server; addr->family = AF_INET; addr->in_addr.in.s_addr = address.s_addr; addr->cinfo.ifa_prefered = lifetime; @@ -988,17 +921,15 @@ static int dhcp4_request_address(Link *link, bool announce) { if (r < 0) return r; - if (address_get(link, addr, NULL) < 0) + if (address_get(link, addr, &existing) < 0) /* The address is new. */ link->dhcp4_configured = false; + else + address_unmark(existing); r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages, - dhcp4_address_handler, &req); + dhcp4_address_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m"); - if (r == 0) - return 0; - - req->after_configure = dhcp4_after_address_configure; return 0; } @@ -1008,6 +939,9 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) { assert(link); + link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL); + link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL); + r = dhcp4_request_address(link, announce); if (r < 0) return r; @@ -1016,8 +950,10 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) { if (r < 0) return r; - link_set_state(link, LINK_STATE_CONFIGURING); - link_check_ready(link); + if (!link->dhcp4_configured) { + link_set_state(link, LINK_STATE_CONFIGURING); + link_check_ready(link); + } return 0; } @@ -1372,7 +1308,9 @@ static int dhcp4_set_request_address(Link *link) { if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) return 0; - SET_FOREACH(a, link->addresses_foreign) { + SET_FOREACH(a, link->addresses) { + if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; if (a->family != AF_INET) continue; if (link_address_is_dynamic(link, a)) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0a7471f0e8..23a0f5fb53 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -20,6 +20,7 @@ #include "networkd-manager.h" #include "networkd-queue.h" #include "networkd-radv.h" +#include "networkd-route.h" #include "siphash24.h" #include "string-table.h" #include "string-util.h" @@ -54,140 +55,103 @@ static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; } -DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) { - if (!p) - return NULL; +static int dhcp6_pd_get_link_by_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) { + union in_addr_union u; + Link *link; - if (p->link && p->link->manager) { - hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix); - set_remove(p->link->manager->dhcp6_pd_prefixes, p); - } - - link_unref(p->link); - return mfree(p); -} - -static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) { - assert(p); - - siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state); - siphash24_compress(&p->link, sizeof(p->link), state); -} - -static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) { - int r; - - r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix)); - if (r != 0) - return r; - - return CMP(a->link, b->link); -} - -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 struct in6_addr *prefix) { - DHCP6DelegatedPrefix *pd; - - assert(link); - assert(link->manager); + assert(manager); assert(prefix); - pd = hashmap_get(link->manager->dhcp6_prefixes, prefix); - if (!pd) - return NULL; + u.in6 = *prefix; - return pd->link; -} + HASHMAP_FOREACH(link, manager->links_by_index) { + if (!link_dhcp6_pd_is_enabled(link)) + continue; -static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, struct in6_addr *ret_prefix) { - DHCP6DelegatedPrefix *pd, in; + if (link->network->dhcp6_pd_assign) { + Address *address; - assert(link); - assert(link->manager); - assert(pd_prefix); - assert(ret_prefix); + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + assert(address->family == AF_INET6); - in = (DHCP6DelegatedPrefix) { - .pd_prefix = *pd_prefix, - .link = link, - }; - - pd = set_get(link->manager->dhcp6_pd_prefixes, &in); - if (!pd) - return -ENOENT; - - *ret_prefix = pd->prefix; - return 0; -} - -static int dhcp6_pd_remove_old(Link *link, bool force); - -static int dhcp6_pd_address_callback(Address *address) { - Address *a; - - assert(address); - assert(address->link); - - /* Make this called only once */ - SET_FOREACH(a, address->link->dhcp6_pd_addresses) - a->callback = NULL; - - return dhcp6_pd_remove_old(address->link, true); -} - -static int dhcp6_pd_remove_old(Link *link, bool force) { - Address *address; - Route *route; - int k, r = 0; - - assert(link); - assert(link->manager); - - if (!force && (link->dhcp6_pd_address_messages > 0 || link->dhcp6_pd_route_messages > 0)) - return 0; - - if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old)) - return 0; - - if (!force) { - bool set_callback = !set_isempty(link->dhcp6_pd_addresses); - - SET_FOREACH(address, link->dhcp6_pd_addresses) - if (address_is_ready(address)) { - set_callback = false; - break; + if (in_addr_prefix_covers(AF_INET6, &u, 64, &address->in_addr) > 0) { + if (ret) + *ret = link; + return 0; + } } + } else { + Route *route; - if (set_callback) { - SET_FOREACH(address, link->dhcp6_pd_addresses) - address->callback = dhcp6_pd_address_callback; - return 0; + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + assert(route->family == AF_INET6); + + if (in6_addr_equal(&route->dst.in6, prefix)) { + if (ret) + *ret = link; + return 0; + } + } } } - log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes."); - - SET_FOREACH(route, link->dhcp6_pd_routes_old) { - k = route_remove(route, NULL, link); - if (k < 0) - r = k; - - if (link->radv) - (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); - dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); - } - - SET_FOREACH(address, link->dhcp6_pd_addresses_old) { - k = address_remove(address, link); - if (k < 0) - r = k; - } - - return r; + return -ENOENT; } -int dhcp6_pd_remove(Link *link) { +static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) { + union in_addr_union u; + + assert(link); + assert(pd_prefix); + + if (!link_dhcp6_pd_is_enabled(link)) + return -ENOENT; + + u.in6 = *pd_prefix; + + if (link->network->dhcp6_pd_assign) { + Address *address; + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + assert(address->family == AF_INET6); + + if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &address->in_addr) <= 0) + continue; + + if (ret) { + union in_addr_union prefix = address->in_addr; + + in_addr_mask(AF_INET6, &prefix, 64); + *ret = prefix.in6; + } + return 0; + } + } else { + Route *route; + + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + assert(route->family == AF_INET6); + + if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &route->dst) > 0) { + if (ret) + *ret = route->dst.in6; + return 0; + } + } + } + + return -ENOENT; +} + +int dhcp6_pd_remove(Link *link, bool only_marked) { Address *address; Route *route; int k, r = 0; @@ -198,151 +162,157 @@ int dhcp6_pd_remove(Link *link) { if (!link_dhcp6_pd_is_enabled(link)) return 0; - link->dhcp6_pd_address_configured = false; - link->dhcp6_pd_route_configured = false; + if (!only_marked) + link->dhcp6_pd_configured = false; - k = dhcp6_pd_remove_old(link, true); - if (k < 0) - r = k; - - if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes)) - return r; - - log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes."); - - SET_FOREACH(route, link->dhcp6_pd_routes) { - k = route_remove(route, NULL, link); - if (k < 0) - r = k; + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + if (only_marked && !route_is_marked(route)) + continue; if (link->radv) (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); - dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); - } - SET_FOREACH(address, link->dhcp6_pd_addresses) { - k = address_remove(address, link); + k = route_remove(route); if (k < 0) r = k; + + route_cancel_request(route); + } + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + if (only_marked && !address_is_marked(address)) + continue; + + if (link->radv) { + union in_addr_union prefix = address->in_addr; + + in_addr_mask(AF_INET6, &prefix, 64); + (void) sd_radv_remove_prefix(link->radv, &prefix.in6, 64); + } + + k = address_remove(address); + if (k < 0) + r = k; + + address_cancel_request(address); } return r; } +static int dhcp6_pd_check_ready(Link *link); + +static int dhcp6_pd_address_ready_callback(Address *address) { + Address *a; + + assert(address); + assert(address->link); + + SET_FOREACH(a, address->link->addresses) + if (a->source == NETWORK_CONFIG_SOURCE_DHCP6PD) + a->callback = NULL; + + return dhcp6_pd_check_ready(address->link); +} + +static int dhcp6_pd_check_ready(Link *link) { + bool has_ready = false; + Address *address; + int r; + + assert(link); + + if (link->dhcp6_pd_messages > 0) { + log_link_debug(link, "%s(): DHCPv6PD addresses and routes are not set.", __func__); + return 0; + } + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + continue; + if (address_is_ready(address)) { + has_ready = true; + break; + } + } + + if (!has_ready) { + SET_FOREACH(address, link->addresses) + if (address->source == NETWORK_CONFIG_SOURCE_DHCP6PD) + address->callback = dhcp6_pd_address_ready_callback; + + log_link_debug(link, "%s(): no DHCPv6PD address is ready.", __func__); + return 0; + } + + link->dhcp6_pd_configured = true; + + log_link_debug(link, "DHCPv6 PD addresses and routes set."); + + r = dhcp6_pd_remove(link, /* only_marked = */ true); + if (r < 0) + return r; + + link_check_ready(link); + return 1; +} + static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->dhcp6_pd_route_messages > 0); + assert(link->dhcp6_pd_messages > 0); - link->dhcp6_pd_route_messages--; + link->dhcp6_pd_messages--; 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"); - if (link->dhcp6_pd_prefixes_assigned) - link->dhcp6_pd_route_configured = true; - - r = dhcp6_pd_remove_old(link, false); - if (r < 0) { - link_enter_failed(link); - return 1; - } - - link_check_ready(link); - } + r = dhcp6_pd_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } -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; +static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix) { _cleanup_(route_freep) Route *route = NULL; - Link *assigned_link; - Request *req; + Route *existing; int r; assert(link); - assert(link->manager); + assert(link->network); assert(prefix); - assert(pd_prefix); + + if (link->network->dhcp6_pd_assign) + return 0; r = route_new(&route); if (r < 0) return r; + route->source = NETWORK_CONFIG_SOURCE_DHCP6PD; route->family = AF_INET6; route->dst.in6 = *prefix; route->dst_prefixlen = 64; route->protocol = RTPROT_DHCP; route->priority = link->network->dhcp6_pd_route_metric; - r = link_has_route(link, route); - if (r < 0) - return r; - if (r == 0) - link->dhcp6_pd_route_configured = false; + if (route_get(NULL, link, route, &existing) < 0) + link->dhcp6_pd_configured = false; + else + route_unmark(existing); - r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_route_messages, - dhcp6_pd_route_handler, &req); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_messages, + dhcp6_pd_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m"); - if (r == 0) - return 0; - req->after_configure = dhcp6_pd_after_route_configure; - - assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix); - if (assigned_link) { - assert(assigned_link == link); - return 0; - } - - pd = new(DHCP6DelegatedPrefix, 1); - if (!pd) - return log_oom(); - - *pd = (DHCP6DelegatedPrefix) { - .prefix = *prefix, - .pd_prefix = *pd_prefix, - .link = link_ref(link), - }; - - r = hashmap_ensure_put(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops, &pd->prefix, pd); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); - - r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); - - TAKE_PTR(pd); return 0; } @@ -350,25 +320,17 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin int r; assert(link); - assert(link->dhcp6_pd_address_messages > 0); + assert(link->dhcp6_pd_messages > 0); - link->dhcp6_pd_address_messages--; + link->dhcp6_pd_messages--; 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"); - if (link->dhcp6_pd_prefixes_assigned) - link->dhcp6_pd_address_configured = true; - - r = dhcp6_pd_remove_old(link, false); - if (r < 0) { - link_enter_failed(link); - return 1; - } - } + r = dhcp6_pd_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } @@ -393,27 +355,6 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) { FORMAT_LIFETIME(address->cinfo.ifa_prefered)); } -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 struct in6_addr *prefix, @@ -421,7 +362,7 @@ static int dhcp6_pd_request_address( uint32_t lifetime_valid) { _cleanup_(address_freep) Address *address = NULL; - Request *req; + Address *existing; int r; assert(link); @@ -445,6 +386,7 @@ static int dhcp6_pd_request_address( return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m"); } + address->source = NETWORK_CONFIG_SOURCE_DHCP6PD; address->prefixlen = 64; address->family = AF_INET6; address->cinfo.ifa_prefered = lifetime_preferred; @@ -454,17 +396,15 @@ static int dhcp6_pd_request_address( log_dhcp6_pd_address(link, address); - if (address_get(link, address, NULL) < 0) - link->dhcp6_pd_address_configured = false; + if (address_get(link, address, &existing) < 0) + link->dhcp6_pd_configured = false; + else + address_unmark(existing); - r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_address_messages, - dhcp6_pd_address_handler, &req); + r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_messages, + dhcp6_pd_address_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m"); - if (r == 0) - return 0; - - req->after_configure = dhcp6_pd_after_address_configure; return 0; } @@ -472,7 +412,6 @@ static int dhcp6_pd_request_address( static int dhcp6_pd_assign_prefix( Link *link, const struct in6_addr *prefix, - const struct in6_addr *pd_prefix, uint32_t lifetime_preferred, uint32_t lifetime_valid) { @@ -488,7 +427,7 @@ static int dhcp6_pd_assign_prefix( return r; } - r = dhcp6_pd_request_route(link, prefix, pd_prefix); + r = dhcp6_pd_request_route(link, prefix); if (r < 0) return r; @@ -508,7 +447,7 @@ static bool link_has_preferred_subnet_id(Link *link) { static int dhcp6_get_preferred_delegated_prefix( Link *link, - const struct in6_addr *masked_pd_prefix, + const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) { @@ -520,11 +459,11 @@ static int dhcp6_get_preferred_delegated_prefix( assert(link); assert(link->manager); - assert(masked_pd_prefix); + assert(pd_prefix); assert(pd_prefix_len <= 64); n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); - prefix.in6 = *masked_pd_prefix; + prefix.in6 = *pd_prefix; if (link_has_preferred_subnet_id(link)) { uint64_t subnet_id = link->network->dhcp6_pd_subnet_id; @@ -543,10 +482,10 @@ 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, (const union in_addr_union*) masked_pd_prefix, pd_prefix_len, &prefix) > 0); + assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) pd_prefix, pd_prefix_len, &prefix) > 0); - assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6); - if (assigned_link && assigned_link != link) { + if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 && + assigned_link != link) { _cleanup_free_ char *assigned_buf = NULL; (void) in6_addr_to_string(&prefix.in6, &assigned_buf); @@ -562,8 +501,8 @@ static int dhcp6_get_preferred_delegated_prefix( 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.in6); - if (!assigned_link || assigned_link == link) { + if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) < 0 || + assigned_link == link) { *ret = prefix.in6; return 0; } @@ -577,7 +516,7 @@ static int dhcp6_get_preferred_delegated_prefix( } static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, - const struct in6_addr *masked_pd_prefix, + const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid, @@ -588,7 +527,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, assert(dhcp6_link); assert(dhcp6_link->manager); - assert(masked_pd_prefix); + assert(pd_prefix); assert(pd_prefix_len <= 64); HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { @@ -607,18 +546,15 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link)) continue; - r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix); + r = dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix); if (r < 0) { - r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix); - if (r < 0) { - link->dhcp6_pd_prefixes_assigned = false; + r = dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix); + if (r < 0) continue; - } } (void) in6_addr_to_string(&assigned_prefix, &assigned_buf); - r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix, - lifetime_preferred, lifetime_valid); + r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred, lifetime_valid); if (r < 0) { log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m", strna(assigned_buf)); @@ -629,29 +565,14 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, } static int dhcp6_pd_prepare(Link *link) { - Address *address; - Route *route; - int r; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 0; if (!link_dhcp6_pd_is_enabled(link)) return 0; - link->dhcp6_pd_prefixes_assigned = true; - - while ((address = set_steal_first(link->dhcp6_pd_addresses))) { - r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m"); - } - - while ((route = set_steal_first(link->dhcp6_pd_routes))) { - r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m"); - } + link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL); + link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL); return 0; } @@ -665,23 +586,15 @@ static int dhcp6_pd_finalize(Link *link) { if (!link_dhcp6_pd_is_enabled(link)) return 0; - if (link->dhcp6_pd_address_messages == 0) { - if (link->dhcp6_pd_prefixes_assigned) - link->dhcp6_pd_address_configured = true; - } else - log_link_debug(link, "Setting DHCPv6 PD addresses"); + if (link->dhcp6_pd_messages == 0) { + link->dhcp6_pd_configured = false; - if (link->dhcp6_pd_route_messages == 0) { - if (link->dhcp6_pd_prefixes_assigned) - link->dhcp6_pd_route_configured = true; - } else - log_link_debug(link, "Setting DHCPv6 PD routes"); + r = dhcp6_pd_remove(link, /* only_marked = */ true); + if (r < 0) + return r; + } - r = dhcp6_pd_remove_old(link, false); - if (r < 0) - return r; - - if (!link->dhcp6_pd_address_configured || !link->dhcp6_pd_route_configured) + if (!link->dhcp6_pd_configured) link_set_state(link, LINK_STATE_CONFIGURING); link_check_ready(link); @@ -699,7 +612,7 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { if (link == dhcp6_link) continue; - r = dhcp6_pd_remove(link); + r = dhcp6_pd_remove(link, /* only_marked = */ false); if (r < 0) link_enter_failed(link); } @@ -707,154 +620,124 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { set_clear(dhcp6_link->dhcp6_pd_prefixes); } -static int dhcp6_remove_old(Link *link, bool force); +static int dhcp6_remove(Link *link, bool only_marked) { + Address *address; + Route *route; + int k, r = 0; -static int dhcp6_address_callback(Address *address) { + assert(link); + + if (!only_marked) + link->dhcp6_configured = false; + + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_DHCP6) + continue; + if (only_marked && !route_is_marked(route)) + continue; + + k = route_remove(route); + if (k < 0) + r = k; + + route_cancel_request(route); + } + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6) + continue; + if (only_marked && !address_is_marked(address)) + continue; + + k = address_remove(address); + if (k < 0) + r = k; + + address_cancel_request(address); + } + + return r; +} + +static int dhcp6_check_ready(Link *link); + +static int dhcp6_address_ready_callback(Address *address) { Address *a; assert(address); assert(address->link); - /* Make this called only once */ - SET_FOREACH(a, address->link->dhcp6_addresses) - a->callback = NULL; + SET_FOREACH(a, address->link->addresses) + if (a->source == NETWORK_CONFIG_SOURCE_DHCP6) + a->callback = NULL; - return dhcp6_remove_old(address->link, true); + return dhcp6_check_ready(address->link); } -static int dhcp6_remove_old(Link *link, bool force) { +static int dhcp6_check_ready(Link *link) { + bool has_ready = false; Address *address; - Route *route; - int k, r = 0; + int r; assert(link); - if (!force && (link->dhcp6_address_messages > 0 || link->dhcp6_route_messages > 0)) + if (link->dhcp6_messages > 0) { + log_link_debug(link, "%s(): DHCPv6 addresses and routes are not set.", __func__); return 0; + } - if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old)) - return 0; - - if (!force) { - bool set_callback = !set_isempty(link->dhcp6_addresses); - - SET_FOREACH(address, link->dhcp6_addresses) - if (address_is_ready(address)) { - set_callback = false; - break; - } - - if (set_callback) { - SET_FOREACH(address, link->dhcp6_addresses) - address->callback = dhcp6_address_callback; - return 0; + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_DHCP6) + continue; + if (address_is_ready(address)) { + has_ready = true; + break; } } - log_link_debug(link, "Removing old DHCPv6 addresses and routes."); + if (!has_ready) { + SET_FOREACH(address, link->addresses) + if (address->source == NETWORK_CONFIG_SOURCE_DHCP6) + address->callback = dhcp6_address_ready_callback; - SET_FOREACH(route, link->dhcp6_routes_old) { - k = route_remove(route, NULL, link); - if (k < 0) - r = k; + log_link_debug(link, "%s(): no DHCPv6 address is ready.", __func__); + return 0; } - SET_FOREACH(address, link->dhcp6_addresses_old) { - k = address_remove(address, link); - if (k < 0) - r = k; - } + link->dhcp6_configured = true; + log_link_debug(link, "DHCPv6 addresses and routes set."); - return r; -} - -static int dhcp6_remove(Link *link) { - Address *address; - Route *route; - int k, r = 0; - - assert(link); - - link->dhcp6_address_configured = false; - link->dhcp6_route_configured = false; - - k = dhcp6_remove_old(link, true); - if (k < 0) - r = k; - - if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes)) + r = dhcp6_remove(link, /* only_marked = */ true); + if (r < 0) return r; - log_link_debug(link, "Removing DHCPv6 addresses and routes."); - - SET_FOREACH(route, link->dhcp6_routes) { - k = route_remove(route, NULL, link); - if (k < 0) - r = k; - } - - SET_FOREACH(address, link->dhcp6_addresses) { - k = address_remove(address, link); - if (k < 0) - r = k; - } - - return r; + link_check_ready(link); + return 0; } static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->dhcp6_route_messages > 0); + assert(link->dhcp6_messages > 0); - link->dhcp6_route_messages--; + link->dhcp6_messages--; 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"); - link->dhcp6_route_configured = true; - - r = dhcp6_remove_old(link, false); - if (r < 0) { - link_enter_failed(link); - return 1; - } - - link_check_ready(link); - } + r = dhcp6_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } -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; - Request *req; + Route *existing; int r; assert(link); @@ -872,6 +755,7 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad if (r < 0) return log_oom(); + route->source = NETWORK_CONFIG_SOURCE_DHCP6; route->family = AF_INET6; route->dst.in6 = *addr; route->dst_prefixlen = prefixlen; @@ -879,28 +763,23 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad route->type = RTN_UNREACHABLE; route->protocol = RTPROT_DHCP; - r = link_has_route(link, route); - if (r < 0) - return r; - if (r == 0) - link->dhcp6_route_configured = false; + if (route_get(link->manager, NULL, route, &existing) < 0) + link->dhcp6_configured = false; + else + route_unmark(existing); - r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_route_messages, - dhcp6_route_handler, &req); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_messages, + dhcp6_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m", strna(buf)); - if (r == 0) - return 0; - - req->after_configure = dhcp6_after_route_configure; return 0; } 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; + struct in_addr_prefix *p; int r; assert(link); @@ -927,11 +806,9 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_ prefixlen < 48 ? " with prefix length < 48, looks unusual.": ""); /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */ - r = set_ensure_put(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p); + r = set_ensure_consume(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p); if (r < 0) return log_link_error_errno(link, r, "Failed to store DHCPv6 PD prefix %s: %m", strna(buf)); - if (r > 0) - TAKE_PTR(p); return prefixlen <= 64; } @@ -1029,102 +906,61 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * int r; assert(link); - assert(link->dhcp6_address_messages > 0); + assert(link->dhcp6_messages > 0); - link->dhcp6_address_messages--; + link->dhcp6_messages--; 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"); - link->dhcp6_address_configured = true; - - r = dhcp6_remove_old(link, false); - if (r < 0) { - link_enter_failed(link); - return 1; - } - } + r = dhcp6_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } -static void log_dhcp6_address(Link *link, const Address *address, char **ret) { +static void log_dhcp6_address(Link *link, const Address *address) { _cleanup_free_ char *buffer = NULL; bool by_ndisc = false; Address *existing; - NDiscAddress *na; - int log_level, r; + int log_level; assert(link); assert(address); assert(address->family == AF_INET6); - (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer); + (void) in6_addr_to_string(&address->in_addr.in6, &buffer); - r = address_get(link, address, &existing); - if (r < 0) { + if (address_get(link, address, &existing) < 0) { /* New address. */ log_level = LOG_INFO; goto simple_log; } else log_level = LOG_DEBUG; - if (set_contains(link->dhcp6_addresses, address)) - /* Already warned. */ - goto simple_log; - if (address->prefixlen == existing->prefixlen) /* Currently, only conflict in prefix length is reported. */ goto simple_log; - SET_FOREACH(na, link->ndisc_addresses) - if (address_compare_func(na->address, existing)) { - by_ndisc = true; - break; - } + if (existing->source == NETWORK_CONFIG_SOURCE_NDISC) + by_ndisc = true; - log_link_warning(link, "DHCPv6 address %s (valid %s, preferred %s) conflicts the existing address %s %s.", - strna(buffer), + log_link_warning(link, "DHCPv6 address %s/%u (valid %s, preferred %s) conflicts the address %s/%u%s.", + strna(buffer), address->prefixlen, FORMAT_LIFETIME(address->cinfo.ifa_valid), FORMAT_LIFETIME(address->cinfo.ifa_prefered), - strna(buffer), - by_ndisc ? "assigned by NDISC. Please try to use or update IPv6Token= setting " + strna(buffer), existing->prefixlen, + by_ndisc ? " assigned by NDisc. Please try to use or update IPv6Token= setting " "to change the address generated by NDISC, or disable UseAutonomousPrefix=" : ""); - goto finalize; + return; simple_log: - log_link_full(link, log_level, "DHCPv6 address %s (valid %s, preferred %s)", - strna(buffer), + log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)", + strna(buffer), address->prefixlen, FORMAT_LIFETIME(address->cinfo.ifa_valid), FORMAT_LIFETIME(address->cinfo.ifa_prefered)); - -finalize: - if (ret) - *ret = TAKE_PTR(buffer); -} - -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( @@ -1134,14 +970,14 @@ static int dhcp6_request_address( uint32_t lifetime_valid) { _cleanup_(address_freep) Address *addr = NULL; - _cleanup_free_ char *buffer = NULL; - Request *req; + Address *existing; int r; r = address_new(&addr); if (r < 0) return log_oom(); + addr->source = NETWORK_CONFIG_SOURCE_DHCP6; addr->family = AF_INET6; addr->in_addr.in6 = *ip6_addr; addr->flags = IFA_F_NOPREFIXROUTE; @@ -1149,19 +985,21 @@ static int dhcp6_request_address( addr->cinfo.ifa_prefered = lifetime_preferred; addr->cinfo.ifa_valid = lifetime_valid; - log_dhcp6_address(link, addr, &buffer); + log_dhcp6_address(link, addr); - if (address_get(link, addr, NULL) < 0) - link->dhcp6_address_configured = false; + if (address_get(link, addr, &existing) < 0) + link->dhcp6_configured = false; + else + address_unmark(existing); - 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 request DHCPv6 address %s: %m", strna(buffer)); - if (r == 0) - return 0; + r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_messages, + dhcp6_address_handler, NULL); + if (r < 0) { + _cleanup_free_ char *buffer = NULL; - req->after_configure = dhcp6_after_address_configure; + (void) in6_addr_to_string(ip6_addr, &buffer); + return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s/128: %m", strna(buffer)); + } return 0; } @@ -1215,21 +1053,10 @@ static int dhcp6_address_acquired(Link *link) { static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL; sd_dhcp6_lease *lease; - Address *a; - Route *rt; int r; - while ((a = set_steal_first(link->dhcp6_addresses))) { - r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m"); - } - - while ((rt = set_steal_first(link->dhcp6_routes))) { - r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m"); - } + link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL); + link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL); r = sd_dhcp6_client_get_lease(client, &lease); if (r < 0) @@ -1250,21 +1077,16 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { /* When we had PD prefixes but not now, we need to remove them. */ dhcp6_pd_prefix_lost(link); - if (link->dhcp6_address_messages == 0) - link->dhcp6_address_configured = true; - else - log_link_debug(link, "Setting DHCPv6 addresses"); + if (link->dhcp6_messages == 0) { + link->dhcp6_configured = true; - if (link->dhcp6_route_messages == 0) - link->dhcp6_route_configured = true; - else - log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets"); + r = dhcp6_remove(link, /* only_marked = */ true); + if (r < 0) + return r; + } else + log_link_debug(link, "Setting DHCPv6 addresses and routes"); - r = dhcp6_remove_old(link, false); - if (r < 0) - return r; - - if (!link->dhcp6_address_configured || !link->dhcp6_route_configured) + if (!link->dhcp6_configured) link_set_state(link, LINK_STATE_CONFIGURING); link_check_ready(link); @@ -1288,7 +1110,7 @@ static int dhcp6_lease_lost(Link *link) { link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); - r = dhcp6_remove(link); + r = dhcp6_remove(link, /* only_marked = */ false); if (r < 0) return r; diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 23612454d9..72f196ad51 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -16,18 +16,9 @@ typedef enum DHCP6ClientStartMode { typedef struct Link Link; typedef struct Request Request; -typedef struct DHCP6DelegatedPrefix { - struct in6_addr prefix; /* Prefix assigned to the link */ - struct in6_addr pd_prefix; /* PD prefix provided by DHCP6 lease */ - Link *link; -} DHCP6DelegatedPrefix; - -DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p); -DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free); - bool link_dhcp6_with_address_enabled(Link *link); bool link_dhcp6_pd_is_enabled(Link *link); -int dhcp6_pd_remove(Link *link); +int dhcp6_pd_remove(Link *link, bool only_marked); int dhcp6_update_mac(Link *link); int dhcp6_start(Link *link); int dhcp6_request_information(Link *link, int ir); diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c index 74a669ead4..703278f8b8 100644 --- a/src/network/networkd-ipv4acd.c +++ b/src/network/networkd-ipv4acd.c @@ -9,42 +9,26 @@ #include "networkd-link.h" #include "networkd-manager.h" -static int static_address_on_stop(Link *link, Address *address) { +static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) { int r; assert(link); assert(address); - if (address_get(link, address, NULL) < 0) - return 0; + /* Prevent form the address being freed. */ + address_enter_probing(address); - log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.", - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + if (!address_exists(address)) + return 0; /* Not assigned. */ - r = address_remove(address, link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m", - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - - return 0; -} - -static int static_address_on_conflict(Link *link, Address *address) { - int r; - - assert(link); - assert(address); - - if (address_get(link, address, NULL) < 0) { - log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.", + if (on_conflict) + log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.", IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - return 0; - } + else + log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.", - IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - - r = address_remove(address, link); + r = address_remove(address); if (r < 0) return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m", IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); @@ -52,7 +36,7 @@ static int static_address_on_conflict(Link *link, Address *address) { return 0; } -static int dhcp4_address_on_conflict(Link *link) { +static int dhcp4_address_on_conflict(Link *link, Address *address) { int r; assert(link); @@ -71,11 +55,14 @@ static int dhcp4_address_on_conflict(Link *link) { if (r < 0) return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m"); + /* make the address will be freed. */ + address_cancel_probing(address); + /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */ return 0; } -static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { +static void on_acd(sd_ipv4acd *acd, int event, void *userdata) { Address *address = userdata; Link *link; int r; @@ -85,13 +72,14 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { assert(address->acd == acd); assert(address->link); assert(address->family == AF_INET); + assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4)); link = address->link; switch (event) { case SD_IPV4ACD_EVENT_STOP: - if (is_static) { - r = static_address_on_stop(link, address); + if (address->source == NETWORK_CONFIG_SOURCE_STATIC) { + r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false); if (r < 0) link_enter_failed(link); } @@ -104,14 +92,17 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - address->acd_announced = true; + address_cancel_probing(address); break; case SD_IPV4ACD_EVENT_CONFLICT: - if (is_static) - r = static_address_on_conflict(link, address); + log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.", + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + + if (address->source == NETWORK_CONFIG_SOURCE_STATIC) + r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true); else - r = dhcp4_address_on_conflict(link); + r = dhcp4_address_on_conflict(link, address); if (r < 0) link_enter_failed(link); break; @@ -121,14 +112,6 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { } } -static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - on_acd(acd, event, userdata, true); -} - -static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - on_acd(acd, event, userdata, false); -} - static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) { Manager *m = userdata; struct hw_addr_data hw_addr; @@ -144,27 +127,31 @@ static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; } -static int ipv4acd_configure(Link *link, const Address *a) { - _cleanup_(address_freep) Address *address = NULL; +int ipv4acd_configure(Address *address) { + Link *link; int r; - assert(link); - assert(a); - assert(a->family == AF_INET); + assert(address); + assert(address->link); + + if (address->family != AF_INET) + return 0; + + if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) + return 0; + + /* Currently, only static and DHCP4 addresses are supported. */ + assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4)); + + if (address->acd) { + address_enter_probing(address); + return 0; + } + + link = address->link; log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR, - IPV4_ADDRESS_FMT_VAL(a->in_addr.in)); - - r = address_dup(a, &address); - if (r < 0) - return r; - - r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; - address->link = link; + IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); r = sd_ipv4acd_new(&address->acd); if (r < 0) @@ -186,9 +173,7 @@ static int ipv4acd_configure(Link *link, const Address *a) { if (r < 0) return r; - r = sd_ipv4acd_set_callback(address->acd, - address->is_static ? static_address_on_acd : dhcp4_address_on_acd, - address); + r = sd_ipv4acd_set_callback(address->acd, on_acd, address); if (r < 0) return r; @@ -202,36 +187,10 @@ static int ipv4acd_configure(Link *link, const Address *a) { return r; } - TAKE_PTR(address); + address_enter_probing(address); return 0; } -int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) { - Address *acd_address; - int r; - - acd_address = set_get(link->addresses_ipv4acd, address); - if (!acd_address) { - r = ipv4acd_configure(link, address); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m"); - - return false; - } - - if (!acd_address->acd_announced) - return false; - - r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address); - if (r < 0) - return log_oom(); - if (r == 0) - return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists."); - - acd_address->flags |= IFA_F_TENTATIVE; - return true; -} - int ipv4acd_update_mac(Link *link) { Address *address; int k, r = 0; @@ -243,8 +202,9 @@ int ipv4acd_update_mac(Link *link) { if (ether_addr_is_null(&link->hw_addr.ether)) return 0; - SET_FOREACH(address, link->addresses_ipv4acd) { - assert(address->acd); + SET_FOREACH(address, link->addresses) { + if (!address->acd) + continue; k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether); if (k < 0) @@ -262,7 +222,10 @@ int ipv4acd_start(Link *link) { assert(link); - SET_FOREACH(address, link->addresses_ipv4acd) { + SET_FOREACH(address, link->addresses) { + if (!address->acd) + continue; + if (sd_ipv4acd_is_running(address->acd)) continue; @@ -280,7 +243,10 @@ int ipv4acd_stop(Link *link) { assert(link); - SET_FOREACH(address, link->addresses_ipv4acd) { + SET_FOREACH(address, link->addresses) { + if (!address->acd) + continue; + k = sd_ipv4acd_stop(address->acd); if (k < 0) r = k; @@ -295,7 +261,10 @@ int ipv4acd_set_ifname(Link *link) { assert(link); - SET_FOREACH(address, link->addresses_ipv4acd) { + SET_FOREACH(address, link->addresses) { + if (!address->acd) + continue; + r = sd_ipv4acd_set_ifname(address->acd, link->ifname); if (r < 0) return r; diff --git a/src/network/networkd-ipv4acd.h b/src/network/networkd-ipv4acd.h index c3d8b0ec38..6ebfa36289 100644 --- a/src/network/networkd-ipv4acd.h +++ b/src/network/networkd-ipv4acd.h @@ -4,7 +4,7 @@ typedef struct Address Address; typedef struct Link Link; -int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address); +int ipv4acd_configure(Address *address); int ipv4acd_update_mac(Link *link); int ipv4acd_start(Link *link); int ipv4acd_stop(Link *link); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 83ed3726f7..a3e5b4ba74 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -28,6 +28,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) { if (r < 0) return -ENOMEM; + address->source = NETWORK_CONFIG_SOURCE_IPV4LL; address->family = AF_INET; address->in_addr.in = addr; address->prefixlen = 16; @@ -41,6 +42,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) { static int ipv4ll_address_lost(Link *link) { _cleanup_(address_freep) Address *address = NULL; + Address *existing; int r; assert(link); @@ -53,10 +55,19 @@ static int ipv4ll_address_lost(Link *link) { if (r < 0) return r; + if (address_get(link, address, &existing) < 0) + return 0; + + if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL) + return 0; + + if (!address_exists(existing)) + return 0; + log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); - return address_remove(address, link); + return address_remove(existing); } static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4ec585a414..7c02110796 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -48,6 +48,7 @@ #include "networkd-nexthop.h" #include "networkd-queue.h" #include "networkd-radv.h" +#include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-setlink.h" #include "networkd-sriov.h" @@ -227,29 +228,12 @@ static Link *link_free(Link *link) { link_dns_settings_clear(link); link->routes = set_free(link->routes); - link->routes_foreign = set_free(link->routes_foreign); - link->dhcp_routes = set_free(link->dhcp_routes); - link->dhcp_routes_old = set_free(link->dhcp_routes_old); - link->dhcp6_routes = set_free(link->dhcp6_routes); - link->dhcp6_routes_old = set_free(link->dhcp6_routes_old); - link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes); - link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old); - link->ndisc_routes = set_free(link->ndisc_routes); link->nexthops = set_free(link->nexthops); link->neighbors = set_free(link->neighbors); link->addresses = set_free(link->addresses); - link->addresses_foreign = set_free(link->addresses_foreign); - link->addresses_ipv4acd = set_free(link->addresses_ipv4acd); - link->pool_addresses = set_free(link->pool_addresses); - link->static_addresses = set_free(link->static_addresses); - link->dhcp6_addresses = set_free(link->dhcp6_addresses); - link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old); - link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses); - link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old); - link->ndisc_addresses = set_free(link->ndisc_addresses); link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes); @@ -394,7 +378,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); - k = dhcp6_pd_remove(link); + k = dhcp6_pd_remove(link, /* only_marked = */ false); if (k < 0) r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); @@ -459,7 +443,7 @@ void link_check_ready(Link *link) { _cleanup_free_ char *str = NULL; (void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str); - return (void) log_link_debug(link, "%s(): an address %s is not ready.", __func__, strna(str)); + return (void) log_link_debug(link, "%s(): address %s is not ready.", __func__, strna(str)); } if (!link->static_address_labels_configured) @@ -495,44 +479,42 @@ void link_check_ready(Link *link) { !in6_addr_is_set(&link->ipv6ll_address)) return (void) log_link_debug(link, "%s(): IPv6LL is not configured yet.", __func__); - bool has_ndisc_address = false; - NDiscAddress *n; - SET_FOREACH(n, link->ndisc_addresses) - if (!n->marked) { - has_ndisc_address = true; + bool has_dynamic_address = false; + SET_FOREACH(a, link->addresses) { + if (address_is_marked(a)) + continue; + if (!address_exists(a)) + continue; + if (IN_SET(a->source, + NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4, + NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC)) { + has_dynamic_address = true; break; } + } - if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) && - !link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address && - !link->ipv4ll_address_configured) + if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || + (link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address) /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */ return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__); /* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */ - if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || - (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link)) || - link_ipv4ll_enabled(link)) { + if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || + link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || + (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link))) { - if (!link->dhcp4_configured && - !(link->dhcp6_address_configured && link->dhcp6_route_configured) && - !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) && - !(link->ndisc_addresses_configured && link->ndisc_routes_configured) && - !link->ipv4ll_address_configured) + if (!link->ipv4ll_address_configured && !link->dhcp4_configured && + !link->dhcp6_configured && !link->dhcp6_pd_configured && !link->ndisc_configured) /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */ return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__); - log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s " - "DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s", + log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCPv6PD:%s NDisc:%s", __func__, - yes_no(link->dhcp4_configured), yes_no(link->ipv4ll_address_configured), - yes_no(link->dhcp6_address_configured), - yes_no(link->dhcp6_route_configured), - yes_no(link->dhcp6_pd_address_configured), - yes_no(link->dhcp6_pd_route_configured), - yes_no(link->ndisc_addresses_configured), - yes_no(link->ndisc_routes_configured)); + yes_no(link->dhcp4_configured), + yes_no(link->dhcp6_configured), + yes_no(link->dhcp6_pd_configured), + yes_no(link->ndisc_configured)); } link_set_state(link, LINK_STATE_CONFIGURED); @@ -1627,11 +1609,7 @@ static int link_carrier_lost(Link *link) { return r; } - r = link_drop_config(link); - if (r < 0) - return r; - - return link_drop_foreign_config(link); + return link_drop_config(link); } static int link_admin_state_up(Link *link) { @@ -1757,18 +1735,6 @@ void link_update_operstate(Link *link, bool also_update_master) { ipv6_scope = MIN(ipv6_scope, address->scope); } - /* for operstate we also take foreign addresses into account */ - SET_FOREACH(address, link->addresses_foreign) { - if (!address_is_ready(address)) - continue; - - if (address->family == AF_INET) - ipv4_scope = MIN(ipv4_scope, address->scope); - - if (address->family == AF_INET6) - ipv6_scope = MIN(ipv6_scope, address->scope); - } - ipv4_address_state = address_state_from_scope(ipv4_scope); ipv6_address_state = address_state_from_scope(ipv6_scope); address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope)); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 9424a10830..d02c4fbd1e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -39,7 +39,6 @@ typedef enum LinkState { typedef struct Manager Manager; typedef struct Network Network; -typedef struct Address Address; typedef struct DUID DUID; typedef struct Link { @@ -92,8 +91,6 @@ typedef struct Link { unsigned static_nexthop_messages; unsigned static_route_messages; unsigned static_routing_policy_rule_messages; - unsigned address_remove_messages; - unsigned route_remove_messages; unsigned tc_messages; unsigned sr_iov_messages; unsigned set_link_messages; @@ -102,19 +99,12 @@ typedef struct Link { unsigned create_stacked_netdev_after_configured_messages; Set *addresses; - Set *addresses_foreign; - Set *addresses_ipv4acd; - Set *pool_addresses; - Set *static_addresses; Set *neighbors; Set *routes; - Set *routes_foreign; Set *nexthops; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; - Address *dhcp_address, *dhcp_address_old; - Set *dhcp_routes, *dhcp_routes_old; char *lease_file; unsigned dhcp4_messages; bool dhcp4_route_failed:1; @@ -145,31 +135,18 @@ typedef struct Link { sd_ndisc *ndisc; Set *ndisc_rdnss; Set *ndisc_dnssl; - Set *ndisc_addresses; - Set *ndisc_routes; - unsigned ndisc_addresses_messages; - unsigned ndisc_routes_messages; - bool ndisc_addresses_configured:1; - bool ndisc_routes_configured:1; + unsigned ndisc_messages; + bool ndisc_configured:1; sd_radv *radv; sd_dhcp6_client *dhcp6_client; sd_dhcp6_lease *dhcp6_lease; - Set *dhcp6_addresses, *dhcp6_addresses_old; - Set *dhcp6_routes, *dhcp6_routes_old; Set *dhcp6_pd_prefixes; - Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old; - Set *dhcp6_pd_routes, *dhcp6_pd_routes_old; - unsigned dhcp6_address_messages; - unsigned dhcp6_route_messages; - unsigned dhcp6_pd_address_messages; - unsigned dhcp6_pd_route_messages; - bool dhcp6_address_configured:1; - bool dhcp6_route_configured:1; - bool dhcp6_pd_address_configured:1; - bool dhcp6_pd_route_configured:1; - bool dhcp6_pd_prefixes_assigned:1; + unsigned dhcp6_messages; + unsigned dhcp6_pd_messages; + bool dhcp6_configured:1; + bool dhcp6_pd_configured:1; /* This is about LLDP reception */ sd_lldp_rx *lldp_rx; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 5c6afdb0ff..b4494d5182 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -27,6 +27,7 @@ #include "netlink-util.h" #include "network-internal.h" #include "networkd-address-pool.h" +#include "networkd-address.h" #include "networkd-dhcp-server-bus.h" #include "networkd-dhcp6.h" #include "networkd-link-bus.h" @@ -36,6 +37,7 @@ #include "networkd-network-bus.h" #include "networkd-nexthop.h" #include "networkd-queue.h" +#include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" #include "networkd-state-file.h" @@ -480,9 +482,6 @@ Manager* manager_free(Manager *m) { m->request_queue = ordered_set_free(m->request_queue); - 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); - m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->links_by_name = hashmap_free(m->links_by_name); m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 365b567d1e..5f3f81ad9d 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -50,8 +50,6 @@ struct Manager { Hashmap *links_by_hw_addr; Hashmap *netdevs; OrderedHashmap *networks; - Hashmap *dhcp6_prefixes; - Set *dhcp6_pd_prefixes; OrderedSet *address_pools; usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 866ea89007..4979d83f6c 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -16,6 +16,7 @@ #include "networkd-manager.h" #include "networkd-ndisc.h" #include "networkd-queue.h" +#include "networkd-route.h" #include "networkd-state-file.h" #include "string-table.h" #include "string-util.h" @@ -96,115 +97,65 @@ void network_adjust_ipv6_accept_ra(Network *network) { network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix); } -static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force); - -static int ndisc_address_callback(Address *address) { - struct in6_addr router = {}; - NDiscAddress *n; - - assert(address); - assert(address->link); - assert(address->family == AF_INET6); - - SET_FOREACH(n, address->link->ndisc_addresses) - if (n->address == address) { - router = n->router; - break; - } - - if (in6_addr_is_null(&router)) { - _cleanup_free_ char *buf = NULL; - - (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; - } - - /* Make this called only once */ - SET_FOREACH(n, address->link->ndisc_addresses) - if (in6_addr_equal(&n->router, &router)) - n->address->callback = NULL; - - return ndisc_remove_old_one(address->link, &router, true); -} - -static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force) { - NDiscAddress *na; - NDiscRoute *nr; +static int ndisc_remove(Link *link, struct in6_addr *router) { + bool updated = false; NDiscDNSSL *dnssl; NDiscRDNSS *rdnss; + Address *address; + Route *route; int k, r = 0; - bool updated = false; assert(link); - assert(router); - if (!force) { - bool set_callback = false; + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + if (!route_is_marked(route)) + continue; + if (router && !in6_addr_equal(router, &route->provider.in6)) + continue; - SET_FOREACH(na, link->ndisc_addresses) - if (!na->marked && in6_addr_equal(&na->router, router)) { - set_callback = true; - break; - } + k = route_remove(route); + if (k < 0) + r = k; - if (set_callback) - SET_FOREACH(na, link->ndisc_addresses) - if (!na->marked && address_is_ready(na->address)) { - set_callback = false; - break; - } - - if (set_callback) { - SET_FOREACH(na, link->ndisc_addresses) - if (!na->marked && in6_addr_equal(&na->router, router)) - na->address->callback = ndisc_address_callback; - - if (DEBUG_LOGGING) { - _cleanup_free_ char *buf = NULL; - - (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)); - } - return 0; - } + route_cancel_request(route); } - if (DEBUG_LOGGING) { - _cleanup_free_ char *buf = NULL; + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + if (!address_is_marked(address)) + continue; + if (router && !in6_addr_equal(router, &address->provider.in6)) + continue; - (void) in6_addr_to_string(router, &buf); - log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf)); + k = address_remove(address); + if (k < 0) + r = k; + + address_cancel_request(address); } - SET_FOREACH(na, link->ndisc_addresses) - if (na->marked && in6_addr_equal(&na->router, router)) { - k = address_remove(na->address, link); - if (k < 0) - r = k; - } + SET_FOREACH(rdnss, link->ndisc_rdnss) { + if (!rdnss->marked) + continue; + if (router && !in6_addr_equal(router, &rdnss->router)) + continue; - SET_FOREACH(nr, link->ndisc_routes) - if (nr->marked && in6_addr_equal(&nr->router, router)) { - k = route_remove(nr->route, NULL, link); - if (k < 0) - r = k; - } + free(set_remove(link->ndisc_rdnss, rdnss)); + updated = true; + } - SET_FOREACH(rdnss, link->ndisc_rdnss) - 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) + continue; + if (router && !in6_addr_equal(router, &dnssl->router)) + continue; - SET_FOREACH(dnssl, link->ndisc_dnssl) - if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) { - free(set_remove(link->ndisc_dnssl, dnssl)); - updated = true; - } + free(set_remove(link->ndisc_dnssl, dnssl)); + updated = true; + } if (updated) link_dirty(link); @@ -212,197 +163,100 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool return r; } -static int ndisc_remove_old(Link *link) { - _cleanup_set_free_free_ Set *routers = NULL; - _cleanup_free_ struct in6_addr *router = NULL; - struct in6_addr *a; - NDiscAddress *na; - NDiscRoute *nr; - NDiscDNSSL *dnssl; - NDiscRDNSS *rdnss; - int k, r; +static int ndisc_check_ready(Link *link); + +static int ndisc_address_ready_callback(Address *address) { + Address *a; + + assert(address); + assert(address->link); + + SET_FOREACH(a, address->link->addresses) + if (a->source == NETWORK_CONFIG_SOURCE_NDISC) + a->callback = NULL; + + return ndisc_check_ready(address->link); +} + +static int ndisc_check_ready(Link *link) { + bool found = false, ready = false; + Address *address; + int r; assert(link); - if (link->ndisc_addresses_messages > 0 || - link->ndisc_routes_messages > 0) + if (link->ndisc_messages > 0) { + log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__); return 0; - - routers = set_new(&in6_addr_hash_ops); - if (!routers) - return -ENOMEM; - - SET_FOREACH(na, link->ndisc_addresses) - if (!set_contains(routers, &na->router)) { - router = newdup(struct in6_addr, &na->router, 1); - if (!router) - return -ENOMEM; - - r = set_put(routers, router); - if (r < 0) - return r; - - assert(r > 0); - TAKE_PTR(router); - } - - SET_FOREACH(nr, link->ndisc_routes) - if (!set_contains(routers, &nr->router)) { - router = newdup(struct in6_addr, &nr->router, 1); - if (!router) - return -ENOMEM; - - r = set_put(routers, router); - if (r < 0) - return r; - - assert(r > 0); - TAKE_PTR(router); - } - - SET_FOREACH(rdnss, link->ndisc_rdnss) - if (!set_contains(routers, &rdnss->router)) { - router = newdup(struct in6_addr, &rdnss->router, 1); - if (!router) - return -ENOMEM; - - r = set_put(routers, router); - if (r < 0) - return r; - - assert(r > 0); - TAKE_PTR(router); - } - - SET_FOREACH(dnssl, link->ndisc_dnssl) - if (!set_contains(routers, &dnssl->router)) { - router = newdup(struct in6_addr, &dnssl->router, 1); - if (!router) - return -ENOMEM; - - r = set_put(routers, router); - if (r < 0) - return r; - - assert(r > 0); - TAKE_PTR(router); - } - - r = 0; - SET_FOREACH(a, routers) { - k = ndisc_remove_old_one(link, a, false); - if (k < 0) - r = k; } - return r; -} + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; -static void ndisc_route_hash_func(const NDiscRoute *x, struct siphash *state) { - route_hash_func(x->route, state); -} + found = true; -static int ndisc_route_compare_func(const NDiscRoute *a, const NDiscRoute *b) { - return route_compare_func(a->route, b->route); -} + if (address_is_ready(address)) { + ready = true; + break; + } + } -DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( - ndisc_route_hash_ops, - NDiscRoute, - ndisc_route_hash_func, - ndisc_route_compare_func, - free); + if (found && !ready) { + SET_FOREACH(address, link->addresses) + if (address->source == NETWORK_CONFIG_SOURCE_NDISC) + address->callback = ndisc_address_ready_callback; + + log_link_debug(link, "%s(): no SLAAC address is ready.", __func__); + return 0; + } + + link->ndisc_configured = true; + log_link_debug(link, "SLAAC addresses and routes set."); + + r = ndisc_remove(link, NULL); + if (r < 0) + return r; + + link_check_ready(link); + return 0; +} static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->ndisc_routes_messages > 0); + assert(link->ndisc_messages > 0); - link->ndisc_routes_messages--; + link->ndisc_messages--; 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."); - link->ndisc_routes_configured = true; - - r = ndisc_remove_old(link); - if (r < 0) { - link_enter_failed(link); - return 1; - } - - link_check_ready(link); - } + r = ndisc_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } -static int ndisc_after_route_configure(Request *req, void *object) { - _cleanup_free_ NDiscRoute *nr = NULL; - NDiscRoute *nr_exist; - struct in6_addr router; - 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); - - link = req->link; - rt = req->userdata; - - 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"); - - nr = new(NDiscRoute, 1); - if (!nr) - return log_oom(); - - *nr = (NDiscRoute) { - .router = router, - .route = route, - }; - - nr_exist = set_get(link->ndisc_routes, nr); - if (nr_exist) { - nr_exist->marked = false; - nr_exist->router = router; - return 0; - } - - r = set_ensure_put(&link->ndisc_routes, &ndisc_route_hash_ops, nr); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store NDisc SLAAC route: %m"); - assert(r > 0); - TAKE_PTR(nr); - - 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; + struct in6_addr router; + Route *existing; int r; assert(route); assert(link); assert(rt); + r = sd_ndisc_router_get_address(rt, &router); + if (r < 0) + return r; + + route->source = NETWORK_CONFIG_SOURCE_NDISC; + route->provider.in6 = router; if (!route->table_set) route->table = link_get_ipv6_accept_ra_route_table(link); if (!route->priority_set) @@ -410,134 +264,58 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) { if (!route->protocol_set) route->protocol = RTPROT_RA; - r = link_has_route(link, route); - if (r < 0) - return r; - if (r == 0) - link->ndisc_routes_configured = false; + if (route_get(NULL, link, route, &existing) < 0) + link->ndisc_configured = false; + else + route_unmark(existing); - 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; + return link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages, + ndisc_route_handler, NULL); } -static void ndisc_address_hash_func(const NDiscAddress *x, struct siphash *state) { - address_hash_func(x->address, state); -} - -static int ndisc_address_compare_func(const NDiscAddress *a, const NDiscAddress *b) { - return address_compare_func(a->address, b->address); -} - -DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( - ndisc_address_hash_ops, - NDiscAddress, - ndisc_address_hash_func, - ndisc_address_compare_func, - free); - static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->ndisc_addresses_messages > 0); + assert(link->ndisc_messages > 0); - link->ndisc_addresses_messages--; + link->ndisc_messages--; 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."); - link->ndisc_addresses_configured = true; - - r = ndisc_remove_old(link); - if (r < 0) { - link_enter_failed(link); - return 1; - } - } + r = ndisc_check_ready(link); + if (r < 0) + link_enter_failed(link); return 1; } -static int ndisc_after_address_configure(Request *req, void *object) { - _cleanup_free_ NDiscAddress *na = NULL; - NDiscAddress *na_exist; - struct in6_addr router; - 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); - - link = req->link; - rt = req->userdata; - - 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"); - - na = new(NDiscAddress, 1); - if (!na) - return log_oom(); - - *na = (NDiscAddress) { - .router = router, - .address = address, - }; - - na_exist = set_get(link->ndisc_addresses, na); - if (na_exist) { - na_exist->marked = false; - na_exist->router = router; - return 0; - } - - r = set_ensure_put(&link->ndisc_addresses, &ndisc_address_hash_ops, na); - if (r < 0) - return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m"); - assert(r > 0); - TAKE_PTR(na); - - return 0; -} - static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { _cleanup_(address_freep) Address *address = in; - Request *req; + struct in6_addr router; + Address *existing; 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) + r = sd_ndisc_router_get_address(rt, &router); + 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; + address->source = NETWORK_CONFIG_SOURCE_NDISC; + address->provider.in6 = router; - return 0; + if (address_get(link, address, &existing) < 0) + link->ndisc_configured = false; + else + address_unmark(existing); + + return link_request_address(link, TAKE_PTR(address), true, &link->ndisc_messages, + ndisc_address_handler, NULL); } static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { @@ -987,7 +765,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { uint32_t lifetime; const struct in6_addr *a; struct in6_addr router; - NDiscRDNSS *rdnss; usec_t time_now; bool updated = false; int n, r; @@ -1011,10 +788,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (n < 0) return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); - SET_FOREACH(rdnss, link->ndisc_rdnss) - if (in6_addr_equal(&rdnss->router, &router)) - rdnss->marked = true; - if (lifetime == 0) return 0; @@ -1025,7 +798,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { for (int j = 0; j < n; j++) { _cleanup_free_ NDiscRDNSS *x = NULL; - NDiscRDNSS d = { + NDiscRDNSS *rdnss, d = { .address = a[j], }; @@ -1081,7 +854,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { struct in6_addr router; uint32_t lifetime; usec_t time_now; - NDiscDNSSL *dnssl; bool updated = false; char **j; int r; @@ -1105,10 +877,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); - SET_FOREACH(dnssl, link->ndisc_dnssl) - if (in6_addr_equal(&dnssl->router, &router)) - dnssl->marked = true; - if (lifetime == 0) return 0; @@ -1120,6 +888,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { STRV_FOREACH(j, l) { _cleanup_free_ NDiscDNSSL *s = NULL; + NDiscDNSSL *dnssl; s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); if (!s) @@ -1242,11 +1011,28 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } } +static void ndisc_mark(Link *link, const struct in6_addr *router) { + NDiscRDNSS *rdnss; + NDiscDNSSL *dnssl; + + assert(link); + assert(router); + + link_mark_addresses(link, NETWORK_CONFIG_SOURCE_NDISC, router); + link_mark_routes(link, NETWORK_CONFIG_SOURCE_NDISC, router); + + SET_FOREACH(rdnss, link->ndisc_rdnss) + if (in6_addr_equal(&rdnss->router, router)) + rdnss->marked = true; + + SET_FOREACH(dnssl, link->ndisc_dnssl) + if (in6_addr_equal(&dnssl->router, router)) + dnssl->marked = true; +} + static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { struct in6_addr router; uint64_t flags; - NDiscAddress *na; - NDiscRoute *nr; int r; assert(link); @@ -1271,13 +1057,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { return 0; } - SET_FOREACH(na, link->ndisc_addresses) - if (in6_addr_equal(&na->router, &router)) - na->marked = true; - - SET_FOREACH(nr, link->ndisc_routes) - if (in6_addr_equal(&nr->router, &router)) - nr->marked = true; + ndisc_mark(link, &router); r = sd_ndisc_router_get_flags(rt, &flags); if (r < 0) @@ -1307,21 +1087,16 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - if (link->ndisc_addresses_messages == 0) - link->ndisc_addresses_configured = true; - else - log_link_debug(link, "Setting SLAAC addresses."); + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; - if (link->ndisc_routes_messages == 0) - link->ndisc_routes_configured = true; - else - log_link_debug(link, "Setting NDisc routes."); + r = ndisc_remove(link, &router); + if (r < 0) + return r; + } else + log_link_debug(link, "Setting SLAAC addresses and router."); - r = ndisc_remove_old(link); - if (r < 0) - return r; - - if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) + if (!link->ndisc_configured) link_set_state(link, LINK_STATE_CONFIGURING); link_check_ready(link); @@ -1349,9 +1124,8 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router case SD_NDISC_EVENT_TIMEOUT: log_link_debug(link, "NDisc handler get timeout event"); - if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) { - link->ndisc_addresses_configured = true; - link->ndisc_routes_configured = true; + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; link_check_ready(link); } break; @@ -1403,6 +1177,9 @@ int ndisc_start(Link *link) { if (!link_has_carrier(link)) return 0; + if (in6_addr_is_null(&link->ipv6ll_address)) + return 0; + log_link_debug(link, "Discovering IPv6 routers"); return sd_ndisc_start(link->ndisc); @@ -1559,14 +1336,15 @@ int config_parse_address_generation_type( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains, - "Failed to parse UseDomains= setting"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, - "Failed to parse DHCPv6Client= setting"); static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = { [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no", [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always", [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes", }; -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES); + +DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains, + "Failed to parse UseDomains= setting"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, + "Failed to parse DHCPv6Client= setting"); diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index bb32a68690..514089c225 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -2,11 +2,11 @@ #pragma once #include "conf-parser.h" -#include "networkd-address.h" -#include "networkd-link.h" -#include "networkd-route.h" #include "time-util.h" +typedef struct Link Link; +typedef struct Network Network; + typedef enum IPv6AcceptRAStartDHCP6Client { IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS, @@ -15,20 +15,6 @@ typedef enum IPv6AcceptRAStartDHCP6Client { _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -EINVAL, } IPv6AcceptRAStartDHCP6Client; -typedef struct NDiscAddress { - /* Used when GC'ing old DNS servers when configuration changes. */ - bool marked; - struct in6_addr router; - Address *address; -} NDiscAddress; - -typedef struct NDiscRoute { - /* Used when GC'ing old DNS servers when configuration changes. */ - bool marked; - struct in6_addr router; - Route *route; -} NDiscRoute; - typedef struct NDiscRDNSS { /* Used when GC'ing old DNS servers when configuration changes. */ bool marked; @@ -61,6 +47,3 @@ void ndisc_flush(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains); - -const char* ipv6_accept_ra_start_dhcp6_client_to_string(IPv6AcceptRAStartDHCP6Client i) _const_; -IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client_from_string(const char *s) _pure_; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index a772ae7c8f..9c01d1cb89 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -28,6 +28,7 @@ #include "networkd-network.h" #include "networkd-nexthop.h" #include "networkd-radv.h" +#include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-sriov.h" #include "parse-util.h" diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 6e87872031..d1ac730e15 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -743,8 +743,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { return false; if (nexthop_owned_by_link(nexthop)) { - Link *l; - /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of * kernel. */ @@ -752,13 +750,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { return false; if (!FLAGS_SET(link->flags, IFF_UP)) return false; - - HASHMAP_FOREACH(l, link->manager->links_by_index) { - if (l->address_remove_messages > 0) - return false; - if (l->route_remove_messages > 0) - return false; - } } /* All group members must be configured first. */ diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index dcf5f4f052..8cd5300ce6 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -70,9 +70,6 @@ static Request *request_free(Request *req) { /* To prevent from triggering assertions in hash functions, remove this request before * freeing object below. */ ordered_set_remove(req->link->manager->request_queue, req); - if (req->on_free) - /* on_free() may use object. So, let's call this earlier. */ - req->on_free(req); if (req->consume_object) request_free_object(req->type, req->object); link_unref(req->link); diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index b1c0a9f0d4..c0cdacfbd8 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -15,11 +15,6 @@ 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_ACTIVATE_LINK, REQUEST_TYPE_ADDRESS, @@ -63,8 +58,6 @@ typedef struct Request { 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; void request_drop(Request *req); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index afb3795f2f..c734c4c3b4 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -7,11 +7,13 @@ #include #include "dns-domain.h" +#include "networkd-address.h" #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-queue.h" #include "networkd-radv.h" +#include "networkd-route.h" #include "parse-util.h" #include "string-util.h" #include "string-table.h" @@ -202,6 +204,54 @@ void network_adjust_radv(Network *network) { } } +static bool link_radv_enabled(Link *link) { + assert(link); + + if (!link_ipv6ll_enabled(link)) + return false; + + return link->network->router_prefix_delegation; +} + +int link_request_radv_addresses(Link *link) { + Prefix *p; + int r; + + assert(link); + + if (!link_radv_enabled(link)) + return 0; + + HASHMAP_FOREACH(p, link->network->prefixes_by_section) { + _cleanup_(address_freep) Address *address = NULL; + + if (!p->assign) + continue; + + r = address_new(&address); + if (r < 0) + return log_oom(); + + r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); + if (r < 0) + return r; + + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return r; + + address->source = NETWORK_CONFIG_SOURCE_STATIC; + address->family = AF_INET6; + address->route_metric = p->route_metric; + + r = link_request_static_address(link, TAKE_PTR(address), true); + if (r < 0) + return r; + } + + return 0; +} + int config_parse_prefix( const char *unit, const char *filename, @@ -660,15 +710,6 @@ static int radv_find_uplink(Link *link, Link **ret) { return 0; } -static bool link_radv_enabled(Link *link) { - assert(link); - - if (!link_ipv6ll_enabled(link)) - return false; - - return link->network->router_prefix_delegation; -} - static int radv_configure(Link *link) { uint16_t router_lifetime; Link *uplink = NULL; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index a8adb9bce1..aec3d3386f 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -51,6 +51,8 @@ void network_drop_invalid_prefixes(Network *network); void network_drop_invalid_route_prefixes(Network *network); void network_adjust_radv(Network *network); +int link_request_radv_addresses(Link *link); + int radv_update_mac(Link *link); int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index c94887ef8a..009afa1b29 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "netlink-util.h" +#include "networkd-address.h" #include "networkd-ipv4ll.h" #include "networkd-manager.h" #include "networkd-network.h" @@ -226,6 +227,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec route->protocol = RTPROT_STATIC; route->network = network; route->section = TAKE_PTR(n); + route->source = NETWORK_CONFIG_SOURCE_STATIC; r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route); if (r < 0) @@ -246,26 +248,11 @@ Route *route_free(Route *route) { network_config_section_free(route->section); - if (route->link) { - NDiscRoute *n; - + if (route->link) set_remove(route->link->routes, route); - set_remove(route->link->routes_foreign, route); - set_remove(route->link->dhcp_routes, route); - set_remove(route->link->dhcp_routes_old, route); - set_remove(route->link->dhcp6_routes, route); - set_remove(route->link->dhcp6_routes_old, route); - set_remove(route->link->dhcp6_pd_routes, route); - set_remove(route->link->dhcp6_pd_routes_old, route); - SET_FOREACH(n, route->link->ndisc_routes) - if (route_equal(n->route, route)) - free(set_remove(route->link->ndisc_routes, n)); - } - if (route->manager) { + if (route->manager) set_remove(route->manager->routes, route); - set_remove(route->manager->routes_foreign, route); - } ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free); @@ -414,87 +401,77 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( route_compare_func, route_free); -bool route_equal(const Route *r1, const Route *r2) { - if (r1 == r2) - return true; +static bool route_type_is_reject(const Route *route) { + assert(route); - if (!r1 || !r2) - return false; - - return route_compare_func(r1, r2) == 0; + return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW); } -static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) { - Route *existing; +static bool route_needs_convert(const Route *route) { + assert(route); + + return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes); +} + +static int route_add(Manager *manager, Link *link, Route *route) { + int r; + + assert(route); + + if (route_type_is_reject(route)) { + assert(manager); + + r = set_ensure_put(&manager->routes, &route_hash_ops, route); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + route->manager = manager; + } else { + assert(link); + + r = set_ensure_put(&link->routes, &route_hash_ops, route); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + route->link = link; + } + + return 0; +} + +int route_get(Manager *manager, Link *link, const Route *in, Route **ret) { + Route *route; - assert(manager || link); assert(in); - existing = set_get(link ? link->routes : manager->routes, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } + if (route_type_is_reject(in)) { + if (!manager) + return -ENOENT; - existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - - return -ENOENT; -} - -static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight) { - assert(dest); - assert(src); - - /* This only copies entries used by the above hash and compare functions. */ - - dest->family = src->family; - dest->src = src->src; - dest->src_prefixlen = src->src_prefixlen; - dest->dst = src->dst; - dest->dst_prefixlen = src->dst_prefixlen; - dest->prefsrc = src->prefsrc; - dest->scope = src->scope; - dest->protocol = src->protocol; - if (nh && nh->blackhole) - dest->type = RTN_BLACKHOLE; - else - dest->type = src->type; - dest->tos = src->tos; - dest->priority = src->priority; - dest->table = src->table; - dest->initcwnd = src->initcwnd; - dest->initrwnd = src->initrwnd; - dest->lifetime = src->lifetime; - dest->advmss = src->advmss; - dest->nexthop_id = src->nexthop_id; - - if (nh) { - assert(hashmap_isempty(nh->group)); - - dest->gw_family = nh->family; - dest->gw = nh->gw; - dest->gw_weight = nh_weight != UINT8_MAX ? nh_weight : src->gw_weight; - } else if (m) { - dest->gw_family = m->gateway.family; - dest->gw = m->gateway.address; - dest->gw_weight = m->weight; + route = set_get(manager->routes, in); } else { - dest->gw_family = src->gw_family; - dest->gw = src->gw; - dest->gw_weight = src->gw_weight; + if (!link) + return -ENOENT; + + route = set_get(link->routes, in); } + if (!route) + return -ENOENT; + + if (ret) + *ret = route; + + return 0; } int route_dup(const Route *src, Route **ret) { _cleanup_(route_freep) Route *dest = NULL; - MultipathRoute *m; - int r; + + /* This does not copy mulipath routes. */ assert(src); assert(ret); @@ -511,278 +488,261 @@ int route_dup(const Route *src, Route **ret) { 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; - - assert(manager || link); - assert(routes); - assert(in); - - r = route_new(&route); - if (r < 0) - return r; - - route_copy(route, in, NULL, NULL, UINT8_MAX); - - r = set_ensure_put(routes, &route_hash_ops, route); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; - - route->link = link; - route->manager = manager; - - if (ret) - *ret = route; - - route = NULL; - - return 0; -} - -static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) { - assert(manager || link); - return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret); -} - -static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) { - _cleanup_(route_freep) Route *tmp = NULL; - Route *route; - int r; - - assert(manager || link); - assert(in); - - if (nh) { - assert(hashmap_isempty(nh->group)); - - r = route_new(&tmp); - if (r < 0) - return r; - - route_copy(tmp, in, NULL, nh, nh_weight); - in = tmp; - } else if (m) { - assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex)); - - r = route_new(&tmp); - if (r < 0) - return r; - - route_copy(tmp, in, m, NULL, UINT8_MAX); - in = tmp; - } - - r = route_get(manager, link, in, &route); - if (r == -ENOENT) { - /* Route does not exist, create a new one */ - r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route); - if (r < 0) - return r; - } else if (r == 0) { - /* Take over a foreign route */ - r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route); - if (r < 0) - return r; - - set_remove(link ? link->routes_foreign : manager->routes_foreign, route); - } else if (r == 1) { - /* Route exists, do nothing */ - ; - } else - return r; - - if (ret) - *ret = route; - return 0; -} - -static bool route_type_is_reject(const Route *route) { - assert(route); - - return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW); -} - -static int link_has_route_one(Link *link, const Route *route, const NextHop *nh, uint8_t nh_weight) { - _cleanup_(route_freep) Route *tmp = NULL; - int r; - - assert(link); +static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) { assert(route); assert(nh); + assert(hashmap_isempty(nh->group)); - r = route_new(&tmp); - if (r < 0) - return r; + route->gw_family = nh->family; + route->gw = nh->gw; - route_copy(tmp, route, NULL, nh, nh_weight); + if (nh_weight != UINT8_MAX) + route->gw_weight = nh_weight; - 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 (nh->blackhole) + route->type = RTN_BLACKHOLE; } -int link_has_route(Link *link, const Route *route) { - MultipathRoute *m; +static void route_apply_multipath_route(Route *route, const MultipathRoute *m) { + assert(route); + assert(m); + + route->gw_family = m->gateway.family; + route->gw = m->gateway.address; + route->gw_weight = m->weight; +} + +static int multipath_route_get_link(Manager *manager, const MultipathRoute *m, Link **ret) { int r; - assert(link); + assert(manager); + assert(m); + + if (m->ifname) { + r = link_get_by_name(manager, m->ifname, ret); + return r < 0 ? r : 1; + + } else if (m->ifindex > 0) { /* Always ignore ifindex if ifname is set. */ + r = link_get_by_index(manager, m->ifindex, ret); + return r < 0 ? r : 1; + } + + if (ret) + *ret = NULL; + return 0; +} + +typedef struct ConvertedRoutes { + size_t n; + Route **routes; + Link **links; +} ConvertedRoutes; + +static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) { + if (!c) + return NULL; + + for (size_t i = 0; i < c->n; i++) + route_free(c->routes[i]); + + free(c->routes); + free(c->links); + + return mfree(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free); + +static int converted_routes_new(size_t n, ConvertedRoutes **ret) { + _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL; + _cleanup_free_ Route **routes = NULL; + _cleanup_free_ Link **links = NULL; + + assert(n > 0); + assert(ret); + + routes = new0(Route*, n); + if (!routes) + return -ENOMEM; + + links = new0(Link*, n); + if (!links) + return -ENOMEM; + + c = new(ConvertedRoutes, 1); + if (!c) + return -ENOMEM; + + *c = (ConvertedRoutes) { + .n = n, + .routes = TAKE_PTR(routes), + .links = TAKE_PTR(links), + }; + + *ret = TAKE_PTR(c); + return 0; +} + +static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) { + _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL; + int r; + + assert(manager); assert(route); + assert(ret); + + if (!route_needs_convert(route)) { + *ret = NULL; + return 0; + } if (route->nexthop_id > 0) { struct nexthop_grp *nhg; NextHop *nh; - if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) - return false; - - if (hashmap_isempty(nh->group)) - return link_has_route_one(link, route, nh, UINT8_MAX); - - HASHMAP_FOREACH(nhg, nh->group) { - NextHop *h; - - if (manager_get_nexthop_by_id(link->manager, nhg->id, &h) < 0) - return false; - - r = link_has_route_one(link, route, h, nhg->weight); - if (r <= 0) - return r; - } - - return true; - } - - 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) { - if (link_get_by_name(link->manager, m->ifname, &l) < 0) - return false; - - m->ifindex = l->ifindex; - } else - l = link; - - r = route_new(&tmp); + r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh); if (r < 0) return r; - route_copy(tmp, route, m, NULL, UINT8_MAX); + if (hashmap_isempty(nh->group)) { + r = converted_routes_new(1, &c); + if (r < 0) + return r; + + r = route_dup(route, &c->routes[0]); + if (r < 0) + return r; + + route_apply_nexthop(c->routes[0], nh, UINT8_MAX); + c->links[0] = nh->link; + + *ret = TAKE_PTR(c); + return 1; + } + + r = converted_routes_new(hashmap_size(nh->group), &c); + if (r < 0) + return r; + + size_t i = 0; + HASHMAP_FOREACH(nhg, nh->group) { + NextHop *h; + + r = manager_get_nexthop_by_id(manager, nhg->id, &h); + if (r < 0) + return r; + + r = route_dup(route, &c->routes[i]); + if (r < 0) + return r; + + route_apply_nexthop(c->routes[i], h, nhg->weight); + c->links[i] = h->link; + + i++; + } + + *ret = TAKE_PTR(c); + return 1; - if (route_get(NULL, l, tmp, NULL) < 0) - return false; } - return true; + assert(!ordered_set_isempty(route->multipath_routes)); + + r = converted_routes_new(ordered_set_size(route->multipath_routes), &c); + if (r < 0) + return r; + + size_t i = 0; + MultipathRoute *m; + ORDERED_SET_FOREACH(m, route->multipath_routes) { + r = route_dup(route, &c->routes[i]); + if (r < 0) + return r; + + route_apply_multipath_route(c->routes[i], m); + + r = multipath_route_get_link(manager, m, &c->links[i]); + if (r < 0) + return r; + + i++; + } + + *ret = TAKE_PTR(c); + return 1; } -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); +void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router) { + Route *route; - if (route->family != family) - return false; + assert(link); - if (!in_addr_is_set(route->family, &route->dst)) - return false; + SET_FOREACH(route, link->routes) { + if (route->source != source) + continue; - return in_addr_prefix_intersect( - route->family, - &route->dst, - route->dst_prefixlen, - address, - FAMILY_ADDRESS_SIZE(family) * 8) > 0; -} + if (source == NETWORK_CONFIG_SOURCE_NDISC && + router && !in6_addr_equal(router, &route->provider.in6)) + continue; -static bool prefix_route_address_is_reachable(const Address *a, int family, const union in_addr_union *address) { - assert(a); - assert(IN_SET(family, AF_INET, AF_INET6)); - assert(address); - - if (a->family != family) - return false; - if (!address_is_ready(a)) - return false; - if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE)) - return false; - if (in_addr_is_set(a->family, &a->in_addr_peer)) - return false; - - return in_addr_prefix_intersect( - family, - &a->in_addr, - a->prefixlen, - address, - FAMILY_ADDRESS_SIZE(family) * 8) > 0; + route_mark(route); + } } static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) { Route *route; + Address *a; assert(link); assert(link->manager); assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); + SET_FOREACH(route, link->routes) { + if (!route_exists(route)) + continue; + if (route->family != family) + continue; + if (!in_addr_is_set(route->family, &route->dst)) + continue; + if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0) + return true; + } - 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; + if (link->manager->manage_foreign_routes) + return false; /* If we do not manage foreign routes, then there may exist a prefix route we do not know, * which was created on configuring an address. Hence, also check the addresses. */ - if (!link->manager->manage_foreign_routes) { - Address *a; - - SET_FOREACH(a, link->addresses) - if (prefix_route_address_is_reachable(a, family, address)) - return true; - SET_FOREACH(a, link->addresses_foreign) - if (prefix_route_address_is_reachable(a, family, address)) - return true; + SET_FOREACH(a, link->addresses) { + if (!address_is_ready(a)) + continue; + if (a->family != family) + continue; + if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE)) + continue; + if (in_addr_is_set(a->family, &a->in_addr_peer)) + continue; + if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0) + return true; } return false; } -static Route *routes_get_default_gateway(Set *routes, int family, Route *gw) { +static Route *link_find_default_gateway(Link *link, int family, Route *gw) { Route *route; - SET_FOREACH(route, routes) { + assert(link); + + SET_FOREACH(route, link->routes) { + if (!route_exists(route)) + continue; if (family != AF_UNSPEC && route->family != family) continue; if (route->dst_prefixlen != 0) @@ -826,20 +786,22 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) { if (link->state != LINK_STATE_CONFIGURED) continue; - gw = routes_get_default_gateway(link->routes, family, gw); - gw = routes_get_default_gateway(link->routes_foreign, family, gw); + gw = link_find_default_gateway(link, family, gw); } if (!gw) return -ENOENT; - assert(gw->link); - *ret = gw->link; + if (ret) { + assert(gw->link); + *ret = gw->link; + } + return 0; } 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, + _cleanup_free_ char *state = NULL, *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL, *table = NULL, *scope = NULL, *proto = NULL; const char *gw = NULL; @@ -852,6 +814,7 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin if (!DEBUG_LOGGING) return; + (void) network_config_state_to_string_alloc(route->state, &state); if (in_addr_is_set(route->family, &route->dst)) (void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst); if (in_addr_is_set(route->family, &route->src)) @@ -889,8 +852,10 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin (void) route_protocol_full_to_string_alloc(route->protocol, &proto); log_link_debug(link, - "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32, - str, strna(dst), strna(src), strna(gw), strna(prefsrc), + "%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, " + "proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32, + str, strna(network_config_source_to_string(route->source)), strna(state), + strna(dst), strna(src), strna(gw), strna(prefsrc), strna(scope), strna(table), strna(proto), strna(route_type_to_string(route->type)), route->nexthop_id, route->priority); @@ -1002,16 +967,14 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req return 0; } -static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(m); - assert(link); - assert(link->route_remove_messages > 0); - link->route_remove_messages--; + /* link may be NULL. */ - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 0; r = sd_netlink_message_get_errno(m); @@ -1021,32 +984,19 @@ static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Li return 1; } -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_message_warning_errno(m, r, "Could not drop route, ignoring"); - - return 1; -} - -int route_remove(const Route *route, Manager *manager, Link *link) { +int route_remove(Route *route) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; unsigned char type; + Manager *manager; + Link *link; int r; - assert(link || manager); + assert(route); + assert(route->manager || (route->link && route->link->manager)); assert(IN_SET(route->family, AF_INET, AF_INET6)); - if (!manager) - manager = link->manager; + link = route->link; + manager = route->manager ?: link->manager; log_route_debug(route, "Removing", link, manager); @@ -1075,100 +1025,85 @@ int route_remove(const Route *route, Manager *manager, Link *link) { if (r < 0) return r; - if (link) { - r = netlink_call_async(manager->rtnl, NULL, req, - 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"); + r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler, + link ? link_netlink_destroy_callback : NULL, 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"); - - manager->route_remove_messages++; - } + link_ref(link); + route_enter_removing(route); return 0; } -static bool link_has_static_route(const Link *link, const Route *route) { - Route *net_route; - - assert(link); - assert(route); - - if (!link->network) - return false; - - HASHMAP_FOREACH(net_route, link->network->routes_by_section) - if (route_equal(net_route, route)) - return true; - - return false; -} - -static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) { - Link *link; - - assert(manager); - - HASHMAP_FOREACH(link, manager->links_by_index) { - if (link == except) - continue; - - if (link_has_static_route(link, route)) - return true; - } - - return false; -} - -static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) { +static int manager_drop_routes(Manager *manager, bool foreign, const Link *except) { Route *route; - int k, r = 0; - Set *routes; + Link *link; + int k, r; assert(manager); - routes = foreign ? manager->routes_foreign : manager->routes; - SET_FOREACH(route, routes) { - if (route->removing) - continue; - + /* First, mark all routes. */ + SET_FOREACH(route, manager->routes) { /* Do not touch routes managed by the kernel. */ if (route->protocol == RTPROT_KERNEL) continue; - /* The route will be configured later, or already configured by a link. */ - if (links_have_static_route(manager, route, except)) + /* When 'foreign' is true, do not remove routes we configured. */ + if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; - /* The existing links do not have the route. Let's drop this now. It may be - * re-configured later. */ - k = route_remove(route, manager, NULL); + /* Ignore routes not assigned yet or already removed. */ + if (!route_exists(route)) + continue; + + route_mark(route); + } + + /* Then, unmark all routes requested by active links. */ + HASHMAP_FOREACH(link, manager->links_by_index) { + if (link == except) + continue; + + if (!link->network) + continue; + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; + + HASHMAP_FOREACH(route, link->network->routes_by_section) { + _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; + Route *existing; + + r = route_convert(manager, route, &converted); + if (r < 0) + continue; + if (r == 0) { + if (route_get(manager, NULL, route, &existing) >= 0) + route_unmark(existing); + continue; + } + + for (size_t i = 0; i < converted->n; i++) + if (route_get(manager, NULL, converted->routes[i], &existing) >= 0) + route_unmark(existing); + } + } + + /* Finally, remove all marked routes. */ + r = 0; + SET_FOREACH(route, manager->routes) { + if (!route_is_marked(route)) + continue; + + k = route_remove(route); if (k < 0 && r >= 0) r = k; - - route->removing = true; } return r; } -static int manager_drop_foreign_routes(Manager *manager) { - return manager_drop_routes_internal(manager, true, NULL); -} - -static int manager_drop_routes(Manager *manager, const Link *except) { - return manager_drop_routes_internal(manager, false, except); -} - static bool route_by_kernel(const Route *route) { assert(route); @@ -1189,16 +1124,24 @@ static bool route_by_kernel(const Route *route) { int link_drop_foreign_routes(Link *link) { Route *route; - int k, r = 0; + int k, r; assert(link); assert(link->manager); - SET_FOREACH(route, link->routes_foreign) { + SET_FOREACH(route, link->routes) { /* do not touch routes managed by the kernel */ if (route_by_kernel(route)) continue; + /* Do not remove routes we configured. */ + if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + /* Ignore routes not assigned yet or already removed. */ + if (!route_exists(route)) + continue; + if (route->protocol == RTPROT_STATIC && link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) continue; @@ -1207,15 +1150,38 @@ int link_drop_foreign_routes(Link *link) { FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) continue; - if (link_has_static_route(link, route)) - k = route_add(NULL, link, route, NULL, NULL, UINT8_MAX, NULL); - else - k = route_remove(route, NULL, link); + route_mark(route); + } + + HASHMAP_FOREACH(route, link->network->routes_by_section) { + _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; + Route *existing; + + r = route_convert(link->manager, route, &converted); + if (r < 0) + continue; + if (r == 0) { + if (route_get(NULL, link, route, &existing) >= 0) + route_unmark(existing); + continue; + } + + for (size_t i = 0; i < converted->n; i++) + if (route_get(NULL, link, converted->routes[i], &existing) >= 0) + route_unmark(existing); + } + + r = 0; + SET_FOREACH(route, link->routes) { + if (!route_is_marked(route)) + continue; + + k = route_remove(route); if (k < 0 && r >= 0) r = k; } - k = manager_drop_foreign_routes(link->manager); + k = manager_drop_routes(link->manager, /* foreign = */ true, NULL); if (k < 0 && r >= 0) r = k; @@ -1230,15 +1196,18 @@ int link_drop_routes(Link *link) { SET_FOREACH(route, link->routes) { /* do not touch routes managed by the kernel */ - if (route->protocol == RTPROT_KERNEL) + if (route_by_kernel(route)) continue; - k = route_remove(route, NULL, link); + if (!route_exists(route)) + continue; + + k = route_remove(route); if (k < 0 && r >= 0) r = k; } - k = manager_drop_routes(link->manager, link); + k = manager_drop_routes(link->manager, /* foreign = */ false, link); if (k < 0 && r >= 0) r = k; @@ -1250,131 +1219,37 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat int r; assert(route); + assert(route->link); - r = route_remove(route, route->manager, route->link); + r = route_remove(route); if (r < 0) { log_link_warning_errno(route->link, r, "Could not remove route: %m"); - route_free(route); + link_enter_failed(route->link); } return 1; } -static int route_add_and_setup_timer_one(Link *link, const Route *route, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) { - _cleanup_(sd_event_source_disable_unrefp) sd_event_source *expire = NULL; - Route *nr; - int r; - - assert(link); - assert(link->manager); - assert(route); - assert(!(m && nh)); - assert(ret); - - if (route_type_is_reject(route) || (nh && nh->blackhole)) - r = route_add(link->manager, NULL, route, NULL, nh, nh_weight, &nr); - else if (nh) { - assert(nh->link); - assert(hashmap_isempty(nh->group)); - - r = route_add(NULL, nh->link, route, NULL, nh, nh_weight, &nr); - } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) { - Link *link_gw; - - r = link_get_by_index(link->manager, m->ifindex, &link_gw); - if (r < 0) - return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex); - - r = route_add(NULL, link_gw, route, m, NULL, UINT8_MAX, &nr); - } else - r = route_add(NULL, link, route, m, NULL, UINT8_MAX, &nr); - if (r < 0) - return log_link_error_errno(link, r, "Could not add route: %m"); +static int route_setup_timer(Route *route) { + Manager *manager; /* TODO: drop expiration handling once it can be pushed into the kernel */ - if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) { - r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), - nr->lifetime, 0, route_expire_handler, nr); - if (r < 0) - return log_link_error_errno(link, r, "Could not arm expiration timer: %m"); - } - sd_event_source_disable_unref(nr->expire); - nr->expire = TAKE_PTR(expire); - - *ret = nr; - return 0; -} - -static int route_add_and_setup_timer(Link *link, const Route *route, unsigned *ret_n_routes, Route ***ret_routes) { - _cleanup_free_ Route **routes = NULL; - unsigned n_routes; - NextHop *nh; - Route **p; - int r; - - assert(link); assert(route); - assert(ret_n_routes); - assert(ret_routes); + assert(route->manager || (route->link && route->link->manager)); - if (route->nexthop_id > 0) { - r = manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh); - if (r < 0) - return log_link_error_errno(link, r, "Could not get nexthop by ID %"PRIu32": %m", route->nexthop_id); - } else - nh = NULL; + manager = route->manager ?: route->link->manager; - if (nh && !hashmap_isempty(nh->group)) { - struct nexthop_grp *nhg; + if (route->lifetime == USEC_INFINITY) + return 0; - n_routes = hashmap_size(nh->group); - p = routes = new(Route*, n_routes); - if (!routes) - return log_oom(); + if (kernel_route_expiration_supported()) + return 0; - HASHMAP_FOREACH(nhg, nh->group) { - NextHop *h; + sd_event_source_disable_unref(route->expire); - r = manager_get_nexthop_by_id(link->manager, nhg->id, &h); - if (r < 0) - return log_link_error_errno(link, r, "Could not get nexthop group member by ID %"PRIu32": %m", nhg->id); - - /* The nexthop h may be a blackhole nexthop. In that case, h->link is NULL. */ - r = route_add_and_setup_timer_one(h->link ?: link, route, NULL, h, nhg->weight, p++); - if (r < 0) - return r; - } - } else if (!ordered_set_isempty(route->multipath_routes)) { - MultipathRoute *m; - - assert(!nh); - assert(!in_addr_is_set(route->gw_family, &route->gw)); - - n_routes = ordered_set_size(route->multipath_routes); - p = routes = new(Route*, n_routes); - if (!routes) - return log_oom(); - - ORDERED_SET_FOREACH(m, route->multipath_routes) { - r = route_add_and_setup_timer_one(link, route, m, NULL, UINT8_MAX, p++); - if (r < 0) - return r; - } - } else { - n_routes = 1; - routes = new(Route*, n_routes); - if (!routes) - return log_oom(); - - r = route_add_and_setup_timer_one(link, route, NULL, nh, UINT8_MAX, routes); - if (r < 0) - return r; - } - - *ret_n_routes = n_routes; - *ret_routes = TAKE_PTR(routes); - return 0; + return sd_event_add_time(manager->event, &route->expire, clock_boottime_or_monotonic(), + route->lifetime, 0, route_expire_handler, route); } static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) { @@ -1485,27 +1360,18 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li static int route_configure( const Route *route, Link *link, - link_netlink_message_handler_t callback, - unsigned *ret_n_routes, - Route ***ret_routes) { + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - _cleanup_free_ Route **routes = NULL; - unsigned n_routes = 0; /* avoid false maybe-uninitialized warning */ int r; + assert(route); + assert(IN_SET(route->family, AF_INET, AF_INET6)); assert(link); assert(link->manager); assert(link->manager->rtnl); 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()) - return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), - "Too many routes are configured, refusing: %m"); log_route_debug(route, "Configuring", link, link->manager); @@ -1589,23 +1455,34 @@ static int route_configure( return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m"); } - r = route_add_and_setup_timer(link, route, &n_routes, &routes); - if (r < 0) - return r; - 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); + return 0; +} - if (ret_routes) { - *ret_n_routes = n_routes; - *ret_routes = TAKE_PTR(routes); - } +void route_cancel_request(Route *route) { + Request req; - return r; + assert(route); + + if (!route_is_requesting(route)) + return; + + if (!route->link) + return; + + req = (Request) { + .link = route->link, + .type = REQUEST_TYPE_ROUTE, + .route = route, + }; + + request_drop(ordered_set_get(route->link->manager->request_queue, &req)); + route_cancel_requesting(route); } static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1637,13 +1514,60 @@ int link_request_route( link_netlink_message_handler_t netlink_handler, Request **ret) { + Route *existing; + int r; + + assert(link); + assert(link->manager); + assert(route); + assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN); + assert(!route_needs_convert(route)); + + if (route_get(link->manager, link, route, &existing) < 0) { + _cleanup_(route_freep) Route *tmp = NULL; + + if (consume_object) + tmp = route; + else { + r = route_dup(route, &tmp); + if (r < 0) + return r; + } + + r = route_add(link->manager, link, tmp); + if (r < 0) + return r; + + existing = TAKE_PTR(tmp); + } else { + existing->source = route->source; + existing->provider = route->provider; + if (consume_object) + route_free(route); + } + + log_route_debug(existing, "Requesting", link, link->manager); + r = link_queue_request(link, REQUEST_TYPE_ROUTE, existing, false, + message_counter, netlink_handler, ret); + if (r <= 0) + return r; + + route_enter_requesting(existing); + return 1; +} + +static int link_request_static_route(Link *link, Route *route) { assert(link); assert(link->manager); assert(route); + if (!route_needs_convert(route)) + return link_request_route(link, route, false, &link->static_route_messages, + static_route_handler, NULL); + log_route_debug(route, "Requesting", link, link->manager); - return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object, - message_counter, netlink_handler, ret); + return link_queue_request(link, REQUEST_TYPE_ROUTE, route, false, + &link->static_route_messages, static_route_handler, NULL); } int link_request_static_routes(Link *link, bool only_ipv4) { @@ -1662,8 +1586,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) { if (only_ipv4 && route->family != AF_INET) continue; - r = link_request_route(link, route, false, &link->static_route_messages, - static_route_handler, NULL); + r = link_request_static_route(link, route); if (r < 0) return r; } @@ -1696,15 +1619,20 @@ bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_un } static int route_is_ready_to_configure(const Route *route, Link *link) { - MultipathRoute *m; - NextHop *nh = NULL; int r; assert(route); assert(link); + if (!link_is_ready_to_configure(link, false)) + return false; + + if (set_size(link->routes) >= routes_max()) + return false; + if (route->nexthop_id > 0) { struct nexthop_grp *nhg; + NextHop *nh; if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) return false; @@ -1723,20 +1651,6 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { } } - if (route_type_is_reject(route)) { - if (link->manager->route_remove_messages > 0) - return false; - } else { - Link *l; - - HASHMAP_FOREACH(l, link->manager->links_by_index) { - if (l->address_remove_messages > 0) - return false; - if (l->route_remove_messages > 0) - return false; - } - } - 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) @@ -1746,21 +1660,20 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw)) return false; + MultipathRoute *m; ORDERED_SET_FOREACH(m, route->multipath_routes) { union in_addr_union a = m->gateway.address; Link *l = NULL; - if (m->ifname) { - if (link_get_by_name(link->manager, m->ifname, &l) < 0) + r = multipath_route_get_link(link->manager, m, &l); + if (r < 0) + return false; + if (r > 0) { + if (!link_is_ready_to_configure(l, true)) return false; m->ifindex = l->ifindex; - } else if (m->ifindex > 0) { - if (link_get_by_index(link->manager, m->ifindex, &l) < 0) - return false; } - if (l && !link_is_ready_to_configure(l, true)) - return false; if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a)) return false; @@ -1770,8 +1683,9 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { } int request_process_route(Request *req) { - _cleanup_free_ Route **routes = NULL; - unsigned n_routes; + _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; + Route *route; + Link *link; int r; assert(req); @@ -1779,113 +1693,116 @@ int request_process_route(Request *req) { assert(req->route); assert(req->type == REQUEST_TYPE_ROUTE); - if (!link_is_ready_to_configure(req->link, false)) + link = req->link; + route = req->route; + + r = route_is_ready_to_configure(route, link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m"); + if (r == 0) return 0; - r = route_is_ready_to_configure(req->route, req->link); - if (r <= 0) - return r; + if (route_needs_convert(route)) { + r = route_convert(link->manager, route, &converted); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to convert route: %m"); - 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; + assert(r > 0); + assert(converted); - /* To prevent a double decrement on failure in after_configure(). */ - req->message_counter = NULL; + for (size_t i = 0; i < converted->n; i++) { + Route *existing; - if (req->after_configure) { - assert(n_routes > 0); + if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) { + _cleanup_(route_freep) Route *tmp = NULL; - for (unsigned i = 0; i < n_routes; i++) { - r = req->after_configure(req, routes[i]); - if (r < 0) - return r; + r = route_dup(converted->routes[i], &tmp); + if (r < 0) + return log_oom(); + + r = route_add(link->manager, converted->links[i] ?: link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to add route: %m"); + + TAKE_PTR(tmp); + } else { + existing->source = converted->routes[i]->source; + existing->provider = converted->routes[i]->provider; + } } + } else { + r = route_setup_timer(route); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to setup timer for route: %m"); } + r = route_configure(route, link, req->netlink_handler); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure route: %m"); + + if (converted) + for (size_t i = 0; i < converted->n; i++) { + Route *existing; + + assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0); + route_enter_configuring(existing); + } + else + route_enter_configuring(route); + return 1; } -static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) { - _cleanup_(route_freep) Route *nr = NULL; +static int process_route_one(Manager *manager, Link *link, uint16_t type, Route *in) { + _cleanup_(route_freep) Route *tmp = in; Route *route = NULL; - NextHop *nh = NULL; int r; assert(manager); assert(tmp); assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)); - (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh); - - if (nh && hashmap_isempty(nh->group)) { - if (link && nh->link && link != nh->link) - return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL), - "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring."); - - if (nh->link) - link = nh->link; - - r = route_new(&nr); - if (r < 0) - return log_oom(); - - route_copy(nr, tmp, NULL, nh, UINT8_MAX); - - tmp = nr; - } else if (m) { - if (link) - return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL), - "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring."); - - if (m->ifindex <= 0) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "rtnl: received multipath route with invalid ifindex, ignoring."); - - r = link_get_by_index(manager, m->ifindex, &link); - if (r < 0) { - log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex); - return 0; - } - - r = route_new(&nr); - if (r < 0) - return log_oom(); - - route_copy(nr, tmp, m, NULL, UINT8_MAX); - - tmp = nr; - } + /* link may be NULL. This consumes 'in'. */ (void) route_get(manager, link, tmp, &route); switch (type) { case RTM_NEWROUTE: - if (!route) { - if (!manager->manage_foreign_routes) - log_route_debug(tmp, "Ignoring received foreign", link, manager); - else { - /* A route appeared that we did not request */ - log_route_debug(tmp, "Remembering foreign", link, manager); - r = route_add_foreign(manager, link, tmp, NULL); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); - return 0; - } + if (route) { + route_enter_configured(route); + log_route_debug(route, "Received remembered", link, manager); + + } else if (!manager->manage_foreign_routes) { + route_enter_configured(tmp); + log_route_debug(tmp, "Ignoring received", link, manager); + + } else { + /* A route appeared that we did not request */ + route_enter_configured(tmp); + log_route_debug(tmp, "Received new", link, manager); + r = route_add(manager, link, tmp); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); + return 0; } - } else - log_route_debug(tmp, "Received remembered", link, manager); + TAKE_PTR(tmp); + } break; case RTM_DELROUTE: - log_route_debug(tmp, - route ? "Forgetting" : - manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign", - link, manager); - route_free(route); + if (route) { + route_enter_removed(route); + if (route->state == 0) { + log_route_debug(route, "Forgetting", link, manager); + route_free(route); + } else + log_route_debug(route, "Removed", link, manager); + } else + log_route_debug(tmp, + manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received", + link, manager); + break; default: @@ -1896,7 +1813,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const } int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { - _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL; + _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; _cleanup_(route_freep) Route *tmp = NULL; _cleanup_free_ void *rta_multipath = NULL; Link *link = NULL; @@ -2088,7 +2005,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m"); return 0; } else if (r >= 0) { - r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes); + r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &tmp->multipath_routes); if (r < 0) { log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m"); return 0; @@ -2101,18 +2018,21 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma if (route_type_is_reject(tmp)) link = NULL; - if (ordered_set_isempty(multipath_routes)) - (void) process_route_one(m, link, type, tmp, NULL); - else { - MultipathRoute *mr; + if (!route_needs_convert(tmp)) + return process_route_one(m, link, type, TAKE_PTR(tmp)); - ORDERED_SET_FOREACH(mr, multipath_routes) { - r = process_route_one(m, link, type, tmp, mr); - if (r < 0) - break; - } + r = route_convert(m, tmp, &converted); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m"); + return 0; } + assert(r > 0); + assert(converted); + + for (size_t i = 0; i < converted->n; i++) + (void) process_route_one(m, converted->links[i] ?: link, type, TAKE_PTR(converted->routes[i])); + return 1; } @@ -3094,6 +3014,9 @@ static int route_section_verify(Route *route, Network *network) { if (section_is_invalid(route->section)) return -EINVAL; + /* Currently, we do not support static route with finite lifetime. */ + assert(route->lifetime == USEC_INFINITY); + if (route->gateway_from_dhcp_or_ra) { if (route->gw_family == AF_UNSPEC) { /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */ diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 2e5a073a2b..d3acdfa2e7 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -16,11 +16,13 @@ typedef struct Network Network; typedef struct Request Request; typedef struct Route { - Network *network; - NetworkConfigSection *section; - Link *link; Manager *manager; + Network *network; + NetworkConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; + union in_addr_union provider; /* DHCP server or router address */ int family; int gw_family; @@ -52,7 +54,6 @@ typedef struct Route { bool protocol_set:1; bool pref_set:1; bool gateway_from_dhcp_or_ra:1; - bool removing:1; union in_addr_union gw; union in_addr_union dst; @@ -66,7 +67,6 @@ typedef struct Route { void route_hash_func(const Route *route, struct siphash *state); int route_compare_func(const Route *a, const Route *b); -bool route_equal(const Route *r1, const Route *r2); extern const struct hash_ops route_hash_ops; int route_new(Route **ret); @@ -75,15 +75,16 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); int route_dup(const Route *src, Route **ret); int route_configure_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); +int route_remove(Route *route); -int link_has_route(Link *link, const Route *route); +int route_get(Manager *manager, Link *link, const Route *in, Route **ret); int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret); bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw); int link_drop_routes(Link *link); int link_drop_foreign_routes(Link *link); +void route_cancel_request(Route *route); int link_request_route( Link *link, Route *route, @@ -103,6 +104,9 @@ void network_drop_invalid_routes(Network *network); int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret); int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret); +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route); +void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router); + CONFIG_PARSER_PROTOTYPE(config_parse_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src); CONFIG_PARSER_PROTOTYPE(config_parse_destination); diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 21e20f10f5..54243e7968 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -6,6 +6,7 @@ #include "missing_network.h" #include "netlink-util.h" +#include "networkd-address.h" #include "networkd-can.h" #include "networkd-link.h" #include "networkd-manager.h" diff --git a/src/network/test-network.c b/src/network/test-network.c index 65627182cd..479675722b 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -10,7 +10,9 @@ #include "ether-addr-util.h" #include "hostname-setup.h" #include "network-internal.h" +#include "networkd-address.h" #include "networkd-manager.h" +#include "networkd-route.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -168,6 +170,16 @@ static int test_load_config(Manager *manager) { return 0; } +static bool address_equal(const Address *a1, const Address *a2) { + if (a1 == a2) + return true; + + if (!a1 || !a2) + return false; + + return address_compare_func(a1, a2) == 0; +} + static void test_address_equality(void) { _cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL; diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 5156fcc7d7..3d7ce3aca8 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -4,6 +4,7 @@ #include "log.h" #include "macro.h" #include "net-condition.h" +#include "networkd-address.h" #include "networkd-conf.h" #include "networkd-network.h" #include "strv.h"