diff --git a/man/systemd.network.xml b/man/systemd.network.xml index e75d612c05..c39b364319 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -408,36 +408,48 @@ IPv6Token= - Specifies an optional address generation mode and a required IPv6 address. If - the mode is present, the two parts must be separated with a colon - mode:address. The - address generation mode may be either prefixstable or - static. If not specified, static is assumed. - - When the mode is set to static, or unspecified, the lower bits of - the supplied address are combined with the upper bits of a prefix received in a Router Advertisement - message to form a complete address. Note that if multiple prefixes are received in an RA message, or in - multiple RA messages, addresses will be formed from each of them using the supplied address. This - mode implements SLAAC but uses a static interface identifier instead of an identifier generated - using the EUI-64 algorithm. Because the interface identifier is static, if Duplicate Address Detection - detects that the computed address is a duplicate (in use by another node on the link), then this - mode will fail to provide an address for that prefix. - - When the mode is set to prefixstable the RFC 7217 algorithm for generating - interface identifiers will be used, but only when a prefix received in an RA message matches the supplied address. - See RFC 7217. Prefix matching will be attempted - against each prefixstable IPv6Token variable provided in the configuration; if a received - prefix does not match any of the provided addresses, then the EUI-64 algorithm will be used to form - an interface identifier for that prefix. This mode is also SLAAC, but with a potentially stable interface - identifier which does not directly map to the interface's hardware address. + Specifies an optional address generation mode for the Stateless Address + Autoconfiguration (SLAAC). Supported modes are prefixstable and + static. - Note that the prefixstable algorithm includes both the interface's name and - MAC address in the hash used to compute the interface identifier, so if either of those are changed the resulting - interface identifier (and address) will change, even if the prefix received in the RA message has not changed. + When the mode is set to static, an IPv6 address must be + specified after a colon (:), and the lower bits of the supplied + address are combined with the upper bits of a prefix received in a Router Advertisement + (RA) message to form a complete address. Note that if multiple prefixes are received in an + RA message, or in multiple RA messages, addresses will be formed from each of them using + the supplied address. This mode implements SLAAC but uses a static interface identifier + instead of an identifier generated by using the EUI-64 algorithm. Because the interface + identifier is static, if Duplicate Address Detection detects that the computed address is a + duplicate (in use by another node on the link), then this mode will fail to provide an + address for that prefix. If an IPv6 address without mode is specified, then + static mode is assumed. - Note that if multiple prefixstable IPv6Token variables are supplied with addresses that - match a prefix received in an RA message, only the first one will be used to generate addresses. - + When the mode is set to prefixstable the + RFC 7217 algorithm for generating + interface identifiers will be used. This mode can optionally take an IPv6 address separated + with a colon (:). If an IPv6 address is specified, then an interface + identifier is generated only when a prefix received in an RA message matches the supplied + address. + + If no address generation mode is specified (which is the default), or a received + prefix does not match any of the addresses provided in prefixstable + mode, then the EUI-64 algorithm will be used to form an interface identifier for that + prefix. This mode is also SLAAC, but with a potentially stable interface identifier which + does not directly map to the interface's hardware address. + + Note that the prefixstable algorithm uses both the interface + name and MAC address as input to the hash to compute the interface identifier, so if either + of those are changed the resulting interface identifier (and address) will change, even if + the prefix received in the RA message has not changed. + + This setting can be specified multiple times. If an empty string is assigned, then + the all previous assignments are cleared. + + Examples: + IPv6Token=::1a:2b:3c:4d +IPv6Token=static:::1a:2b:3c:4d +IPv6Token=prefixstable +IPv6Token=prefixstable:2002:da8:1:: diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 349f0548af..45c1aaa051 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -384,14 +384,14 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, _cleanup_free_ struct in6_addr *new_address = NULL; if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE - && IN6_ARE_ADDR_EQUAL(&j->prefix, address)) { + && (IN6_IS_ADDR_UNSPECIFIED(&j->prefix) || IN6_ARE_ADDR_EQUAL(&j->prefix, address))) { /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop does not actually attempt Duplicate Address Detection; the counter will be incremented only when the address generation algorithm produces an invalid address, and the loop may exit with an address which ends up being unusable due to duplication on the link. */ for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) { - r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address); + r = make_stableprivate_address(link, address, prefixlen, j->dad_counter, &new_address); if (r < 0) return r; if (r > 0) @@ -1176,30 +1176,43 @@ int config_parse_address_generation_type( if (r < 0) return log_oom(); - if ((p = startswith(rvalue, "static:"))) - token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC; - else if ((p = startswith(rvalue, "prefixstable:"))) + if ((p = startswith(rvalue, "prefixstable"))) { token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE; - else { + if (*p == ':') + p++; + else if (*p == '\0') + p = NULL; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid IPv6 token mode in %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + } else { token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC; - p = rvalue; + p = startswith(rvalue, "static:"); + if (!p) + p = rvalue; } - r = in_addr_from_string(AF_INET6, p, &buffer); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue); - return 0; + if (p) { + r = in_addr_from_string(AF_INET6, p, &buffer); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse IP address in %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (token->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC && + in_addr_is_null(AF_INET6, &buffer)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "IPv6 address in %s= cannot be the ANY address, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + token->prefix = buffer.in6; } - if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue); - return 0; - } - - token->prefix = buffer.in6; - r = ordered_set_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops); if (r < 0) return log_oom();