resolved: let's preferably route reverse lookups for local subnets to matching interfaces

Let's preferably route traffic for reverse lookups to LLMNR/mDNS/DNS on
the matching interface if the IP address is in the local subnet. Also,
if looking up an IP address of our own host, let's avoid doing
LLMNR/mDNS at all.

This is useful if "~." is a routing domain to DNS, as it means, local
reverse lookups still go to LLMNR/mDNS, too.

Fixes: #16243 #10081
This commit is contained in:
Lennart Poettering
2020-11-09 22:22:56 +01:00
parent 1e69eaddf8
commit 13eb76ef06
3 changed files with 80 additions and 1 deletions

View File

@@ -477,6 +477,65 @@ static DnsScopeMatch match_link_local_reverse_lookups(const char *domain) {
return _DNS_SCOPE_MATCH_INVALID;
}
static DnsScopeMatch match_subnet_reverse_lookups(
DnsScope *s,
const char *domain,
bool exclude_own) {
union in_addr_union ia;
LinkAddress *a;
int f, r;
assert(s);
assert(domain);
/* Checks whether the specified domain is a reverse address domain (i.e. in the .in-addr.arpa or
* .ip6.arpa area), and if so, whether the address matches any of the local subnets of the link the
* scope is associated with. If so, our scope should consider itself relevant for any lookup in the
* domain, since it apparently refers to hosts on this link's subnet.
*
* If 'exclude_own' is true this will return DNS_SCOPE_NO for any IP addresses assigned locally. This
* is useful for LLMNR/mDNS as we never want to look up our own hostname on LLMNR/mDNS but always use
* the locally synthesized one. */
if (!s->link)
return _DNS_SCOPE_MATCH_INVALID; /* No link, hence no local addresses to check */
r = dns_name_address(domain, &f, &ia);
if (r < 0)
log_debug_errno(r, "Failed to determine whether '%s' is an address domain: %m", domain);
if (r <= 0)
return _DNS_SCOPE_MATCH_INVALID;
if (s->family != AF_UNSPEC && f != s->family)
return _DNS_SCOPE_MATCH_INVALID; /* Don't look for IPv4 addresses on LLMNR/mDNS over IPv6 and vice versa */
LIST_FOREACH(addresses, a, s->link->addresses) {
if (a->family != f)
continue;
/* Equals our own address? nah, let's not use this scope. The local synthesizer will pick it up for us. */
if (exclude_own &&
in_addr_equal(f, &a->in_addr, &ia) > 0)
return DNS_SCOPE_NO;
if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */
continue;
/* Check if the address is in the local subnet */
r = in_addr_prefix_covers(f, &a->in_addr, a->prefixlen, &ia);
if (r < 0)
log_debug_errno(r, "Failed to determine whether link address covers lookup address '%s': %m", domain);
if (r > 0)
/* Note that we only claim zero labels match. This is so that this is at the same
* priority a DNS scope with "." as routing domain is. */
return DNS_SCOPE_YES_BASE + 0;
}
return _DNS_SCOPE_MATCH_INVALID;
}
DnsScopeMatch dns_scope_good_domain(
DnsScope *s,
int ifindex,
@@ -533,6 +592,7 @@ DnsScopeMatch dns_scope_good_domain(
case DNS_PROTOCOL_DNS: {
bool has_search_domains = false;
DnsScopeMatch m;
int n_best = -1;
/* Never route things to scopes that lack DNS servers */
@@ -579,6 +639,13 @@ DnsScopeMatch dns_scope_good_domain(
dns_name_endswith(domain, "local") > 0)
return DNS_SCOPE_NO;
/* If the IP address to look up matches the local subnet, then implicity synthesizes
* DNS_SCOPE_YES_BASE + 0 on this interface, i.e. preferably resolve IP addresses via the DNS
* server belonging to this interface. */
m = match_subnet_reverse_lookups(s, domain, false);
if (m >= 0)
return m;
/* If there was no match at all, then see if this scope is suitable as default route. */
if (!dns_scope_is_default_route(s))
return DNS_SCOPE_NO;
@@ -593,6 +660,10 @@ DnsScopeMatch dns_scope_good_domain(
if (m >= 0)
return m;
m = match_subnet_reverse_lookups(s, domain, true);
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))
return DNS_SCOPE_MAYBE;
@@ -612,6 +683,10 @@ DnsScopeMatch dns_scope_good_domain(
if (m >= 0)
return m;
m = match_subnet_reverse_lookups(s, domain, true);
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))
return DNS_SCOPE_MAYBE;

View File

@@ -802,6 +802,7 @@ int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr
.family = family,
.in_addr = *in_addr,
.link = l,
.prefixlen = UCHAR_MAX,
};
LIST_PREPEND(addresses, l->addresses, a);
@@ -1094,6 +1095,7 @@ fail:
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
int r;
assert(a);
assert(m);
@@ -1101,7 +1103,8 @@ int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
if (r < 0)
return r;
sd_rtnl_message_addr_get_scope(m, &a->scope);
(void) sd_rtnl_message_addr_get_prefixlen(m, &a->prefixlen);
(void) sd_rtnl_message_addr_get_scope(m, &a->scope);
link_allocate_scopes(a->link);
link_add_rrs(a->link, false);

View File

@@ -23,6 +23,7 @@ struct LinkAddress {
int family;
union in_addr_union in_addr;
unsigned char prefixlen;
unsigned char flags, scope;