resolved: filter repeated stub queries

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.
This commit is contained in:
Lennart Poettering
2021-02-15 16:27:40 +01:00
parent ed8a48c9b6
commit bde69bbd89
5 changed files with 75 additions and 0 deletions
+7
View 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);
+63
View 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");
+2
View 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;
+2
View 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
+1
View 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];