diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index a7aa166b4b..2aa8c88bb2 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -516,11 +516,9 @@ static int address_compare_func(const Address *a1, const Address *a2) { } } -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 @@ -545,32 +543,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 @@ -578,7 +576,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; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index cc2429094e..350d2f6b38 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -85,6 +85,8 @@ void link_get_address_states( extern const struct hash_ops address_hash_ops; +bool address_can_update(const Address *existing, const Address *requesting); + Address* address_ref(Address *address); Address* address_unref(Address *address); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index eb1420be5c..de4588c93e 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -158,8 +158,7 @@ static int verify_dhcp6_address(Link *link, const Address *address) { } else log_level = LOG_DEBUG; - if (address->prefixlen == existing->prefixlen) - /* Currently, only conflict in prefix length is reported. */ + if (address_can_update(existing, address)) goto simple_log; if (existing->source == NETWORK_CONFIG_SOURCE_NDISC) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f5621e219b..030b4a4339 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -249,8 +249,7 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques return 1; } -static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { - _cleanup_(address_unrefp) Address *address = in; +static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *rt) { struct in6_addr router; bool is_new; int r; @@ -270,7 +269,26 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - is_new = address_get(link, address, NULL) < 0; + Address *existing; + if (address_get_harder(link, address, &existing) < 0) + is_new = true; + else if (address_can_update(existing, address)) + is_new = false; + else if (existing->source == NETWORK_CONFIG_SOURCE_DHCP6) { + /* SLAAC address is preferred over DHCPv6 address. */ + log_link_debug(link, "Conflicting DHCPv6 address %s exists, removing.", + IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen)); + r = address_remove(existing, link); + if (r < 0) + return r; + + is_new = true; + } else { + /* Conflicting static address is configured?? */ + log_link_debug(link, "Conflicting address %s exists, ignoring request.", + IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen)); + return 0; + } r = link_request_address(link, address, &link->ndisc_messages, ndisc_address_handler, NULL); @@ -485,7 +503,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r address->lifetime_valid_usec = lifetime_valid_usec; address->lifetime_preferred_usec = lifetime_preferred_usec; - r = ndisc_request_address(TAKE_PTR(address), link, rt); + r = ndisc_request_address(address, link, rt); if (r < 0) return log_link_warning_errno(link, r, "Could not request SLAAC address: %m"); }