mirror of
https://github.com/systemd/systemd.git
synced 2024-10-28 20:25:38 +03:00
Merge pull request #16618 from yuwata/network-ipv6token-prefixstable
network: make prefixstable mode of IPv6Token= can be applied any received prefixes
This commit is contained in:
commit
a9aa8deb29
@ -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>
|
||||
|
@ -586,14 +586,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)
|
||||
@ -1396,30 +1396,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();
|
||||
|
@ -3,4 +3,4 @@ Name=veth99
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=true
|
||||
IPv6Token=static:::1a:2b:3c:4d
|
||||
IPv6Token=prefixstable
|
@ -1,9 +0,0 @@
|
||||
[Match]
|
||||
Name=veth99
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=true
|
||||
IPv6Token=::1a:2b:3c:4d
|
||||
IPv6Token=::1a:2b:3c:4d
|
||||
IPv6Token=::1a:2b:3c:4d
|
||||
IPv6Token=::fa:de:ca:fe
|
@ -4,3 +4,7 @@ Name=veth99
|
||||
[Network]
|
||||
IPv6AcceptRA=true
|
||||
IPv6Token=::1a:2b:3c:4d
|
||||
IPv6Token=static:::fa:de:ca:fe
|
||||
IPv6Token=::1a:2b:3c:4d
|
||||
IPv6Token=static:::1a:2b:3c:4d
|
||||
IPv6Token=::fa:de:ca:fe
|
||||
|
@ -12,3 +12,8 @@ DNSLifetimeSec=1min
|
||||
Prefix=2002:da8:1:0::/64
|
||||
PreferredLifetimeSec=1000s
|
||||
ValidLifetimeSec=2100s
|
||||
|
||||
[IPv6Prefix]
|
||||
Prefix=2002:da8:2:0::/64
|
||||
PreferredLifetimeSec=1000s
|
||||
ValidLifetimeSec=2100s
|
||||
|
@ -3088,9 +3088,8 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||
'ipv6-prefix.network',
|
||||
'ipv6-prefix-veth.network',
|
||||
'ipv6-prefix-veth-token-static.network',
|
||||
'ipv6-prefix-veth-token-static-explicit.network',
|
||||
'ipv6-prefix-veth-token-static-multiple.network',
|
||||
'ipv6-prefix-veth-token-prefixstable.network']
|
||||
'ipv6-prefix-veth-token-prefixstable.network',
|
||||
'ipv6-prefix-veth-token-prefixstable-without-address.network']
|
||||
|
||||
def setUp(self):
|
||||
remove_links(self.links)
|
||||
@ -3123,25 +3122,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
|
||||
|
||||
def test_ipv6_token_static_explicit(self):
|
||||
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-explicit.network')
|
||||
start_networkd()
|
||||
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
|
||||
|
||||
def test_ipv6_token_static_multiple(self):
|
||||
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-multiple.network')
|
||||
start_networkd()
|
||||
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
|
||||
self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
|
||||
self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
|
||||
self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
|
||||
|
||||
def test_ipv6_token_prefixstable(self):
|
||||
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable.network')
|
||||
@ -3151,6 +3134,17 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, '2002:da8:1:0')
|
||||
self.assertRegex(output, '2002:da8:2:0.*78:9abc') # EUI
|
||||
|
||||
def test_ipv6_token_prefixstable_without_address(self):
|
||||
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable-without-address.network')
|
||||
start_networkd()
|
||||
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, '2002:da8:1:0')
|
||||
self.assertRegex(output, '2002:da8:2:0')
|
||||
|
||||
class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
|
||||
links = ['veth99']
|
||||
|
Loading…
Reference in New Issue
Block a user