diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index c6de89a7f0..da458dcb64 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -95,8 +95,10 @@ void address_free(Address *address) { UINT_TO_PTR(address->section)); } - if (address->link) + if (address->link) { set_remove(address->link->addresses, address); + set_remove(address->link->addresses_foreign, address); + } free(address); } @@ -225,13 +227,17 @@ static int address_establish(Address *address, Link *link) { return 0; } -int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { +static int address_add_internal(Link *link, Set **addresses, + int family, + const union in_addr_union *in_addr, + unsigned char prefixlen, + Address **ret) { _cleanup_address_free_ Address *address = NULL; int r; assert(link); + assert(addresses); assert(in_addr); - assert(ret); r = address_new(&address); if (r < 0) @@ -241,22 +247,32 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi address->in_addr = *in_addr; address->prefixlen = prefixlen; - r = set_ensure_allocated(&link->addresses, &address_hash_ops); + r = set_ensure_allocated(addresses, &address_hash_ops); if (r < 0) return r; - r = set_put(link->addresses, address); + r = set_put(*addresses, address); if (r < 0) return r; address->link = link; - *ret = address; + if (ret) + *ret = address; + address = NULL; return 0; } +int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret); +} + +static int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + return address_add_internal(link, &link->addresses, family, in_addr, prefixlen, ret); +} + static int address_release(Address *address) { int r; @@ -286,6 +302,7 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s ready = address_is_ready(address); + address->added = true; address->flags = flags; address->scope = scope; address->cinfo = *cinfo; @@ -326,8 +343,11 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi address.prefixlen = prefixlen; existing = set_get(link->addresses, &address); - if (!existing) - return -ENOENT; + if (!existing) { + existing = set_get(link->addresses_foreign, &address); + if (!existing) + return -ENOENT; + } *ret = existing; @@ -519,6 +539,12 @@ int address_configure(Address *address, Link *link, sd_netlink_message_handler_t link_ref(link); + r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); + if (r < 0) { + address_release(address); + return log_error_errno(r, "Could not add address: %m"); + } + return 0; } @@ -702,5 +728,5 @@ int config_parse_label(const char *unit, bool address_is_ready(const Address *a) { assert(a); - return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); + return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index cb29acc7be..fd309bebb6 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -52,7 +52,8 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool ip_masquerade_done; + bool added:1; + bool ip_masquerade_done:1; LIST_FIELDS(Address, addresses); }; @@ -60,7 +61,7 @@ struct Address { int address_new_static(Network *network, unsigned section, Address **ret); int address_new(Address **ret); void address_free(Address *address); -int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); +int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo); int address_drop(Address *address); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1ac76c4255..c0f27aaca8 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -297,6 +297,11 @@ static void link_free(Link *link) { set_free(link->addresses); + while (!set_isempty(link->addresses_foreign)) + address_free(set_first(link->addresses_foreign)); + + set_free(link->addresses_foreign); + while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); address_free(address); @@ -508,7 +513,9 @@ void link_check_ready(Link *link) { Iterator i; assert(link); - assert(link->network); + + if (!link->network) + return; if (!link->static_configured) return; @@ -2324,6 +2331,15 @@ static void link_update_operstate(Link *link) { scope = address->scope; } + /* for operstate we also take foreign addresses into account */ + SET_FOREACH(address, link->addresses_foreign, i) { + if (!address_is_ready(address)) + continue; + + if (address->scope < scope) + scope = address->scope; + } + if (scope < RT_SCOPE_SITE) /* universally accessible addresses found */ operstate = LINK_OPERSTATE_ROUTABLE; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a94bb2f714..90ad08a306 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -84,6 +84,7 @@ struct Link { unsigned enslaving; Set *addresses; + Set *addresses_foreign; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 9a7e71fce8..2b83ee81ed 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -400,7 +400,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (address) log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); else { - r = address_add(link, family, &in_addr, prefixlen, &address); + /* An address appeared that we did not request */ + r = address_add_foreign(link, family, &in_addr, prefixlen, &address); if (r < 0) { log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); return 0;