From 5ead5352244530a854d4bbd430c30fa8faa0d30b Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Thu, 27 Feb 2020 12:40:57 +0100 Subject: [PATCH 1/5] network: Introduce method to generate EUI-64 addresses --- src/network/networkd-address.c | 18 ++++++++++++++++++ src/network/networkd-address.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 6244f75201..1567bd7ee9 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -20,6 +20,24 @@ #define ADDRESSES_PER_LINK_MAX 2048U #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U +int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) { + assert(link); + assert(ret); + + /* see RFC4291 section 2.5.1 */ + ret->s6_addr[8] = link->mac.ether_addr_octet[0]; + ret->s6_addr[8] ^= 1 << 1; + ret->s6_addr[9] = link->mac.ether_addr_octet[1]; + ret->s6_addr[10] = link->mac.ether_addr_octet[2]; + ret->s6_addr[11] = 0xff; + ret->s6_addr[12] = 0xfe; + ret->s6_addr[13] = link->mac.ether_addr_octet[3]; + ret->s6_addr[14] = link->mac.ether_addr_octet[4]; + ret->s6_addr[15] = link->mac.ether_addr_octet[5]; + + return 0; +} + int address_new(Address **ret) { _cleanup_(address_freep) Address *address = NULL; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index ad2412c75a..bd0485e0ab 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -66,6 +66,8 @@ bool address_is_ready(const Address *a); int address_section_verify(Address *a); int configure_ipv4_duplicate_address_detection(Link *link, Address *address); +int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret); + DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); extern const struct hash_ops address_hash_ops; From 34332af2bc6f63e5f35c7d71f4bfde993a6bd5f2 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Thu, 27 Feb 2020 13:31:09 +0100 Subject: [PATCH 2/5] network: radv introduce sd_radv_prefix_get_prefix --- src/libsystemd-network/sd-radv.c | 12 ++++++++++++ src/systemd/sd-radv.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 873a2f40f8..e7ffe879a8 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -829,6 +829,18 @@ _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr return 0; } +_public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr, + unsigned char *ret_prefixlen) { + assert_return(p, -EINVAL); + assert_return(ret_in6_addr, -EINVAL); + assert_return(ret_prefixlen, -EINVAL); + + *ret_in6_addr = p->opt.in6_addr; + *ret_prefixlen = p->opt.prefixlen; + + return 0; +} + _public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) { assert_return(p, -EINVAL); diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index f085231934..011e40d8a5 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -74,6 +74,8 @@ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra); int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen); +int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr, + unsigned char *ret_prefixlen); int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink); int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration); From bd6379ec57c5a25b1cb4ae61771c8a12179ebd7a Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Thu, 27 Feb 2020 13:32:43 +0100 Subject: [PATCH 3/5] network: introduce IPv6 prefix assign Expose a boolean flag to automatically add an address from the delegated prefix to the interface --- man/systemd.network.xml | 5 +++ src/network/networkd-link.c | 30 ++++++++++++++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-radv.c | 40 +++++++++++++++++++ src/network/networkd-radv.h | 3 ++ .../fuzz-network-parser/directives.network | 1 + 6 files changed, 80 insertions(+) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 209787bf1b..6c85917881 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2033,6 +2033,11 @@ to 2592000 seconds (30 days). + + Assign= + Takes a boolean. When true, adds an address from the prefix. Default to false. + + diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3dca043d99..ca8f9da280 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1222,6 +1222,7 @@ static int link_set_bridge_fdb(Link *link) { static int link_request_set_addresses(Link *link) { AddressLabel *label; Address *ad; + Prefix *p; int r; assert(link); @@ -1257,6 +1258,35 @@ static int link_request_set_addresses(Link *link) { link->address_messages++; } + if (IN_SET(link->network->router_prefix_delegation, + RADV_PREFIX_DELEGATION_STATIC, + RADV_PREFIX_DELEGATION_BOTH)) + LIST_FOREACH(prefixes, p, link->network->static_prefixes) { + _cleanup_(address_freep) Address *address = NULL; + + if (!p->assign) + continue; + + r = address_new(&address); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate address: %m"); + + r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); + if (r < 0) + return r; + + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return r; + + address->family = AF_INET6; + r = address_configure(address, link, address_handler, true); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set addresses: %m"); + if (r > 0) + link->address_messages++; + } + LIST_FOREACH(labels, label, link->network->address_labels) { r = address_label_configure(label, link, NULL, false); if (r < 0) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 134f1535d6..02be2f01aa 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -245,6 +245,7 @@ IPv6Prefix.OnLink, config_parse_prefix_flags, IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 +IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0 IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0 IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0 CAN.BitRate, config_parse_si_uint64, 0, offsetof(Network, can_bitrate) diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 620939e251..d9267dd805 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -319,6 +319,46 @@ int config_parse_prefix_lifetime(const char *unit, return 0; } +int config_parse_prefix_assign( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + p->assign = r; + p = NULL; + + return 0; +} + int config_parse_route_prefix(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 21b323e83e..8bf697aff0 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -28,6 +28,8 @@ struct Prefix { sd_radv_prefix *radv_prefix; + bool assign; + LIST_FIELDS(Prefix, prefixes); }; @@ -59,6 +61,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_router_preference); CONFIG_PARSER_PROTOTYPE(config_parse_prefix); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime); +CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign); CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns); CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains); CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 2b41239b74..ea872cd7e2 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -186,6 +186,7 @@ OnLink= PreferredLifetimeSec= AddressAutoconfiguration= ValidLifetimeSec= +Assign= [IPv6RoutePrefix] Route= LifetimeSec= From a781ddefe5043c9f0750a125694760a3c43b003a Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Fri, 28 Feb 2020 03:51:28 +0100 Subject: [PATCH 4/5] network: make use of generate_ipv6_eui_64_address --- src/network/networkd-ndisc.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index c45fec5430..90cd0c81ab 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -324,16 +324,10 @@ static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint3 *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]; + r = generate_ipv6_eui_64_address(link, &new_address->in_addr.in6); + if (r < 0) + return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m"); + new_address->prefixlen = prefixlen; new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; new_address->cinfo.ifa_prefered = lifetime_preferred; From 3c874fd79d15c161435582794148c41763bf107f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 1 Mar 2020 00:46:56 +0900 Subject: [PATCH 5/5] test-network: add a test case for [IPv6Prefix] Assign=yes --- test/test-network/conf/ipv6ra-prefix.network | 7 +++++-- test/test-network/systemd-networkd-tests.py | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/test/test-network/conf/ipv6ra-prefix.network b/test/test-network/conf/ipv6ra-prefix.network index 7bb6661362..9dc32cb4da 100644 --- a/test/test-network/conf/ipv6ra-prefix.network +++ b/test/test-network/conf/ipv6ra-prefix.network @@ -4,10 +4,13 @@ Name=veth99 [Network] DHCP=no IPv6PrefixDelegation=yes -Address=2001:db8:0:1::1/64 [IPv6Prefix] -Prefix=2001:db8:0:1::4/64 +Prefix=2001:db8:0:1::/64 + +[IPv6Prefix] +Prefix=2001:db8:0:2::/64 +Assign=yes [IPv6RoutePrefix] Route=2001:db0:fff::/64 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 6cdc96daf7..2fba95d375 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3575,10 +3575,15 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): start_networkd() self.wait_online(['veth99:routable', 'veth-peer:routable']) - output = check_output('ip', '-6', 'route', 'show', 'dev', 'veth-peer') + output = check_output('ip -6 route show dev veth-peer') print(output) self.assertRegex(output, '2001:db8:0:1::/64 proto ra') + output = check_output('ip addr show dev veth99') + print(output) + self.assertNotRegex(output, '2001:db8:0:1') + self.assertRegex(output, '2001:db8:0:2') + class NetworkdMTUTests(unittest.TestCase, Utilities): links = ['dummy98']