1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-31 16:21:26 +03:00

resolved: disable path MTU discovery for UDP traffic

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.
This commit is contained in:
Lennart Poettering 2020-11-16 14:02:52 +01:00
parent 95aa3937da
commit eb170e75ab
4 changed files with 73 additions and 0 deletions

View File

@ -410,6 +410,11 @@ static int dns_scope_socket(
r = socket_set_recvpktinfo(fd, sa.sa.sa_family, true); r = socket_set_recvpktinfo(fd, sa.sa.sa_family, true);
if (r < 0) if (r < 0)
return r; 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) if (ret_socket_address)

View File

@ -1117,6 +1117,12 @@ static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int ty
if (r < 0) if (r < 0)
goto fail; 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) { if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
r = -errno; r = -errno;
goto fail; goto fail;

View File

@ -1658,3 +1658,63 @@ bool manager_server_is_stub(Manager *m, DnsServer *s) {
return false; 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;
}
}

View File

@ -204,3 +204,5 @@ void manager_cleanup_saved_user(Manager *m);
bool manager_next_dnssd_names(Manager *m); bool manager_next_dnssd_names(Manager *m);
bool manager_server_is_stub(Manager *m, DnsServer *s); bool manager_server_is_stub(Manager *m, DnsServer *s);
int socket_disable_pmtud(int fd, int af);