diff options
author | Lennart Poettering <lennart@poettering.net> | 2019-07-17 12:07:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-17 12:07:39 +0200 |
commit | 52f4b5ac8a046172aacf3fa441e89997445b80ed (patch) | |
tree | 47c9c619c6ac2b8408c32fde3d66ac36b2193c8f | |
parent | d34a40082db3ffca8de66bfa4df50951101bdae5 (diff) | |
parent | 217246a990cc34155f02fcecce1df95ca2e1abed (diff) | |
download | systemd-52f4b5ac8a046172aacf3fa441e89997445b80ed.tar.gz |
Merge pull request #12675 from yuwata/network-set-dns
resolvectl: support to modify interfaces managed by networkd
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']) |