]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: filter repeated stub queries 18605/head
authorLennart Poettering <lennart@poettering.net>
Fri, 6 Nov 2020 16:30:58 +0000 (17:30 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 15 Feb 2021 15:27:40 +0000 (16:27 +0100)
Let's suppress repeated stub queries coming in, to minimize resource
usage. Many DNS clients are pretty aggressive regarding repeating DNS
requests, hence let's find them and suppress the follow-ups should we
need more time to fulfill the queries.

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

index 562924e67c37d8cf04023b07fe501fc0395f6cae..aa058446421d63e9f0a7907fc54980a9b3c1952d 100644 (file)
@@ -382,6 +382,13 @@ DnsQuery *dns_query_free(DnsQuery *q) {
                 varlink_unref(q->varlink_request);
         }
 
+        if (q->request_packet)
+                hashmap_remove_value(q->stub_listener_extra ?
+                                     q->stub_listener_extra->queries_by_packet :
+                                     q->manager->stub_queries_by_packet,
+                                     q->request_packet,
+                                     q);
+
         dns_packet_unref(q->request_packet);
         dns_answer_unref(q->reply_answer);
         dns_answer_unref(q->reply_authoritative);
index 5f548abe048de3ca147a9f52ce90b2c5ae28c059..2e92795f3c1800fee9de1183224ac415001c945e 100644 (file)
@@ -82,6 +82,8 @@ DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p) {
         p->udp_event_source = sd_event_source_unref(p->udp_event_source);
         p->tcp_event_source = sd_event_source_unref(p->tcp_event_source);
 
+        hashmap_free(p->queries_by_packet);
+
         return mfree(p);
 }
 
@@ -94,6 +96,47 @@ uint16_t dns_stub_listener_extra_port(DnsStubListenerExtra *p) {
         return 53;
 }
 
+static void stub_packet_hash_func(const DnsPacket *p, struct siphash *state) {
+        assert(p);
+
+        siphash24_compress(&p->protocol, sizeof(p->protocol), state);
+        siphash24_compress(&p->family, sizeof(p->family), state);
+        siphash24_compress(&p->sender, sizeof(p->sender), state);
+        siphash24_compress(&p->ipproto, sizeof(p->ipproto), state);
+        siphash24_compress(&p->sender_port, sizeof(p->sender_port), state);
+        siphash24_compress(DNS_PACKET_HEADER(p), sizeof(DnsPacketHeader), state);
+
+        /* We don't bother hashing the full packet here, just the header */
+}
+
+static int stub_packet_compare_func(const DnsPacket *x, const DnsPacket *y) {
+        int r;
+
+        r = CMP(x->protocol, y->protocol);
+        if (r != 0)
+                return r;
+
+        r = CMP(x->family, y->family);
+        if (r != 0)
+                return r;
+
+        r = memcmp(&x->sender, &y->sender, sizeof(x->sender));
+        if (r != 0)
+                return r;
+
+        r = CMP(x->ipproto, y->ipproto);
+        if (r != 0)
+                return r;
+
+        r = CMP(x->sender_port, y->sender_port);
+        if (r != 0)
+                return r;
+
+        return memcmp(DNS_PACKET_HEADER(x), DNS_PACKET_HEADER(y), sizeof(DnsPacketHeader));
+}
+
+DEFINE_HASH_OPS(stub_packet_hash_ops, DnsPacket, stub_packet_hash_func, stub_packet_compare_func);
+
 static int dns_stub_collect_answer_by_question(
                 DnsAnswer **reply,
                 DnsAnswer *answer,
@@ -685,6 +728,8 @@ static int dns_stub_stream_complete(DnsStream *s, int error) {
 
 static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStream *s, DnsPacket *p) {
         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
+        Hashmap **queries_by_packet;
+        DnsQuery *existing;
         int r;
 
         assert(m);
@@ -703,6 +748,13 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
                 return;
         }
 
+        queries_by_packet = l ? &l->queries_by_packet : &m->stub_queries_by_packet;
+        existing = hashmap_get(*queries_by_packet, p);
+        if (existing && dns_packet_equal(existing->request_packet, p)) {
+                log_debug("Got repeat packet from client, ignoring.");
+                return;
+        }
+
         r = dns_packet_extract(p);
         if (r < 0) {
                 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
@@ -735,6 +787,12 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
                 return;
         }
 
+        r = hashmap_ensure_allocated(queries_by_packet, &stub_packet_hash_ops);
+        if (r < 0) {
+                log_oom();
+                return;
+        }
+
         if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
                 log_debug("Got request with DNSSEC checking disabled, enabling bypass logic.");
 
@@ -774,6 +832,11 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
                 assert(r > 0);
         }
 
+        /* Add the query to the hash table we use to determine repeat packets now. We don't care about
+         * failures here, since in the worst case we'll not recognize duplicate incoming requests, which
+         * isn't particularly bad. */
+        (void) hashmap_put(*queries_by_packet, q->request_packet, q);
+
         r = dns_query_go(q);
         if (r < 0) {
                 log_error_errno(r, "Failed to start query: %m");
index 655c5deec48d1c9f3fee837617716a0d966d64e8..cf03d7f7000c08ec7acee5bea00de1bdddec7f08 100644 (file)
@@ -27,6 +27,8 @@ struct DnsStubListenerExtra {
 
         sd_event_source *udp_event_source;
         sd_event_source *tcp_event_source;
+
+        Hashmap *queries_by_packet;
 };
 
 extern const struct hash_ops dns_stub_listener_extra_hash_ops;
index b41308204e861f72722555c40d7151f0b3005dca..a0997700540d0be550b1303fe23c67307e218137 100644 (file)
@@ -739,6 +739,8 @@ Manager *manager_free(Manager *m) {
         while (m->dns_queries)
                 dns_query_free(m->dns_queries);
 
+        m->stub_queries_by_packet = hashmap_free(m->stub_queries_by_packet);
+
         dns_scope_free(m->unicast_scope);
 
         /* At this point only orphaned streams should remain. All others should have been freed already by their
index 10c2994c2b02b05ba859cab381e94fa0c4b3e790..fd1ac1a5f9c1f3e962dc29b173ddab38eb246303 100644 (file)
@@ -59,6 +59,7 @@ struct Manager {
         Hashmap *dns_transactions;
         LIST_HEAD(DnsQuery, dns_queries);
         unsigned n_dns_queries;
+        Hashmap *stub_queries_by_packet;
 
         LIST_HEAD(DnsStream, dns_streams);
         unsigned n_dns_streams[_DNS_STREAM_TYPE_MAX];