network: warn when NDISC and DHCPv6 provide the same address

With some router, the address in NDISC generated with EUI-64 conflicts
with an address provided by DHCPv6.

Prompted by #17831.
This commit is contained in:
Yu Watanabe 2020-12-08 13:35:48 +09:00
parent 450fa34bd0
commit b432080dc8
1 changed files with 67 additions and 6 deletions

View File

@ -969,6 +969,70 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
return 1;
}
static void log_dhcp6_address(Link *link, const Address *address, char **ret) {
char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX];
const char *valid_str = NULL, *preferred_str = NULL;
_cleanup_free_ char *buffer = NULL;
bool by_ndisc = false;
Address *existing;
NDiscAddress *na;
int log_level, r;
assert(link);
assert(address);
(void) in_addr_to_string(address->family, &address->in_addr, &buffer);
if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
address->cinfo.ifa_valid * USEC_PER_SEC,
USEC_PER_SEC);
if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
preferred_str = format_timespan(preferred_buf, FORMAT_TIMESPAN_MAX,
address->cinfo.ifa_prefered * USEC_PER_SEC,
USEC_PER_SEC);
r = address_get(link, address, &existing);
if (r < 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;
}
log_link_warning(link, "DHCPv6 address %s/%u (valid %s%s, preferred %s%s) conflicts the existing address %s/%u%s.",
strnull(buffer), address->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str),
preferred_str ? "for " : "forever", strempty(preferred_str),
strnull(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;
simple_log:
log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s%s, preferred %s%s)",
strnull(buffer), address->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str),
preferred_str ? "for " : "forever", strempty(preferred_str));
finalize:
if (ret)
*ret = TAKE_PTR(buffer);
}
static int dhcp6_update_address(
Link *link,
const struct in6_addr *ip6_addr,
@ -991,22 +1055,19 @@ static int dhcp6_update_address(
addr->cinfo.ifa_prefered = lifetime_preferred;
addr->cinfo.ifa_valid = lifetime_valid;
(void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
log_link_full(link, set_contains(link->dhcp6_addresses, addr) ? LOG_DEBUG : LOG_INFO,
"DHCPv6 address %s/%u timeout preferred %d valid %d",
strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
log_dhcp6_address(link, addr, &buffer);
r = address_configure(addr, link, dhcp6_address_handler, true, &ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s/%u: %m",
strna(buffer), addr->prefixlen);
strnull(buffer), addr->prefixlen);
link->dhcp6_address_messages++;
r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s/%u: %m",
strna(buffer), addr->prefixlen);
strnull(buffer), addr->prefixlen);
(void) set_remove(link->dhcp6_addresses_old, ret);