summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2019-07-17 12:07:39 +0200
committerGitHub <noreply@github.com>2019-07-17 12:07:39 +0200
commit52f4b5ac8a046172aacf3fa441e89997445b80ed (patch)
tree47c9c619c6ac2b8408c32fde3d66ac36b2193c8f
parentd34a40082db3ffca8de66bfa4df50951101bdae5 (diff)
parent217246a990cc34155f02fcecce1df95ca2e1abed (diff)
downloadsystemd-52f4b5ac8a046172aacf3fa441e89997445b80ed.tar.gz
systemd-52f4b5ac8a046172aacf3fa441e89997445b80ed.tar.bz2
systemd-52f4b5ac8a046172aacf3fa441e89997445b80ed.zip
Merge pull request #12675 from yuwata/network-set-dns
resolvectl: support to modify interfaces managed by networkd
-rw-r--r--man/resolvectl.xml7
-rw-r--r--man/timedatectl.xml14
-rw-r--r--shell-completion/bash/timedatectl9
-rw-r--r--src/network/meson.build3
-rw-r--r--src/network/networkd-link-bus.c528
-rw-r--r--src/network/networkd-link-bus.h12
-rw-r--r--src/network/networkd-link.c104
-rw-r--r--src/network/networkd-link.h18
-rw-r--r--src/network/networkd-manager-bus.c192
-rw-r--r--src/network/networkd-manager-bus.h10
-rw-r--r--src/network/networkd-manager.c8
-rw-r--r--src/network/networkd-manager.h4
-rw-r--r--src/network/org.freedesktop.network1.conf17
-rw-r--r--src/network/org.freedesktop.network1.policy142
-rw-r--r--src/resolve/resolvectl.c396
-rw-r--r--src/resolve/resolved-def.h3
-rw-r--r--src/resolve/resolved-dns-server.c13
-rw-r--r--src/resolve/resolved-dns-server.h2
-rw-r--r--src/resolve/resolved-manager.c3
-rw-r--r--src/shared/resolve-util.c13
-rw-r--r--src/shared/resolve-util.h6
-rw-r--r--src/timedate/timedatectl.c103
-rw-r--r--test/test-network/conf/state-file-tests.network15
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py122
24 files changed, 1513 insertions, 231 deletions
diff --git a/man/resolvectl.xml b/man/resolvectl.xml
index d4c04a1e32..a59fa4c66d 100644
--- a/man/resolvectl.xml
+++ b/man/resolvectl.xml
@@ -247,11 +247,8 @@
<listitem>
<para>Get/set per-interface DNS configuration. These commands may be used to configure various DNS settings
- for network interfaces that aren't managed by
- <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. (These
- commands will fail when used on interfaces that are managed by <command>systemd-networkd</command>, please
- configure their DNS settings directly inside the <filename>.network</filename> files instead.) These commands
- may be used to inform <command>systemd-resolved</command> about per-interface DNS configuration determined
+ for network interfaces. These commands may be used to inform <command>systemd-resolved</command> or
+ <command>systemd-networkd</command> about per-interface DNS configuration determined
through external means. The <option>dns</option> command expects IPv4 or IPv6 address specifications of DNS
servers to use. The <option>domain</option> command expects valid DNS domains, possibly prefixed with
<literal>~</literal>, and configures a per-interface search or route-only domain. The
diff --git a/man/timedatectl.xml b/man/timedatectl.xml
index b4b99103de..262b9126e7 100644
--- a/man/timedatectl.xml
+++ b/man/timedatectl.xml
@@ -227,6 +227,20 @@
<para>By default, empty properties are suppressed. Use <option>--all</option> to show those too.
To select specific properties to show, use <option>--property=</option>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><command>ntp-servers <replaceable>INTERFACE</replaceable> <replaceable>SERVER</replaceable>…</command></term>
+
+ <listitem><para>Set the interface specific NTP servers. This command can be used only when the
+ interface is managed by <command>systemd-networkd</command>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>revert <replaceable>INTERFACE</replaceable></command></term>
+
+ <listitem><para>Revert the interface specific NTP servers. This command can be used only when
+ the interface is managed by <command>systemd-networkd</command>.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect2>
diff --git a/shell-completion/bash/timedatectl b/shell-completion/bash/timedatectl
index 75796ff382..385dee7dd4 100644
--- a/shell-completion/bash/timedatectl
+++ b/shell-completion/bash/timedatectl
@@ -30,6 +30,12 @@ __get_machines() {
machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; };
}
+__get_interfaces(){
+ local name
+ for name in $(cd /sys/class/net && ls); do
+ [[ "$name" != "lo" ]] && echo "$name"
+ done
+}
_timedatectl() {
local i verb comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
@@ -60,6 +66,7 @@ _timedatectl() {
[STANDALONE]='status show list-timezones timesync-status show-timesync'
[TIMEZONES]='set-timezone'
[TIME]='set-time'
+ [IFNAME]='ntp-servers revert'
)
for ((i=0; i < COMP_CWORD; i++)); do
@@ -77,6 +84,8 @@ _timedatectl() {
comps=$(command timedatectl list-timezones)
elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[TIME]}; then
comps=''
+ elif __contains_word "$verb" ${VERBS[TIMEZONES]}; then
+ comps=$( __get_interfaces )
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/src/network/meson.build b/src/network/meson.build
index 6725e9d1b0..6bed37a170 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -80,6 +80,7 @@ sources = files('''
networkd-lldp-tx.c
networkd-lldp-tx.h
networkd-manager-bus.c
+ networkd-manager-bus.h
networkd-manager.c
networkd-manager.h
networkd-ndisc.c
@@ -155,6 +156,8 @@ if conf.get('ENABLE_NETWORKD') == 1
install_dir : dbuspolicydir)
install_data('org.freedesktop.network1.service',
install_dir : dbussystemservicedir)
+ install_data('org.freedesktop.network1.policy',
+ install_dir : polkitpolicydir)
if install_polkit
install_data('systemd-networkd.rules',
install_dir : polkitrulesdir)
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index eda0325c2b..03552725ed 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -1,13 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/capability.h>
+
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
+#include "dns-domain.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "parse-util.h"
+#include "resolve-util.h"
#include "strv.h"
+#include "user-util.h"
BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
BUS_DEFINE_PROPERTY_GET_ENUM(property_get_carrier_state, link_carrier_state, LinkCarrierState);
@@ -59,6 +66,515 @@ static int property_get_bit_rates(
return sd_bus_message_append(reply, "(tt)", tx, rx);
}
+static int verify_managed_link(Link *l, sd_bus_error *error) {
+ assert(l);
+
+ if (l->flags & IFF_LOOPBACK)
+ return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->ifname);
+
+ return 0;
+}
+
+int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **ntp = NULL;
+ Link *l = userdata;
+ int r;
+ char **i;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &ntp);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, ntp) {
+ r = dns_name_is_valid_or_address(*i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-ntp-servers",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ strv_free_and_replace(l->ntp, ntp);
+
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ struct in_addr_data *dns = NULL;
+ size_t allocated = 0, n = 0;
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(message, 'a', "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ int family;
+ size_t sz;
+ const void *d;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_enter_container(message, 'r', "iay");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ if (!dns_server_address_valid(family, d))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1))
+ return -ENOMEM;
+
+ dns[n].family = family;
+ memcpy(&dns[n].address, d, sz);
+ n++;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-dns-servers",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ free_and_replace(l->dns, dns);
+ l->n_dns = n;
+
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(ordered_set_freep) OrderedSet *search_domains = NULL, *route_domains = NULL;
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(message, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *str = NULL;
+ OrderedSet **domains;
+ const char *name;
+ int route_only;
+
+ r = sd_bus_message_read(message, "(sb)", &name, &route_only);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
+ if (!route_only && dns_name_is_root(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+
+ r = dns_name_normalize(name, 0, &str);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
+
+ domains = route_only ? &route_domains : &search_domains;
+ r = ordered_set_ensure_allocated(domains, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_put(*domains, str);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(str);
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-domains",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ ordered_set_free_free(l->search_domains);
+ ordered_set_free_free(l->route_domains);
+ l->search_domains = TAKE_PTR(search_domains);
+ l->route_domains = TAKE_PTR(route_domains);
+
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r, b;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-default-route",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->dns_default_route != b) {
+ l->dns_default_route = b;
+ (void) link_dirty(l);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *llmnr;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &llmnr);
+ if (r < 0)
+ return r;
+
+ if (isempty(llmnr))
+ mode = RESOLVE_SUPPORT_YES;
+ else {
+ mode = resolve_support_from_string(llmnr);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-llmnr",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->llmnr != mode) {
+ l->llmnr = mode;
+ (void) link_dirty(l);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *mdns;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &mdns);
+ if (r < 0)
+ return r;
+
+ if (isempty(mdns))
+ mode = RESOLVE_SUPPORT_NO;
+ else {
+ mode = resolve_support_from_string(mdns);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-mdns",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->mdns != mode) {
+ l->mdns = mode;
+ (void) link_dirty(l);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ const char *dns_over_tls;
+ DnsOverTlsMode mode;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &dns_over_tls);
+ if (r < 0)
+ return r;
+
+ if (isempty(dns_over_tls))
+ mode = _DNS_OVER_TLS_MODE_INVALID;
+ else {
+ mode = dns_over_tls_mode_from_string(dns_over_tls);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-dns-over-tls",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->dns_over_tls_mode != mode) {
+ l->dns_over_tls_mode = mode;
+ (void) link_dirty(l);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ const char *dnssec;
+ DnssecMode mode;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &dnssec);
+ if (r < 0)
+ return r;
+
+ if (isempty(dnssec))
+ mode = _DNSSEC_MODE_INVALID;
+ else {
+ mode = dnssec_mode_from_string(dnssec);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-dnssec",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->dnssec_mode != mode) {
+ l->dnssec_mode = mode;
+ (void) link_dirty(l);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_set_free_free_ Set *ns = NULL;
+ _cleanup_strv_free_ char **ntas = NULL;
+ Link *l = userdata;
+ int r;
+ char **i;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &ntas);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, ntas) {
+ r = dns_name_is_valid(*i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
+ }
+
+ ns = set_new(&dns_name_hash_ops);
+ if (!ns)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, ntas) {
+ r = set_put_strdup(ns, *i);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ set_free_free(l->dnssec_negative_trust_anchors);
+ l->dnssec_negative_trust_anchors = TAKE_PTR(ns);
+
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.revert-ntp",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ link_ntp_settings_clear(l);
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = verify_managed_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.revert-dns",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ link_dns_settings_clear(l);
+ (void) link_dirty(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable link_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -68,6 +584,18 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),
+ SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
+
SD_BUS_VTABLE_END
};
diff --git a/src/network/networkd-link-bus.h b/src/network/networkd-link-bus.h
index 58005a4bbe..2a653f5058 100644
--- a/src/network/networkd-link-bus.h
+++ b/src/network/networkd-link-bus.h
@@ -18,3 +18,15 @@ int link_send_changed(Link *link, const char *property, ...) _sentinel_;
int property_get_operational_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
int property_get_carrier_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
int property_get_address_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+
+int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 46830290c6..a08ad80f15 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -609,6 +609,13 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
.ifindex = ifindex,
.iftype = iftype,
.sysctl_ipv6_enabled = -1,
+
+ .n_dns = (unsigned) -1,
+ .dns_default_route = -1,
+ .llmnr = _RESOLVE_SUPPORT_INVALID,
+ .mdns = _RESOLVE_SUPPORT_INVALID,
+ .dnssec_mode = _DNSSEC_MODE_INVALID,
+ .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
};
link->ifname = strdup(ifname);
@@ -655,11 +662,34 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
return 0;
}
+void link_ntp_settings_clear(Link *link) {
+ link->ntp = strv_free(link->ntp);
+}
+
+void link_dns_settings_clear(Link *link) {
+ link->dns = mfree(link->dns);
+ link->n_dns = (unsigned) -1;
+
+ link->search_domains = ordered_set_free_free(link->search_domains);
+ link->route_domains = ordered_set_free_free(link->route_domains);
+
+ link->dns_default_route = -1;
+ link->llmnr = _RESOLVE_SUPPORT_INVALID;
+ link->mdns = _RESOLVE_SUPPORT_INVALID;
+ link->dnssec_mode = _DNSSEC_MODE_INVALID;
+ link->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
+
+ link->dnssec_negative_trust_anchors = set_free_free(link->dnssec_negative_trust_anchors);
+}
+
static Link *link_free(Link *link) {
Address *address;
assert(link);
+ link_ntp_settings_clear(link);
+ link_dns_settings_clear(link);
+
link->routes = set_free_with_destructor(link->routes, route_free);
link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
@@ -3386,6 +3416,26 @@ static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
fputc('\n', f);
}
+static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) {
+ unsigned j;
+ int r;
+
+ for (j = 0; j < n_dns; j++) {
+ _cleanup_free_ char *b = NULL;
+
+ r = in_addr_to_string(dns[j].family, &dns[j].address, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to format address, ignoring: %m");
+ continue;
+ }
+
+ if (*space)
+ fputc(' ', f);
+ fputs(b, f);
+ *space = true;
+ }
+}
+
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -3437,7 +3487,6 @@ int link_save(Link *link) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
const char *dhcp_domainname = NULL, *p;
sd_dhcp6_lease *dhcp6_lease = NULL;
- unsigned j;
bool space;
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
@@ -3457,21 +3506,10 @@ int link_save(Link *link) {
fputs("DNS=", f);
space = false;
- for (j = 0; j < link->network->n_dns; j++) {
- _cleanup_free_ char *b = NULL;
-
- r = in_addr_to_string(link->network->dns[j].family,
- &link->network->dns[j].address, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to format address, ignoring: %m");
- continue;
- }
-
- if (space)
- fputc(' ', f);
- fputs(b, f);
- space = true;
- }
+ if (link->n_dns != (unsigned) -1)
+ link_save_dns(f, link->dns, link->n_dns, &space);
+ else
+ link_save_dns(f, link->network->dns, link->network->n_dns, &space);
if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
@@ -3514,7 +3552,7 @@ int link_save(Link *link) {
fputs("NTP=", f);
space = false;
- fputstrv(f, link->network->ntp, NULL, &space);
+ fputstrv(f, link->ntp ?: link->network->ntp, NULL, &space);
if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
@@ -3557,7 +3595,7 @@ int link_save(Link *link) {
fputs("DOMAINS=", f);
space = false;
- ORDERED_SET_FOREACH(p, link->network->search_domains, i)
+ ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains, i)
fputs_with_space(f, p, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
@@ -3580,7 +3618,7 @@ int link_save(Link *link) {
fputs("ROUTE_DOMAINS=", f);
space = false;
- ORDERED_SET_FOREACH(p, link->network->route_domains, i)
+ ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains, i)
fputs_with_space(f, p, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
@@ -3602,21 +3640,37 @@ int link_save(Link *link) {
fputc('\n', f);
fprintf(f, "LLMNR=%s\n",
- resolve_support_to_string(link->network->llmnr));
+ resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
fprintf(f, "MDNS=%s\n",
- resolve_support_to_string(link->network->mdns));
- if (link->network->dns_default_route >= 0)
+ resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
+ if (link->dns_default_route >= 0)
+ fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->dns_default_route));
+ else if (link->network->dns_default_route >= 0)
fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route));
- if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+ if (link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+ fprintf(f, "DNS_OVER_TLS=%s\n",
+ dns_over_tls_mode_to_string(link->dns_over_tls_mode));
+ else if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
fprintf(f, "DNS_OVER_TLS=%s\n",
dns_over_tls_mode_to_string(link->network->dns_over_tls_mode));
- if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
+ if (link->dnssec_mode != _DNSSEC_MODE_INVALID)
+ fprintf(f, "DNSSEC=%s\n",
+ dnssec_mode_to_string(link->dnssec_mode));
+ else if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
fprintf(f, "DNSSEC=%s\n",
dnssec_mode_to_string(link->network->dnssec_mode));
- if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
+ if (!set_isempty(link->dnssec_negative_trust_anchors)) {
+ const char *n;
+
+ fputs("DNSSEC_NTA=", f);
+ space = false;
+ SET_FOREACH(n, link->dnssec_negative_trust_anchors, i)
+ fputs_with_space(f, n, NULL, &space);
+ fputc('\n', f);
+ } else if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
const char *n;
fputs("DNSSEC_NTA=", f);
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 43efa42312..e8680bbb1f 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -18,6 +18,8 @@
#include "log-link.h"
#include "network-util.h"
#include "networkd-util.h"
+#include "ordered-set.h"
+#include "resolve-util.h"
#include "set.h"
typedef enum LinkState {
@@ -130,6 +132,20 @@ typedef struct Link {
bool stats_updated;
int sysctl_ipv6_enabled;
+
+ /* All kinds of DNS configuration */
+ struct in_addr_data *dns;
+ unsigned n_dns;
+ OrderedSet *search_domains, *route_domains;
+
+ int dns_default_route;
+ ResolveSupport llmnr;
+ ResolveSupport mdns;
+ DnssecMode dnssec_mode;
+ DnsOverTlsMode dns_over_tls_mode;
+ Set *dnssec_negative_trust_anchors;
+
+ char **ntp;
} Link;
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
@@ -137,6 +153,8 @@ typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*,
DUID *link_get_duid(Link *link);
int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
+void link_ntp_settings_clear(Link *link);
+void link_dns_settings_clear(Link *link);
Link *link_unref(Link *link);
Link *link_ref(Link *link);
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index a40220d108..37b04ce556 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -1,11 +1,188 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <net/if.h>
+
#include "alloc-util.h"
+#include "bus-common-errors.h"
#include "bus-util.h"
#include "networkd-link-bus.h"
+#include "networkd-link.h"
+#include "networkd-manager-bus.h"
#include "networkd-manager.h"
+#include "path-util.h"
#include "strv.h"
+static int method_list_links(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *manager = userdata;
+ Iterator i;
+ Link *link;
+ int r;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(iso)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(link, manager->links, i) {
+ _cleanup_free_ char *path = NULL;
+
+ path = link_bus_path(link);
+ if (!path)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(
+ reply, "(iso)",
+ link->ifindex,
+ link->ifname,
+ empty_to_root(path));
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_get_link_by_name(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *path = NULL;
+ Manager *manager = userdata;
+ const char *name;
+ int index, r;
+ Link *link;
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ index = if_nametoindex(name);
+ if (index <= 0)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %s not known", name);
+
+ link = hashmap_get(manager->links, INT_TO_PTR(index));
+ if (!link)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %s not known", name);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ path = link_bus_path(link);
+ if (!path)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "io", link->ifindex, empty_to_root(path));
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *path = NULL;
+ Manager *manager = userdata;
+ int32_t index;
+ Link *link;
+ int r;
+
+ r = sd_bus_message_read(message, "i", &index);
+ if (r < 0)
+ return r;
+
+ link = hashmap_get(manager->links, INT_TO_PTR((int) index));
+ if (!link)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %" PRIi32 " not known", index);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ path = link_bus_path(link);
+ if (!path)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "so", link->ifname, empty_to_root(path));
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
+ int ifindex, r;
+ Link *l;
+
+ assert(m);
+ assert(message);
+ assert(handler);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ if (!l)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
+
+ return handler(message, l, error);
+}
+
+static int bus_method_set_link_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_ntp_servers, error);
+}
+
+static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
+}
+
+static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_domains, error);
+}
+
+static int bus_method_set_link_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_default_route, error);
+}
+
+static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
+}
+
+static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_mdns, error);
+}
+
+static int bus_method_set_link_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_over_tls, error);
+}
+
+static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec, error);
+}
+
+static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error);
+}
+
+static int bus_method_revert_link_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_revert_ntp, error);
+}
+
+static int bus_method_revert_link_dns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_revert_dns, error);
+}
+
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -13,6 +190,21 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_METHOD("ListLinks", NULL, "a(iso)", method_list_links, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetLinkByName", "s", "io", method_get_link_by_name, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetLinkByIndex", "i", "so", method_get_link_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkNTP", "ias", NULL, bus_method_set_link_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
+
SD_BUS_VTABLE_END
};
diff --git a/src/network/networkd-manager-bus.h b/src/network/networkd-manager-bus.h
new file mode 100644
index 0000000000..e19d706927
--- /dev/null
+++ b/src/network/networkd-manager-bus.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef struct Manager Manager;
+
+extern const sd_bus_vtable manager_vtable[];
+
+int manager_send_changed_strv(Manager *m, char **properties);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index ec3bb47ea6..51cc8f654b 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -23,6 +23,7 @@
#include "network-internal.h"
#include "networkd-dhcp6.h"
#include "networkd-link-bus.h"
+#include "networkd-manager-bus.h"
#include "networkd-manager.h"
#include "networkd-network-bus.h"
#include "networkd-speed-meter.h"
@@ -1163,15 +1164,15 @@ static int manager_save(Manager *m) {
if (r < 0)
return r;
- r = ordered_set_put_strdupv(ntp, link->network->ntp);
+ r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp);
if (r < 0)
return r;
- r = ordered_set_put_string_set(search_domains, link->network->search_domains);
+ r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains);
if (r < 0)
return r;
- r = ordered_set_put_string_set(route_domains, link->network->route_domains);
+ r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains);
if (r < 0)
return r;
@@ -1432,6 +1433,7 @@ void manager_free(Manager *m) {
sd_device_monitor_unref(m->device_monitor);
+ bus_verify_polkit_async_registry_free(m->polkit_registry);
sd_bus_flush_close_unref(m->bus);
free(m->dynamic_timezone);
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 9b3b05bccc..5422b56dc2 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -25,6 +25,7 @@ struct Manager {
sd_resolve *resolve;
sd_bus *bus;
sd_device_monitor *device_monitor;
+ Hashmap *polkit_registry;
bool enumerating:1;
bool dirty:1;
@@ -65,8 +66,6 @@ struct Manager {
usec_t speed_meter_usec_old;
};
-extern const sd_bus_vtable manager_vtable[];
-
int manager_new(Manager **ret);
void manager_free(Manager *m);
@@ -85,7 +84,6 @@ int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, vo
int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_send_changed_strv(Manager *m, char **properties);
void manager_dirty(Manager *m);
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
diff --git a/src/network/org.freedesktop.network1.conf b/src/network/org.freedesktop.network1.conf
index 52dad33668..366c630702 100644
--- a/src/network/org.freedesktop.network1.conf
+++ b/src/network/org.freedesktop.network1.conf
@@ -20,22 +20,7 @@
</policy>
<policy context="default">
- <deny send_destination="org.freedesktop.network1"/>
-
- <allow send_destination="org.freedesktop.network1"
- send_interface="org.freedesktop.DBus.Introspectable"/>
-
- <allow send_destination="org.freedesktop.network1"
- send_interface="org.freedesktop.DBus.Peer"/>
-
- <allow send_destination="org.freedesktop.network1"
- send_interface="org.freedesktop.DBus.Properties"
- send_member="Get"/>
-
- <allow send_destination="org.freedesktop.network1"
- send_interface="org.freedesktop.DBus.Properties"
- send_member="GetAll"/>
-
+ <allow send_destination="org.freedesktop.network1"/>
<allow receive_sender="org.freedesktop.network1"/>
</policy>
diff --git a/src/network/org.freedesktop.network1.policy b/src/network/org.freedesktop.network1.policy
new file mode 100644
index 0000000000..07091bffe5
--- /dev/null
+++ b/src/network/org.freedesktop.network1.policy
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+
+ This file is part of systemd.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+-->
+
+<policyconfig>
+
+ <vendor>The systemd Project</vendor>
+ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+ <action id="org.freedesktop.network1.set-ntp-servers">
+ <description gettext-domain="systemd">Set NTP servers</description>
+ <message gettext-domain="systemd">Authentication is required to set NTP servers.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-dns-servers">
+ <description gettext-domain="systemd">Set DNS servers</description>
+ <message gettext-domain="systemd">Authentication is required to set DNS servers.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-domains">
+ <description gettext-domain="systemd">Set domains</description>
+ <message gettext-domain="systemd">Authentication is required to set domains.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-default-route">
+ <description gettext-domain="systemd">Set default route</description>
+ <message gettext-domain="systemd">Authentication is required to set default route.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-llmnr">
+ <description gettext-domain="systemd">Enable/disable LLMNR</description>
+ <message gettext-domain="systemd">Authentication is required to enable or disable LLMNR.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-mdns">
+ <description gettext-domain="systemd">Enable/disable multicast DNS</description>
+ <message gettext-domain="systemd">Authentication is required to enable or disable multicast DNS.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-dns-over-tls">
+ <description gettext-domain="systemd">Enable/disable DNS over TLS</description>
+ <message gettext-domain="systemd">Authentication is required to enable or disable DNS over TLS.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-dnssec">
+ <description gettext-domain="systemd">Enable/disable DNSSEC</description>
+ <message gettext-domain="systemd">Authentication is required to enable or disable DNSSEC.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.set-dnssec-negative-trust-anchors">
+ <description gettext-domain="systemd">Set DNSSEC Negative Trust Anchors</description>
+ <message gettext-domain="systemd">Authentication is required to set DNSSEC Negative Trust Anchros.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.revert-ntp">
+ <description gettext-domain="systemd">Revert NTP settings</description>
+ <message gettext-domain="systemd">Authentication is required to revert NTP settings.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.revert-dns">
+ <description gettext-domain="systemd">Revert DNS settings</description>
+ <message gettext-domain="systemd">Authentication is required to revert DNS settings.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+</policyconfig>
diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
index f44d0b8343..8d2ae89281 100644
--- a/src/resolve/resolvectl.c
+++ b/src/resolve/resolvectl.c
@@ -1844,42 +1844,17 @@ static int verb_status(int argc, char **argv, void *userdata) {
return r;
}
-static int log_interface_is_managed(int r, int ifindex) {
- char ifname[IF_NAMESIZE + 1];
-
- return log_error_errno(r,
- "The specified interface %s is managed by systemd-networkd. Operation refused.\n"
- "Please configure DNS settings for systemd-networkd managed interfaces directly in their .network files.",
- strna(format_ifname(ifindex, ifname)));
-}
-
-static int verb_dns(int argc, char **argv, void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int call_dns(sd_bus *bus, char **dns, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
- sd_bus *bus = userdata;
char **p;
int r;
- assert(bus);
-
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(bus, STATUS_DNS);
-
- if (argc < 3)
- return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
-
r = sd_bus_message_new_method_call(
bus,
&req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
+ destination,
+ path,
+ interface,
"SetLinkDNS");
if (r < 0)
return bus_log_create_error(r);
@@ -1894,8 +1869,8 @@ static int verb_dns(int argc, char **argv, void *userdata) {
/* If only argument is the empty string, then call SetLinkDNS() with an
* empty list, which will clear the list of domains for an interface. */
- if (!strv_equal(argv + 2, STRV_MAKE(""))) {
- STRV_FOREACH(p, argv + 2) {
+ if (!strv_equal(dns, STRV_MAKE("")))
+ STRV_FOREACH(p, dns) {
struct in_addr_data data;
r = in_addr_from_string_auto(*p, &data.family, &data.address);
@@ -1918,32 +1893,17 @@ static int verb_dns(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
}
- }
r = sd_bus_message_close_container(req);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, req, 0, &error, NULL);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
-
- return log_error_errno(r, "Failed to set DNS configuration: %s", bus_error_message(&error, r));
- }
-
- return 0;
+ return sd_bus_call(bus, req, 0, error, NULL);
}
-static int verb_domain(int argc, char **argv, void *userdata) {
+static int verb_dns(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
sd_bus *bus = userdata;
- char **p;
int r;
assert(bus);
@@ -1955,17 +1915,47 @@ static int verb_domain(int argc, char **argv, void *userdata) {
}
if (arg_ifindex <= 0)
- return status_all(bus, STATUS_DOMAIN);
+ return status_all(bus, STATUS_DNS);
if (argc < 3)
- return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
+
+ r = call_dns(bus, argv + 2,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ &error);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = call_dns(bus, argv + 2,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ &error);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
+
+ return log_error_errno(r, "Failed to set DNS configuration: %s", bus_error_message(&error, r));
+ }
+
+ return 0;
+}
+
+static int call_domain(sd_bus *bus, char **domain, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+ char **p;
+ int r;
r = sd_bus_message_new_method_call(
bus,
&req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
+ destination,
+ path,
+ interface,
"SetLinkDomains");
if (r < 0)
return bus_log_create_error(r);
@@ -1980,8 +1970,8 @@ static int verb_domain(int argc, char **argv, void *userdata) {
/* If only argument is the empty string, then call SetLinkDomains() with an
* empty list, which will clear the list of domains for an interface. */
- if (!strv_equal(argv + 2, STRV_MAKE(""))) {
- STRV_FOREACH(p, argv + 2) {
+ if (!strv_equal(domain, STRV_MAKE("")))
+ STRV_FOREACH(p, domain) {
const char *n;
n = **p == '~' ? *p + 1 : *p;
@@ -1998,17 +1988,48 @@ static int verb_domain(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
}
- }
r = sd_bus_message_close_container(req);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, req, 0, &error, NULL);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
+ return sd_bus_call(bus, req, 0, error, NULL);
+}
+
+static int verb_domain(int argc, char **argv, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_DOMAIN);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
+
+ r = call_domain(bus, argv + 2,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ &error);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+ r = call_domain(bus, argv + 2,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ &error);
+ }
+ if (r < 0) {
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2042,18 +2063,29 @@ static int verb_default_route(int argc, char **argv, void *userdata) {
if (b < 0)
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkDefaultRoute",
- &error,
- NULL,
- "ib", arg_ifindex, b);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkDefaultRoute",
+ &error,
+ NULL,
+ "ib", arg_ifindex, b);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkDefaultRoute",
+ &error,
+ NULL,
+ "ib", arg_ifindex, b);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2083,18 +2115,29 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkLLMNR",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkLLMNR",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkLLMNR",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2124,18 +2167,29 @@ static int verb_mdns(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkMulticastDNS",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkMulticastDNS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkMulticastDNS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2165,18 +2219,29 @@ static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL);
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkDNSOverTLS",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkDNSOverTLS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkDNSOverTLS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2206,18 +2271,29 @@ static int verb_dnssec(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL);
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkDNSSEC",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkDNSSEC",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkDNSSEC",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2228,9 +2304,33 @@ static int verb_dnssec(int argc, char **argv, void *userdata) {
return 0;
}
+static int call_nta(sd_bus *bus, char **nta, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+ int r;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &req,
+ destination,
+ path,
+ interface,
+ "SetLinkDNSSECNegativeTrustAnchors");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "i", arg_ifindex);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(req, nta);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return sd_bus_call(bus, req, 0, error, NULL);
+}
+
static int verb_nta(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
sd_bus *bus = userdata;
char **p;
int r;
@@ -2265,29 +2365,21 @@ static int verb_nta(int argc, char **argv, void *userdata) {
}
}
- r = sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "SetLinkDNSSECNegativeTrustAnchors");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "i", arg_ifindex);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append_strv(req, clear ? NULL : argv + 2);
- if (r < 0)
- return bus_log_create_error(r);
+ r = call_nta(bus, clear ? NULL : argv + 2,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ &error);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
- r = sd_bus_call(bus, req, 0, &error, NULL);
+ r = call_nta(bus, clear ? NULL : argv + 2,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ &error);
+ }
if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, arg_ifindex);
-
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
@@ -2314,14 +2406,28 @@ static int verb_revert_link(int argc, char **argv, void *userdata) {
if (arg_ifindex <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "RevertLink",
- &error,
- NULL,
- "i", arg_ifindex);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "RevertLink",
+ &error,
+ NULL,
+ "i", arg_ifindex);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "RevertLinkDNS",
+ &error,
+ NULL,
+ "i", arg_ifindex);
+ }
if (r < 0) {
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h
index ea2851b7b1..63c24a023f 100644
--- a/src/resolve/resolved-def.h
+++ b/src/resolve/resolved-def.h
@@ -22,6 +22,3 @@
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
#define SD_RESOLVED_QUERY_TIMEOUT_USEC (120 * USEC_PER_SEC)
-
-/* 127.0.0.53 in native endian */
-#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 78e5953b30..cbb3d524b0 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -739,19 +739,6 @@ void manager_next_dns_server(Manager *m) {
manager_set_dns_server(m, m->dns_servers);
}
-bool dns_server_address_valid(int family, const union in_addr_union *sa) {
-
- /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */
-
- if (in_addr_is_null(family, sa))
- return false;
-
- if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB))
- return false;
-
- return true;
-}
-
DnssecMode dns_server_get_dnssec_mode(DnsServer *s) {
assert(s);
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 2c0457243b..54339355aa 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -132,8 +132,6 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
DnsServer *manager_get_dns_server(Manager *m);
void manager_next_dns_server(Manager *m);
-bool dns_server_address_valid(int family, const union in_addr_union *sa);
-
DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index d65c780791..fc8607f79a 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -14,6 +14,7 @@
#include "af-list.h"
#include "alloc-util.h"
+#include "bus-util.h"
#include "dirent-util.h"
#include "dns-domain.h"
#include "fd-util.h"
@@ -700,6 +701,8 @@ Manager *manager_free(Manager *m) {
manager_mdns_stop(m);
manager_dns_stub_stop(m);
+ bus_verify_polkit_async_registry_free(m->polkit_registry);
+
sd_bus_flush_close_unref(m->bus);
sd_event_source_unref(m->sigusr1_event_source);
diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c
index b433cd24bc..3d14410e32 100644
--- a/src/shared/resolve-util.c
+++ b/src/shared/resolve-util.c
@@ -28,3 +28,16 @@ static const char* const dns_over_tls_mode_table[_DNS_OVER_TLS_MODE_MAX] = {
[DNS_OVER_TLS_YES] = "yes",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, DNS_OVER_TLS_YES);
+
+bool dns_server_address_valid(int family, const union in_addr_union *sa) {
+
+ /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */
+
+ if (in_addr_is_null(family, sa))
+ return false;
+
+ if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB))
+ return false;
+
+ return true;
+}
diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h
index 6ce2508466..cf097dfaa3 100644
--- a/src/shared/resolve-util.h
+++ b/src/shared/resolve-util.h
@@ -2,8 +2,12 @@
#pragma once
#include "conf-parser.h"
+#include "in-addr-util.h"
#include "macro.h"
+/* 127.0.0.53 in native endian */
+#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
+
typedef enum ResolveSupport ResolveSupport;
typedef enum DnssecMode DnssecMode;
typedef enum DnsOverTlsMode DnsOverTlsMode;
@@ -61,3 +65,5 @@ DnssecMode dnssec_mode_from_string(const char *s) _pure_;
const char* dns_over_tls_mode_to_string(DnsOverTlsMode p) _const_;
DnsOverTlsMode dns_over_tls_mode_from_string(const char *s) _pure_;
+
+bool dns_server_address_valid(int family, const union in_addr_union *sa);
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index e03f1796e7..4553699d1c 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -700,6 +700,107 @@ static int show_timesync(int argc, char **argv, void *userdata) {
return 0;
}
+static int parse_ifindex_bus(sd_bus *bus, const char *str, int *ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int32_t i;
+ int r;
+
+ assert(bus);
+ assert(str);
+ assert(ret);
+
+ r = parse_ifindex(str, ret);
+ if (r >= 0)
+ return 0;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "GetLinkByName",
+ &error,
+ &reply,
+ "s", str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get ifindex of interfaces %s: %s", str, bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "io", &i, NULL);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ *ret = i;
+ return 0;
+}
+
+static int verb_ntp_servers(int argc, char **argv, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+ sd_bus *bus = userdata;
+ int ifindex, r;
+
+ assert(bus);
+
+ r = parse_ifindex_bus(bus, argv[1], &ifindex);
+ if (r < 0)
+ return r;
+
+ polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "SetLinkNTP");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "i", ifindex);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(req, argv + 2);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, req, 0, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set NTP servers: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int verb_revert(int argc, char **argv, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int ifindex, r;
+
+ assert(bus);
+
+ r = parse_ifindex_bus(bus, argv[1], &ifindex);
+ if (r < 0)
+ return r;
+
+ polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "RevertLinkNTP",
+ &error,
+ NULL,
+ "i", ifindex);
+ if (r < 0)
+ return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -854,6 +955,8 @@ static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
{ "set-ntp", 2, 2, 0, set_ntp },
{ "timesync-status", VERB_ANY, 1, 0, show_timesync_status },
{ "show-timesync", VERB_ANY, 1, 0, show_timesync },
+ { "ntp-servers", 3, VERB_ANY, 0, verb_ntp_servers },
+ { "revert", 2, 2, 0, verb_revert },
{ "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
{}
};
diff --git a/test/test-network/conf/state-file-tests.network b/test/test-network/conf/state-file-tests.network
new file mode 100644
index 0000000000..d0233841fd
--- /dev/null
+++ b/test/test-network/conf/state-file-tests.network
@@ -0,0 +1,15 @@
+[Match]
+Name=dummy98
+
+[Link]
+RequiredForOnline=routable
+
+[Network]
+DNS=10.10.10.10 10.10.10.11
+NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org
+Domains=hogehoge ~foofoo
+LLMNR=no
+MulticastDNS=yes
+DNSSEC=no
+Address=192.168.10.10/24
+Address=192.168.12.12/24
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 2fbd9ae7b7..7d059f47e7 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -35,6 +35,8 @@ asan_options=None
lsan_options=None
ubsan_options=None
+running_units = []
+
def check_output(*command, **kwargs):
# This replaces both check_output and check_call (output can be ignored)
command = command[0].split() + list(command[1:])
@@ -112,15 +114,18 @@ def expectedFailureIfLinkFileFieldIsNotSet():
return f
def setUpModule():
+ global running_units
+
os.makedirs(network_unit_file_path, exist_ok=True)
os.makedirs(networkd_ci_path, exist_ok=True)
shutil.rmtree(networkd_ci_path)
copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
- check_output('systemctl stop systemd-networkd.socket')
- check_output('systemctl stop systemd-networkd.service')
- check_output('systemctl stop systemd-resolved.service')
+ for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service', 'firewalld.service']:
+ if call(f'systemctl is-active --quiet {u}') == 0:
+ check_output(f'systemctl stop {u}')
+ running_units.append(u)
drop_in = [
'[Service]',
@@ -183,17 +188,19 @@ def setUpModule():
check_output('systemctl restart systemd-resolved')
def tearDownModule():
+ global running_units
+
shutil.rmtree(networkd_ci_path)
- check_output('systemctl stop systemd-networkd.service')
- check_output('systemctl stop systemd-resolved.service')
+ for u in ['systemd-networkd.service', 'systemd-resolved.service']:
+ check_output(f'systemctl stop {u}')
shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
check_output('systemctl daemon-reload')
- check_output('systemctl start systemd-networkd.socket')
- check_output('systemctl start systemd-resolved.service')
+ for u in running_units:
+ check_output(f'systemctl start {u}')
def read_link_attr(link, dev, attribute):
with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f:
@@ -260,13 +267,7 @@ def remove_unit_from_networkd_path(units):
if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))):
shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d'))
-def warn_about_firewalld():
- rc = call('systemctl -q is-active firewalld.service')
- if rc == 0:
- print('\nWARNING: firewalld.service is active. The test may fail.')
-
def start_dnsmasq(additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'):
- warn_about_firewalld()
dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options
check_output(dnsmasq_command)
@@ -1864,6 +1865,98 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
+class NetworkdStateFileTests(unittest.TestCase, Utilities):
+ links = [
+ 'dummy98',
+ ]
+
+ units = [
+ '12-dummy.netdev',
+ 'state-file-tests.network',
+ ]
+
+ def setUp(self):
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
+
+ def tearDown(self):
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
+
+ def test_state_file(self):
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'state-file-tests.network')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output(*networkctl_cmd, '--no-legend', 'list', 'dummy98', env=env)
+ print(output)
+ ifindex = output.split()[0]
+
+ path = os.path.join('/run/systemd/netif/links/', ifindex)
+ self.assertTrue(os.path.exists(path))
+ time.sleep(2)
+
+ with open(path) as f:
+ data = f.read()
+ self.assertRegex(data, r'ADMIN_STATE=configured')
+ self.assertRegex(data, r'OPER_STATE=routable')
+ self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
+ self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
+ self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
+ self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
+ self.assertRegex(data, r'DOMAINS=hogehoge')
+ self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
+ self.assertRegex(data, r'LLMNR=no')
+ self.assertRegex(data, r'MDNS=yes')
+ self.assertRegex(data, r'DNSSEC=no')
+ self.assertRegex(data, r'ADDRESSES=192.168.(?:10.10|12.12)/24 192.168.(?:12.12|10.10)/24')
+
+ check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12', '10.10.10.13', env=env)
+ check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
+ check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
+ check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
+ check_output(*resolvectl_cmd, 'dnssec', 'dummy98', 'yes', env=env)
+ check_output(*timedatectl_cmd, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env=env)
+ time.sleep(2)
+
+ with open(path) as f:
+ data = f.read()
+ self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
+ self.assertRegex(data, r'DOMAINS=hogehogehoge')
+ self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
+ self.assertRegex(data, r'LLMNR=yes')
+ self.assertRegex(data, r'MDNS=no')
+ self.assertRegex(data, r'DNSSEC=yes')
+
+ check_output(*timedatectl_cmd, 'revert', 'dummy98', env=env)
+ time.sleep(2)
+
+ with open(path) as f:
+ data = f.read()
+ self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
+ self.assertRegex(data, r'DOMAINS=hogehogehoge')
+ self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
+ self.assertRegex(data, r'LLMNR=yes')
+ self.assertRegex(data, r'MDNS=no')
+ self.assertRegex(data, r'DNSSEC=yes')
+
+ check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
+ time.sleep(2)
+
+ with open(path) as f:
+ data = f.read()
+ self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
+ self.assertRegex(data, r'DOMAINS=hogehoge')
+ self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
+ self.assertRegex(data, r'LLMNR=no')
+ self.assertRegex(data, r'MDNS=yes')
+ self.assertRegex(data, r'DNSSEC=no')
+
class NetworkdBondTests(unittest.TestCase, Utilities):
links = [
'bond199',
@@ -2166,7 +2259,6 @@ class NetworkdRATests(unittest.TestCase, Utilities):
stop_networkd(show_logs=True)
def test_ipv6_prefix_delegation(self):
- warn_about_firewalld()
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
@@ -2195,7 +2287,6 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
stop_networkd(show_logs=True)
def test_dhcp_server(self):
- warn_about_firewalld()
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])
@@ -2208,7 +2299,6 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'NTP: 192.168.5.1')
def test_emit_router_timezone(self):
- warn_about_firewalld()
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])