From 700f1186e3e1601144c0ec968a83e7bd9d60c9b5 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 16 Aug 2017 13:29:51 +0300 Subject: [PATCH 1/5] networkd: Parse DNS search domain information for Router Advertisement Parse DNS search domains from .network files so that they are included in Router Advertisement DNSSL options. DNS search domains are added to the [IPv6PrefixDelegation] section using the following syntax: Domains=foo.example.com bar.example.com If IDNA libraries are enabled in systemd, international domain names are supported. --- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 50 ++++++++++++++++++++++++ src/network/networkd-network.h | 2 + 3 files changed, 53 insertions(+) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index f01e78a356..274d2a8bbe 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -152,6 +152,7 @@ IPv6PrefixDelegation.Managed, config_parse_bool, 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.Domains, config_parse_radv_search_domains, 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 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 1694b1e5eb..f2da3141f8 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -35,6 +35,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "util.h" static void network_config_hash_func(const void *p, struct siphash *state) { @@ -1127,6 +1128,55 @@ int config_parse_radv_dns( return 0; } +int config_parse_radv_search_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) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + _cleanup_free_ char *idna = NULL; + + 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; + + r = dns_name_apply_idna(w, &idna); + if (r > 0) { + r = strv_push(&n->router_search_domains, idna); + if (r >= 0) + idna = NULL; + } else if (r == 0) { + r = strv_push(&n->router_search_domains, w); + if (r >= 0) + w = NULL; + } + } + + 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 7af3171ef9..11f5bb1174 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -169,6 +169,7 @@ struct Network { usec_t router_dns_lifetime_usec; struct in6_addr *router_dns; unsigned n_router_dns; + char **router_search_domains; /* Bridge Support */ bool use_bpdu; @@ -275,6 +276,7 @@ int config_parse_hostname(const char *unit, const char *filename, unsigned line, 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_radv_search_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); 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 e965d6aba3d6a23783173a19fe850e8b384ad4fb Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 21 Aug 2017 13:41:20 +0300 Subject: [PATCH 2/5] sd-radv: Add Router Advertisement DNS Search List option Add Router Advertisement DNS Search List option as specified in RFC 8106. The search list option uses and identical option header as the RDNSS option and therefore the option header structure can be reused. If systemd is compiled with IDNA support, internationalization of the provided search domain is applied, after which the search list is written in wire format into the DNSSL option. --- src/libsystemd-network/radv-internal.h | 2 + src/libsystemd-network/sd-radv.c | 60 ++++++++++++++++++++++++++ src/systemd/sd-radv.h | 1 + 3 files changed, 63 insertions(+) diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index c3f847ec05..5601be844d 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -36,6 +36,7 @@ assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC) #define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC) #define SD_RADV_OPT_RDNSS 25 +#define SD_RADV_OPT_DNSSL 31 enum RAdvState { SD_RADV_STATE_IDLE = 0, @@ -75,6 +76,7 @@ struct sd_radv { size_t n_rdnss; struct sd_radv_opt_dns *rdnss; + struct sd_radv_opt_dns *dnssl; }; struct sd_radv_prefix { diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 70772b4f15..f05c44032d 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -26,12 +26,14 @@ #include "macro.h" #include "alloc-util.h" +#include "dns-domain.h" #include "fd-util.h" #include "icmp6-util.h" #include "in-addr-util.h" #include "radv-internal.h" #include "socket-util.h" #include "string-util.h" +#include "strv.h" #include "util.h" #include "random-util.h" @@ -204,6 +206,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, msg.msg_iovlen++; } + if (ra->dnssl) { + iov[msg.msg_iovlen].iov_base = ra->dnssl; + iov[msg.msg_iovlen].iov_len = ra->dnssl->length * 8; + msg.msg_iovlen++; + } + if (sendmsg(ra->fd, &msg, 0) < 0) return -errno; @@ -590,6 +598,58 @@ _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, return 0; } +_public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, + char **search_list) { + _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL; + size_t len = 0; + char **s; + uint8_t *p; + + assert_return(ra, -EINVAL); + + if (!search_list || *search_list == NULL) { + ra->dnssl = mfree(ra->dnssl); + + return 0; + } + + STRV_FOREACH(s, search_list) + len += strlen(*s) + 2; + + len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7; + + opt_dnssl = malloc0(len); + if (!opt_dnssl) + return -ENOMEM; + + opt_dnssl->type = SD_RADV_OPT_DNSSL; + opt_dnssl->length = len / 8; + opt_dnssl->lifetime = htobe32(lifetime); + + p = (uint8_t *)(opt_dnssl + 1); + len -= sizeof(struct sd_radv_opt_dns); + + STRV_FOREACH(s, search_list) { + int r; + + r = dns_name_to_wire_format(*s, p, len, false); + if (r < 0) + return r; + + if (len < (size_t)r) + return -ENOBUFS; + + p += r; + len -= r; + } + + free(ra->dnssl); + ra->dnssl = opt_dnssl; + opt_dnssl = NULL; + + 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 ce10ae0606..0cc1d670b9 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -59,6 +59,7 @@ 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); +int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list); /* Advertised prefixes */ int sd_radv_prefix_new(sd_radv_prefix **ret); From 5e35aa815b90c71547ead38e97ee3acc0399a708 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 21 Aug 2017 13:44:25 +0300 Subject: [PATCH 3/5] networkd-radv: Set DNSSL information on Router Advertisement enabling --- src/network/networkd-radv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index d47a2fd3fd..6768208cfa 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -85,5 +85,14 @@ int radv_configure(Link *link) { return r; } + if (link->network->router_search_domains) { + r = sd_radv_set_dnssl(link->radv, + DIV_ROUND_UP(link->network->router_dns_lifetime_usec, + USEC_PER_SEC), + link->network->router_search_domains); + if (r < 0) + return r; + } + return 0; } From fa178dd2a215e685c87686b714678541d777076d Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 21 Aug 2017 15:20:56 +0300 Subject: [PATCH 4/5] test-ndisc-ra: Update test to include DNSSL option Update the test to include the already provided DNSSL option. --- src/libsystemd-network/test-ndisc-ra.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index eaa49c6a72..18205ef2c9 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -57,7 +57,7 @@ static uint8_t advertisement[] = { 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - /* DNS Search List Option - not yet supported */ + /* DNS Search List Option */ 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -110,6 +110,8 @@ static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }; +static const char *test_dnssl[] = { "lab.intra", + NULL }; static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { @@ -220,6 +222,11 @@ static void test_radv(void) { assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0); assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0); + assert_se(sd_radv_set_dnssl(ra, 0, NULL) >= 0); + assert_se(sd_radv_set_dnssl(ra, 600, NULL) >= 0); + assert_se(sd_radv_set_dnssl(ra, 0, (char **)test_dnssl) >= 0); + assert_se(sd_radv_set_dnssl(ra, 600, (char **)test_dnssl) >= 0); + ra = sd_radv_unref(ra); assert_se(!ra); } @@ -251,7 +258,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[144]; + unsigned char buf[168]; size_t i; read(test_fd[0], &buf, sizeof(buf)); @@ -319,6 +326,7 @@ static void test_ra(void) { 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); + assert_se(sd_radv_set_dnssl(ra, 60, (char **)test_dnssl) >= 0); for (i = 0; i < ELEMENTSOF(prefix); i++) { sd_radv_prefix *p; From 760021c02c04350e2e5e0bb9a6a731e70d1fb885 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 7 Sep 2017 12:24:00 +0300 Subject: [PATCH 5/5] man: Document Domains for Router Advertisement network configuration --- man/systemd.network.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index ea3bf71eea..2055808741 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1443,11 +1443,20 @@ + + Domains= + + A list of DNS search domains distributed via + Router Advertisement messages. Defaults to empty, i.e. no search + domains are sent. + + DNSLifetimeSec= Lifetime in seconds for the DNS server addresses listed - in DNS=. + in DNS= and search domains listed in + Domains=.