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:
parent
95aa3937da
commit
eb170e75ab
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user