mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
resolve: extend systemd-resolve so that it can push per-interface DNS configuration into systemd-resolved (#7576)
This is useful to debug things, but also to hook up external post-up scripts with resolved. Eventually this code might be useful to implement a resolvconf(8)-compatible interface for compatibility purposes. Since the semantics don't map entirely cleanly as first step we add a native interface for pushing DNS configuration into resolved, that exposes the correct semantics, before adding any compatibility interface. See: #7202
This commit is contained in:
parent
fbd0b64f44
commit
14965b94f2
@ -321,6 +321,48 @@
|
||||
<listitem><para>Shows the global and per-link DNS settings in currently in effect.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--set-dns=SERVER</option></term>
|
||||
<term><option>--set-domain=DOMAIN</option></term>
|
||||
<term><option>--set-llmnr=MODE</option></term>
|
||||
<term><option>--set-mdns=MODE</option></term>
|
||||
<term><option>--set-dnssec=MODE</option></term>
|
||||
<term><option>--set-nta=DOMAIN</option></term>
|
||||
|
||||
<listitem><para>Set per-interface DNS configuration. These switches 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 switches
|
||||
may be used to inform <command>systemd-resolved</command> about per-interface DNS configuration determined
|
||||
through external means. Multiple of these switches may be passed on a single invocation of
|
||||
<command>systemd-resolve</command> in order to set multiple configuration options at once. If any of these
|
||||
switches is used, it must be combined with <option>--interface=</option> to indicate the network interface the
|
||||
new DNS configuration belongs to. The <option>--set-dns=</option> option expects an IPv4 or IPv6 address
|
||||
specification of a DNS server to use, and may be used multiple times to define multiple servers for the same
|
||||
interface. The <option>--set-domain=</option> option expects a valid DNS domain, possibly prefixed with
|
||||
<literal>~</literal>, and configures a per-interface search or route-only domain. It may be used multiple times
|
||||
to configure multiple such domains. The <option>--set-llmnr=</option>, <option>--set-mdns=</option> and
|
||||
<option>--set-dnssec=</option> options may be used to configure the per-interface LLMNR, MulticastDNS and
|
||||
DNSSEC settings. Finally, <option>--set-nta=</option> may be used to configure additional per-interface DNSSEC
|
||||
NTA domains and may also be used multiple times. For details about these settings, their possible values and
|
||||
their effect, see the corresponding options in
|
||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--revert</option></term>
|
||||
|
||||
<listitem><para>Revert the per-interface DNS configuration. This option must be combined with
|
||||
<option>--interface=</option> to indicate the network interface the DNS configuration shall be reverted on. If
|
||||
the DNS configuration is reverted all per-interface DNS setting are reset to their defaults, undoing all
|
||||
effects of <option>--set-dns=</option>, <option>--set-domain=</option>, <option>--set-llmnr=</option>,
|
||||
<option>--set-mdns=</option>, <option>--set-dnssec=</option>, <option>--set-nta=</option>. Note that when a
|
||||
network interface disappears all configuration is lost automatically, an explicit reverting is not necessary in
|
||||
that case.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
@ -403,8 +445,9 @@ _443._tcp.fedoraproject.org IN TLSA 0 0 1 19400be5b7a31fb733917700789d2f0a2471c0
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
<citerefentry><refentrytitle>systemd.dnssd</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.dnssd</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
@ -26,8 +26,10 @@
|
||||
|
||||
#include "af-list.h"
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "escape.h"
|
||||
#include "gcrypt-util.h"
|
||||
#include "in-addr-util.h"
|
||||
@ -75,8 +77,18 @@ static enum {
|
||||
MODE_FLUSH_CACHES,
|
||||
MODE_RESET_SERVER_FEATURES,
|
||||
MODE_STATUS,
|
||||
MODE_SET_LINK,
|
||||
MODE_REVERT_LINK,
|
||||
} arg_mode = MODE_RESOLVE_HOST;
|
||||
|
||||
static struct in_addr_data *arg_set_dns = NULL;
|
||||
static size_t arg_n_set_dns = 0;
|
||||
static char **arg_set_domain = NULL;
|
||||
static char *arg_set_llmnr = NULL;
|
||||
static char *arg_set_mdns = NULL;
|
||||
static char *arg_set_dnssec = NULL;
|
||||
static char **arg_set_nta = NULL;
|
||||
|
||||
static ServiceFamily service_family_from_string(const char *s) {
|
||||
if (s == NULL || streq(s, "tcp"))
|
||||
return SERVICE_FAMILY_TCP;
|
||||
@ -1545,6 +1557,234 @@ static int status_all(sd_bus *bus) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int set_link(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r = 0, q;
|
||||
|
||||
assert(bus);
|
||||
|
||||
if (arg_n_set_dns > 0) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
|
||||
size_t i;
|
||||
|
||||
q = sd_bus_message_new_method_call(
|
||||
bus,
|
||||
&req,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkDNS");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append(req, "i", arg_ifindex);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_open_container(req, 'a', "(iay)");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
for (i = 0; i < arg_n_set_dns; i++) {
|
||||
q = sd_bus_message_open_container(req, 'r', "iay");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append(req, "i", arg_set_dns[i].family);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append_array(req, 'y', &arg_set_dns[i].address, FAMILY_ADDRESS_SIZE(arg_set_dns[i].family));
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_close_container(req);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
}
|
||||
|
||||
q = sd_bus_message_close_container(req);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_call(bus, req, 0, &error, NULL);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set DNS configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strv_isempty(arg_set_domain)) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
|
||||
char **p;
|
||||
|
||||
q = sd_bus_message_new_method_call(
|
||||
bus,
|
||||
&req,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkDomains");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append(req, "i", arg_ifindex);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_open_container(req, 'a', "(sb)");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
STRV_FOREACH(p, arg_set_domain) {
|
||||
const char *n;
|
||||
|
||||
n = **p == '~' ? *p + 1 : *p;
|
||||
q = sd_bus_message_append(req, "(sb)", n, **p == '~');
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
}
|
||||
|
||||
q = sd_bus_message_close_container(req);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_call(bus, req, 0, &error, NULL);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set domain configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_set_llmnr) {
|
||||
q = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkLLMNR",
|
||||
&error,
|
||||
NULL,
|
||||
"is", arg_ifindex, arg_set_llmnr);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set LLMNR configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_set_mdns) {
|
||||
q = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkMulticastDNS",
|
||||
&error,
|
||||
NULL,
|
||||
"is", arg_ifindex, arg_set_mdns);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set MulticastDNS configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_set_dnssec) {
|
||||
q = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkDNSSEC",
|
||||
&error,
|
||||
NULL,
|
||||
"is", arg_ifindex, arg_set_dnssec);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set DNSSEC configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strv_isempty(arg_set_nta)) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
|
||||
|
||||
q = sd_bus_message_new_method_call(
|
||||
bus,
|
||||
&req,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkDNSSECNegativeTrustAnchors");
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append(req, "i", arg_ifindex);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_message_append_strv(req, arg_set_nta);
|
||||
if (q < 0)
|
||||
return bus_log_create_error(q);
|
||||
|
||||
q = sd_bus_call(bus, req, 0, &error, NULL);
|
||||
if (q < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
goto is_managed;
|
||||
|
||||
log_error_errno(q, "Failed to set DNSSEC NTA configuration: %s", bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
r = q;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
is_managed:
|
||||
{
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
return log_error_errno(q,
|
||||
"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(if_indextoname(arg_ifindex, ifname)));
|
||||
}
|
||||
}
|
||||
|
||||
static int revert_link(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
||||
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)
|
||||
return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help_protocol_types(void) {
|
||||
if (arg_legend)
|
||||
puts("Known protocol types:");
|
||||
@ -1610,6 +1850,13 @@ static void help(void) {
|
||||
" --flush-caches Flush all local DNS caches\n"
|
||||
" --reset-server-features\n"
|
||||
" Forget learnt DNS server feature levels\n"
|
||||
" --set-dns=SERVER Set per-interface DNS server address\n"
|
||||
" --set-domain=DOMAIN Set per-interface search domain\n"
|
||||
" --set-llmnr=MODE Set per-interface LLMNR mode\n"
|
||||
" --set-mdns=MODE Set per-interface MulticastDNS mode\n"
|
||||
" --set-dnssec=MODE Set per-interface DNSSEC mode\n"
|
||||
" --set-nta=DOMAIN Set per-interface DNSSEC NTA\n"
|
||||
" --revert Revert per-interface configuration\n"
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
|
||||
@ -1631,6 +1878,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_FLUSH_CACHES,
|
||||
ARG_RESET_SERVER_FEATURES,
|
||||
ARG_NO_PAGER,
|
||||
ARG_SET_DNS,
|
||||
ARG_SET_DOMAIN,
|
||||
ARG_SET_LLMNR,
|
||||
ARG_SET_MDNS,
|
||||
ARG_SET_DNSSEC,
|
||||
ARG_SET_NTA,
|
||||
ARG_REVERT_LINK,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -1655,6 +1909,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES },
|
||||
{ "reset-server-features", no_argument, NULL, ARG_RESET_SERVER_FEATURES },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "set-dns", required_argument, NULL, ARG_SET_DNS },
|
||||
{ "set-domain", required_argument, NULL, ARG_SET_DOMAIN },
|
||||
{ "set-llmnr", required_argument, NULL, ARG_SET_LLMNR },
|
||||
{ "set-mdns", required_argument, NULL, ARG_SET_MDNS },
|
||||
{ "set-dnssec", required_argument, NULL, ARG_SET_DNSSEC },
|
||||
{ "set-nta", required_argument, NULL, ARG_SET_NTA },
|
||||
{ "revert", no_argument, NULL, ARG_REVERT_LINK },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1850,6 +2111,84 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_no_pager = true;
|
||||
break;
|
||||
|
||||
case ARG_SET_DNS: {
|
||||
struct in_addr_data data, *n;
|
||||
|
||||
r = in_addr_from_string_auto(optarg, &data.family, &data.address);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse DNS server address: %s", optarg);
|
||||
|
||||
n = realloc(arg_set_dns, sizeof(struct in_addr_data) * (arg_n_set_dns + 1));
|
||||
if (!n)
|
||||
return log_oom();
|
||||
arg_set_dns = n;
|
||||
|
||||
arg_set_dns[arg_n_set_dns++] = data;
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_SET_DOMAIN: {
|
||||
const char *p;
|
||||
|
||||
p = optarg[0] == '~' ? optarg + 1 : optarg;
|
||||
|
||||
r = dns_name_is_valid(p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate specified domain %s: %m", p);
|
||||
if (r == 0)
|
||||
return log_error_errno(r, "Domain not valid: %s", p);
|
||||
|
||||
r = strv_extend(&arg_set_domain, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_SET_LLMNR:
|
||||
r = free_and_strdup(&arg_set_llmnr, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
|
||||
case ARG_SET_MDNS:
|
||||
r = free_and_strdup(&arg_set_mdns, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
|
||||
case ARG_SET_DNSSEC:
|
||||
r = free_and_strdup(&arg_set_dnssec, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
|
||||
case ARG_SET_NTA:
|
||||
r = dns_name_is_valid(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate specified domain %s: %m", optarg);
|
||||
if (r == 0)
|
||||
return log_error_errno(r, "Domain not valid: %s", optarg);
|
||||
|
||||
r = strv_extend(&arg_set_nta, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
arg_mode = MODE_SET_LINK;
|
||||
break;
|
||||
|
||||
case ARG_REVERT_LINK:
|
||||
arg_mode = MODE_REVERT_LINK;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -1873,6 +2212,19 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_class != 0 && arg_type == 0)
|
||||
arg_type = DNS_TYPE_A;
|
||||
|
||||
if (IN_SET(arg_mode, MODE_SET_LINK, MODE_REVERT_LINK)) {
|
||||
|
||||
if (arg_ifindex <= 0) {
|
||||
log_error("--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnssec=, --set-nta= and --revert require --interface=.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_ifindex == LOOPBACK_IFINDEX) {
|
||||
log_error("Interface can't be the loopback interface (lo). Sorry.");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1 /* work to do */;
|
||||
}
|
||||
|
||||
@ -2064,10 +2416,38 @@ int main(int argc, char **argv) {
|
||||
r = status_all(bus);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case MODE_SET_LINK:
|
||||
if (argc > optind) {
|
||||
log_error("Too many arguments.");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = set_link(bus);
|
||||
break;
|
||||
|
||||
case MODE_REVERT_LINK:
|
||||
if (argc > optind) {
|
||||
log_error("Too many arguments.");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = revert_link(bus);
|
||||
break;
|
||||
}
|
||||
|
||||
finish:
|
||||
pager_close();
|
||||
|
||||
free(arg_set_dns);
|
||||
strv_free(arg_set_domain);
|
||||
free(arg_set_llmnr);
|
||||
free(arg_set_mdns);
|
||||
free(arg_set_dnssec);
|
||||
strv_free(arg_set_nta);
|
||||
|
||||
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user