diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 6f1b48fffb..2bbb85c1bd 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -5,6 +5,7 @@ #include "af-list.h" #include "alloc-util.h" #include "dns-domain.h" +#include "errno-util.h" #include "fd-util.h" #include "hostname-util.h" #include "missing_network.h" @@ -185,43 +186,73 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) { s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC); } -static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { - union in_addr_union addr; - int ifindex = 0, r; - int family; - uint32_t mtu; +static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) { + int r; assert(s); assert(p); assert(p->protocol == s->protocol); - if (s->link) { - mtu = s->link->mtu; - ifindex = s->link->ifindex; - } else - mtu = manager_find_mtu(s->manager); + if (family == AF_UNSPEC) { + if (s->family == AF_UNSPEC) + return -EAFNOSUPPORT; + + family = s->family; + } switch (s->protocol) { - case DNS_PROTOCOL_DNS: + case DNS_PROTOCOL_DNS: { + size_t mtu, udp_size, min_mtu, socket_mtu = 0; + assert(fd >= 0); - if (DNS_PACKET_QDCOUNT(p) > 1) + if (DNS_PACKET_QDCOUNT(p) > 1) /* Classic DNS only allows one question per packet */ return -EOPNOTSUPP; if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) return -EMSGSIZE; - if (p->size + UDP4_PACKET_HEADER_SIZE > mtu) - return -EMSGSIZE; + /* Determine the local most accurate MTU */ + if (s->link) + mtu = s->link->mtu; + else + mtu = manager_find_mtu(s->manager); + + /* Acquire the socket's PMDU MTU */ + r = socket_get_mtu(fd, family, &socket_mtu); + if (r < 0 && !ERRNO_IS_DISCONNECT(r)) /* Will return ENOTCONN if no information is available yet */ + return log_debug_errno(r, "Failed to read socket MTU: %m"); + + /* Determine the appropriate UDP header size */ + udp_size = udp_header_size(family); + min_mtu = udp_size + DNS_PACKET_HEADER_SIZE; + + log_debug("Emitting UDP, link MTU is %zu, socket MTU is %zu, minimal MTU is %zu", + mtu, socket_mtu, min_mtu); + + /* Clamp by the kernel's idea of the (path) MTU */ + if (socket_mtu != 0 && socket_mtu < mtu) + mtu = socket_mtu; + + /* Put a lower limit, in case all MTU data we acquired was rubbish */ + if (mtu < min_mtu) + mtu = min_mtu; + + /* Now check our packet size against the MTU we determined */ + if (udp_size + p->size > mtu) + return -EMSGSIZE; /* This means: try TCP instead */ r = manager_write(s->manager, fd, p); if (r < 0) return r; break; + } + + case DNS_PROTOCOL_LLMNR: { + union in_addr_union addr; - case DNS_PROTOCOL_LLMNR: assert(fd < 0); if (DNS_PACKET_QDCOUNT(p) > 1) @@ -230,8 +261,6 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { if (!ratelimit_below(&s->ratelimit)) return -EBUSY; - family = s->family; - if (family == AF_INET) { addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; fd = manager_llmnr_ipv4_udp_fd(s->manager); @@ -243,20 +272,20 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { if (fd < 0) return fd; - r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, p); + r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, LLMNR_PORT, NULL, p); if (r < 0) return r; break; + } - case DNS_PROTOCOL_MDNS: + case DNS_PROTOCOL_MDNS: { + union in_addr_union addr; assert(fd < 0); if (!ratelimit_below(&s->ratelimit)) return -EBUSY; - family = s->family; - if (family == AF_INET) { addr.in = MDNS_MULTICAST_IPV4_ADDRESS; fd = manager_mdns_ipv4_fd(s->manager); @@ -268,11 +297,12 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { if (fd < 0) return fd; - r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, NULL, p); + r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, MDNS_PORT, NULL, p); if (r < 0) return r; break; + } default: return -EAFNOSUPPORT; @@ -281,7 +311,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { return 1; } -int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) { +int dns_scope_emit_udp(DnsScope *s, int fd, int af, DnsPacket *p) { int r; assert(s); @@ -296,7 +326,7 @@ int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) { dns_packet_set_flags(p, true, true); } - r = dns_scope_emit_one(s, fd, p); + r = dns_scope_emit_one(s, fd, af, p); if (r < 0) return r; @@ -1133,7 +1163,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata return 0; } - r = dns_scope_emit_udp(scope, -1, p); + r = dns_scope_emit_udp(scope, -1, AF_UNSPEC, p); if (r < 0) log_debug_errno(r, "Failed to send conflict packet: %m"); } @@ -1430,7 +1460,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { if (r < 0) return log_debug_errno(r, "Failed to build reply packet: %m"); - r = dns_scope_emit_udp(scope, -1, p); + r = dns_scope_emit_udp(scope, -1, AF_UNSPEC, p); if (r < 0) return log_debug_errno(r, "Failed to send reply packet: %m"); diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 7e863d3f66..f63452330c 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -71,7 +71,7 @@ DnsScope* dns_scope_free(DnsScope *s); void dns_scope_packet_received(DnsScope *s, usec_t rtt); void dns_scope_packet_lost(DnsScope *s, usec_t usec); -int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p); +int dns_scope_emit_udp(DnsScope *s, int fd, int af, DnsPacket *p); int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address); int dns_scope_socket_udp(DnsScope *s, DnsServer *server); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 1f396239f9..24f006be5a 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1470,7 +1470,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) { } else dns_transaction_close_connection(t, true); - r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->sent); + r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->server ? t->server->family : AF_UNSPEC, t->sent); if (r < 0) return r; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 5b4d08cce8..cf310ea01e 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -237,7 +237,7 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { if (!ratelimit_below(&s->ratelimit)) return 0; - r = dns_scope_emit_udp(s, -1, reply); + r = dns_scope_emit_udp(s, -1, AF_UNSPEC, reply); if (r < 0) return log_debug_errno(r, "Failed to send reply packet: %m");