1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-23 21:35:11 +03:00

Merge pull request #14833 from kpfleming/multiple-ipv6token-addresses

Support multiple IPv6Token 'static' addreses on an interface
This commit is contained in:
Yu Watanabe 2020-02-12 02:10:44 +09:00 committed by GitHub
commit 1d6cfd25a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 63 deletions

View File

@ -308,18 +308,35 @@
<varlistentry>
<term><varname>IPv6Token=</varname></term>
<listitem>
<para>Specifies an optional address generation mechanism and an optional address prefix. If
the mechanism is present, the two parts must be separated with a colon
<literal><replaceable>type</replaceable>:<replaceable>prefix</replaceable></literal>. The
address generation mechanism may be either <constant>prefixstable</constant> or
<constant>static</constant>. If not specified, <constant>static</constant> is assumed. When
set to <literal>prefixstable</literal> a method for generating IPv6 Interface Identifiers to
be used with IPv6 Stateless Address Autocon figuration (SLAAC). See
<ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink>. When IPv6 address is set,
indicates the 64-bit interface part of SLAAC IPv6 addresses for this link.</para>
<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>Note that the token is only ever used for SLAAC, and not for DHCPv6 addresses, even in
the case DHCP is requested by router advertisement. By default, the token is autogenerated.
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.
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>
</listitem>
</varlistentry>

View File

@ -187,7 +187,7 @@ static int address_compare_func(const Address *a1, const Address *a2) {
}
}
DEFINE_PRIVATE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
bool address_equal(Address *a1, Address *a2) {
if (a1 == a2)

View File

@ -67,6 +67,8 @@ int configure_ipv4_duplicate_address_detection(Link *link, Address *address);
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
extern const struct hash_ops address_hash_ops;
CONFIG_PARSER_PROTOTYPE(config_parse_address);
CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);
CONFIG_PARSER_PROTOTYPE(config_parse_label);

View File

@ -254,62 +254,109 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
return 0;
}
static int ndisc_router_generate_address(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address) {
bool prefix = false;
static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) {
_cleanup_set_free_free_ Set *addresses = NULL;
struct in6_addr addr;
IPv6Token *j;
Iterator i;
int r;
assert(address);
assert(link);
assert(address);
assert(ret);
addresses = set_new(&address_hash_ops);
if (!addresses)
return log_oom();
addr = address->in_addr.in6;
ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i)
ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i) {
bool have_address = false;
_cleanup_(address_freep) Address *new_address = NULL;
r = address_new(&new_address);
if (r < 0)
return log_oom();
*new_address = *address;
if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
&& memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) {
/* 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, &address->in_addr.in6);
r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address->in_addr.in6);
if (r < 0)
return r;
break;
if (stableprivate_address_is_valid(&address->in_addr.in6)) {
prefix = true;
if (stableprivate_address_is_valid(&new_address->in_addr.in6)) {
have_address = true;
break;
}
}
} else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) {
memcpy(((uint8_t *)&address->in_addr.in6) + 8, ((uint8_t *) &j->prefix) + 8, 8);
prefix = true;
break;
memcpy(((uint8_t *)&new_address->in_addr.in6) + 8, ((uint8_t *) &j->prefix) + 8, 8);
have_address = true;
}
/* fallback to eui64 if prefixstable or static do not match */
if (!prefix) {
/* see RFC4291 section 2.5.1 */
address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
address->in_addr.in6.s6_addr[8] ^= 1 << 1;
address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
address->in_addr.in6.s6_addr[11] = 0xff;
address->in_addr.in6.s6_addr[12] = 0xfe;
address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
if (have_address) {
new_address->prefixlen = prefixlen;
new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
new_address->cinfo.ifa_prefered = lifetime_preferred;
r = set_put(addresses, new_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to store address: %m");
TAKE_PTR(new_address);
}
}
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
address->cinfo.ifa_prefered = lifetime_preferred;
/* fall back to EUI-64 if no tokens provided addresses */
if (set_isempty(addresses)) {
_cleanup_(address_freep) Address *new_address = NULL;
r = address_new(&new_address);
if (r < 0)
return log_oom();
*new_address = *address;
/* see RFC4291 section 2.5.1 */
new_address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
new_address->in_addr.in6.s6_addr[8] ^= 1 << 1;
new_address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
new_address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
new_address->in_addr.in6.s6_addr[11] = 0xff;
new_address->in_addr.in6.s6_addr[12] = 0xfe;
new_address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
new_address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
new_address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
new_address->prefixlen = prefixlen;
new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
new_address->cinfo.ifa_prefered = lifetime_preferred;
r = set_put(addresses, new_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to store address: %m");
TAKE_PTR(new_address);
}
*ret = TAKE_PTR(addresses);
return 0;
}
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
_cleanup_set_free_free_ Set *addresses = NULL;
_cleanup_(address_freep) Address *address = NULL;
Address *existing_address;
unsigned prefixlen;
usec_t time_now;
Address *existing_address, *a;
Iterator i;
int r;
assert(link);
@ -344,36 +391,39 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
r = ndisc_router_generate_address(link, prefixlen, lifetime_preferred, address);
r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses);
if (r < 0)
return log_link_error_errno(link, r, "Falied to generate prefix stable address: %m");
return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m");
/* see RFC4862 section 5.5.3.e */
r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
if (r > 0) {
lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
address->cinfo.ifa_valid = lifetime_valid;
else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = lifetime_remaining;
SET_FOREACH(a, addresses, i) {
/* see RFC4862 section 5.5.3.e */
r = address_get(link, a->family, &a->in_addr, a->prefixlen, &existing_address);
if (r > 0) {
lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
a->cinfo.ifa_valid = lifetime_valid;
else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
a->cinfo.ifa_valid = lifetime_remaining;
else
a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
} else if (lifetime_valid > 0)
a->cinfo.ifa_valid = lifetime_valid;
else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
} else if (lifetime_valid > 0)
address->cinfo.ifa_valid = lifetime_valid;
else
return 0; /* see RFC4862 section 5.5.3.d */
return 0; /* see RFC4862 section 5.5.3.d */
if (address->cinfo.ifa_valid == 0)
return 0;
if (a->cinfo.ifa_valid == 0)
continue;
r = address_configure(address, link, ndisc_netlink_address_message_handler, true);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
link_enter_failed(link);
return r;
r = address_configure(a, link, ndisc_netlink_address_message_handler, true);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
link_enter_failed(link);
return r;
}
if (r > 0)
link->ndisc_messages++;
}
if (r > 0)
link->ndisc_messages++;
return 0;
}

View File

@ -0,0 +1,7 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=true
IPv6Token=::1a:2b:3c:4d
IPv6Token=::fa:de:ca:fe

View File

@ -2685,6 +2685,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
'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']
def setUp(self):
@ -2728,6 +2729,16 @@ class NetworkdRATests(unittest.TestCase, Utilities):
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')
def test_ipv6_token_prefixstable(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable.network')
start_networkd()