]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: announce support for large UDP packets 2043/head
authorTom Gundersen <teg@jklm.no>
Mon, 6 Jul 2015 14:48:24 +0000 (16:48 +0200)
committerTom Gundersen <teg@jklm.no>
Fri, 27 Nov 2015 00:35:47 +0000 (01:35 +0100)
This is often needed for proper DNSSEC support, and even to handle AAAA records
without falling back to TCP.

If the path between the client and server is fully compliant, this should always
work, however, that is not the case, and overlarge packets will get mysteriously
lost in some cases.

For that reason, we use a similar fallback mechanism as we do for palin EDNS0,
EDNS0+DO, etc.:

The large UDP size feature is different from the other supported feature, as we
cannot simply verify that it works based on receiving a reply (as the server
will usually send us much smaller packets than what we claim to support, so
simply receiving a reply does not mean much).

For that reason, we keep track of the largest UDP packet we ever received, as this
is the smallest known good size (defaulting to the standard 512 bytes). If
announcing the default large size of 4096 fails (in the same way as the other
features), we fall back to the known good size. The same logic of retrying after a
grace-period applies.

src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c

index feded09db31d495c9afe26619f84d02a98f23da5..25dfb2642fb386e6151fb617f7b469cded476d90 100644 (file)
@@ -65,6 +65,9 @@ struct DnsPacketHeader {
 /* RFC 1035 say 512 is the maximum, for classic unicast DNS */
 #define DNS_PACKET_UNICAST_SIZE_MAX 512
 
+/* With EDNS0 we can use larger packets, default to 4096, which is what is commonly used */
+#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096
+
 #define DNS_PACKET_SIZE_START 512
 
 struct DnsPacket {
index 80070da2b921c51fcc2b89aa7eafefeac5aa5e09..09e6872d26204bfba4c492774193c12bdb668db7 100644 (file)
@@ -186,10 +186,16 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
 
                 if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
                         bool edns_do;
+                        size_t packet_size;
 
                         edns_do = server->possible_features >= DNS_SERVER_FEATURE_LEVEL_DO;
 
-                        r = dns_packet_append_opt_rr(p, DNS_PACKET_UNICAST_SIZE_MAX, edns_do, &saved_size);
+                        if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_LARGE)
+                                packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
+                        else
+                                packet_size = server->received_udp_packet_max;
+
+                        r = dns_packet_append_opt_rr(p, packet_size, edns_do, &saved_size);
                         if (r < 0)
                                 return r;
 
index 916f5dadb8def0b120514dc5fbd3cc9fc0aa9740..d565f99c09fe4b0cf82af173c65c41843885a0fc 100644 (file)
@@ -71,6 +71,7 @@ int dns_server_new(
         s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
         s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
         s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
+        s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
         s->type = type;
         s->family = family;
         s->address = *in_addr;
@@ -223,10 +224,18 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
         }
 }
 
-void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt) {
+void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) {
         assert(s);
 
-        if (s->verified_features < features) {
+        if (features == DNS_SERVER_FEATURE_LEVEL_LARGE) {
+                /* even if we successfully receive a reply to a request announcing
+                   support for large packets, that does not mean we can necessarily
+                   receive large packets. */
+                if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
+                        s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
+                        assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
+                }
+        } else if (s->verified_features < features) {
                 s->verified_features = features;
                 assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
         }
@@ -234,6 +243,12 @@ void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, us
         if (s->possible_features == features)
                 s->n_failed_attempts = 0;
 
+        /* Remember the size of the largest UDP packet we received from a server,
+           we know that we can always announce support for packets with at least
+           this size. */
+        if (s->received_udp_packet_max < size)
+                s->received_udp_packet_max = size;
+
         if (s->max_rtt < rtt) {
                 s->max_rtt = rtt;
                 s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
@@ -480,5 +495,6 @@ static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVE
         [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
         [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
         [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
+        [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
 };
 DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);
index 9dd4961d5fc449de397271b10bfc14d7a72eeeb8..00366a48c90636e06d85894df72062941c43247e 100644 (file)
@@ -36,6 +36,7 @@ typedef enum DnsServerFeatureLevel {
         DNS_SERVER_FEATURE_LEVEL_UDP,
         DNS_SERVER_FEATURE_LEVEL_EDNS0,
         DNS_SERVER_FEATURE_LEVEL_DO,
+        DNS_SERVER_FEATURE_LEVEL_LARGE,
         _DNS_SERVER_FEATURE_LEVEL_MAX,
         _DNS_SERVER_FEATURE_LEVEL_INVALID = -1
 } DnsServerFeatureLevel;
@@ -66,6 +67,7 @@ struct DnsServer {
         bool marked:1;
         DnsServerFeatureLevel verified_features;
         DnsServerFeatureLevel possible_features;
+        size_t received_udp_packet_max;
         unsigned n_failed_attempts;
         usec_t verified_usec;
         usec_t features_grace_period_usec;
@@ -89,7 +91,7 @@ DnsServer* dns_server_unref(DnsServer *s);
 void dns_server_unlink(DnsServer *s);
 void dns_server_move_back_and_unmark(DnsServer *s);
 
-void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt);
+void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size);
 void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec);
 void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features);
 
index f81461a4fea11813f8ec28a1a6d90d6e26ac9b2e..90133cb3327b06ed52519b23558f150f3073cbd0 100644 (file)
@@ -433,7 +433,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
 
                         return;
                 } else
-                        dns_server_packet_received(t->server, t->current_features, ts - t->start_usec);
+                        dns_server_packet_received(t->server, t->current_features, ts - t->start_usec, p->size);
 
                 break;
         case DNS_PROTOCOL_LLMNR: