Compare commits

...

10 Commits

Author SHA1 Message Date
Yu Watanabe 1d6cfd25a2
Merge pull request #14833 from kpfleming/multiple-ipv6token-addresses
Support multiple IPv6Token 'static' addreses on an interface
2020-02-12 02:10:44 +09:00
Evgeny Vereshchagin 4ac78b1a37
Merge pull request #14849 from mrc0mmand/lgtm-override-TMPDIR
lgtm: set TMPDIR to /var/tmp
2020-02-11 19:15:48 +03:00
Frantisek Sumsal 7ad1f04398 lgtm: use the system version of meson
LGTM was upgraded to Ubuntu Eoan, so the meson there is pretty new.
Also, the pip command didn't work anyway, since it didn't attempt to
update the previously installed meson.
2020-02-10 19:49:42 +01:00
Frantisek Sumsal 65be7042a8 lgtm: set TMPDIR to /var/tmp
`/tmp` has `noexec` set in the LGTM infrastructure which breaks meson's
compilation checks. Let's use `/var/tmp` for now, until the issue is
properly resolved

See: https://discuss.lgtm.com/t/meson-fails-to-determine-sizes-of-basic-data-types/2639
2020-02-10 18:44:55 +01:00
Kevin P. Fleming c24c83dc67 network: Allow multiple IPv6Token 'static' items to generate addresses
This patch allows multiple addresses using 'static' IPv6Tokens to
be generated for a single network interface.
2020-02-10 18:40:57 +09:00
Kevin P. Fleming 8a98f11ed0 network: Make address_hash_ops available outside of networkd-address.c
In order to allow other parts of systemd-networkd to use sets/hashmaps
of Address objects, the address_hash_ops structure needs to be made
available to them.
2020-02-10 15:37:37 +09:00
Kevin P. Fleming 0ddad04eda network: Document the lack of actual DAD usage in prefixstable algorithm
The RFC 7217 (prefixstable) algorithm can use Duplicate Address
Detection to produce multiple candidate addresses, but the implementation
here does not currently employ that mechanism.
2020-02-10 15:37:30 +09:00
Kevin P. Fleming 8dcce054e3 network: Rewrite IPv6Token documentation for new modes
This patch provides new documentation for IPv6Token,
reflecting the new modes (and the existing mode), and documents
various caveats users should be aware of when using these
modes.
2020-02-10 15:37:22 +09:00
Kevin P. Fleming 53f8cced45 network: Correct typo and naming in error message
This error message will be emitted when any form of SLAAC address
generation fails, not just 'prefix stable', so the message should
only refer to SLAAC.
2020-02-10 15:37:13 +09:00
Kevin P. Fleming 87f9d6ea8e network: Improve variable name for address generation
The logic which can produce an IPv6 address using SLAAC produces an
address, not a prefix, so the boolean variable used to detect whether
it succeeded should reflect that.
2020-02-10 15:36:46 +09:00
7 changed files with 163 additions and 69 deletions

View File

@ -1,17 +1,24 @@
---
# vi: ts=2 sw=2 et:
extraction:
cpp:
prepare:
packages:
- python3-pip
- python3-setuptools
- python3-wheel
- libpwquality-dev
- libfdisk-dev
- libp11-kit-dev
- libssl-dev
after_prepare:
- pip3 install meson
- export PATH="$HOME/.local/bin/:$PATH"
before_index:
# /tmp has `noexec` set in the LGTM infra which breaks meson's
# compilation checks. Let's temporarily override TMPDIR to /var/tmp which
# should work as expected
# See: https://discuss.lgtm.com/t/meson-fails-to-determine-sizes-of-basic-data-types/2639
- export TMPDIR=/var/tmp
index:
build_command:
- meson build
- ninja -C build
python:
python_setup:
version: 3

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