mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
Merge pull request #11050 from poettering/resolved-domain-route
resolved: beef up domain routing
This commit is contained in:
commit
44f52cce9e
@ -241,6 +241,7 @@
|
||||
<varlistentry>
|
||||
<term><option>dns [<replaceable>LINK</replaceable> [<replaceable>SERVER</replaceable>…]]</option></term>
|
||||
<term><option>domain [<replaceable>LINK</replaceable> [<replaceable>DOMAIN</replaceable>…]]</option></term>
|
||||
<term><option>default-route [<replaceable>LINK</replaceable> [<replaceable>BOOL</replaceable>…]]</option></term>
|
||||
<term><option>llmnr [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
|
||||
<term><option>mdns [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
|
||||
<term><option>dnssec [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
|
||||
@ -248,18 +249,21 @@
|
||||
<term><option>nta [<replaceable>LINK</replaceable> [<replaceable>DOMAIN</replaceable>…]]</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>Get/set per-interface DNS configuration. These commands may be used to configure various DNS
|
||||
settings for network interfaces that aren't managed by
|
||||
<para>Get/set per-interface DNS configuration. These commands may be used to configure various DNS settings
|
||||
for network interfaces that aren't managed by
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. (These
|
||||
commands will fail when used on interfaces that are managed by <command>systemd-networkd</command>, please
|
||||
configure their DNS settings directly inside the <filename>.network</filename> files instead.) These commands
|
||||
may be used to inform <command>systemd-resolved</command> about per-interface DNS configuration determined
|
||||
through external means. The <option>dns</option> command expects IPv4 or IPv6 address specifications of DNS
|
||||
servers to use. The <option>domain</option> command expects valid DNS domains, possibly prefixed with
|
||||
<literal>~</literal>, and configures a per-interface search or route-only domain. The <option>llmnr</option>,
|
||||
<option>mdns</option>, <option>dnssec</option> and <option>dnsovertls</option> commands may be used to configure
|
||||
the per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <option>nta</option> command
|
||||
may be used to configure additional per-interface DNSSEC NTA domains.</para>
|
||||
<literal>~</literal>, and configures a per-interface search or route-only domain. The
|
||||
<option>default-route</option> command expects a boolean paremeter, and configures whether the link may be
|
||||
used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no other link explicitly
|
||||
is configured for. The <option>llmnr</option>, <option>mdns</option>, <option>dnssec</option> and
|
||||
<option>dnsovertls</option> commands may be used to configure the per-interface LLMNR, MulticastDNS, DNSSEC
|
||||
and DNSOverTLS settings. Finally, <option>nta</option> command may be used to configure additional
|
||||
per-interface DNSSEC NTA domains.</para>
|
||||
|
||||
<para>Options <option>dns</option>, <option>domain</option> and <option>nta</option> can take
|
||||
a single empty string argument to clear their respective value lists.</para>
|
||||
@ -274,9 +278,10 @@
|
||||
|
||||
<listitem><para>Revert the per-interface DNS configuration. If the DNS configuration is reverted all
|
||||
per-interface DNS setting are reset to their defaults, undoing all effects of <option>dns</option>,
|
||||
<option>domain</option>, <option>llmnr</option>, <option>mdns</option>, <option>dnssec</option>,
|
||||
<option>dnsovertls</option>, <option>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>
|
||||
<option>domain</option>, <option>default-route</option>, <option>llmnr</option>, <option>mdns</option>,
|
||||
<option>dnssec</option>, <option>dnsovertls</option>, <option>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>
|
||||
|
||||
</variablelist>
|
||||
|
@ -66,14 +66,21 @@
|
||||
<filename>/etc/systemd/resolved.conf</filename>, the per-link static settings in
|
||||
<filename>/etc/systemd/network/*.network</filename> files (in case
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
|
||||
used), the per-link dynamic settings received over DHCP, and any DNS server information made available by other
|
||||
system services. See
|
||||
used), the per-link dynamic settings received over DHCP, user request made via
|
||||
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, and any DNS server
|
||||
information made available by other system services. See
|
||||
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details
|
||||
about systemd's own configuration files for DNS servers. To improve compatibility,
|
||||
<filename>/etc/resolv.conf</filename> is read in order to discover configured system DNS servers, but only if it is
|
||||
not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename> or
|
||||
<filename>/run/systemd/resolve/resolv.conf</filename> (see below).</para>
|
||||
not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename>,
|
||||
<filename>/usr/lib/systemd/resolv.conf</filename> or <filename>/run/systemd/resolve/resolv.conf</filename> (see
|
||||
below).</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Synthetic Records</title>
|
||||
|
||||
<para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following cases:</para>
|
||||
|
||||
@ -99,6 +106,10 @@
|
||||
to their configured addresses and back, but they will not affect lookups for
|
||||
non-address types (like MX).</para></listitem>
|
||||
</itemizedlist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Protocols and Routing</title>
|
||||
|
||||
<para>Lookup requests are routed to the available DNS servers, LLMNR and MulticastDNS interfaces according to the
|
||||
following rules:</para>
|
||||
@ -132,16 +143,45 @@
|
||||
lookup zones on all matching interfaces). If the lookup failed on
|
||||
all interfaces, the last failing response is returned.</para>
|
||||
|
||||
<para>Routing of lookups may be influenced by configuring
|
||||
per-interface domain names. See
|
||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details. Lookups for a hostname ending in one of the
|
||||
per-interface domains are exclusively routed to the matching
|
||||
interfaces.</para>
|
||||
<para>Routing of lookups may be influenced by configuring per-interface domain names and other settings. See
|
||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details. The
|
||||
following query routing logic applies for unicast DNS traffic:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the configured search
|
||||
or route-only domains of any link (or the globally configured DNS settings), the "best matching"
|
||||
search/route-only domain is determined: the matching one with the most labels. The query is then sent to all DNS
|
||||
servers of any links or the globally configured DNS servers associated with this "best matching"
|
||||
search/route-only domain. (Note that more than one link might have this same "best matching" search/route-only
|
||||
domain configured, in which case the query is sent to all of them in parallel).</para></listitem>
|
||||
|
||||
<listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor global),
|
||||
it is sent to all DNS servers that are configured on links with the "DNS default route" option set, as well as
|
||||
the globally configured DNS server.</para></listitem>
|
||||
|
||||
<listitem><para>If there is no link configured as "DNS default route" and no global DNS server configured, the
|
||||
compiled-in fallback DNS server is used.</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The "DNS default route" option is a boolean setting configureable with <command>resolvectl</command> or in
|
||||
<filename>.network</filename> files. If not set, it is implicitly determined based on the configured DNS domains
|
||||
for a link: if there's any route-only domain (not matching <literal>~.</literal>) it defaults to false, otherwise
|
||||
to true.</para>
|
||||
|
||||
<para>Effectively this means: in order to preferably route all DNS queries not explicitly matched by
|
||||
search/route-only domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on
|
||||
it. This will ensure that other links will not be considered for the queries (unless they too carry such a
|
||||
route-only domain). In order to route all such DNS queries to a specific link only in case no other link is
|
||||
preferable, then set the "DNS default route" option for the link to true, and do not configure a
|
||||
<literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never receives any
|
||||
DNS traffic not matching any of its configured search/route-only domains, set the "DNS default route" option for it
|
||||
to false.</para>
|
||||
|
||||
<para>See the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/resolved"> resolved D-Bus API
|
||||
Documentation</ulink> for information about the APIs <filename>systemd-resolved</filename> provides.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -547,6 +547,17 @@
|
||||
name servers limited to a specific link.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>DNSDefaultRoute=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean argument. If true, this link's configured DNS servers are used for resolving domain
|
||||
names that do not match any link's configured <varname>Domains=</varname> setting. If false, this link's
|
||||
configured DNS servers are never used for such domains, and are exclusively used for resolving names that
|
||||
match at least one of the domains configured on this link. If not specified defaults to an automatic mode:
|
||||
queries not matching any link's configured domains will be routed to this link if it has no routing-only
|
||||
domains configured.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>NTP=</varname></term>
|
||||
<listitem>
|
||||
|
@ -204,6 +204,25 @@ _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
|
||||
return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
|
||||
}
|
||||
|
||||
_public_ int sd_network_link_get_dns_default_route(int ifindex) {
|
||||
char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
|
||||
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
|
||||
|
||||
r = parse_env_file(NULL, path, "DNS_DEFAULT_ROUTE", &s);
|
||||
if (r == -ENOENT)
|
||||
return -ENODATA;
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(s))
|
||||
return -ENODATA;
|
||||
return parse_boolean(s);
|
||||
}
|
||||
|
||||
static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
|
||||
char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
|
||||
_cleanup_free_ int *ifis = NULL;
|
||||
|
@ -3929,6 +3929,8 @@ int link_save(Link *link) {
|
||||
resolve_support_to_string(link->network->llmnr));
|
||||
fprintf(f, "MDNS=%s\n",
|
||||
resolve_support_to_string(link->network->mdns));
|
||||
if (link->network->dns_default_route >= 0)
|
||||
fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route));
|
||||
|
||||
if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
|
||||
fprintf(f, "DNS_OVER_TLS=%s\n",
|
||||
|
@ -58,6 +58,7 @@ Network.Address, config_parse_address,
|
||||
Network.Gateway, config_parse_gateway, 0, 0
|
||||
Network.Domains, config_parse_domains, 0, 0
|
||||
Network.DNS, config_parse_dns, 0, 0
|
||||
Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route)
|
||||
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
|
||||
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
|
||||
Network.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Network, dns_over_tls_mode)
|
||||
|
@ -168,6 +168,7 @@ int network_load_one(Manager *manager, const char *filename) {
|
||||
|
||||
.lldp_mode = LLDP_MODE_ROUTERS_ONLY,
|
||||
|
||||
.dns_default_route = -1,
|
||||
.llmnr = RESOLVE_SUPPORT_YES,
|
||||
.mdns = RESOLVE_SUPPORT_NO,
|
||||
.dnssec_mode = _DNSSEC_MODE_INVALID,
|
||||
@ -657,7 +658,6 @@ int config_parse_domains(
|
||||
* routing domain, unconditionally. */
|
||||
is_route = true;
|
||||
domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
|
||||
|
||||
} else {
|
||||
r = dns_name_normalize(domain, 0, &normalized);
|
||||
if (r < 0) {
|
||||
@ -673,16 +673,12 @@ int config_parse_domains(
|
||||
}
|
||||
}
|
||||
|
||||
if (is_route) {
|
||||
if (is_route)
|
||||
r = strv_extend(&n->route_domains, domain);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else {
|
||||
else
|
||||
r = strv_extend(&n->search_domains, domain);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
strv_uniq(n->route_domains);
|
||||
|
@ -260,17 +260,20 @@ struct Network {
|
||||
Hashmap *prefixes_by_section;
|
||||
Hashmap *rules_by_section;
|
||||
|
||||
/* All kinds of DNS configuration */
|
||||
struct in_addr_data *dns;
|
||||
unsigned n_dns;
|
||||
|
||||
char **search_domains, **route_domains, **ntp, **bind_carrier;
|
||||
|
||||
char **search_domains, **route_domains;
|
||||
int dns_default_route;
|
||||
ResolveSupport llmnr;
|
||||
ResolveSupport mdns;
|
||||
DnssecMode dnssec_mode;
|
||||
DnsOverTlsMode dns_over_tls_mode;
|
||||
Set *dnssec_negative_trust_anchors;
|
||||
|
||||
char **ntp;
|
||||
char **bind_carrier;
|
||||
|
||||
LIST_FIELDS(Network, networks);
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,7 @@ typedef enum StatusMode {
|
||||
STATUS_ALL,
|
||||
STATUS_DNS,
|
||||
STATUS_DOMAIN,
|
||||
STATUS_DEFAULT_ROUTE,
|
||||
STATUS_LLMNR,
|
||||
STATUS_MDNS,
|
||||
STATUS_PRIVATE,
|
||||
@ -1369,6 +1370,7 @@ struct link_info {
|
||||
char **domains;
|
||||
char **ntas;
|
||||
bool dnssec_supported;
|
||||
bool default_route;
|
||||
};
|
||||
|
||||
static void link_info_clear(struct link_info *p) {
|
||||
@ -1384,6 +1386,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
|
||||
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
|
||||
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
|
||||
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
|
||||
{ "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
|
||||
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
|
||||
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
|
||||
{ "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
|
||||
@ -1439,6 +1442,14 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
|
||||
if (mode == STATUS_NTA)
|
||||
return status_print_strv_ifindex(ifindex, name, link_info.ntas);
|
||||
|
||||
if (mode == STATUS_DEFAULT_ROUTE) {
|
||||
printf("%sLink %i (%s)%s: %s\n",
|
||||
ansi_highlight(), ifindex, name, ansi_normal(),
|
||||
yes_no(link_info.default_route));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode == STATUS_LLMNR) {
|
||||
printf("%sLink %i (%s)%s: %s\n",
|
||||
ansi_highlight(), ifindex, name, ansi_normal(),
|
||||
@ -1487,11 +1498,13 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
|
||||
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
|
||||
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
|
||||
|
||||
printf(" LLMNR setting: %s\n"
|
||||
printf("DefaultRoute setting: %s\n"
|
||||
" LLMNR setting: %s\n"
|
||||
"MulticastDNS setting: %s\n"
|
||||
" DNSOverTLS setting: %s\n"
|
||||
" DNSSEC setting: %s\n"
|
||||
" DNSSEC supported: %s\n",
|
||||
yes_no(link_info.default_route),
|
||||
strna(link_info.llmnr),
|
||||
strna(link_info.mdns),
|
||||
strna(link_info.dns_over_tls),
|
||||
@ -2020,6 +2033,51 @@ static int verb_domain(int argc, char **argv, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_default_route(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus = userdata;
|
||||
int r, b;
|
||||
|
||||
assert(bus);
|
||||
|
||||
if (argc >= 2) {
|
||||
r = ifname_mangle(argv[1]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_ifindex <= 0)
|
||||
return status_all(bus, STATUS_DEFAULT_ROUTE);
|
||||
|
||||
if (argc < 3)
|
||||
return status_ifindex(bus, arg_ifindex, NULL, STATUS_DEFAULT_ROUTE, NULL);
|
||||
|
||||
b = parse_boolean(argv[2]);
|
||||
if (b < 0)
|
||||
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
|
||||
|
||||
r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"SetLinkDefaultRoute",
|
||||
&error,
|
||||
NULL,
|
||||
"ib", arg_ifindex, b);
|
||||
if (r < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
|
||||
return log_interface_is_managed(r, arg_ifindex);
|
||||
|
||||
if (arg_ifindex_permissive &&
|
||||
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
|
||||
return 0;
|
||||
|
||||
return log_error_errno(r, "Failed to set default route configuration: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_llmnr(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus = userdata;
|
||||
@ -2407,6 +2465,7 @@ static int native_help(void) {
|
||||
" reset-server-features Forget learnt DNS server feature levels\n"
|
||||
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
|
||||
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
|
||||
" default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
|
||||
" llmnr [LINK [MODE]] Get/set per-interface LLMNR mode\n"
|
||||
" mdns [LINK [MODE]] Get/set per-interface MulticastDNS mode\n"
|
||||
" dnsovertls [LINK [MODE]] Get/set per-interface DNS-over-TLS mode\n"
|
||||
@ -2950,9 +3009,10 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
|
||||
{ "reset-server-features", VERB_ANY, 1, 0, reset_server_features },
|
||||
{ "dns", VERB_ANY, VERB_ANY, 0, verb_dns },
|
||||
{ "domain", VERB_ANY, VERB_ANY, 0, verb_domain },
|
||||
{ "default-route", VERB_ANY, 3, 0, verb_default_route },
|
||||
{ "llmnr", VERB_ANY, 3, 0, verb_llmnr },
|
||||
{ "mdns", VERB_ANY, 3, 0, verb_mdns },
|
||||
{ "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
|
||||
{ "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
|
||||
{ "dnssec", VERB_ANY, 3, 0, verb_dnssec },
|
||||
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
|
||||
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
|
||||
|
@ -1530,6 +1530,10 @@ static int bus_method_set_link_domains(sd_bus_message *message, void *userdata,
|
||||
return call_link_method(userdata, message, bus_link_method_set_domains, error);
|
||||
}
|
||||
|
||||
static int bus_method_set_link_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return call_link_method(userdata, message, bus_link_method_set_default_route, error);
|
||||
}
|
||||
|
||||
static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
|
||||
}
|
||||
@ -1855,6 +1859,7 @@ static const sd_bus_vtable resolve_vtable[] = {
|
||||
SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
|
||||
SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0),
|
||||
SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, 0),
|
||||
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
|
||||
SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
|
||||
SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, 0),
|
||||
|
@ -682,22 +682,15 @@ int dns_query_go(DnsQuery *q) {
|
||||
continue;
|
||||
|
||||
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
|
||||
if (match < 0)
|
||||
return match;
|
||||
|
||||
if (match == DNS_SCOPE_NO)
|
||||
if (match < 0) {
|
||||
log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
found = match;
|
||||
|
||||
if (match == DNS_SCOPE_YES) {
|
||||
if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
|
||||
* that matches this well */
|
||||
found = match;
|
||||
first = s;
|
||||
break;
|
||||
} else {
|
||||
assert(match == DNS_SCOPE_MAYBE);
|
||||
|
||||
if (!first)
|
||||
first = s;
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,10 +718,12 @@ int dns_query_go(DnsQuery *q) {
|
||||
continue;
|
||||
|
||||
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
|
||||
if (match < 0)
|
||||
goto fail;
|
||||
if (match < 0) {
|
||||
log_debug("Couldn't check if '%s' matches agains scope, ignoring.", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match != found)
|
||||
if (match < found)
|
||||
continue;
|
||||
|
||||
r = dns_query_add_candidate(q, s);
|
||||
|
@ -30,15 +30,17 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
s = new0(DnsScope, 1);
|
||||
s = new(DnsScope, 1);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
s->manager = m;
|
||||
s->link = l;
|
||||
s->protocol = protocol;
|
||||
s->family = family;
|
||||
s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
|
||||
*s = (DnsScope) {
|
||||
.manager = m,
|
||||
.link = l,
|
||||
.protocol = protocol,
|
||||
.family = family,
|
||||
.resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC,
|
||||
};
|
||||
|
||||
if (protocol == DNS_PROTOCOL_DNS) {
|
||||
/* Copy DNSSEC mode from the link if it is set there,
|
||||
@ -457,9 +459,40 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
|
||||
return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address);
|
||||
}
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
|
||||
static DnsScopeMatch accept_link_local_reverse_lookups(const char *domain) {
|
||||
assert(domain);
|
||||
|
||||
if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0)
|
||||
return DNS_SCOPE_YES_BASE + 4; /* 4 labels match */
|
||||
|
||||
if (dns_name_endswith(domain, "8.e.f.ip6.arpa") > 0 ||
|
||||
dns_name_endswith(domain, "9.e.f.ip6.arpa") > 0 ||
|
||||
dns_name_endswith(domain, "a.e.f.ip6.arpa") > 0 ||
|
||||
dns_name_endswith(domain, "b.e.f.ip6.arpa") > 0)
|
||||
return DNS_SCOPE_YES_BASE + 5; /* 5 labels match */
|
||||
|
||||
return _DNS_SCOPE_MATCH_INVALID;
|
||||
}
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(
|
||||
DnsScope *s,
|
||||
int ifindex,
|
||||
uint64_t flags,
|
||||
const char *domain) {
|
||||
|
||||
DnsSearchDomain *d;
|
||||
|
||||
/* This returns the following return values:
|
||||
*
|
||||
* DNS_SCOPE_NO → This scope is not suitable for lookups of this domain, at all
|
||||
* DNS_SCOPE_MAYBE → This scope is suitable, but only if nothing else wants it
|
||||
* DNS_SCOPE_YES_BASE+n → This scope is suitable, and 'n' suffix labels match
|
||||
*
|
||||
* (The idea is that the caller will only use the scopes with the longest 'n' returned. If no scopes return
|
||||
* DNS_SCOPE_YES_BASE+n, then it should use those which returned DNS_SCOPE_MAYBE. It should never use those
|
||||
* which returned DNS_SCOPE_NO.)
|
||||
*/
|
||||
|
||||
assert(s);
|
||||
assert(domain);
|
||||
|
||||
@ -494,23 +527,35 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
||||
switch (s->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS: {
|
||||
DnsServer *dns_server;
|
||||
int n_best = -1;
|
||||
|
||||
/* Never route things to scopes that lack DNS servers */
|
||||
dns_server = dns_scope_get_dns_server(s);
|
||||
if (!dns_server)
|
||||
if (!dns_scope_get_dns_server(s))
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Always honour search domains for routing queries, except if this scope lacks DNS servers. Note that
|
||||
* we return DNS_SCOPE_YES here, rather than just DNS_SCOPE_MAYBE, which means other wildcard scopes
|
||||
* won't be considered anymore. */
|
||||
LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
|
||||
if (dns_name_endswith(domain, d->name) > 0)
|
||||
return DNS_SCOPE_YES;
|
||||
if (dns_name_endswith(domain, d->name) > 0) {
|
||||
int c;
|
||||
|
||||
/* If the DNS server has route-only domains, don't send other requests to it. This would be a privacy
|
||||
* violation, will most probably fail anyway, and adds unnecessary load. */
|
||||
if (dns_server_limited_domains(dns_server))
|
||||
c = dns_name_count_labels(d->name);
|
||||
if (c < 0)
|
||||
continue;
|
||||
|
||||
if (c > n_best)
|
||||
n_best = c;
|
||||
}
|
||||
|
||||
/* Let's return the number of labels in the best matching result */
|
||||
if (n_best >= 0) {
|
||||
assert(n_best <= DNS_SCOPE_YES_END - DNS_SCOPE_YES_BASE);
|
||||
return DNS_SCOPE_YES_BASE + n_best;
|
||||
}
|
||||
|
||||
/* See if this scope is suitable as default route. */
|
||||
if (!dns_scope_is_default_route(s))
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Exclude link-local IP ranges */
|
||||
@ -528,25 +573,48 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
||||
return DNS_SCOPE_NO;
|
||||
}
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
case DNS_PROTOCOL_MDNS: {
|
||||
DnsScopeMatch m;
|
||||
|
||||
m = accept_link_local_reverse_lookups(domain);
|
||||
if (m >= 0)
|
||||
return m;
|
||||
|
||||
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
|
||||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
|
||||
(dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
|
||||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
|
||||
return DNS_SCOPE_MAYBE;
|
||||
|
||||
if ((dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
|
||||
dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
|
||||
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */
|
||||
return DNS_SCOPE_MAYBE;
|
||||
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as the top-level .local domain matches, i.e. one label */
|
||||
|
||||
return DNS_SCOPE_NO;
|
||||
}
|
||||
|
||||
case DNS_PROTOCOL_LLMNR: {
|
||||
DnsScopeMatch m;
|
||||
|
||||
m = accept_link_local_reverse_lookups(domain);
|
||||
if (m >= 0)
|
||||
return m;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
|
||||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
|
||||
(dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
|
||||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
|
||||
return DNS_SCOPE_MAYBE;
|
||||
|
||||
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
|
||||
!is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
|
||||
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
|
||||
return DNS_SCOPE_MAYBE;
|
||||
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative for
|
||||
* single-label names, i.e. one label. This is particular
|
||||
* relevant as it means a "." route on some other scope won't
|
||||
* pull all traffic away from us. (If people actually want to
|
||||
* pull traffic away from us they should turn off LLMNR on the
|
||||
* link) */
|
||||
|
||||
return DNS_SCOPE_NO;
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown scope protocol");
|
||||
@ -1322,3 +1390,56 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dns_scope_has_route_only_domains(DnsScope *scope) {
|
||||
DnsSearchDomain *domain, *first;
|
||||
bool route_only = false;
|
||||
|
||||
assert(scope);
|
||||
assert(scope->protocol == DNS_PROTOCOL_DNS);
|
||||
|
||||
/* Returns 'true' if this scope is suitable for queries to specific domains only. For that we check
|
||||
* if there are any route-only domains on this interface, as a heuristic to discern VPN-style links
|
||||
* from non-VPN-style links. Returns 'false' for all other cases, i.e. if the scope is intended to
|
||||
* take queries to arbitrary domains, i.e. has no routing domains set. */
|
||||
|
||||
if (scope->link)
|
||||
first = scope->link->search_domains;
|
||||
else
|
||||
first = scope->manager->search_domains;
|
||||
|
||||
LIST_FOREACH(domains, domain, first) {
|
||||
/* "." means "any domain", thus the interface takes any kind of traffic. Thus, we exit early
|
||||
* here, as it doesn't really matter whether this link has any route-only domains or not,
|
||||
* "~." really trumps everything and clearly indicates that this interface shall receive all
|
||||
* traffic it can get. */
|
||||
if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
|
||||
return false;
|
||||
|
||||
if (domain->route_only)
|
||||
route_only = true;
|
||||
}
|
||||
|
||||
return route_only;
|
||||
}
|
||||
|
||||
bool dns_scope_is_default_route(DnsScope *scope) {
|
||||
assert(scope);
|
||||
|
||||
/* Only use DNS scopes as default routes */
|
||||
if (scope->protocol != DNS_PROTOCOL_DNS)
|
||||
return false;
|
||||
|
||||
/* The global DNS scope is always suitable as default route */
|
||||
if (!scope->link)
|
||||
return true;
|
||||
|
||||
/* Honour whatever is explicitly configured. This is really the best approach, and trumps any
|
||||
* automatic logic. */
|
||||
if (scope->link->default_route >= 0)
|
||||
return scope->link->default_route;
|
||||
|
||||
/* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
|
||||
* volunteer as default route. */
|
||||
return !dns_scope_has_route_only_domains(scope);
|
||||
}
|
||||
|
@ -18,9 +18,10 @@ typedef struct DnsScope DnsScope;
|
||||
typedef enum DnsScopeMatch {
|
||||
DNS_SCOPE_NO,
|
||||
DNS_SCOPE_MAYBE,
|
||||
DNS_SCOPE_YES,
|
||||
DNS_SCOPE_YES_BASE, /* Add the number of matching labels to this */
|
||||
DNS_SCOPE_YES_END = DNS_SCOPE_YES_BASE + DNS_N_LABELS_MAX,
|
||||
_DNS_SCOPE_MATCH_MAX,
|
||||
_DNS_SCOPE_INVALID = -1
|
||||
_DNS_SCOPE_MATCH_INVALID = -1
|
||||
} DnsScopeMatch;
|
||||
|
||||
struct DnsScope {
|
||||
@ -28,6 +29,8 @@ struct DnsScope {
|
||||
|
||||
DnsProtocol protocol;
|
||||
int family;
|
||||
|
||||
/* Copied at scope creation time from the link/manager */
|
||||
DnssecMode dnssec_mode;
|
||||
DnsOverTlsMode dns_over_tls_mode;
|
||||
|
||||
@ -104,5 +107,6 @@ int dns_scope_ifindex(DnsScope *s);
|
||||
int dns_scope_announce(DnsScope *scope, bool goodbye);
|
||||
|
||||
int dns_scope_add_dnssd_services(DnsScope *scope);
|
||||
|
||||
int dns_scope_remove_dnssd_services(DnsScope *scope);
|
||||
|
||||
bool dns_scope_is_default_route(DnsScope *scope);
|
||||
|
@ -580,26 +580,6 @@ void dns_server_warn_downgrade(DnsServer *server) {
|
||||
server->warned_downgrade = true;
|
||||
}
|
||||
|
||||
bool dns_server_limited_domains(DnsServer *server) {
|
||||
DnsSearchDomain *domain;
|
||||
bool domain_restricted = false;
|
||||
|
||||
/* Check if the server has route-only domains without ~., i. e. whether
|
||||
* it should only be used for particular domains */
|
||||
if (!server->link)
|
||||
return false;
|
||||
|
||||
LIST_FOREACH(domains, domain, server->link->search_domains)
|
||||
if (domain->route_only) {
|
||||
domain_restricted = true;
|
||||
/* ~. means "any domain", thus it is a global server */
|
||||
if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return domain_restricted;
|
||||
}
|
||||
|
||||
static void dns_server_hash_func(const DnsServer *s, struct siphash *state) {
|
||||
assert(s);
|
||||
|
||||
@ -906,6 +886,16 @@ void dns_server_unref_stream(DnsServer *s) {
|
||||
dns_stream_unref(ref);
|
||||
}
|
||||
|
||||
DnsScope *dns_server_scope(DnsServer *s) {
|
||||
assert(s);
|
||||
assert((s->type == DNS_SERVER_LINK) == !!s->link);
|
||||
|
||||
if (s->link)
|
||||
return s->link->unicast_scope;
|
||||
|
||||
return s->manager->unicast_scope;
|
||||
}
|
||||
|
||||
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
|
||||
[DNS_SERVER_SYSTEM] = "system",
|
||||
[DNS_SERVER_FALLBACK] = "fallback",
|
||||
|
@ -122,8 +122,6 @@ bool dns_server_dnssec_supported(DnsServer *server);
|
||||
|
||||
void dns_server_warn_downgrade(DnsServer *server);
|
||||
|
||||
bool dns_server_limited_domains(DnsServer *server);
|
||||
|
||||
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
|
||||
|
||||
void dns_server_unlink_all(DnsServer *first);
|
||||
@ -153,3 +151,5 @@ void dns_server_reset_features_all(DnsServer *s);
|
||||
void dns_server_dump(DnsServer *s, FILE *f);
|
||||
|
||||
void dns_server_unref_stream(DnsServer *s);
|
||||
|
||||
DnsScope *dns_server_scope(DnsServer *s);
|
||||
|
@ -107,6 +107,31 @@ static int property_get_domains(
|
||||
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 = userdata;
|
||||
|
||||
assert(reply);
|
||||
assert(l);
|
||||
|
||||
/* 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(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -346,6 +371,31 @@ clear:
|
||||
return r;
|
||||
}
|
||||
|
||||
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Link *l = userdata;
|
||||
int r, b;
|
||||
|
||||
assert(message);
|
||||
assert(l);
|
||||
|
||||
r = verify_unmanaged_link(l, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(message, "b", &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (l->default_route != b) {
|
||||
l->default_route = b;
|
||||
|
||||
(void) link_save_user(l);
|
||||
(void) manager_write_resolv_conf(l->manager);
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Link *l = userdata;
|
||||
ResolveSupport mode;
|
||||
@ -550,6 +600,7 @@ const sd_bus_vtable link_vtable[] = {
|
||||
SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
|
||||
SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
|
||||
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
|
||||
SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
|
||||
SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
|
||||
SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
|
||||
SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
|
||||
@ -559,6 +610,7 @@ const sd_bus_vtable link_vtable[] = {
|
||||
|
||||
SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
|
||||
SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0),
|
||||
SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, 0),
|
||||
SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
|
||||
SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
|
||||
SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, 0),
|
||||
|
@ -13,6 +13,7 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
|
||||
|
||||
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
|
@ -30,16 +30,19 @@ int link_new(Manager *m, Link **ret, int ifindex) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
l = new0(Link, 1);
|
||||
l = new(Link, 1);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
l->ifindex = ifindex;
|
||||
l->llmnr_support = RESOLVE_SUPPORT_YES;
|
||||
l->mdns_support = RESOLVE_SUPPORT_NO;
|
||||
l->dnssec_mode = _DNSSEC_MODE_INVALID;
|
||||
l->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
|
||||
l->operstate = IF_OPER_UNKNOWN;
|
||||
*l = (Link) {
|
||||
.ifindex = ifindex,
|
||||
.default_route = -1,
|
||||
.llmnr_support = RESOLVE_SUPPORT_YES,
|
||||
.mdns_support = RESOLVE_SUPPORT_NO,
|
||||
.dnssec_mode = _DNSSEC_MODE_INVALID,
|
||||
.dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
|
||||
.operstate = IF_OPER_UNKNOWN,
|
||||
};
|
||||
|
||||
if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
|
||||
return -ENOMEM;
|
||||
@ -60,6 +63,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
|
||||
void link_flush_settings(Link *l) {
|
||||
assert(l);
|
||||
|
||||
l->default_route = -1;
|
||||
l->llmnr_support = RESOLVE_SUPPORT_YES;
|
||||
l->mdns_support = RESOLVE_SUPPORT_NO;
|
||||
l->dnssec_mode = _DNSSEC_MODE_INVALID;
|
||||
@ -297,6 +301,27 @@ clear:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_update_default_route(Link *l) {
|
||||
int r;
|
||||
|
||||
assert(l);
|
||||
|
||||
r = sd_network_link_get_dns_default_route(l->ifindex);
|
||||
if (r == -ENODATA) {
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
l->default_route = r > 0;
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
l->default_route = -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_update_llmnr_support(Link *l) {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
int r;
|
||||
@ -613,6 +638,10 @@ static void link_read_settings(Link *l) {
|
||||
r = link_update_search_domains(l);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
|
||||
|
||||
r = link_update_default_route(l);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read default route setting for interface %s, proceeding anyway: %m", l->name);
|
||||
}
|
||||
|
||||
int link_update(Link *l) {
|
||||
@ -1109,7 +1138,8 @@ static bool link_needs_save(Link *l) {
|
||||
|
||||
if (l->llmnr_support != RESOLVE_SUPPORT_YES ||
|
||||
l->mdns_support != RESOLVE_SUPPORT_NO ||
|
||||
l->dnssec_mode != _DNSSEC_MODE_INVALID)
|
||||
l->dnssec_mode != _DNSSEC_MODE_INVALID ||
|
||||
l->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
|
||||
return true;
|
||||
|
||||
if (l->dns_servers ||
|
||||
@ -1119,6 +1149,9 @@ static bool link_needs_save(Link *l) {
|
||||
if (!set_isempty(l->dnssec_negative_trust_anchors))
|
||||
return true;
|
||||
|
||||
if (l->default_route >= 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1161,6 +1194,9 @@ int link_save_user(Link *l) {
|
||||
if (v)
|
||||
fprintf(f, "DNSSEC=%s\n", v);
|
||||
|
||||
if (l->default_route >= 0)
|
||||
fprintf(f, "DEFAULT_ROUTE=%s\n", yes_no(l->default_route));
|
||||
|
||||
if (l->dns_servers) {
|
||||
DnsServer *server;
|
||||
|
||||
@ -1242,7 +1278,8 @@ int link_load_user(Link *l) {
|
||||
*dnssec = NULL,
|
||||
*servers = NULL,
|
||||
*domains = NULL,
|
||||
*ntas = NULL;
|
||||
*ntas = NULL,
|
||||
*default_route = NULL;
|
||||
|
||||
ResolveSupport s;
|
||||
const char *p;
|
||||
@ -1265,7 +1302,8 @@ int link_load_user(Link *l) {
|
||||
"DNSSEC", &dnssec,
|
||||
"SERVERS", &servers,
|
||||
"DOMAINS", &domains,
|
||||
"NTAS", &ntas);
|
||||
"NTAS", &ntas,
|
||||
"DEFAULT_ROUTE", &default_route);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
@ -1282,6 +1320,10 @@ int link_load_user(Link *l) {
|
||||
if (s >= 0)
|
||||
l->mdns_support = s;
|
||||
|
||||
r = parse_boolean(default_route);
|
||||
if (r >= 0)
|
||||
l->default_route = r;
|
||||
|
||||
/* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
|
||||
l->dnssec_mode = dnssec_mode_from_string(dnssec);
|
||||
|
||||
|
@ -51,6 +51,8 @@ struct Link {
|
||||
LIST_HEAD(DnsSearchDomain, search_domains);
|
||||
unsigned n_search_domains;
|
||||
|
||||
int default_route;
|
||||
|
||||
ResolveSupport llmnr_support;
|
||||
ResolveSupport mdns_support;
|
||||
DnsOverTlsMode dns_over_tls_mode;
|
||||
|
@ -217,6 +217,8 @@ clear:
|
||||
}
|
||||
|
||||
static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
|
||||
DnsScope *scope;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(count);
|
||||
@ -226,13 +228,12 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the DNS server is limited to particular domains;
|
||||
* resolv.conf does not have a syntax to express that, so it must not
|
||||
* appear as a global name server to avoid routing unrelated domains to
|
||||
* it (which is a privacy violation, will most probably fail anyway,
|
||||
* and adds unnecessary load) */
|
||||
if (dns_server_limited_domains(s)) {
|
||||
log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s));
|
||||
/* Check if the scope this DNS server belongs to is suitable as 'default' route for lookups; resolv.conf does
|
||||
* not have a syntax to express that, so it must not appear as a global name server to avoid routing unrelated
|
||||
* domains to it (which is a privacy violation, will most probably fail anyway, and adds unnecessary load) */
|
||||
scope = dns_server_scope(s);
|
||||
if (scope && !dns_scope_is_default_route(scope)) {
|
||||
log_debug("Scope of DNS server %s has only route-only domains, not using as global name server", dns_server_string(s));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,9 @@ int sd_network_link_get_search_domains(int ifindex, char ***domains);
|
||||
/* Get the route DNS domain names for a given link. */
|
||||
int sd_network_link_get_route_domains(int ifindex, char ***domains);
|
||||
|
||||
/* Get whether this link shall be used as 'default route' for DNS queries */
|
||||
int sd_network_link_get_dns_default_route(int ifindex);
|
||||
|
||||
/* Get the carrier interface indexes to which current link is bound to. */
|
||||
int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes);
|
||||
|
||||
|
@ -159,6 +159,7 @@ InvertRule=
|
||||
RouterPreference=
|
||||
DNSLifetimeSec=
|
||||
DNS=
|
||||
DNSDefaultRoute=
|
||||
RouterLifetimeSec=
|
||||
Domains=
|
||||
EmitDNS=
|
||||
|
@ -652,7 +652,7 @@ Domains= ~company ~lab''')
|
||||
conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf'
|
||||
os.makedirs(os.path.dirname(conf), exist_ok=True)
|
||||
with open(conf, 'w') as f:
|
||||
f.write('[Resolve]\nDNSSEC=no')
|
||||
f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n')
|
||||
self.addCleanup(os.remove, conf)
|
||||
|
||||
# create /etc/hosts bind mount which resolves my.example for IPv4
|
||||
|
Loading…
Reference in New Issue
Block a user