From 88295a05dbb0c69de4bae77d80d0566b52ec1cac Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 14 Aug 2017 12:53:10 +0300 Subject: [PATCH 1/5] networkd: Parse DNS IPv6 information for Router Advertisement Parse DNS IPv6 addresses and DNS server lifetime from .network files so that they can be included in Router Advertisement RDNSS options. RDNSS address and lifetime information is added to the [IPv6PrefixDelegation] section according to the following syntax: DNS=2001:db8::a:b 2001:db8::c:d DNSLifetimeSec=1042 --- src/network/networkd-network-gperf.gperf | 2 + src/network/networkd-network.c | 53 ++++++++++++++++++++++++ src/network/networkd-network.h | 4 ++ 3 files changed, 59 insertions(+) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 54385b59d7..804affd1d7 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -144,6 +144,8 @@ IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0 +IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0 +IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) IPv6Prefix.Prefix, config_parse_prefix, 0, 0 IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 77acee10f0..026f318520 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -26,6 +26,7 @@ #include "dns-domain.h" #include "fd-util.h" #include "hostname-util.h" +#include "in-addr-util.h" #include "network-internal.h" #include "networkd-manager.h" #include "networkd-network.h" @@ -1008,6 +1009,58 @@ int config_parse_dhcp_server_dns( return 0; } +int config_parse_radv_dns( + 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 *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + union in_addr_union a; + + r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + break; + + if (in_addr_from_string(AF_INET6, w, &a) >= 0) { + struct in6_addr *m; + + m = realloc(n->router_dns, (n->n_router_dns + 1) * sizeof(struct in6_addr)); + if (!m) + return log_oom(); + + m[n->n_router_dns++] = a.in6; + n->router_dns = m; + + } else + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); + + } + + return 0; +} + int config_parse_dhcp_server_ntp( const char *unit, const char *filename, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index d55e6f73f9..1c0b947f2f 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -164,6 +164,9 @@ struct Network { uint8_t router_preference; bool router_managed; bool router_other_information; + usec_t router_dns_lifetime_usec; + struct in6_addr *router_dns; + unsigned n_router_dns; /* Bridge Support */ bool use_bpdu; @@ -265,6 +268,7 @@ int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename, int config_parse_hostname(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); int config_parse_timezone(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); int config_parse_dhcp_server_dns(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); +int config_parse_radv_dns(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); int config_parse_dhcp_server_ntp(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); int config_parse_dnssec_negative_trust_anchors(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); int config_parse_dhcp_use_domains(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); From e9c6da386849a6d262c1d605be911922f2cb7428 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 14 Aug 2017 12:53:11 +0300 Subject: [PATCH 2/5] sd-radv: Add Router Advertisement DNS information Add Router Advertisement Recursive DNS Server information as specified in RFC 8106. --- src/libsystemd-network/radv-internal.h | 12 +++++++ src/libsystemd-network/sd-radv.c | 48 ++++++++++++++++++++++++-- src/systemd/sd-radv.h | 2 ++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index b21d4e54cb..c3f847ec05 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -35,12 +35,21 @@ assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC) #define SD_RADV_MIN_DELAY_BETWEEN_RAS 3 #define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC) +#define SD_RADV_OPT_RDNSS 25 + enum RAdvState { SD_RADV_STATE_IDLE = 0, SD_RADV_STATE_ADVERTISING = 1, }; typedef enum RAdvState RAdvState; +struct sd_radv_opt_dns { + uint8_t type; + uint8_t length; + uint16_t reserved; + be32_t lifetime; +} _packed_; + struct sd_radv { unsigned n_ref; RAdvState state; @@ -63,6 +72,9 @@ struct sd_radv { unsigned n_prefixes; LIST_HEAD(sd_radv_prefix, prefixes); + + size_t n_rdnss; + struct sd_radv_opt_dns *rdnss; }; struct sd_radv_prefix { diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index f23275a80c..70772b4f15 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -126,6 +126,8 @@ _public_ sd_radv *sd_radv_unref(sd_radv *ra) { sd_radv_prefix_unref(p); } + free(ra->rdnss); + radv_reset(ra); sd_radv_detach_event(ra); @@ -155,8 +157,8 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, .nd_opt_mtu_type = ND_OPT_MTU, .nd_opt_mtu_len = 1, }; - /* Reserve iov space for RA header, linkaddr, MTU + N prefixes */ - struct iovec iov[3 + ra->n_prefixes]; + /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS */ + struct iovec iov[4 + ra->n_prefixes]; struct msghdr msg = { .msg_name = &dst_addr, .msg_namelen = sizeof(dst_addr), @@ -196,6 +198,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, msg.msg_iovlen++; } + if (ra->rdnss) { + iov[msg.msg_iovlen].iov_base = ra->rdnss; + iov[msg.msg_iovlen].iov_len = ra->rdnss->length * 8; + msg.msg_iovlen++; + } + if (sendmsg(ra->fd, &msg, 0) < 0) return -errno; @@ -546,6 +554,42 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { return 0; } +_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, + const struct in6_addr *dns, size_t n_dns) { + _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL; + size_t len; + + assert_return(ra, -EINVAL); + assert_return(n_dns < 128, -EINVAL); + + if (!dns || n_dns == 0) { + ra->rdnss = mfree(ra->rdnss); + ra->n_rdnss = 0; + + return 0; + } + + len = sizeof(struct sd_radv_opt_dns) + sizeof(struct in6_addr) * n_dns; + + opt_rdnss = malloc0(len); + if (!opt_rdnss) + return -ENOMEM; + + opt_rdnss->type = SD_RADV_OPT_RDNSS; + opt_rdnss->length = len / 8; + opt_rdnss->lifetime = htobe32(lifetime); + + memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr)); + + free(ra->rdnss); + ra->rdnss = opt_rdnss; + opt_rdnss = NULL; + + ra->n_rdnss = n_dns; + + return 0; +} + _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 4cbd80db68..ce10ae0606 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -57,6 +57,8 @@ int sd_radv_set_managed_information(sd_radv *ra, int managed); int sd_radv_set_other_information(sd_radv *ra, int other); int sd_radv_set_preference(sd_radv *ra, unsigned preference); int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); +int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, + const struct in6_addr *dns, size_t n_dns); /* Advertised prefixes */ int sd_radv_prefix_new(sd_radv_prefix **ret); From f1eca3774d2822eefc16141269c1a1b67976b09b Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 14 Aug 2017 12:53:12 +0300 Subject: [PATCH 3/5] networkd-radv: Set RDNSS information on Router Advertisement enabling --- src/network/networkd-radv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index af9e116936..d47a2fd3fd 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -75,5 +75,15 @@ int radv_configure(Link *link) { return r; } + if (link->network->router_dns) { + r = sd_radv_set_rdnss(link->radv, + DIV_ROUND_UP(link->network->router_dns_lifetime_usec, + USEC_PER_SEC), + link->network->router_dns, + link->network->n_router_dns); + if (r < 0) + return r; + } + return 0; } From 06e6c805e56ba9d8f60ac3576756d0b4b3613521 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 14 Aug 2017 12:53:13 +0300 Subject: [PATCH 4/5] test-ndisc-ra: Update test with RDNSS option --- src/libsystemd-network/test-ndisc-ra.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index 6e6d05642d..eaa49c6a72 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -53,7 +53,7 @@ static uint8_t advertisement[] = { 0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Recursive DNS Server Option - not yet supported */ + /* Recursive DNS Server Option */ 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, @@ -106,6 +106,11 @@ static struct { false }, }; +static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8, + 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 } } }; + static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { assert_se(false); @@ -207,6 +212,14 @@ static void test_radv(void) { assert_se(sd_radv_set_other_information(ra, true) >= 0); assert_se(sd_radv_set_other_information(ra, false) >= 0); + assert_se(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0); + assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0); + assert_se(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0); + assert_se(sd_radv_set_rdnss(ra, 600, &test_rdnss, 0) >= 0); + assert_se(sd_radv_set_rdnss(ra, 600, &test_rdnss, 1) >= 0); + assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0); + assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0); + ra = sd_radv_unref(ra); assert_se(!ra); } @@ -238,7 +251,7 @@ int icmp6_receive(int fd, void *iov_base, size_t iov_len, static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_radv *ra = userdata; - unsigned char buf[120]; + unsigned char buf[144]; size_t i; read(test_fd[0], &buf, sizeof(buf)); @@ -254,6 +267,9 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat /* test only up to buf size, rest is not yet implemented */ for (i = 0; i < sizeof(buf); i++) { + if (!(i % 8)) + printf("%3zd: ", i); + printf("0x%02x", buf[i]); assert_se(buf[i] == advertisement[i]); @@ -302,6 +318,7 @@ static void test_ra(void) { assert_se(sd_radv_set_hop_limit(ra, 64) >= 0); assert_se(sd_radv_set_managed_information(ra, true) >= 0); assert_se(sd_radv_set_other_information(ra, true) >= 0); + assert_se(sd_radv_set_rdnss(ra, 60, &test_rdnss, 1) >= 0); for (i = 0; i < ELEMENTSOF(prefix); i++) { sd_radv_prefix *p; From 3f9e023673434cb0406c1040a885ed6834d3940a Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 6 Sep 2017 14:20:08 +0300 Subject: [PATCH 5/5] man: Document prefix delegation in systemd.network Add prefix delegation documentation covering IPv6PrefixDelegation= setting in the Network section as well as all the parameters and the IPv6PrefixDelegation and IPv6Prefix sections implemented so far, including DNS= and DNSLifetimeSec= settings. --- man/systemd.network.xml | 117 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 8b0f7363df..5ef8616a95 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -636,6 +636,13 @@ has been set to false. Defaults to unset. + + IPv6PrefixDelegation= + Whether to enable or disable Router Advertisement sending on a link. + Defaults to false. See the [IPv6PrefixDelegation] + and the [IPv6Prefix] sections for configuration options. + + Bridge= @@ -1312,6 +1319,116 @@ + [IPv6PrefixDelegation] Section Options + The [IPv6PrefixDelegation] section contains + settings for sending IPv6 Router Advertisements and whether to act as + a router, if enabled via the IPv6PrefixDelegation= + option described above. IPv6 network prefixes are defined with one or + more [IPv6Prefix] sections. + + + + + Managed= + OtherInformation= + + Controls whether a DHCPv6 server is used to acquire IPv6 + addresses on the network link when Managed= boolean + is set to true or if only additional network + information can be obtained via DHCPv6 for the network link when + OtherInformation= boolean is set to + true. Both settings default to + false, which means that a DHCPv6 server is not being + used. + + + + RouterLifetimeSec= + + Configures the IPv6 router lifetime in seconds. If set, + this host also announces itself in Router Advertisements as an IPv6 + router for the network link. Defaults to unset, which means the host is + not acting as a router. + + + + + RouterPreference= + + Configures IPv6 router preference if + RouterLifetimeSec= is non-zero. Valid values are + high, medium and + low, with normal and + default added as synonyms for + medium just to make configuration easier. See + RFC 4191 + for details. Defaults to medium. + + + + DNS= + + A list of recursive DNS server IPv6 addresses + distributed via Router Advertisement messages. + + + + + DNSLifetimeSec= + + Lifetime in seconds for the DNS server addresses listed + in DNS=. + + + + + + + [IPv6Prefix] Section Options + One or more [IPv6Prefix] sections contain the IPv6 + prefixes that are announced via Router Advertisements. See + RFC 4861 + for further details. + + + + + AddressAutoconfiguration= + OnLink= + + Boolean values to specify whether IPv6 addresses can be + autoconfigured with this prefix and whether the prefix can be used for + onlink determination. Both settings default to true + in order to ease configuration. + + + + + Prefix= + + The IPv6 prefix that is to be distributed to hosts. + Similarly to configuring static IPv6 addresses, the setting is + configured as an IPv6 prefix and its prefix length, separated by a + / character. Use multiple + [IPv6Prefix] sections to configure multiple IPv6 + prefixes since prefix lifetimes, address autoconfiguration and onlink + status may differ from one prefix to another. + + + + PreferredLifetimeSec= + ValidLifetimeSec= + + Preferred and valid lifetimes for the prefix measured in + seconds. PreferredLifetimeSec= defaults to 604800 + seconds (one week) and ValidLifetimeSec= defaults + to 2592000 seconds (30 days). + + + + + + [Bridge] Section Options The [Bridge] section accepts the following keys.