diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
commit | fc53809803cd2bc2434e312b19a18fa36776da12 (patch) | |
tree | b4b43bd6538f51965ce32856e9c053d0f90919c8 /src/network/networkd-address.c | |
parent | Adding upstream version 255.5. (diff) | |
download | systemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip |
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/network/networkd-address.c')
-rw-r--r-- | src/network/networkd-address.c | 563 |
1 files changed, 344 insertions, 219 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 0e4d87b..b4ac0bc 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -10,9 +10,11 @@ #include "netlink-util.h" #include "networkd-address-pool.h" #include "networkd-address.h" +#include "networkd-dhcp-prefix-delegation.h" #include "networkd-dhcp-server.h" #include "networkd-ipv4acd.h" #include "networkd-manager.h" +#include "networkd-ndisc.h" #include "networkd-netlabel.h" #include "networkd-network.h" #include "networkd-queue.h" @@ -133,14 +135,40 @@ void link_get_address_states( *ret_all = address_state_from_scope(MIN(ipv4_scope, ipv6_scope)); } +static void address_hash_func(const Address *a, struct siphash *state); +static int address_compare_func(const Address *a1, const Address *a2); +static void address_detach(Address *address); + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + address_hash_ops_detach, + Address, + address_hash_func, + address_compare_func, + address_detach); + +DEFINE_HASH_OPS( + address_hash_ops, + Address, + address_hash_func, + address_compare_func); + +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + address_section_hash_ops, + ConfigSection, + config_section_hash_func, + config_section_compare_func, + Address, + address_detach); + int address_new(Address **ret) { - _cleanup_(address_freep) Address *address = NULL; + _cleanup_(address_unrefp) Address *address = NULL; address = new(Address, 1); if (!address) return -ENOMEM; *address = (Address) { + .n_ref = 1, .family = AF_UNSPEC, .scope = RT_SCOPE_UNIVERSE, .lifetime_valid_usec = USEC_INFINITY, @@ -155,7 +183,7 @@ int address_new(Address **ret) { int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) { _cleanup_(config_section_freep) ConfigSection *n = NULL; - _cleanup_(address_freep) Address *address = NULL; + _cleanup_(address_unrefp) Address *address = NULL; int r; assert(network); @@ -186,7 +214,7 @@ int address_new_static(Network *network, const char *filename, unsigned section_ /* This will be adjusted in address_section_verify(). */ address->duplicate_address_detection = _ADDRESS_FAMILY_INVALID; - r = ordered_hashmap_ensure_put(&network->addresses_by_section, &config_section_hash_ops, address->section, address); + r = ordered_hashmap_ensure_put(&network->addresses_by_section, &address_section_hash_ops, address->section, address); if (r < 0) return r; @@ -194,32 +222,53 @@ int address_new_static(Network *network, const char *filename, unsigned section_ return 0; } -Address *address_free(Address *address) { - if (!address) - return NULL; +static Address* address_detach_impl(Address *address) { + assert(address); + assert(!address->link || !address->network); if (address->network) { assert(address->section); ordered_hashmap_remove(address->network->addresses_by_section, address->section); + + if (address->network->dhcp_server_address == address) + address->network->dhcp_server_address = NULL; + + address->network = NULL; + return address; } if (address->link) { set_remove(address->link->addresses, address); - if (address->family == AF_INET6 && - in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address)) - memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); - - ipv4acd_detach(address->link, address); + address->link = NULL; + return address; } + return NULL; +} + +static void address_detach(Address *address) { + assert(address); + + address_unref(address_detach_impl(address)); +} + +static Address* address_free(Address *address) { + if (!address) + return NULL; + + address_detach_impl(address); + config_section_free(address->section); free(address->label); free(address->netlabel); + ipv6_token_unref(address->token); nft_set_context_clear(&address->nft_set_context); return mfree(address); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(Address, address, address_free); + static bool address_lifetime_is_valid(const Address *a) { assert(a); @@ -400,25 +449,25 @@ static int address_ipv4_prefix(const Address *a, struct in_addr *ret) { static void address_hash_func(const Address *a, struct siphash *state) { assert(a); - siphash24_compress(&a->family, sizeof(a->family), state); + siphash24_compress_typesafe(a->family, state); switch (a->family) { case AF_INET: { struct in_addr prefix; - siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); + siphash24_compress_typesafe(a->prefixlen, state); assert_se(address_ipv4_prefix(a, &prefix) >= 0); - siphash24_compress(&prefix, sizeof(prefix), state); + siphash24_compress_typesafe(prefix, state); - siphash24_compress(&a->in_addr.in, sizeof(a->in_addr.in), state); + siphash24_compress_typesafe(a->in_addr.in, state); break; } case AF_INET6: - siphash24_compress(&a->in_addr.in6, sizeof(a->in_addr.in6), state); + siphash24_compress_typesafe(a->in_addr.in6, state); if (in6_addr_is_null(&a->in_addr.in6)) - siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); + siphash24_compress_typesafe(a->prefixlen, state); break; default: @@ -470,24 +519,9 @@ static int address_compare_func(const Address *a1, const Address *a2) { } } -DEFINE_HASH_OPS( - address_hash_ops, - Address, - address_hash_func, - address_compare_func); - -DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( - address_hash_ops_free, - Address, - address_hash_func, - address_compare_func, - address_free); - -static bool address_can_update(const Address *la, const Address *na) { - assert(la); - assert(la->link); - assert(na); - assert(na->network); +bool address_can_update(const Address *existing, const Address *requesting) { + assert(existing); + assert(requesting); /* * property | IPv4 | IPv6 @@ -512,32 +546,32 @@ static bool address_can_update(const Address *la, const Address *na) { * IPv6 : See inet6_addr_modify() in net/ipv6/addrconf.c. */ - if (la->family != na->family) + if (existing->family != requesting->family) return false; - if (la->prefixlen != na->prefixlen) + if (existing->prefixlen != requesting->prefixlen) return false; /* When a null address is requested, the address to be assigned/updated will be determined later. */ - if (!address_is_static_null(na) && - in_addr_equal(la->family, &la->in_addr, &na->in_addr) <= 0) + if (!address_is_static_null(requesting) && + in_addr_equal(existing->family, &existing->in_addr, &requesting->in_addr) <= 0) return false; - switch (la->family) { + switch (existing->family) { case AF_INET: { struct in_addr bcast; - if (la->scope != na->scope) + if (existing->scope != requesting->scope) return false; - if (((la->flags ^ na->flags) & KNOWN_FLAGS & ~IPV6ONLY_FLAGS & ~UNMANAGED_FLAGS) != 0) + if (((existing->flags ^ requesting->flags) & KNOWN_FLAGS & ~IPV6ONLY_FLAGS & ~UNMANAGED_FLAGS) != 0) return false; - if (!streq_ptr(la->label, na->label)) + if (!streq_ptr(existing->label, requesting->label)) return false; - if (!in4_addr_equal(&la->in_addr_peer.in, &na->in_addr_peer.in)) + if (!in4_addr_equal(&existing->in_addr_peer.in, &requesting->in_addr_peer.in)) return false; - if (address_get_broadcast(na, la->link, &bcast) >= 0) { + if (existing->link && address_get_broadcast(requesting, existing->link, &bcast) >= 0) { /* If the broadcast address can be determined now, check if they match. */ - if (!in4_addr_equal(&la->broadcast, &bcast)) + if (!in4_addr_equal(&existing->broadcast, &bcast)) return false; } else { /* When a null address is requested, then the broadcast address will be @@ -545,7 +579,7 @@ static bool address_can_update(const Address *la, const Address *na) { * 192.168.0.10/24 -> 192.168.0.255 * So, here let's only check if the broadcast is the last address in the range, e.g. * 0.0.0.0/24 -> 0.0.0.255 */ - if (!FLAGS_SET(la->broadcast.s_addr, htobe32(UINT32_C(0xffffffff) >> la->prefixlen))) + if (!FLAGS_SET(existing->broadcast.s_addr, htobe32(UINT32_C(0xffffffff) >> existing->prefixlen))) return false; } break; @@ -561,7 +595,7 @@ static bool address_can_update(const Address *la, const Address *na) { } int address_dup(const Address *src, Address **ret) { - _cleanup_(address_freep) Address *dest = NULL; + _cleanup_(address_unrefp) Address *dest = NULL; int r; assert(src); @@ -571,22 +605,24 @@ int address_dup(const Address *src, Address **ret) { if (!dest) return -ENOMEM; - /* clear all pointers */ + /* clear the reference counter and all pointers */ + dest->n_ref = 1; dest->network = NULL; dest->section = NULL; dest->link = NULL; dest->label = NULL; + dest->token = ipv6_token_ref(src->token); dest->netlabel = NULL; dest->nft_set_context.sets = NULL; dest->nft_set_context.n_sets = 0; if (src->family == AF_INET) { - r = free_and_strdup(&dest->label, src->label); + r = strdup_to(&dest->label, src->label); if (r < 0) return r; } - r = free_and_strdup(&dest->netlabel, src->netlabel); + r = strdup_to(&dest->netlabel, src->netlabel); if (r < 0) return r; @@ -674,8 +710,8 @@ static void address_modify_nft_set_context(Address *address, bool add, NFTSetCon } if (r < 0) - log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring", - add? "add" : "delete", + log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring: %m", + add ? "add" : "delete", nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set, IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen)); else @@ -712,19 +748,21 @@ static void address_modify_nft_set(Address *address, bool add) { } } -static int address_add(Link *link, Address *address) { +static int address_attach(Link *link, Address *address) { int r; assert(link); assert(address); + assert(!address->link); - r = set_ensure_put(&link->addresses, &address_hash_ops_free, address); + r = set_ensure_put(&link->addresses, &address_hash_ops_detach, address); if (r < 0) return r; if (r == 0) return -EEXIST; address->link = link; + address_ref(address); return 0; } @@ -766,8 +804,49 @@ static int address_update(Address *address) { return 0; } -static int address_drop(Address *address) { - Link *link = ASSERT_PTR(ASSERT_PTR(address)->link); +static int address_removed_maybe_kernel_dad(Link *link, Address *address) { + int r; + + assert(link); + assert(address); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + if (address->family != AF_INET6) + return 0; + + if (!FLAGS_SET(address->flags, IFA_F_TENTATIVE)) + return 0; + + log_link_info(link, "Address %s with tentative flag is removed, maybe a duplicated address is assigned on another node or link?", + IN6_ADDR_TO_STRING(&address->in_addr.in6)); + + /* Reset the address state, as the object may be reused in the below. */ + address->state = 0; + + switch (address->source) { + case NETWORK_CONFIG_SOURCE_STATIC: + r = link_reconfigure_radv_address(address, link); + break; + case NETWORK_CONFIG_SOURCE_DHCP_PD: + r = dhcp_pd_reconfigure_address(address, link); + break; + case NETWORK_CONFIG_SOURCE_NDISC: + r = ndisc_reconfigure_address(address, link); + break; + default: + r = 0; + } + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure an alternative address: %m"); + + return 0; +} + +static int address_drop(Address *in, bool removed_by_us) { + _cleanup_(address_unrefp) Address *address = address_ref(ASSERT_PTR(in)); + Link *link = ASSERT_PTR(address->link); int r; r = address_set_masquerade(address, /* add = */ false); @@ -778,7 +857,22 @@ static int address_drop(Address *address) { address_del_netlabel(address); - address_free(address); + /* FIXME: if the IPv6LL address is dropped, stop DHCPv6, NDISC, RADV. */ + if (address->family == AF_INET6 && + in6_addr_equal(&address->in_addr.in6, &link->ipv6ll_address)) + link->ipv6ll_address = (const struct in6_addr) {}; + + ipv4acd_detach(link, address); + + address_detach(address); + + if (!removed_by_us) { + r = address_removed_maybe_kernel_dad(link, address); + if (r < 0) { + link_enter_failed(link); + return r; + } + } link_update_operstate(link, /* also_update_master = */ true); link_check_ready(link); @@ -907,7 +1001,7 @@ int link_get_address(Link *link, int family, const union in_addr_union *address, * arbitrary prefixlen will be returned. */ if (family == AF_INET6 || prefixlen != 0) { - _cleanup_(address_freep) Address *tmp = NULL; + _cleanup_(address_unrefp) Address *tmp = NULL; /* In this case, we can use address_get(). */ @@ -975,7 +1069,7 @@ int manager_get_address(Manager *manager, int family, const union in_addr_union return -ENOENT; } -bool manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { +bool manager_has_address(Manager *manager, int family, const union in_addr_union *address) { Address *a; assert(manager); @@ -985,7 +1079,7 @@ bool manager_has_address(Manager *manager, int family, const union in_addr_union if (manager_get_address(manager, family, address, 0, &a) < 0) return false; - return check_ready ? address_is_ready(a) : (address_exists(a) && address_lifetime_is_valid(a)); + return address_is_ready(a); } const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) { @@ -1068,36 +1162,52 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag return 0; } -static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) { int r; assert(m); - assert(link); + assert(rreq); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + Link *link = ASSERT_PTR(rreq->link); + Address *address = ASSERT_PTR(rreq->userdata); + + if (link->state == LINK_STATE_LINGER) return 0; r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EADDRNOTAVAIL) - log_link_message_warning_errno(link, m, r, "Could not drop address"); + if (r < 0) { + log_link_message_full_errno(link, m, + (r == -EADDRNOTAVAIL || !address->link) ? LOG_DEBUG : LOG_WARNING, + r, "Could not drop address"); + + if (address->link) { + /* If the address cannot be removed, then assume the address is already removed. */ + log_address_debug(address, "Forgetting", link); + + Request *req; + if (address_get_request(link, address, &req) >= 0) + address_enter_removed(req->userdata); + + (void) address_drop(address, /* removed_by_us = */ true); + } + } return 1; } -int address_remove(Address *address) { +int address_remove(Address *address, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - Request *req; - Link *link; int r; assert(address); assert(IN_SET(address->family, AF_INET, AF_INET6)); - assert(address->link); - assert(address->link->ifindex > 0); - assert(address->link->manager); - assert(address->link->manager->rtnl); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); - link = address->link; + /* If the address is remembered, use the remembered object. */ + (void) address_get(link, address, &address); log_address_debug(address, "Removing", link); @@ -1110,17 +1220,11 @@ int address_remove(Address *address) { if (r < 0) return log_link_warning_errno(link, r, "Could not set netlink attributes: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, m, - address_remove_handler, - link_netlink_destroy_callback, link); + r = link_remove_request_add(link, address, address, link->manager->rtnl, m, address_remove_handler); if (r < 0) - return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); + return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m"); address_enter_removing(address); - if (address_get_request(link, address, &req) >= 0) - address_enter_removing(req->userdata); /* The operational state is determined by address state and carrier state. Hence, if we remove * an address, the operational state may be changed. */ @@ -1128,22 +1232,38 @@ int address_remove(Address *address) { return 0; } -int address_remove_and_drop(Address *address) { - if (!address) - return 0; +int address_remove_and_cancel(Address *address, Link *link) { + _cleanup_(request_unrefp) Request *req = NULL; + bool waiting = false; + + assert(address); + assert(link); + assert(link->manager); - address_cancel_request(address); + /* If the address is remembered by the link, then use the remembered object. */ + (void) address_get(link, address, &address); - if (address_exists(address)) - return address_remove(address); + /* Cancel the request for the address. If the request is already called but we have not received the + * notification about the request, then explicitly remove the address. */ + if (address_get_request(link, address, &req) >= 0) { + request_ref(req); /* avoid the request freed by request_detach() */ + waiting = req->waiting_reply; + request_detach(req); + address_cancel_requesting(address); + } + + /* If we know the address will come or already exists, remove it. */ + if (waiting || (address->link && address_exists(address))) + return address_remove(address, link); - return address_drop(address); + return 0; } bool link_address_is_dynamic(const Link *link, const Address *address) { Route *route; assert(link); + assert(link->manager); assert(address); if (address->lifetime_preferred_usec != USEC_INFINITY) @@ -1152,7 +1272,7 @@ 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) { + SET_FOREACH(route, link->manager->routes) { if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; @@ -1163,6 +1283,9 @@ bool link_address_is_dynamic(const Link *link, const Address *address) { if (route->protocol != RTPROT_DHCP) continue; + if (route->nexthop.ifindex != link->ifindex) + continue; + if (address->family != route->family) continue; @@ -1200,10 +1323,9 @@ int link_drop_ipv6ll_addresses(Link *link) { return r; for (sd_netlink_message *addr = reply; addr; addr = sd_netlink_message_next(addr)) { - _cleanup_(address_freep) Address *a = NULL; + _cleanup_(address_unrefp) 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 @@ -1249,15 +1371,7 @@ int link_drop_ipv6ll_addresses(Link *link) { a->prefixlen = prefixlen; a->flags = flags; - 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); + r = address_remove(a, link); if (r < 0) return r; } @@ -1317,28 +1431,29 @@ int link_drop_foreign_addresses(Link *link) { if (!address_is_marked(address)) continue; - RET_GATHER(r, address_remove(address)); + RET_GATHER(r, address_remove(address, link)); } return r; } -int link_drop_managed_addresses(Link *link) { +int link_drop_static_addresses(Link *link) { Address *address; int r = 0; assert(link); SET_FOREACH(address, link->addresses) { - /* Do not touch addresses managed by kernel or other tools. */ - if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN) + /* Remove only static addresses here. Dynamic addresses will be removed e.g. on lease + * expiration or stopping the DHCP client. */ + if (address->source != NETWORK_CONFIG_SOURCE_STATIC) continue; /* Ignore addresses not assigned yet or already removing. */ if (!address_exists(address)) continue; - RET_GATHER(r, address_remove(address)); + RET_GATHER(r, address_remove(address, link)); } return r; @@ -1353,43 +1468,6 @@ void link_foreignize_addresses(Link *link) { address->source = NETWORK_CONFIG_SOURCE_FOREIGN; } -static int address_acquire(Link *link, const Address *original, Address **ret) { - _cleanup_(address_freep) Address *na = NULL; - union in_addr_union in_addr; - int r; - - assert(link); - assert(original); - assert(ret); - - /* Something useful was configured? just use it */ - if (in_addr_is_set(original->family, &original->in_addr)) - return address_dup(original, ret); - - /* The address is configured to be 0.0.0.0 or [::] by the user? - * Then let's acquire something more useful from the pool. */ - r = address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); - if (r < 0) - return r; - if (r == 0) - return -EBUSY; - - /* Pick first address in range for ourselves. */ - if (original->family == AF_INET) - in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1); - else if (original->family == AF_INET6) - in_addr.in6.s6_addr[15] |= 1; - - r = address_dup(original, &na); - if (r < 0) - return r; - - na->in_addr = in_addr; - - *ret = TAKE_PTR(na); - return 0; -} - int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { int r; @@ -1462,25 +1540,75 @@ static int address_configure(const Address *address, const struct ifa_cacheinfo return request_call_netlink_async(link->manager->rtnl, m, req); } -static bool address_is_ready_to_configure(Link *link, const Address *address) { +static int address_acquire(Link *link, const Address *address, union in_addr_union *ret) { + union in_addr_union a; + int r; + assert(link); assert(address); + assert(ret); - if (!link_is_ready_to_configure(link, false)) - return false; + r = address_acquire_from_dhcp_server_leases_file(link, address, ret); + if (!IN_SET(r, -ENOENT, -ENXIO, -EINVAL)) + return r; - if (!ipv4acd_bound(link, address)) - return false; + r = address_pool_acquire(link->manager, address->family, address->prefixlen, &a); + if (r < 0) + return r; + if (r == 0) + return -EBUSY; - /* Refuse adding more than the limit */ - if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return false; + /* Pick first address in range for ourselves. */ + if (address->family == AF_INET) + a.in.s_addr |= htobe32(1); + else if (address->family == AF_INET6) + a.in6.s6_addr[15] |= 1; + else + assert_not_reached(); - return true; + *ret = a; + return 0; +} + +static int address_requeue_request(Request *req, Link *link, const Address *address) { + int r; + + assert(req); + assert(link); + assert(link->manager); + assert(link->network); + assert(address); + + /* Something useful was configured? just use it */ + if (in_addr_is_set(address->family, &address->in_addr)) + return 0; + + /* The address is configured to be 0.0.0.0 or [::] by the user? + * Then let's acquire something more useful. */ + union in_addr_union a; + r = address_acquire(link, address, &a); + if (r < 0) + return r; + + _cleanup_(address_unrefp) Address *tmp = NULL; + r = address_dup(address, &tmp); + if (r < 0) + return r; + + tmp->in_addr = a; + + r = link_requeue_request(link, req, tmp, NULL); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; /* Already queued?? Strange... */ + + TAKE_PTR(tmp); + return 1; /* A new request is queued. it is not necessary to process this request anymore. */ } static int address_process_request(Request *req, Link *link, Address *address) { - struct Address *existing; + Address *existing; struct ifa_cacheinfo c; int r; @@ -1488,7 +1616,26 @@ static int address_process_request(Request *req, Link *link, Address *address) { assert(link); assert(address); - if (!address_is_ready_to_configure(link, address)) + if (!link_is_ready_to_configure(link, false)) + return 0; + + /* Refuse adding more than the limit */ + if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) + return 0; + + r = address_requeue_request(req, link, address); + if (r == -EBUSY) + return 0; + if (r != 0) + return r; + + address_set_broadcast(address, link); + + r = ipv4acd_configure(link, address); + if (r < 0) + return r; + + if (!ipv4acd_bound(link, address)) return 0; address_set_cinfo(link->manager, address, &c); @@ -1521,7 +1668,7 @@ int link_request_address( address_netlink_handler_t netlink_handler, Request **ret) { - _cleanup_(address_freep) Address *tmp = NULL; + _cleanup_(address_unrefp) Address *tmp = NULL; Address *existing = NULL; int r; @@ -1533,22 +1680,14 @@ int link_request_address( /* The requested address is outdated. Let's ignore the request. */ return 0; - if (address_get(link, address, &existing) < 0) { - if (address_get_request(link, address, NULL) >= 0) - return 0; /* already requested, skipping. */ - - r = address_acquire(link, address, &tmp); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m"); + if (address_get_request(link, address, NULL) >= 0) + return 0; /* already requested, skipping. */ - /* Consider address tentative until we get the real flags from the kernel */ - tmp->flags |= IFA_F_TENTATIVE; - - } else { - r = address_dup(address, &tmp); - if (r < 0) - return log_oom(); + r = address_dup(address, &tmp); + if (r < 0) + return log_oom(); + if (address_get(link, address, &existing) >= 0) { /* Copy already assigned address when it is requested as a null address. */ if (address_is_static_null(address)) tmp->in_addr = existing->in_addr; @@ -1557,16 +1696,10 @@ int link_request_address( tmp->state = existing->state; } - address_set_broadcast(tmp, link); - - r = ipv4acd_configure(link, tmp); - if (r < 0) - return r; - log_address_debug(tmp, "Requesting", link); r = link_queue_request_safe(link, REQUEST_TYPE_ADDRESS, tmp, - address_free, + address_unref, address_hash_func, address_compare_func, address_process_request, @@ -1641,29 +1774,8 @@ int link_request_static_addresses(Link *link) { 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, - .userdata = address, - .hash_func = (hash_func_t) address_hash_func, - .compare_func = (compare_func_t) address_compare_func, - }; - - request_detach(address->link->manager, &req); - address_cancel_requesting(address); -} - int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { - _cleanup_(address_freep) Address *tmp = NULL; + _cleanup_(address_unrefp) Address *tmp = NULL; struct ifa_cacheinfo cinfo; Link *link; uint16_t type; @@ -1786,9 +1898,11 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (type == RTM_DELADDR) { if (address) { + bool removed_by_us = FLAGS_SET(address->state, NETWORK_CONFIG_STATE_REMOVING); + address_enter_removed(address); log_address_debug(address, "Forgetting removed", link); - (void) address_drop(address); + (void) address_drop(address, removed_by_us); } else log_address_debug(tmp, "Kernel removed unknown", link); @@ -1800,13 +1914,13 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (!address) { /* If we did not know the address, then save it. */ - r = address_add(link, tmp); + r = address_attach(link, tmp); if (r < 0) { log_link_warning_errno(link, r, "Failed to save received address %s, ignoring: %m", IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen)); return 0; } - address = TAKE_PTR(tmp); + address = tmp; is_new = true; @@ -1827,6 +1941,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, (void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context); address->requested_as_null = a->requested_as_null; address->callback = a->callback; + + ipv6_token_ref(a->token); + ipv6_token_unref(address->token); + address->token = a->token; } /* Then, update miscellaneous info. */ @@ -1906,7 +2024,7 @@ int config_parse_broadcast( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; union in_addr_union u; int r; @@ -1983,7 +2101,7 @@ int config_parse_address( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; union in_addr_union buffer; unsigned char prefixlen; int r, f; @@ -1994,10 +2112,16 @@ int config_parse_address( assert(rvalue); assert(data); - if (streq(section, "Network")) + if (streq(section, "Network")) { + if (isempty(rvalue)) { + /* If an empty string specified in [Network] section, clear previously assigned addresses. */ + network->addresses_by_section = ordered_hashmap_free(network->addresses_by_section); + return 0; + } + /* we are not in an Address section, so use line number instead. */ r = address_new_static(network, filename, line, &n); - else + } else r = address_new_static(network, filename, section_line, &n); if (r == -ENOMEM) return log_oom(); @@ -2064,7 +2188,7 @@ int config_parse_label( void *data, void *userdata) { - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; Network *network = userdata; int r; @@ -2116,7 +2240,7 @@ int config_parse_lifetime( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; usec_t k; int r; @@ -2165,7 +2289,7 @@ int config_parse_address_flags( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -2212,7 +2336,7 @@ int config_parse_address_scope( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -2256,7 +2380,7 @@ int config_parse_address_route_metric( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -2298,7 +2422,7 @@ int config_parse_duplicate_address_detection( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -2352,7 +2476,7 @@ int config_parse_address_netlabel( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -2494,8 +2618,8 @@ int network_drop_invalid_addresses(Network *network) { if (address_section_verify(address) < 0) { /* Drop invalid [Address] sections or Address= settings in [Network]. - * Note that address_free() will drop the address from addresses_by_section. */ - address_free(address); + * Note that address_detach() will drop the address from addresses_by_section. */ + address_detach(address); continue; } @@ -2508,12 +2632,13 @@ int network_drop_invalid_addresses(Network *network) { IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen), address->section->line, dup->section->line, dup->section->line); - /* address_free() will drop the address from addresses_by_section. */ - address_free(dup); + + /* address_detach() will drop the address from addresses_by_section. */ + address_detach(dup); } - /* Use address_hash_ops, instead of address_hash_ops_free. Otherwise, the Address objects - * will be freed. */ + /* Use address_hash_ops, instead of address_hash_ops_detach. Otherwise, the Address objects + * will be detached. */ r = set_ensure_put(&addresses, &address_hash_ops, address); if (r < 0) return log_oom(); @@ -2540,7 +2665,7 @@ int config_parse_address_ip_nft_set( void *userdata) { Network *network = userdata; - _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + _cleanup_(address_unref_or_set_invalidp) Address *n = NULL; int r; assert(filename); |