]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: tweak how we calculate MTU for sending packets
authorLennart Poettering <lennart@poettering.net>
Mon, 16 Nov 2020 22:27:21 +0000 (23:27 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 18 Feb 2021 14:55:58 +0000 (15:55 +0100)
Let's take all MTU info we possibly have into account, i.e. the one
reported via netlink, as before and the one the socket might now (from
PMTUD and such), clamped by our own ideas.

src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-mdns.c

index 6f1b48fffb5a9a65577cbc532bea3211a44585f7..2bbb85c1bd88cd12c0e6e3fb35680e874ce89619 100644 (file)
@@ -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");
 
index 7e863d3f665d9b1b43e2ea2b16e20b95fe0f2f82..f63452330ccd77841a51f3ea3aff91af28f08f08 100644 (file)
@@ -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);
 
index 1f396239f993aad85a9822d5c6ea106a1eb2e0a5..24f006be5a4d1b8eea3e825a9047e247955e4ef7 100644 (file)
@@ -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;
 
index 5b4d08cce8ddd024429824d0865a39bd98cdeb98..cf310ea01eb8844fab9adaa2d493cb636f2007ce 100644 (file)
@@ -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");