diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 1492f2032a..1bff564d59 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1926,15 +1926,14 @@ EmitDNS= DNS= - DNS= specifies a list of recursive - DNS server IPv6 addresses that distributed via Router Advertisement - messages when EmitDNS= is true. If DNS= - is empty, DNS servers are read from the - [Network] section. If the - [Network] section does not contain any DNS servers - either, DNS servers from the uplink with the highest priority default - route are used. When EmitDNS= is false, no DNS server - information is sent in Router Advertisement messages. + DNS= specifies a list of recursive DNS server IPv6 addresses + that are distributed via Router Advertisement messages when EmitDNS= is + true. DNS= also takes special value _link_local; in that + case the IPv6 link local address is distributed. If DNS= is empty, DNS + servers are read from the [Network] section. If the + [Network] section does not contain any DNS servers either, DNS servers from + the uplink with the highest priority default route are used. When EmitDNS= + is false, no DNS server information is sent in Router Advertisement messages. EmitDNS= defaults to true. diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index f3744e2354..cfecf76a79 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1514,6 +1514,10 @@ static int link_acquire_ipv6_conf(Link *link) { log_link_debug(link, "Starting IPv6 Router Advertisements"); + r = radv_emit_dns(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m"); + r = sd_radv_start(link->radv); if (r < 0 && r != -EBUSY) return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m"); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index d48609f985..620939e251 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -443,20 +443,29 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns, static int radv_set_dns(Link *link, Link *uplink) { _cleanup_free_ struct in6_addr *dns = NULL; - size_t n_dns; usec_t lifetime_usec; + size_t n_dns; int r; if (!link->network->router_emit_dns) return 0; if (link->network->router_dns) { - dns = newdup(struct in6_addr, link->network->router_dns, - link->network->n_router_dns); + struct in6_addr *p; + + dns = new(struct in6_addr, link->network->n_router_dns); if (!dns) return -ENOMEM; - n_dns = link->network->n_router_dns; + p = dns; + for (size_t i = 0; i < link->network->n_router_dns; i++) + if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) { + if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address)) + *(p++) = link->ipv6ll_address; + } else + *(p++) = link->network->router_dns[i]; + + n_dns = p - dns; lifetime_usec = link->network->router_dns_lifetime_usec; goto set_dns; @@ -620,7 +629,7 @@ int radv_configure(Link *link) { } - return radv_emit_dns(link); + return 0; } int config_parse_radv_dns( @@ -658,19 +667,30 @@ int config_parse_radv_dns( if (r == 0) break; - if (in_addr_from_string(AF_INET6, w, &a) >= 0) { - struct in6_addr *m; + if (streq(w, "_link_local")) + a = IN_ADDR_NULL; + else { + r = in_addr_from_string(AF_INET6, w, &a); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DNS server address, ignoring: %s", w); + continue; + } - m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); - if (!m) - return log_oom(); + if (in_addr_is_null(AF_INET6, &a)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "DNS server address is null, ignoring: %s", w); + continue; + } + } - m[n->n_router_dns++] = a.in6; - n->router_dns = m; + struct in6_addr *m; + m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); + if (!m) + return log_oom(); - } else - log_syntax(unit, LOG_ERR, filename, line, 0, - "Failed to parse DNS server address, ignoring: %s", w); + m[n->n_router_dns++] = a.in6; + n->router_dns = m; } return 0; diff --git a/test/test-network/conf/ipv6-prefix.network b/test/test-network/conf/ipv6-prefix.network index c1932a84d3..215cc9071b 100644 --- a/test/test-network/conf/ipv6-prefix.network +++ b/test/test-network/conf/ipv6-prefix.network @@ -4,6 +4,10 @@ Name=veth-peer [Network] IPv6PrefixDelegation=yes +[IPv6PrefixDelegation] +DNS=_link_local 2002:da8:1:0::1 +DNSLifetimeSec=1min + [IPv6Prefix] Prefix=2002:da8:1:0::/64 PreferredLifetimeSec=1000s diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 01292d7c3b..2c1f99ca2e 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2646,6 +2646,11 @@ class NetworkdRATests(unittest.TestCase, Utilities): start_networkd() self.wait_online(['veth99:routable', 'veth-peer:degraded']) + output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env) + print(output) + self.assertRegex(output, 'fe80::') + self.assertRegex(output, '2002:da8:1::1') + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env) print(output) self.assertRegex(output, '2002:da8:1:0')