Skip to content

Commit 13eb76e

Browse files
committed
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: systemd#16243 systemd#10081
1 parent 1e69ead commit 13eb76e

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

src/resolve/resolved-dns-scope.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,65 @@ static DnsScopeMatch match_link_local_reverse_lookups(const char *domain) {
477477
return _DNS_SCOPE_MATCH_INVALID;
478478
}
479479

480+
static DnsScopeMatch match_subnet_reverse_lookups(
481+
DnsScope *s,
482+
const char *domain,
483+
bool exclude_own) {
484+
485+
union in_addr_union ia;
486+
LinkAddress *a;
487+
int f, r;
488+
489+
assert(s);
490+
assert(domain);
491+
492+
/* Checks whether the specified domain is a reverse address domain (i.e. in the .in-addr.arpa or
493+
* .ip6.arpa area), and if so, whether the address matches any of the local subnets of the link the
494+
* scope is associated with. If so, our scope should consider itself relevant for any lookup in the
495+
* domain, since it apparently refers to hosts on this link's subnet.
496+
*
497+
* If 'exclude_own' is true this will return DNS_SCOPE_NO for any IP addresses assigned locally. This
498+
* is useful for LLMNR/mDNS as we never want to look up our own hostname on LLMNR/mDNS but always use
499+
* the locally synthesized one. */
500+
501+
if (!s->link)
502+
return _DNS_SCOPE_MATCH_INVALID; /* No link, hence no local addresses to check */
503+
504+
r = dns_name_address(domain, &f, &ia);
505+
if (r < 0)
506+
log_debug_errno(r, "Failed to determine whether '%s' is an address domain: %m", domain);
507+
if (r <= 0)
508+
return _DNS_SCOPE_MATCH_INVALID;
509+
510+
if (s->family != AF_UNSPEC && f != s->family)
511+
return _DNS_SCOPE_MATCH_INVALID; /* Don't look for IPv4 addresses on LLMNR/mDNS over IPv6 and vice versa */
512+
513+
LIST_FOREACH(addresses, a, s->link->addresses) {
514+
515+
if (a->family != f)
516+
continue;
517+
518+
/* Equals our own address? nah, let's not use this scope. The local synthesizer will pick it up for us. */
519+
if (exclude_own &&
520+
in_addr_equal(f, &a->in_addr, &ia) > 0)
521+
return DNS_SCOPE_NO;
522+
523+
if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */
524+
continue;
525+
526+
/* Check if the address is in the local subnet */
527+
r = in_addr_prefix_covers(f, &a->in_addr, a->prefixlen, &ia);
528+
if (r < 0)
529+
log_debug_errno(r, "Failed to determine whether link address covers lookup address '%s': %m", domain);
530+
if (r > 0)
531+
/* Note that we only claim zero labels match. This is so that this is at the same
532+
* priority a DNS scope with "." as routing domain is. */
533+
return DNS_SCOPE_YES_BASE + 0;
534+
}
535+
536+
return _DNS_SCOPE_MATCH_INVALID;
537+
}
538+
480539
DnsScopeMatch dns_scope_good_domain(
481540
DnsScope *s,
482541
int ifindex,
@@ -533,6 +592,7 @@ DnsScopeMatch dns_scope_good_domain(
533592

534593
case DNS_PROTOCOL_DNS: {
535594
bool has_search_domains = false;
595+
DnsScopeMatch m;
536596
int n_best = -1;
537597

538598
/* Never route things to scopes that lack DNS servers */
@@ -579,6 +639,13 @@ DnsScopeMatch dns_scope_good_domain(
579639
dns_name_endswith(domain, "local") > 0)
580640
return DNS_SCOPE_NO;
581641

642+
/* If the IP address to look up matches the local subnet, then implicity synthesizes
643+
* DNS_SCOPE_YES_BASE + 0 on this interface, i.e. preferably resolve IP addresses via the DNS
644+
* server belonging to this interface. */
645+
m = match_subnet_reverse_lookups(s, domain, false);
646+
if (m >= 0)
647+
return m;
648+
582649
/* If there was no match at all, then see if this scope is suitable as default route. */
583650
if (!dns_scope_is_default_route(s))
584651
return DNS_SCOPE_NO;
@@ -593,6 +660,10 @@ DnsScopeMatch dns_scope_good_domain(
593660
if (m >= 0)
594661
return m;
595662

663+
m = match_subnet_reverse_lookups(s, domain, true);
664+
if (m >= 0)
665+
return m;
666+
596667
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
597668
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
598669
return DNS_SCOPE_MAYBE;
@@ -612,6 +683,10 @@ DnsScopeMatch dns_scope_good_domain(
612683
if (m >= 0)
613684
return m;
614685

686+
m = match_subnet_reverse_lookups(s, domain, true);
687+
if (m >= 0)
688+
return m;
689+
615690
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
616691
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
617692
return DNS_SCOPE_MAYBE;

src/resolve/resolved-link.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr
802802
.family = family,
803803
.in_addr = *in_addr,
804804
.link = l,
805+
.prefixlen = UCHAR_MAX,
805806
};
806807

807808
LIST_PREPEND(addresses, l->addresses, a);
@@ -1094,14 +1095,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
10941095

10951096
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
10961097
int r;
1098+
10971099
assert(a);
10981100
assert(m);
10991101

11001102
r = sd_rtnl_message_addr_get_flags(m, &a->flags);
11011103
if (r < 0)
11021104
return r;
11031105

1104-
sd_rtnl_message_addr_get_scope(m, &a->scope);
1106+
(void) sd_rtnl_message_addr_get_prefixlen(m, &a->prefixlen);
1107+
(void) sd_rtnl_message_addr_get_scope(m, &a->scope);
11051108

11061109
link_allocate_scopes(a->link);
11071110
link_add_rrs(a->link, false);

src/resolve/resolved-link.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct LinkAddress {
2323

2424
int family;
2525
union in_addr_union in_addr;
26+
unsigned char prefixlen;
2627

2728
unsigned char flags, scope;
2829

0 commit comments

Comments
 (0)