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;