Compare commits
10 Commits
323d219df1
...
f3b53486c8
Author | SHA1 | Date |
---|---|---|
Nick Rosbrook | f3b53486c8 | |
Nick Rosbrook | b818627789 | |
Nick Rosbrook | 0c405df64a | |
Nick Rosbrook | 3385de576a | |
Nick Rosbrook | 007e24be8e | |
Nick Rosbrook | 671c07a8ac | |
Nick Rosbrook | efc0b06a9a | |
Nick Rosbrook | df1cc414f3 | |
Nick Rosbrook | ad68334774 | |
Nick Rosbrook | 133314b44e |
|
@ -144,6 +144,22 @@
|
||||||
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--dns</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Waiting for DNS servers to be accessible on each interface. If this
|
||||||
|
option is specified with <option>--any</option>, then
|
||||||
|
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
|
||||||
|
becomes online and has an accessible DNS server.</para>
|
||||||
|
<para>If a link has the property <varname>DefaultRoute=yes</varname> (either because the
|
||||||
|
<varname>DNSDefaultRoute=</varname> network property is explicitly configured, or
|
||||||
|
because the link does not have any "routing-only" domains), or if the search domain "." is
|
||||||
|
configured, then wait for link-specific DNS servers to be accessible. Otherwise, allow global
|
||||||
|
DNS servers to satisfy the condition.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--any</option></term>
|
<term><option>--any</option></term>
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,19 @@ int varlink_callb_and_log(
|
||||||
return varlink_call_and_log(v, method, parameters, ret_parameters);
|
return varlink_call_and_log(v, method, parameters, ret_parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int varlink_many_notify(Set *s, sd_json_variant *parameters) {
|
||||||
|
sd_varlink *link;
|
||||||
|
int r = 1;
|
||||||
|
|
||||||
|
if (set_isempty(s))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SET_FOREACH(link, s)
|
||||||
|
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
int varlink_many_notifyb(Set *s, ...) {
|
int varlink_many_notifyb(Set *s, ...) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -102,12 +115,7 @@ int varlink_many_notifyb(Set *s, ...) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = 1;
|
return varlink_many_notify(s, parameters);
|
||||||
sd_varlink *link;
|
|
||||||
SET_FOREACH(link, s)
|
|
||||||
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int varlink_many_reply(Set *s, sd_json_variant *parameters) {
|
int varlink_many_reply(Set *s, sd_json_variant *parameters) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ int varlink_callb_and_log(sd_varlink *v, const char *method, sd_json_variant **r
|
||||||
#define varlink_callbo_and_log(v, method, ret_parameters, ...) \
|
#define varlink_callbo_and_log(v, method, ret_parameters, ...) \
|
||||||
varlink_callb_and_log((v), (method), (ret_parameters), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
varlink_callb_and_log((v), (method), (ret_parameters), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||||
|
|
||||||
|
int varlink_many_notify(Set *s, sd_json_variant *parameters);
|
||||||
int varlink_many_notifyb(Set *s, ...);
|
int varlink_many_notifyb(Set *s, ...);
|
||||||
#define varlink_many_notifybo(s, ...) \
|
#define varlink_many_notifybo(s, ...) \
|
||||||
varlink_many_notifyb((s), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
varlink_many_notifyb((s), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||||
|
|
|
@ -114,6 +114,7 @@ sources = files(
|
||||||
systemd_networkd_sources = files('networkd.c')
|
systemd_networkd_sources = files('networkd.c')
|
||||||
|
|
||||||
systemd_networkd_wait_online_sources = files(
|
systemd_networkd_wait_online_sources = files(
|
||||||
|
'wait-online/dns-configuration.c',
|
||||||
'wait-online/link.c',
|
'wait-online/link.c',
|
||||||
'wait-online/manager.c',
|
'wait-online/manager.c',
|
||||||
'wait-online/wait-online.c',
|
'wait-online/wait-online.c',
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "sd-json.h"
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "dns-configuration.h"
|
||||||
|
#include "iovec-util.h"
|
||||||
|
#include "hash-funcs.h"
|
||||||
|
#include "json-util.h"
|
||||||
|
#include "set.h"
|
||||||
|
#include "strv.h"
|
||||||
|
|
||||||
|
DNSServer *dns_server_free(DNSServer *s) {
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
free(s->server_name);
|
||||||
|
|
||||||
|
return mfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||||
|
dns_server_hash_ops,
|
||||||
|
void,
|
||||||
|
trivial_hash_func,
|
||||||
|
trivial_compare_func,
|
||||||
|
DNSServer,
|
||||||
|
dns_server_free);
|
||||||
|
|
||||||
|
static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||||
|
static const sd_json_dispatch_field dns_server_dispatch_table[] = {
|
||||||
|
{ "address", SD_JSON_VARIANT_ARRAY, NULL, 0, SD_JSON_MANDATORY },
|
||||||
|
{ "family", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSServer, family), SD_JSON_MANDATORY },
|
||||||
|
{ "port", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint16, offsetof(DNSServer, port), 0 },
|
||||||
|
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSServer, ifindex), SD_JSON_RELAX },
|
||||||
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSServer, server_name), 0 },
|
||||||
|
{ "accessible", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSServer, accessible), SD_JSON_MANDATORY },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
DNSServer **ret = ASSERT_PTR(userdata);
|
||||||
|
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||||
|
_cleanup_(iovec_done) struct iovec iov = {};
|
||||||
|
int r;
|
||||||
|
|
||||||
|
s = new0(DNSServer, 1);
|
||||||
|
if (!s)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sd_json_dispatch(variant, dns_server_dispatch_table, flags, s);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* We ignored the address field above. Handle it now. */
|
||||||
|
r = json_dispatch_byte_array_iovec("address", sd_json_variant_by_key(variant, "address"), flags, &iov);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (iov.iov_len != FAMILY_ADDRESS_SIZE_SAFE(s->family))
|
||||||
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field 'address' is array of unexpected size.");
|
||||||
|
|
||||||
|
memcpy(&s->addr, iov.iov_base, iov.iov_len);
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispatch_dns_server_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||||
|
Set **ret = ASSERT_PTR(userdata);
|
||||||
|
Set *dns_servers;
|
||||||
|
sd_json_variant *v = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
dns_servers = set_new(&dns_server_hash_ops);
|
||||||
|
if (!dns_servers)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
|
||||||
|
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||||
|
|
||||||
|
s = new0(DNSServer, 1);
|
||||||
|
if (!s)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = dispatch_dns_server(name, v, flags, &s);
|
||||||
|
if (r < 0)
|
||||||
|
return json_log(v, flags, r, "JSON array element is not a valid DNSServer.");
|
||||||
|
|
||||||
|
r = set_put(dns_servers, TAKE_PTR(s));
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
set_free_and_replace(*ret, dns_servers);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchDomain *search_domain_free(SearchDomain *d) {
|
||||||
|
if (!d)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
free(d->name);
|
||||||
|
|
||||||
|
return mfree(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||||
|
search_domain_hash_ops,
|
||||||
|
void,
|
||||||
|
trivial_hash_func,
|
||||||
|
trivial_compare_func,
|
||||||
|
SearchDomain,
|
||||||
|
search_domain_free);
|
||||||
|
|
||||||
|
static int dispatch_search_domain(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||||
|
static const sd_json_dispatch_field search_domain_dispatch_table[] = {
|
||||||
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(SearchDomain, name), SD_JSON_MANDATORY },
|
||||||
|
{ "routeOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(SearchDomain, route_only), SD_JSON_MANDATORY },
|
||||||
|
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(SearchDomain, ifindex), SD_JSON_RELAX },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
SearchDomain **ret = ASSERT_PTR(userdata);
|
||||||
|
_cleanup_(search_domain_freep) SearchDomain *d = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
d = new0(SearchDomain, 1);
|
||||||
|
if (!d)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sd_json_dispatch(variant, search_domain_dispatch_table, flags, d);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(d);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispatch_search_domain_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||||
|
Set **ret = ASSERT_PTR(userdata);
|
||||||
|
Set *search_domains;
|
||||||
|
sd_json_variant *v = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
search_domains = set_new(&search_domain_hash_ops);
|
||||||
|
if (!search_domains)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
|
||||||
|
_cleanup_(search_domain_freep) SearchDomain *d = NULL;
|
||||||
|
|
||||||
|
d = new0(SearchDomain, 1);
|
||||||
|
if (!d)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = dispatch_search_domain(name, v, flags, &d);
|
||||||
|
if (r < 0)
|
||||||
|
return json_log(v, flags, r, "JSON array element is not a valid SearchDomain.");
|
||||||
|
|
||||||
|
r = set_put(search_domains, TAKE_PTR(d));
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
set_free_and_replace(*ret, search_domains);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSConfiguration *dns_configuration_free(DNSConfiguration *c) {
|
||||||
|
if (!c)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dns_server_free(c->current_dns_server);
|
||||||
|
set_free(c->dns_servers);
|
||||||
|
set_free(c->search_domains);
|
||||||
|
free(c->ifname);
|
||||||
|
|
||||||
|
return mfree(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispatch_dns_configuration(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||||
|
static const sd_json_dispatch_field dns_configuration_dispatch_table[] = {
|
||||||
|
{ "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
|
||||||
|
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSConfiguration, ifindex), SD_JSON_RELAX },
|
||||||
|
{ "defaultRoute", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, default_route), 0 },
|
||||||
|
{ "currentServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 },
|
||||||
|
{ "servers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 },
|
||||||
|
{ "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, offsetof(DNSConfiguration, search_domains), 0 },
|
||||||
|
{},
|
||||||
|
|
||||||
|
};
|
||||||
|
DNSConfiguration **ret = ASSERT_PTR(userdata);
|
||||||
|
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
c = new0(DNSConfiguration, 1);
|
||||||
|
if (!c)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sd_json_dispatch(variant, dns_configuration_dispatch_table, flags, c);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret) {
|
||||||
|
return dispatch_dns_configuration(NULL, variant, SD_JSON_LOG, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dns_is_accessible(DNSConfiguration *c) {
|
||||||
|
DNSServer *s = NULL;
|
||||||
|
|
||||||
|
if (!c)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (c->current_dns_server && c->current_dns_server->accessible)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SET_FOREACH(s, c->dns_servers)
|
||||||
|
if (s->accessible)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain) {
|
||||||
|
SearchDomain *d = NULL;
|
||||||
|
|
||||||
|
assert(domain);
|
||||||
|
|
||||||
|
if (!c)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SET_FOREACH(d, c->search_domains)
|
||||||
|
if (streq(d->name, domain))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-json.h"
|
||||||
|
|
||||||
|
#include "in-addr-util.h"
|
||||||
|
#include "macro-fundamental.h"
|
||||||
|
#include "set.h"
|
||||||
|
|
||||||
|
typedef struct DNSServer DNSServer;
|
||||||
|
typedef struct SearchDomain SearchDomain;
|
||||||
|
typedef struct DNSConfiguration DNSConfiguration;
|
||||||
|
|
||||||
|
struct DNSServer {
|
||||||
|
union in_addr_union addr;
|
||||||
|
int family;
|
||||||
|
uint16_t port;
|
||||||
|
int ifindex;
|
||||||
|
char *server_name;
|
||||||
|
bool accessible;
|
||||||
|
};
|
||||||
|
|
||||||
|
DNSServer *dns_server_free(DNSServer *s);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSServer*, dns_server_free);
|
||||||
|
|
||||||
|
struct SearchDomain {
|
||||||
|
char *name;
|
||||||
|
bool route_only;
|
||||||
|
int ifindex;
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchDomain *search_domain_free(SearchDomain *d);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free);
|
||||||
|
|
||||||
|
struct DNSConfiguration {
|
||||||
|
char *ifname;
|
||||||
|
int ifindex;
|
||||||
|
bool default_route;
|
||||||
|
DNSServer *current_dns_server;
|
||||||
|
Set *dns_servers;
|
||||||
|
Set *search_domains;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret);
|
||||||
|
bool dns_is_accessible(DNSConfiguration *c);
|
||||||
|
bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain);
|
||||||
|
|
||||||
|
DNSConfiguration *dns_configuration_free(DNSConfiguration *c);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSConfiguration*, dns_configuration_free);
|
|
@ -3,6 +3,7 @@
|
||||||
#include "sd-network.h"
|
#include "sd-network.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "dns-configuration.h"
|
||||||
#include "format-ifname.h"
|
#include "format-ifname.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "link.h"
|
#include "link.h"
|
||||||
|
@ -62,6 +63,8 @@ Link *link_free(Link *l) {
|
||||||
hashmap_remove(l->manager->links_by_name, *n);
|
hashmap_remove(l->manager->links_by_name, *n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dns_configuration_free(l->dns_configuration);
|
||||||
|
|
||||||
free(l->state);
|
free(l->state);
|
||||||
free(l->ifname);
|
free(l->ifname);
|
||||||
strv_free(l->altnames);
|
strv_free(l->altnames);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "sd-netlink.h"
|
#include "sd-netlink.h"
|
||||||
|
|
||||||
|
#include "dns-configuration.h"
|
||||||
#include "log-link.h"
|
#include "log-link.h"
|
||||||
#include "network-util.h"
|
#include "network-util.h"
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ struct Link {
|
||||||
LinkAddressState ipv4_address_state;
|
LinkAddressState ipv4_address_state;
|
||||||
LinkAddressState ipv6_address_state;
|
LinkAddressState ipv6_address_state;
|
||||||
char *state;
|
char *state;
|
||||||
|
DNSConfiguration *dns_configuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname);
|
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname);
|
||||||
|
|
|
@ -4,7 +4,13 @@
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
|
|
||||||
|
#include "sd-event.h"
|
||||||
|
#include "sd-json.h"
|
||||||
|
#include "sd-varlink.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "dns-configuration.h"
|
||||||
|
#include "json-util.h"
|
||||||
#include "link.h"
|
#include "link.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "netlink-util.h"
|
#include "netlink-util.h"
|
||||||
|
@ -133,6 +139,26 @@ static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStat
|
||||||
"No routable IPv6 address is configured.");
|
"No routable IPv6 address is configured.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m->requires_dns) {
|
||||||
|
if (!l->dns_configuration)
|
||||||
|
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||||
|
"No DNS configuration yet");
|
||||||
|
|
||||||
|
/* If a link is configured with DNSDefaultRoute=yes, or is configured with the
|
||||||
|
* search domain '.', then require link-specific DNS servers to be available.
|
||||||
|
* Otherwise, we check the global DNS configuration. */
|
||||||
|
if (l->dns_configuration->default_route ||
|
||||||
|
dns_configuration_contains_search_domain(l->dns_configuration, ".")) {
|
||||||
|
|
||||||
|
if (!dns_is_accessible(l->dns_configuration))
|
||||||
|
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||||
|
"No link-specific DNS server is accessible.");
|
||||||
|
|
||||||
|
} else if (!dns_is_accessible(m->dns_configuration))
|
||||||
|
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||||
|
"No DNS server is accessible.");
|
||||||
|
}
|
||||||
|
|
||||||
log_link_debug(l, "link is configured by networkd and online.");
|
log_link_debug(l, "link is configured by networkd and online.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -381,13 +407,102 @@ static int manager_network_monitor_listen(Manager *m) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int on_dns_configuration_event(
|
||||||
|
sd_varlink *link,
|
||||||
|
sd_json_variant *parameters,
|
||||||
|
const char *error_id,
|
||||||
|
sd_varlink_reply_flags_t flags,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Manager *m = ASSERT_PTR(userdata);
|
||||||
|
sd_json_variant *configurations = NULL, *v = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
if (error_id) {
|
||||||
|
log_warning("DNS configuration event error, ignoring: %s", error_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations = sd_json_variant_by_key(parameters, "configuration");
|
||||||
|
if (!sd_json_variant_is_array(configurations)) {
|
||||||
|
log_warning("DNS configuration JSON data does not have configuration key, ignoring.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(v, configurations) {
|
||||||
|
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||||
|
|
||||||
|
r = dns_configuration_from_json(v, &c);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning_errno(r, "Failed to get DNS configuration JSON, ignoring: %m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->ifindex > 0) {
|
||||||
|
Link *l = hashmap_get(m->links_by_index, INT_TO_PTR(c->ifindex));
|
||||||
|
if (l)
|
||||||
|
free_and_replace_full(l->dns_configuration, c, dns_configuration_free);
|
||||||
|
} else
|
||||||
|
/* Global DNS configuration */
|
||||||
|
free_and_replace_full(m->dns_configuration, c, dns_configuration_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager_configured(m))
|
||||||
|
sd_event_exit(m->event, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int manager_dns_configuration_listen(Manager *m) {
|
||||||
|
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(m->event);
|
||||||
|
|
||||||
|
if (!m->requires_dns)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to connect to io.systemd.Resolve.Monitor: %m");
|
||||||
|
|
||||||
|
r = sd_varlink_set_relative_timeout(vl, USEC_INFINITY);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set varlink timeout: %m");
|
||||||
|
|
||||||
|
r = sd_varlink_attach_event(vl, m->event, SD_EVENT_PRIORITY_NORMAL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||||
|
|
||||||
|
(void) sd_varlink_set_userdata(vl, m);
|
||||||
|
|
||||||
|
r = sd_varlink_bind_reply(vl, on_dns_configuration_event);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to bind varlink reply callback: %m");
|
||||||
|
|
||||||
|
r = sd_varlink_observebo(
|
||||||
|
vl,
|
||||||
|
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration",
|
||||||
|
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", false));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to issue SubscribeDNSConfiguration: %m");
|
||||||
|
|
||||||
|
m->varlink_client = TAKE_PTR(vl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int manager_new(Manager **ret,
|
int manager_new(Manager **ret,
|
||||||
Hashmap *command_line_interfaces_by_name,
|
Hashmap *command_line_interfaces_by_name,
|
||||||
char **ignored_interfaces,
|
char **ignored_interfaces,
|
||||||
LinkOperationalStateRange required_operstate,
|
LinkOperationalStateRange required_operstate,
|
||||||
AddressFamily required_family,
|
AddressFamily required_family,
|
||||||
bool any,
|
bool any,
|
||||||
usec_t timeout) {
|
usec_t timeout,
|
||||||
|
bool requires_dns) {
|
||||||
|
|
||||||
_cleanup_(manager_freep) Manager *m = NULL;
|
_cleanup_(manager_freep) Manager *m = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -404,6 +519,7 @@ int manager_new(Manager **ret,
|
||||||
.required_operstate = required_operstate,
|
.required_operstate = required_operstate,
|
||||||
.required_family = required_family,
|
.required_family = required_family,
|
||||||
.any = any,
|
.any = any,
|
||||||
|
.requires_dns = requires_dns,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sd_event_default(&m->event);
|
r = sd_event_default(&m->event);
|
||||||
|
@ -428,6 +544,10 @@ int manager_new(Manager **ret,
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = manager_dns_configuration_listen(m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
*ret = TAKE_PTR(m);
|
*ret = TAKE_PTR(m);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -445,6 +565,9 @@ Manager* manager_free(Manager *m) {
|
||||||
sd_event_source_unref(m->rtnl_event_source);
|
sd_event_source_unref(m->rtnl_event_source);
|
||||||
sd_netlink_unref(m->rtnl);
|
sd_netlink_unref(m->rtnl);
|
||||||
sd_event_unref(m->event);
|
sd_event_unref(m->event);
|
||||||
|
sd_varlink_unref(m->varlink_client);
|
||||||
|
|
||||||
|
dns_configuration_free(m->dns_configuration);
|
||||||
|
|
||||||
return mfree(m);
|
return mfree(m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
#include "sd-event.h"
|
#include "sd-event.h"
|
||||||
#include "sd-netlink.h"
|
#include "sd-netlink.h"
|
||||||
#include "sd-network.h"
|
#include "sd-network.h"
|
||||||
|
#include "sd-varlink.h"
|
||||||
|
|
||||||
|
#include "dns-configuration.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "network-util.h"
|
#include "network-util.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
|
@ -23,6 +25,7 @@ struct Manager {
|
||||||
LinkOperationalStateRange required_operstate;
|
LinkOperationalStateRange required_operstate;
|
||||||
AddressFamily required_family;
|
AddressFamily required_family;
|
||||||
bool any;
|
bool any;
|
||||||
|
bool requires_dns;
|
||||||
|
|
||||||
sd_netlink *rtnl;
|
sd_netlink *rtnl;
|
||||||
sd_event_source *rtnl_event_source;
|
sd_event_source *rtnl_event_source;
|
||||||
|
@ -31,13 +34,16 @@ struct Manager {
|
||||||
sd_event_source *network_monitor_event_source;
|
sd_event_source *network_monitor_event_source;
|
||||||
|
|
||||||
sd_event *event;
|
sd_event *event;
|
||||||
|
|
||||||
|
sd_varlink *varlink_client;
|
||||||
|
DNSConfiguration *dns_configuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
Manager* manager_free(Manager *m);
|
Manager* manager_free(Manager *m);
|
||||||
int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
|
int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
|
||||||
LinkOperationalStateRange required_operstate,
|
LinkOperationalStateRange required_operstate,
|
||||||
AddressFamily required_family,
|
AddressFamily required_family,
|
||||||
bool any, usec_t timeout);
|
bool any, usec_t timeout, bool requires_dns);
|
||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "daemon-util.h"
|
#include "daemon-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "parse-argument.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
#include "signal-util.h"
|
#include "signal-util.h"
|
||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
|
@ -22,6 +23,7 @@ static char **arg_ignore = NULL;
|
||||||
static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
|
static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
|
||||||
static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
|
static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
|
||||||
static bool arg_any = false;
|
static bool arg_any = false;
|
||||||
|
static bool arg_requires_dns = false;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
||||||
|
@ -48,6 +50,7 @@ static int help(void) {
|
||||||
" -6 --ipv6 Requires at least one IPv6 address\n"
|
" -6 --ipv6 Requires at least one IPv6 address\n"
|
||||||
" --any Wait until at least one of the interfaces is online\n"
|
" --any Wait until at least one of the interfaces is online\n"
|
||||||
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
||||||
|
" --dns Requires at least one DNS server to be accessible\n"
|
||||||
"\nSee the %s for details.\n",
|
"\nSee the %s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
link);
|
link);
|
||||||
|
@ -106,6 +109,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_IGNORE,
|
ARG_IGNORE,
|
||||||
ARG_ANY,
|
ARG_ANY,
|
||||||
ARG_TIMEOUT,
|
ARG_TIMEOUT,
|
||||||
|
ARG_DNS,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -119,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "ipv6", no_argument, NULL, '6' },
|
{ "ipv6", no_argument, NULL, '6' },
|
||||||
{ "any", no_argument, NULL, ARG_ANY },
|
{ "any", no_argument, NULL, ARG_ANY },
|
||||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||||
|
{ "dns", optional_argument, NULL, ARG_DNS },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,6 +183,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return r;
|
return r;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_DNS:
|
||||||
|
r = parse_boolean_argument("--dns", optarg, &arg_requires_dns);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -204,7 +215,14 @@ static int run(int argc, char *argv[]) {
|
||||||
if (arg_quiet)
|
if (arg_quiet)
|
||||||
log_set_max_level(LOG_ERR);
|
log_set_max_level(LOG_ERR);
|
||||||
|
|
||||||
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
|
r = manager_new(&m,
|
||||||
|
arg_interfaces,
|
||||||
|
arg_ignore,
|
||||||
|
arg_required_operstate,
|
||||||
|
arg_required_family,
|
||||||
|
arg_any,
|
||||||
|
arg_timeout,
|
||||||
|
arg_requires_dns);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not create manager: %m");
|
return log_error_errno(r, "Could not create manager: %m");
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,17 @@
|
||||||
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="org.freedesktop.resolve1.subscribe-dns-configuration">
|
||||||
|
<description gettext-domain="systemd">Subscribe to DNS configuration</description>
|
||||||
|
<message gettext-domain="systemd">Authentication is required to subscribe to DNS configuration.</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-resolve</annotate>
|
||||||
|
</action>
|
||||||
|
|
||||||
<action id="org.freedesktop.resolve1.dump-cache">
|
<action id="org.freedesktop.resolve1.dump-cache">
|
||||||
<description gettext-domain="systemd">Dump cache</description>
|
<description gettext-domain="systemd">Dump cache</description>
|
||||||
<message gettext-domain="systemd">Authentication is required to dump cache.</message>
|
<message gettext-domain="systemd">Authentication is required to dump cache.</message>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "sd-json.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dns-domain.h"
|
#include "dns-domain.h"
|
||||||
#include "resolved-dns-search-domain.h"
|
#include "resolved-dns-search-domain.h"
|
||||||
|
@ -197,3 +199,21 @@ int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDo
|
||||||
*ret = NULL;
|
*ret = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret) {
|
||||||
|
int ifindex = 0;
|
||||||
|
|
||||||
|
assert(domain);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (domain->type == DNS_SEARCH_DOMAIN_LINK) {
|
||||||
|
assert(domain->link);
|
||||||
|
ifindex = domain->link->ifindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_json_buildo(
|
||||||
|
ret,
|
||||||
|
SD_JSON_BUILD_PAIR_STRING("name", domain->name),
|
||||||
|
SD_JSON_BUILD_PAIR_BOOLEAN("routeOnly", domain->route_only),
|
||||||
|
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)));
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-json.h"
|
||||||
|
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
|
|
||||||
|
@ -54,3 +56,5 @@ static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
|
||||||
|
|
||||||
|
int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret);
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
|
||||||
#include "sd-messages.h"
|
#include "sd-messages.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "errno-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "json-util.h"
|
||||||
#include "resolved-bus.h"
|
#include "resolved-bus.h"
|
||||||
#include "resolved-dns-server.h"
|
#include "resolved-dns-server.h"
|
||||||
#include "resolved-dns-stub.h"
|
#include "resolved-dns-stub.h"
|
||||||
#include "resolved-manager.h"
|
#include "resolved-manager.h"
|
||||||
#include "resolved-resolv-conf.h"
|
#include "resolved-resolv-conf.h"
|
||||||
#include "siphash24.h"
|
#include "siphash24.h"
|
||||||
|
#include "socket-util.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|
||||||
|
@ -69,6 +75,7 @@ int dns_server_new(
|
||||||
.ifindex = ifindex,
|
.ifindex = ifindex,
|
||||||
.server_name = TAKE_PTR(name),
|
.server_name = TAKE_PTR(name),
|
||||||
.config_source = config_source,
|
.config_source = config_source,
|
||||||
|
.accessible = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
dns_server_reset_features(s);
|
dns_server_reset_features(s);
|
||||||
|
@ -875,6 +882,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
||||||
dns_cache_flush(&m->unicast_scope->cache);
|
dns_cache_flush(&m->unicast_scope->cache);
|
||||||
|
|
||||||
(void) manager_send_changed(m, "CurrentDNSServer");
|
(void) manager_send_changed(m, "CurrentDNSServer");
|
||||||
|
(void) manager_send_dns_configuration_changed(m);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -1128,3 +1136,55 @@ int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret) {
|
||||||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketInvalid", server->packet_invalid),
|
SD_JSON_BUILD_PAIR_BOOLEAN("PacketInvalid", server->packet_invalid),
|
||||||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketDoOff", server->packet_do_off));
|
SD_JSON_BUILD_PAIR_BOOLEAN("PacketDoOff", server->packet_do_off));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dns_server_is_accessible(DnsServer *s) {
|
||||||
|
_cleanup_close_ int fd = -EBADF;
|
||||||
|
union sockaddr_union sa;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
if (s->accessible >= 0)
|
||||||
|
return s->accessible;
|
||||||
|
|
||||||
|
r = sockaddr_set_in_addr(&sa, s->family, &s->address, dns_server_port(s));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
fd = socket(s->family, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||||
|
if (fd < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = RET_NERRNO(connect(fd, &sa.sa, SOCKADDR_LEN(sa)));
|
||||||
|
if (!IN_SET(r, 0, -ENETUNREACH))
|
||||||
|
/* If we did not receive one of the expected return values,
|
||||||
|
* then leave the accessible flag untouched. */
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return (s->accessible = r >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret) {
|
||||||
|
bool accessible = false;
|
||||||
|
int ifindex, r;
|
||||||
|
|
||||||
|
assert(server);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
ifindex = dns_server_ifindex(server);
|
||||||
|
|
||||||
|
r = dns_server_is_accessible(server);
|
||||||
|
if (r < 0)
|
||||||
|
log_debug_errno(r, "Failed to check if %s is accessible, assume not: %m", dns_server_string_full(server));
|
||||||
|
else
|
||||||
|
accessible = r;
|
||||||
|
|
||||||
|
return sd_json_buildo(
|
||||||
|
ret,
|
||||||
|
JSON_BUILD_PAIR_IN_ADDR("address", &server->address, server->family),
|
||||||
|
SD_JSON_BUILD_PAIR_INTEGER("family", server->family),
|
||||||
|
SD_JSON_BUILD_PAIR_UNSIGNED("port", dns_server_port(server)),
|
||||||
|
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||||
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("name", server->server_name),
|
||||||
|
SD_JSON_BUILD_PAIR_BOOLEAN("accessible", accessible));
|
||||||
|
}
|
||||||
|
|
|
@ -106,6 +106,9 @@ struct DnsServer {
|
||||||
|
|
||||||
/* Servers registered via D-Bus are not removed on reload */
|
/* Servers registered via D-Bus are not removed on reload */
|
||||||
ResolveConfigSource config_source;
|
ResolveConfigSource config_source;
|
||||||
|
|
||||||
|
/* Tri-state to indicate if the DNS server is accessible. */
|
||||||
|
int accessible;
|
||||||
};
|
};
|
||||||
|
|
||||||
int dns_server_new(
|
int dns_server_new(
|
||||||
|
@ -187,3 +190,9 @@ static inline bool dns_server_is_fallback(DnsServer *s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret);
|
int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret);
|
||||||
|
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret);
|
||||||
|
|
||||||
|
int dns_server_is_accessible(DnsServer *s);
|
||||||
|
static inline void dns_server_reset_accessible(DnsServer *s) {
|
||||||
|
s->accessible = -1;
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dn
|
||||||
static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
|
static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
|
||||||
static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
|
static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
|
||||||
static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
|
static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
|
||||||
|
static BUS_DEFINE_PROPERTY_GET(property_get_default_route, "b", Link, link_get_default_route);
|
||||||
|
|
||||||
static int property_get_dns_over_tls_mode(
|
static int property_get_dns_over_tls_mode(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
|
@ -160,30 +161,6 @@ static int property_get_domains(
|
||||||
return sd_bus_message_close_container(reply);
|
return sd_bus_message_close_container(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int property_get_default_route(
|
|
||||||
sd_bus *bus,
|
|
||||||
const char *path,
|
|
||||||
const char *interface,
|
|
||||||
const char *property,
|
|
||||||
sd_bus_message *reply,
|
|
||||||
void *userdata,
|
|
||||||
sd_bus_error *error) {
|
|
||||||
|
|
||||||
Link *l = ASSERT_PTR(userdata);
|
|
||||||
|
|
||||||
assert(reply);
|
|
||||||
|
|
||||||
/* Return what is configured, if there's something configured */
|
|
||||||
if (l->default_route >= 0)
|
|
||||||
return sd_bus_message_append(reply, "b", l->default_route);
|
|
||||||
|
|
||||||
/* Otherwise report what is in effect */
|
|
||||||
if (l->unicast_scope)
|
|
||||||
return sd_bus_message_append(reply, "b", dns_scope_is_default_route(l->unicast_scope));
|
|
||||||
|
|
||||||
return sd_bus_message_append(reply, "b", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int property_get_scopes_mask(
|
static int property_get_scopes_mask(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
@ -293,6 +270,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
|
||||||
(void) link_save_user(l);
|
(void) link_save_user(l);
|
||||||
(void) manager_write_resolv_conf(l->manager);
|
(void) manager_write_resolv_conf(l->manager);
|
||||||
(void) manager_send_changed(l->manager, "DNS");
|
(void) manager_send_changed(l->manager, "DNS");
|
||||||
|
(void) manager_send_dns_configuration_changed(l->manager);
|
||||||
|
|
||||||
if (j)
|
if (j)
|
||||||
log_link_info(l, "Bus client set DNS server list to: %s", j);
|
log_link_info(l, "Bus client set DNS server list to: %s", j);
|
||||||
|
@ -768,6 +746,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
|
||||||
(void) link_save_user(l);
|
(void) link_save_user(l);
|
||||||
(void) manager_write_resolv_conf(l->manager);
|
(void) manager_write_resolv_conf(l->manager);
|
||||||
(void) manager_send_changed(l->manager, "DNS");
|
(void) manager_send_changed(l->manager, "DNS");
|
||||||
|
(void) manager_send_dns_configuration_changed(l->manager);
|
||||||
|
|
||||||
return sd_bus_reply_method_return(message, NULL);
|
return sd_bus_reply_method_return(message, NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "netif-util.h"
|
#include "netif-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
#include "resolved-dns-scope.h"
|
||||||
#include "resolved-link.h"
|
#include "resolved-link.h"
|
||||||
#include "resolved-llmnr.h"
|
#include "resolved-llmnr.h"
|
||||||
#include "resolved-mdns.h"
|
#include "resolved-mdns.h"
|
||||||
|
@ -299,6 +300,15 @@ static int link_update_dns_servers(Link *l) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dns_server_unlink_marked(l->dns_servers);
|
dns_server_unlink_marked(l->dns_servers);
|
||||||
|
|
||||||
|
/* If the link state changed, we should re-check if DNS servers
|
||||||
|
* are accessible. */
|
||||||
|
LIST_FOREACH(servers, s, l->dns_servers)
|
||||||
|
dns_server_reset_accessible(s);
|
||||||
|
|
||||||
|
LIST_FOREACH(servers, s, l->manager->dns_servers)
|
||||||
|
dns_server_reset_accessible(s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clear:
|
clear:
|
||||||
|
@ -831,6 +841,20 @@ ResolveSupport link_get_mdns_support(Link *link) {
|
||||||
return MIN(link->mdns_support, link->manager->mdns_support);
|
return MIN(link->mdns_support, link->manager->mdns_support);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool link_get_default_route(Link *l) {
|
||||||
|
assert(l);
|
||||||
|
|
||||||
|
/* Return what is configured, if there's something configured */
|
||||||
|
if (l->default_route >= 0)
|
||||||
|
return l->default_route;
|
||||||
|
|
||||||
|
/* Otherwise report what is in effect */
|
||||||
|
if (l->unicast_scope)
|
||||||
|
return dns_scope_is_default_route(l->unicast_scope);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int link_address_new(Link *l,
|
int link_address_new(Link *l,
|
||||||
LinkAddress **ret,
|
LinkAddress **ret,
|
||||||
int family,
|
int family,
|
||||||
|
|
|
@ -109,6 +109,8 @@ DnsOverTlsMode link_get_dns_over_tls_mode(Link *l);
|
||||||
ResolveSupport link_get_llmnr_support(Link *link);
|
ResolveSupport link_get_llmnr_support(Link *link);
|
||||||
ResolveSupport link_get_mdns_support(Link *link);
|
ResolveSupport link_get_mdns_support(Link *link);
|
||||||
|
|
||||||
|
bool link_get_default_route(Link *l);
|
||||||
|
|
||||||
int link_save_user(Link *l);
|
int link_save_user(Link *l);
|
||||||
int link_load_user(Link *l);
|
int link_load_user(Link *l);
|
||||||
void link_remove_user(Link *l);
|
void link_remove_user(Link *l);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "idn-util.h"
|
#include "idn-util.h"
|
||||||
#include "io-util.h"
|
#include "io-util.h"
|
||||||
#include "iovec-util.h"
|
#include "iovec-util.h"
|
||||||
|
#include "json-util.h"
|
||||||
#include "memstream-util.h"
|
#include "memstream-util.h"
|
||||||
#include "missing_network.h"
|
#include "missing_network.h"
|
||||||
#include "missing_socket.h"
|
#include "missing_socket.h"
|
||||||
|
@ -196,6 +197,40 @@ fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int manager_process_route(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
|
||||||
|
Manager *m = ASSERT_PTR(userdata);
|
||||||
|
Link *l;
|
||||||
|
uint16_t type;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(rtnl);
|
||||||
|
assert(mm);
|
||||||
|
|
||||||
|
r = sd_netlink_message_get_type(mm, &type);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning_errno(r, "Failed not get message type, ignoring: %m");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
|
||||||
|
log_warning("Unexpected message type %u when processing route, ignoring.", type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Routing table changes may affect the accessibility of DNS servers. */
|
||||||
|
HASHMAP_FOREACH(l, m->links) {
|
||||||
|
LIST_FOREACH(servers, s, l->dns_servers)
|
||||||
|
dns_server_reset_accessible(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(servers, s, m->dns_servers)
|
||||||
|
dns_server_reset_accessible(s);
|
||||||
|
|
||||||
|
(void) manager_send_dns_configuration_changed(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int manager_rtnl_listen(Manager *m) {
|
static int manager_rtnl_listen(Manager *m) {
|
||||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -227,6 +262,14 @@ static int manager_rtnl_listen(Manager *m) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, manager_process_route, NULL, m, "resolve-NEWROUTE");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, manager_process_route, NULL, m, "resolve-DELROUTE");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
/* Then, enumerate all links */
|
/* Then, enumerate all links */
|
||||||
r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
|
r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -286,6 +329,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
|
||||||
|
|
||||||
(void) manager_write_resolv_conf(m);
|
(void) manager_write_resolv_conf(m);
|
||||||
(void) manager_send_changed(m, "DNS");
|
(void) manager_send_changed(m, "DNS");
|
||||||
|
(void) manager_send_dns_configuration_changed(m);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1184,7 +1228,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (set_isempty(m->varlink_subscription))
|
if (set_isempty(m->varlink_query_results_subscription))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Merge all questions into one */
|
/* Merge all questions into one */
|
||||||
|
@ -1234,7 +1278,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
r = varlink_many_notifybo(
|
r = varlink_many_notifybo(
|
||||||
m->varlink_subscription,
|
m->varlink_query_results_subscription,
|
||||||
SD_JSON_BUILD_PAIR("state", SD_JSON_BUILD_STRING(dns_transaction_state_to_string(q->state))),
|
SD_JSON_BUILD_PAIR("state", SD_JSON_BUILD_STRING(dns_transaction_state_to_string(q->state))),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_DNSSEC_FAILED,
|
SD_JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_DNSSEC_FAILED,
|
||||||
"result", SD_JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
|
"result", SD_JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
|
||||||
|
@ -1932,3 +1976,124 @@ void dns_manager_reset_statistics(Manager *m) {
|
||||||
m->n_failure_responses_served_stale_total = 0;
|
m->n_failure_responses_served_stale_total = 0;
|
||||||
zero(m->n_dnssec_verdict);
|
zero(m->n_dnssec_verdict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dns_configuration_json_append(
|
||||||
|
const char *ifname,
|
||||||
|
int ifindex,
|
||||||
|
int default_route,
|
||||||
|
DnsServer *current_dns_server,
|
||||||
|
DnsServer *dns_servers,
|
||||||
|
DnsSearchDomain *search_domains,
|
||||||
|
sd_json_variant **configuration) {
|
||||||
|
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *dns_servers_json = NULL,
|
||||||
|
*search_domains_json = NULL,
|
||||||
|
*current_dns_server_json = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(configuration);
|
||||||
|
|
||||||
|
r = sd_json_variant_new_array(&dns_servers_json, NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_json_variant_new_array(&search_domains_json, NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (current_dns_server) {
|
||||||
|
r = dns_server_dump_configuration_to_json(current_dns_server, ¤t_dns_server_json);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(servers, s, dns_servers) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||||
|
|
||||||
|
r = dns_server_dump_configuration_to_json(s, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_json_variant_append_array(&dns_servers_json, v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(domains, d, search_domains) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||||
|
|
||||||
|
r = dns_search_domain_dump_to_json(d, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_json_variant_append_array(&search_domains_json, v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_json_variant_append_arraybo(
|
||||||
|
configuration,
|
||||||
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("ifname", ifname),
|
||||||
|
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||||
|
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "defaultRoute", SD_JSON_BUILD_BOOLEAN(default_route > 0)),
|
||||||
|
JSON_BUILD_PAIR_VARIANT_NON_NULL("currentServer", current_dns_server_json),
|
||||||
|
SD_JSON_BUILD_PAIR_VARIANT("servers", dns_servers_json),
|
||||||
|
SD_JSON_BUILD_PAIR_VARIANT("searchDomains", search_domains_json));
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||||
|
Link *l;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
/* Global DNS configuration */
|
||||||
|
r = dns_configuration_json_append(
|
||||||
|
/* ifname = */ NULL,
|
||||||
|
/* ifindex = */ 0,
|
||||||
|
/* default_route = */ 0,
|
||||||
|
manager_get_dns_server(m),
|
||||||
|
m->dns_servers,
|
||||||
|
m->search_domains,
|
||||||
|
&configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Append configuration for each link */
|
||||||
|
HASHMAP_FOREACH(l, m->links) {
|
||||||
|
r = dns_configuration_json_append(
|
||||||
|
l->ifname,
|
||||||
|
l->ifindex,
|
||||||
|
link_get_default_route(l),
|
||||||
|
link_get_dns_server(l),
|
||||||
|
l->dns_servers,
|
||||||
|
l->search_domains,
|
||||||
|
&configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_json_buildo(ret, SD_JSON_BUILD_PAIR_VARIANT("configuration", configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_send_dns_configuration_changed(Manager *m) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
if (set_isempty(m->varlink_dns_configuration_subscription))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return log_warning_errno(r, "Failed to dump DNS configuration json: %m");
|
||||||
|
|
||||||
|
r = varlink_many_notify(m->varlink_dns_configuration_subscription, configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return log_warning_errno(r, "Failed to send DNS configuration event: %m");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -152,7 +152,8 @@ struct Manager {
|
||||||
sd_varlink_server *varlink_server;
|
sd_varlink_server *varlink_server;
|
||||||
sd_varlink_server *varlink_monitor_server;
|
sd_varlink_server *varlink_monitor_server;
|
||||||
|
|
||||||
Set *varlink_subscription;
|
Set *varlink_query_results_subscription;
|
||||||
|
Set *varlink_dns_configuration_subscription;
|
||||||
|
|
||||||
sd_event_source *clock_change_event_source;
|
sd_event_source *clock_change_event_source;
|
||||||
|
|
||||||
|
@ -225,3 +226,6 @@ int socket_disable_pmtud(int fd, int af);
|
||||||
int dns_manager_dump_statistics_json(Manager *m, sd_json_variant **ret);
|
int dns_manager_dump_statistics_json(Manager *m, sd_json_variant **ret);
|
||||||
|
|
||||||
void dns_manager_reset_statistics(Manager *m);
|
void dns_manager_reset_statistics(Manager *m);
|
||||||
|
|
||||||
|
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret);
|
||||||
|
int manager_send_dns_configuration_changed(Manager *m);
|
||||||
|
|
|
@ -125,14 +125,21 @@ static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userd
|
||||||
|
|
||||||
static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
|
static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
|
||||||
Manager *m = ASSERT_PTR(userdata);
|
Manager *m = ASSERT_PTR(userdata);
|
||||||
|
sd_varlink *removed_link = NULL;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
assert(link);
|
assert(link);
|
||||||
|
|
||||||
sd_varlink *removed_link = set_remove(m->varlink_subscription, link);
|
removed_link = set_remove(m->varlink_query_results_subscription, link);
|
||||||
if (removed_link) {
|
if (removed_link) {
|
||||||
sd_varlink_unref(removed_link);
|
sd_varlink_unref(removed_link);
|
||||||
log_debug("%u monitor clients remain active", set_size(m->varlink_subscription));
|
log_debug("%u query result monitor clients remain active", set_size(m->varlink_query_results_subscription));
|
||||||
|
}
|
||||||
|
|
||||||
|
removed_link = set_remove(m->varlink_dns_configuration_subscription, link);
|
||||||
|
if (removed_link) {
|
||||||
|
sd_varlink_unref(removed_link);
|
||||||
|
log_debug("%u DNS monitor clients remain active", set_size(m->varlink_dns_configuration_subscription));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,12 +1234,12 @@ static int vl_method_subscribe_query_results(sd_varlink *link, sd_json_variant *
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to report monitor to be established: %m");
|
return log_error_errno(r, "Failed to report monitor to be established: %m");
|
||||||
|
|
||||||
r = set_ensure_put(&m->varlink_subscription, NULL, link);
|
r = set_ensure_put(&m->varlink_query_results_subscription, NULL, link);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||||
sd_varlink_ref(link);
|
sd_varlink_ref(link);
|
||||||
|
|
||||||
log_debug("%u clients now attached for varlink notifications", set_size(m->varlink_subscription));
|
log_debug("%u clients now attached for query result varlink notifications", set_size(m->varlink_query_results_subscription));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1352,6 +1359,38 @@ static int vl_method_reset_statistics(sd_varlink *link, sd_json_variant *paramet
|
||||||
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
|
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vl_method_subscribe_dns_configuration(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||||
|
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link)));
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* if the client didn't set the more flag, it is using us incorrectly */
|
||||||
|
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||||
|
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||||
|
|
||||||
|
r = verify_polkit(link, parameters, "org.freedesktop.resolve1.subscribe-dns-configuration");
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to dump current DNS configuration: %m");
|
||||||
|
|
||||||
|
r = sd_varlink_notify(link, configuration);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to send current DNS configuration: %m");
|
||||||
|
|
||||||
|
r = set_ensure_put(&m->varlink_dns_configuration_subscription, NULL, link);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||||
|
sd_varlink_ref(link);
|
||||||
|
|
||||||
|
log_debug("%u clients now attached for link configuration varlink notifications",
|
||||||
|
set_size(m->varlink_dns_configuration_subscription));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int varlink_monitor_server_init(Manager *m) {
|
static int varlink_monitor_server_init(Manager *m) {
|
||||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
|
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -1377,7 +1416,8 @@ static int varlink_monitor_server_init(Manager *m) {
|
||||||
"io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache,
|
"io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache,
|
||||||
"io.systemd.Resolve.Monitor.DumpServerState", vl_method_dump_server_state,
|
"io.systemd.Resolve.Monitor.DumpServerState", vl_method_dump_server_state,
|
||||||
"io.systemd.Resolve.Monitor.DumpStatistics", vl_method_dump_statistics,
|
"io.systemd.Resolve.Monitor.DumpStatistics", vl_method_dump_statistics,
|
||||||
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics);
|
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics,
|
||||||
|
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration", vl_method_subscribe_dns_configuration);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,52 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||||
ResetStatistics,
|
ResetStatistics,
|
||||||
VARLINK_DEFINE_POLKIT_INPUT);
|
VARLINK_DEFINE_POLKIT_INPUT);
|
||||||
|
|
||||||
|
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||||
|
DNSServer,
|
||||||
|
SD_VARLINK_FIELD_COMMENT("IPv4 or IPv6 address of the server."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Address family of the server, one of AF_INET or AF_INET6."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(family, SD_VARLINK_INT, 0),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Port number of the server."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(port, SD_VARLINK_INT, 0),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Interface index for which this server is configured."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Server Name Indication (SNI) of the server."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Indicates if the DNS server is accessible or not."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(accessible, SD_VARLINK_BOOL, 0));
|
||||||
|
|
||||||
|
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||||
|
SearchDomain,
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Domain name."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Indicates whether or not this is a routing-only domain."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(routeOnly, SD_VARLINK_BOOL, 0),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Interface index for which this search domain is configured."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||||
|
DNSConfiguration,
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Interface name, if any, associated with this configuration. Empty for global configuration."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(ifname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Interface index, if any, associated with this configuration. Empty for global configuration."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Indicates whether or not this link's DNS servers will be used for resolving domain names that do not match any link's configured domains."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD(defaultRoute, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("DNS server currently selected to use for lookups."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD_BY_TYPE(currentServer, DNSServer, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Array of configured DNS servers."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD_BY_TYPE(servers, DNSServer, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Array of configured search domains."),
|
||||||
|
SD_VARLINK_DEFINE_FIELD_BY_TYPE(searchDomains, SearchDomain, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||||
|
SubscribeDNSConfiguration,
|
||||||
|
SD_VARLINK_REQUIRES_MORE,
|
||||||
|
SD_VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("The current global and per-interface DNS configurations"),
|
||||||
|
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(configuration, DNSConfiguration, SD_VARLINK_ARRAY));
|
||||||
|
|
||||||
SD_VARLINK_DEFINE_INTERFACE(
|
SD_VARLINK_DEFINE_INTERFACE(
|
||||||
io_systemd_Resolve_Monitor,
|
io_systemd_Resolve_Monitor,
|
||||||
"io.systemd.Resolve.Monitor",
|
"io.systemd.Resolve.Monitor",
|
||||||
|
@ -129,4 +175,12 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||||
&vl_type_TransactionStatistics,
|
&vl_type_TransactionStatistics,
|
||||||
&vl_type_CacheStatistics,
|
&vl_type_CacheStatistics,
|
||||||
&vl_type_DnssecStatistics,
|
&vl_type_DnssecStatistics,
|
||||||
&vl_type_ServerState);
|
&vl_type_ServerState,
|
||||||
|
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a DNS server address specification."),
|
||||||
|
&vl_type_DNSServer,
|
||||||
|
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a search domain specification."),
|
||||||
|
&vl_type_SearchDomain,
|
||||||
|
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a global or per-link DNS configuration, including configured DNS servers, search domains, and more."),
|
||||||
|
&vl_type_DNSConfiguration,
|
||||||
|
SD_VARLINK_SYMBOL_COMMENT("Sends the complete global and per-link DNS configurations when any changes are made to them. The current configurations are given immediately when this method is invoked."),
|
||||||
|
&vl_method_SubscribeDNSConfiguration);
|
||||||
|
|
|
@ -1086,6 +1086,134 @@ DNS=127.0.0.1
|
||||||
self.show_journal('systemd-timedated.service')
|
self.show_journal('systemd-timedated.service')
|
||||||
self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu')
|
self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu')
|
||||||
|
|
||||||
|
def do_test_wait_online_dns(
|
||||||
|
self,
|
||||||
|
iface,
|
||||||
|
global_dns='',
|
||||||
|
fallback_dns='',
|
||||||
|
expect_timeout=False
|
||||||
|
):
|
||||||
|
self.start_unit('systemd-networkd')
|
||||||
|
self.start_unit('systemd-resolved')
|
||||||
|
|
||||||
|
# Unless given, clear global DNS configuration
|
||||||
|
conf = '/run/systemd/resolved.conf.d/global-dns.conf'
|
||||||
|
os.makedirs(os.path.dirname(conf), exist_ok=True)
|
||||||
|
with open(conf, 'w') as f:
|
||||||
|
f.write((
|
||||||
|
'[Resolve]\n'
|
||||||
|
f'DNS={global_dns}\n'
|
||||||
|
f'FallbackDNS={fallback_dns}\n'
|
||||||
|
))
|
||||||
|
self.addCleanup(os.remove, conf)
|
||||||
|
subprocess.check_call(
|
||||||
|
['systemctl', 'reload', 'systemd-resolved']
|
||||||
|
)
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['SYSTEMD_LOG_LEVEL'] = 'debug'
|
||||||
|
env['SYSTEMD_LOG_TARGET'] = 'console'
|
||||||
|
r = subprocess.run(
|
||||||
|
[NETWORKD_WAIT_ONLINE, '--dns', '--interface', iface, '--timeout=10'],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
if expect_timeout:
|
||||||
|
self.assertNotEqual(
|
||||||
|
r.returncode,
|
||||||
|
0,
|
||||||
|
f'Command succeeded unexpectedly:\n{r.stderr.decode()}'
|
||||||
|
)
|
||||||
|
self.assertRegex(
|
||||||
|
r.stderr.decode(),
|
||||||
|
rf'{iface}: No link-specific DNS server is accessible',
|
||||||
|
f'Missing expected log message:\n{r.stderr.decode()}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assertEqual(
|
||||||
|
r.returncode,
|
||||||
|
0,
|
||||||
|
f'Command timed out:\n{r.stderr.decode()}'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_wait_online_dns(self):
|
||||||
|
''' test systemd-networkd-wait-online with --dns '''
|
||||||
|
self.write_network(
|
||||||
|
self.config,
|
||||||
|
(
|
||||||
|
'[Match]\n'
|
||||||
|
f'Name={self.iface}\n'
|
||||||
|
'[Network]\n'
|
||||||
|
'DHCP=ipv4\n'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.create_iface()
|
||||||
|
self.do_test_wait_online_dns(self.iface)
|
||||||
|
|
||||||
|
def test_wait_online_dns_global(self):
|
||||||
|
'''
|
||||||
|
test systemd-networkd-wait-online with --dns, expect pass due to global DNS
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Set UseDNS=no, and allow global DNS to be used.
|
||||||
|
self.write_network(
|
||||||
|
self.config,
|
||||||
|
(
|
||||||
|
'[Match]\n'
|
||||||
|
f'Name={self.iface}\n'
|
||||||
|
'[Network]\n'
|
||||||
|
'DHCP=ipv4\n'
|
||||||
|
'[DHCPv4]\n'
|
||||||
|
'UseDNS=no\n'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.create_iface()
|
||||||
|
self.do_test_wait_online_dns(self.iface, global_dns='192.168.5.1')
|
||||||
|
|
||||||
|
def test_wait_online_dns_expect_timeout(self):
|
||||||
|
''' test systemd-networkd-wait-online with --dns, and expect timeout '''
|
||||||
|
|
||||||
|
# Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
|
||||||
|
self.write_network(
|
||||||
|
self.config,
|
||||||
|
(
|
||||||
|
'[Match]\n'
|
||||||
|
f'Name={self.iface}\n'
|
||||||
|
'[Network]\n'
|
||||||
|
'DHCP=ipv4\n'
|
||||||
|
'DNSDefaultRoute=yes\n'
|
||||||
|
'[DHCPv4]\n'
|
||||||
|
'UseDNS=no\n'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.create_iface()
|
||||||
|
self.do_test_wait_online_dns(self.iface, expect_timeout=True)
|
||||||
|
|
||||||
|
def test_wait_online_dns_expect_timeout_global(self):
|
||||||
|
'''
|
||||||
|
test systemd-networkd-wait-online with --dns, and expect timeout
|
||||||
|
despite global DNS
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Configure Domains=~., and expect timeout despite global DNS servers
|
||||||
|
# being available.
|
||||||
|
self.write_network(
|
||||||
|
self.config,
|
||||||
|
(
|
||||||
|
'[Match]\n'
|
||||||
|
f'Name={self.iface}\n'
|
||||||
|
'[Network]\n'
|
||||||
|
'DHCP=ipv4\n'
|
||||||
|
'Domains=~.\n'
|
||||||
|
'[DHCPv4]\n'
|
||||||
|
'UseDNS=no\n'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.create_iface()
|
||||||
|
self.do_test_wait_online_dns(self.iface, expect_timeout=True, global_dns='192.168.5.1')
|
||||||
|
|
||||||
|
|
||||||
class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
|
class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
|
||||||
"""Test [Match] sections in .network files.
|
"""Test [Match] sections in .network files.
|
||||||
|
|
|
@ -934,6 +934,15 @@ testcase_11_nft() {
|
||||||
|
|
||||||
# Test resolvectl show-server-state
|
# Test resolvectl show-server-state
|
||||||
testcase_12_resolvectl2() {
|
testcase_12_resolvectl2() {
|
||||||
|
# Cleanup
|
||||||
|
# shellcheck disable=SC2317
|
||||||
|
cleanup() {
|
||||||
|
rm -f /run/systemd/resolved.conf.d/reload.conf
|
||||||
|
systemctl reload systemd-resolved.service
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup RETURN
|
||||||
|
|
||||||
run resolvectl show-server-state
|
run resolvectl show-server-state
|
||||||
grep -qF "10.0.0.1" "$RUN_OUT"
|
grep -qF "10.0.0.1" "$RUN_OUT"
|
||||||
grep -qF "Interface" "$RUN_OUT"
|
grep -qF "Interface" "$RUN_OUT"
|
||||||
|
@ -996,6 +1005,78 @@ testcase_12_resolvectl2() {
|
||||||
restart_resolved
|
restart_resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Test io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||||
|
testcase_13_varlink_subscribe_dns_configuration() {
|
||||||
|
# Cleanup
|
||||||
|
# shellcheck disable=SC2317
|
||||||
|
cleanup() {
|
||||||
|
echo "===== io.systemd.Resolve.Monitor.SubscribeDNSConfiguration output: ====="
|
||||||
|
cat "$tmpfile"
|
||||||
|
echo "=========="
|
||||||
|
rm -f /run/systemd/resolved.conf.d/global-dns.conf
|
||||||
|
restart_resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup RETURN
|
||||||
|
|
||||||
|
local unit
|
||||||
|
local tmpfile
|
||||||
|
|
||||||
|
unit="subscribe-dns-configuration-$(systemd-id128 new -u).service"
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
|
||||||
|
# Clear global and per-interface DNS before monitoring the configuration change.
|
||||||
|
mkdir -p /run/systemd/resolved.conf.d/
|
||||||
|
{
|
||||||
|
echo "[Resolve]"
|
||||||
|
echo "DNS="
|
||||||
|
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||||
|
systemctl reload systemd-resolved.service
|
||||||
|
resolvectl dns dns0 ""
|
||||||
|
|
||||||
|
# Start the call to io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||||
|
systemd-run -u "$unit" -p "Type=exec" -p "StandardOutput=truncate:$tmpfile" \
|
||||||
|
varlinkctl call --more --timeout=5 --graceful=io.systemd.TimedOut /run/systemd/resolve/io.systemd.Resolve.Monitor io.systemd.Resolve.Monitor.SubscribeDNSConfiguration '{}'
|
||||||
|
|
||||||
|
# Update the global configuration.
|
||||||
|
mkdir -p /run/systemd/resolved.conf.d/
|
||||||
|
{
|
||||||
|
echo "[Resolve]"
|
||||||
|
echo "DNS=8.8.8.8"
|
||||||
|
echo "Domains=lan"
|
||||||
|
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||||
|
systemctl reload systemd-resolved.service
|
||||||
|
|
||||||
|
# Update a link configuration.
|
||||||
|
resolvectl dns dns0 8.8.4.4 1.1.1.1
|
||||||
|
|
||||||
|
# Wait for the monitor to exit gracefully.
|
||||||
|
while systemctl --quiet is-active "$unit"; do
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
# Hack to remove the "Method call returned expected error" line from the output.
|
||||||
|
sed -i '/^Method call.*returned expected error/d' "$tmpfile"
|
||||||
|
|
||||||
|
# Check that an initial reply was given with the settings applied BEFORE the monitor started.
|
||||||
|
grep -qF \
|
||||||
|
'{"global":{"servers":[],"domains":[]}}' \
|
||||||
|
<(jq -cr --seq '.configuration[] | select(.ifname == null) | {"global": {servers: .servers, domains: .searchDomains}}' "$tmpfile")
|
||||||
|
grep -qF \
|
||||||
|
'{"dns0":{"servers":[],"domains":[]}}' \
|
||||||
|
<(jq -cr --seq '.configuration[] | select(.ifname == "dns0") | {"dns0": {servers: .servers, domains: .searchDomains}}' "$tmpfile")
|
||||||
|
|
||||||
|
# Check that the global configuration change was reflected.
|
||||||
|
grep -qF \
|
||||||
|
'{"global":{"servers":[[8,8,8,8]],"domains":["lan"]}}' \
|
||||||
|
<(jq -cr --seq '.configuration[] | select(.ifname == null) | {"global":{servers: [.servers[] | .address], domains: [.searchDomains[] | .name]}}' "$tmpfile")
|
||||||
|
|
||||||
|
# Check that the link configuration change was reflected.
|
||||||
|
grep -qF \
|
||||||
|
'{"dns0":{"servers":[[8,8,4,4],[1,1,1,1]],"domains":[]}}' \
|
||||||
|
<(jq -cr --seq '.configuration[] | select(.ifname == "dns0") | {"dns0":{servers: [.servers[] | .address], domains: [.searchDomains[] | .name]}}' "$tmpfile")
|
||||||
|
}
|
||||||
|
|
||||||
# PRE-SETUP
|
# PRE-SETUP
|
||||||
systemctl unmask systemd-resolved.service
|
systemctl unmask systemd-resolved.service
|
||||||
systemctl enable --now systemd-resolved.service
|
systemctl enable --now systemd-resolved.service
|
||||||
|
|
|
@ -14,7 +14,7 @@ ConditionCapability=CAP_NET_ADMIN
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Conflicts=shutdown.target
|
Conflicts=shutdown.target
|
||||||
BindsTo=systemd-networkd.service
|
BindsTo=systemd-networkd.service
|
||||||
After=systemd-networkd.service
|
After=systemd-networkd.service systemd-resolved.service
|
||||||
Before=network-online.target shutdown.target
|
Before=network-online.target shutdown.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|
Loading…
Reference in New Issue