]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: disable path MTU discovery for UDP traffic
authorLennart Poettering <lennart@poettering.net>
Mon, 16 Nov 2020 13:02:52 +0000 (14:02 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 18 Feb 2021 14:42:18 +0000 (15:42 +0100)
This disables path MTU discovery both for our UDP upstream connections
and our UDP stub, following the suggestions of:

https://blog.apnic.net/2019/07/12/its-time-to-consider-avoiding-ip-fragmentation-in-the-dns/

This more or less follows the model of other DNS servers on this.

src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-stub.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h

index f1dff95a86d552e23705b942298668201e7e4563..6e02e3f8d505821d46b459df876e3000ce40cc97 100644 (file)
@@ -410,6 +410,11 @@ static int dns_scope_socket(
                 r = socket_set_recvpktinfo(fd, sa.sa.sa_family, true);
                 if (r < 0)
                         return r;
+
+                /* Turn of path MTU discovery for security reasons */
+                r = socket_disable_pmtud(fd, sa.sa.sa_family);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to disable UDP PMTUD, ignoring: %m");
         }
 
         if (ret_socket_address)
index 5311bc88743c58e18b5a052e1c2dbd00b4804a58..416a672bda5d3200b20c0a1a754ee01ba12a1283 100644 (file)
@@ -1117,6 +1117,12 @@ static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int ty
         if (r < 0)
                 goto fail;
 
+        if (type == SOCK_DGRAM) {
+                r = socket_disable_pmtud(fd, l->family);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to disable UDP PMTUD, ignoring: %m");
+        }
+
         if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
                 r = -errno;
                 goto fail;
index 6cc3a5d56f737c8e34815a0a41d155150dcd528a..a8b17143b9f61b417b8285b167e06cfc6cdb25f5 100644 (file)
@@ -1658,3 +1658,63 @@ bool manager_server_is_stub(Manager *m, DnsServer *s) {
 
         return false;
 }
+
+int socket_disable_pmtud(int fd, int af) {
+        int r;
+
+        assert(fd >= 0);
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET: {
+                /* Turn off path MTU discovery, let's rather fragment on the way than to open us up against
+                 * PMTU forgery vulnerabilities.
+                 *
+                 * There appears to be no documentation about IP_PMTUDISC_OMIT, but it has the effect that
+                 * the "Don't Fragment" bit in the IPv4 header is turned off, thus enforcing fragmentation if
+                 * our datagram size exceeds the MTU of a router in the path, and turning off path MTU
+                 * discovery.
+                 *
+                 * This helps mitigating the PMTUD vulnerability described here:
+                 *
+                 * https://blog.apnic.net/2019/07/12/its-time-to-consider-avoiding-ip-fragmentation-in-the-dns/
+                 *
+                 * Similar logic is in place in most DNS servers.
+                 *
+                 * There are multiple conflicting goals: we want to allow the largest datagrams possible (for
+                 * efficiency reasons), but not have fragmentation (for security reasons), nor use PMTUD (for
+                 * security reasons, too). Our strategy to deal with this is: use large packets, turn off
+                 * PMTUD, but watch fragmentation taking place, and then size our packets to the max of the
+                 * fragments seen — and if we need larger packets always go to TCP.
+                 */
+
+                r = setsockopt_int(fd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
+                if (r < 0)
+                        return r;
+
+                return 0;
+        }
+
+        case AF_INET6: {
+                /* On IPv6 fragmentation only is done by the sender — never by routers on the path. PMTUD is
+                 * mandatory. If we want to turn off PMTUD, the only way is by sending with minimal MTU only,
+                 * so that we apply maximum fragmentation locally already, and thus PMTUD doesn't happen
+                 * because there's nothing that could be fragmented further anymore. */
+
+                r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_MTU, IPV6_MIN_MTU);
+                if (r < 0)
+                        return r;
+
+                return 0;
+        }
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
index 90f55862301ee92c19434195edace99596b73f2f..1371c41b92e76a1f4343786217bfeae3e269e448 100644 (file)
@@ -204,3 +204,5 @@ void manager_cleanup_saved_user(Manager *m);
 bool manager_next_dnssd_names(Manager *m);
 
 bool manager_server_is_stub(Manager *m, DnsServer *s);
+
+int socket_disable_pmtud(int fd, int af);