network: make prefixstable mode of IPv6Token= can be applied to any received prefixes

Closes #4625.
This commit is contained in:
Yu Watanabe 2020-07-30 12:12:23 +09:00
parent 6743a1caf4
commit b27caa34f6
2 changed files with 73 additions and 48 deletions

View File

@ -408,36 +408,48 @@
<varlistentry>
<term><varname>IPv6Token=</varname></term>
<listitem>
<para>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
<literal><replaceable>mode</replaceable>:<replaceable>address</replaceable></literal>. The
address generation mode may be either <constant>prefixstable</constant> or
<constant>static</constant>. If not specified, <constant>static</constant> is assumed.
</para>
<para>When the mode is set to <constant>static</constant>, 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.
</para>
<para>When the mode is set to <literal>prefixstable</literal> 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 <ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink>. Prefix matching will be attempted
against each <constant>prefixstable</constant> 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.
<para>Specifies an optional address generation mode for the Stateless Address
Autoconfiguration (SLAAC). Supported modes are <literal>prefixstable</literal> and
<literal>static</literal>.</para>
Note that the <constant>prefixstable</constant> 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.
<para>When the mode is set to <literal>static</literal>, an IPv6 address must be
specified after a colon (<literal>:</literal>), 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
<literal>static</literal> mode is assumed.</para>
Note that if multiple <constant>prefixstable</constant> 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.
</para>
<para>When the mode is set to <literal>prefixstable</literal> the
<ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink> algorithm for generating
interface identifiers will be used. This mode can optionally take an IPv6 address separated
with a colon (<literal>:</literal>). 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.</para>
<para>If no address generation mode is specified (which is the default), or a received
prefix does not match any of the addresses provided in <literal>prefixstable</literal>
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.</para>
<para>Note that the <literal>prefixstable</literal> 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.</para>
<para>This setting can be specified multiple times. If an empty string is assigned, then
the all previous assignments are cleared.</para>
<para>Examples:
<programlisting>IPv6Token=::1a:2b:3c:4d
IPv6Token=static:::1a:2b:3c:4d
IPv6Token=prefixstable
IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -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();