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

resolved: take fragment size into consideration when determining EDNS0 udp packet size

This commit is contained in:
Lennart Poettering 2020-11-16 21:02:06 +01:00
parent acbf761b5d
commit 980821f3f0
2 changed files with 51 additions and 3 deletions

View File

@ -617,11 +617,48 @@ int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeature
edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO; edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) {
packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX; size_t udp_size;
else
/* In large mode, advertise the local MTU, in order to avoid fragmentation (for security
* reasons) except if we are talking to localhost (where the security considerations don't
* matter). If we see fragmentation, lower the reported size to the largest fragment, to
* avoid it. */
udp_size = udp_header_size(server->family);
if (in_addr_is_localhost(server->family, &server->address) > 0)
packet_size = 65536 - udp_size; /* force linux loopback MTU if localhost address */
else {
/* Use the MTU pointing to the server, subtract the IP/UDP header size */
packet_size = LESS_BY(dns_server_get_mtu(server), udp_size);
/* On the Internet we want to avoid fragmentation for security reasons. If we saw
* fragmented packets, the above was too large, let's clamp it to the largest
* fragment we saw */
if (server->packet_fragmented)
packet_size = MIN(server->received_udp_fragment_max, packet_size);
/* Let's not pick ridiculously large sizes, i.e. not more than 4K. Noone appears to
* ever use such large sized on the Internet IRL, hence let's not either. */
packet_size = MIN(packet_size, 4096U);
}
/* Strictly speaking we quite possibly can receive larger datagrams than the MTU (since the
* MTU is for egress, not for ingress), but more often than not the value is symmetric, and
* we want something that does the right thing in the majority of cases, and not just in the
* theoretical edge case. */
} else
/* In non-large mode, let's advertise the size of the largest fragment we ever managed to accept. */
packet_size = server->received_udp_fragment_max; packet_size = server->received_udp_fragment_max;
/* Safety clamp, never advertise less than 512 or more than 65535 */
packet_size = CLAMP(packet_size,
DNS_PACKET_UNICAST_SIZE_MAX,
DNS_PACKET_SIZE_MAX);
log_debug("Announcing packet size %zu in egress EDNS(0) packet.", packet_size);
return dns_packet_append_opt(packet, packet_size, edns_do, /* include_rfc6975 = */ true, NULL, 0, NULL); return dns_packet_append_opt(packet, packet_size, edns_do, /* include_rfc6975 = */ true, NULL, 0, NULL);
} }
@ -713,6 +750,15 @@ void dns_server_warn_downgrade(DnsServer *server) {
server->warned_downgrade = true; server->warned_downgrade = true;
} }
size_t dns_server_get_mtu(DnsServer *s) {
assert(s);
if (s->link && s->link->mtu != 0)
return s->link->mtu;
return manager_find_mtu(s->manager);
}
static void dns_server_hash_func(const DnsServer *s, struct siphash *state) { static void dns_server_hash_func(const DnsServer *s, struct siphash *state) {
assert(s); assert(s);

View File

@ -157,6 +157,8 @@ void manager_next_dns_server(Manager *m, DnsServer *if_current);
DnssecMode dns_server_get_dnssec_mode(DnsServer *s); DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s); DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s);
size_t dns_server_get_mtu(DnsServer *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops; extern const struct hash_ops dns_server_hash_ops;