Merge pull request #841 from teg/resolved-exponential-resend
resolved: exponential backoff of packet resend
This commit is contained in:
@@ -152,7 +152,7 @@ void dns_cache_prune(DnsCache *c) {
|
||||
break;
|
||||
|
||||
if (t <= 0)
|
||||
t = now(CLOCK_BOOTTIME);
|
||||
t = now(clock_boottime_or_monotonic());
|
||||
|
||||
if (i->until > t)
|
||||
break;
|
||||
@@ -406,7 +406,7 @@ int dns_cache_put(
|
||||
dns_cache_make_space(c, answer->n_rrs + q->n_keys);
|
||||
|
||||
if (timestamp <= 0)
|
||||
timestamp = now(CLOCK_BOOTTIME);
|
||||
timestamp = now(clock_boottime_or_monotonic());
|
||||
|
||||
/* Second, add in positive entries for all contained RRs */
|
||||
for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
|
||||
#define MULTICAST_RATELIMIT_BURST 1000
|
||||
|
||||
/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
|
||||
#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC)
|
||||
#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC)
|
||||
|
||||
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
|
||||
DnsScope *s;
|
||||
|
||||
@@ -48,6 +52,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
|
||||
s->link = l;
|
||||
s->protocol = protocol;
|
||||
s->family = family;
|
||||
s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
|
||||
|
||||
LIST_PREPEND(scopes, m->dns_scopes, s);
|
||||
|
||||
@@ -125,6 +130,23 @@ void dns_scope_next_dns_server(DnsScope *s) {
|
||||
manager_next_dns_server(s->manager);
|
||||
}
|
||||
|
||||
void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
|
||||
assert(s);
|
||||
|
||||
if (rtt > s->max_rtt) {
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2),
|
||||
MULTICAST_RESEND_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
|
||||
assert(s);
|
||||
|
||||
if (s->resend_timeout <= usec)
|
||||
s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
|
||||
union in_addr_union addr;
|
||||
int ifindex = 0, r;
|
||||
|
||||
@@ -57,6 +57,9 @@ struct DnsScope {
|
||||
|
||||
RateLimit ratelimit;
|
||||
|
||||
usec_t resend_timeout;
|
||||
usec_t max_rtt;
|
||||
|
||||
LIST_HEAD(DnsTransaction, transactions);
|
||||
|
||||
LIST_FIELDS(DnsScope, scopes);
|
||||
@@ -65,6 +68,9 @@ struct DnsScope {
|
||||
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
|
||||
DnsScope* dns_scope_free(DnsScope *s);
|
||||
|
||||
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
|
||||
void dns_scope_packet_lost(DnsScope *s, usec_t usec);
|
||||
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p);
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
|
||||
int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
#include "resolved-dns-server.h"
|
||||
|
||||
/* After how much time to repeat classic DNS requests */
|
||||
#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
|
||||
#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
|
||||
|
||||
int dns_server_new(
|
||||
Manager *m,
|
||||
DnsServer **ret,
|
||||
@@ -45,6 +49,7 @@ int dns_server_new(
|
||||
s->type = type;
|
||||
s->family = family;
|
||||
s->address = *in_addr;
|
||||
s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
|
||||
|
||||
if (type == DNS_SERVER_LINK) {
|
||||
LIST_FIND_TAIL(servers, l->dns_servers, tail);
|
||||
@@ -115,6 +120,23 @@ DnsServer* dns_server_unref(DnsServer *s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, usec_t rtt) {
|
||||
assert(s);
|
||||
|
||||
if (rtt > s->max_rtt) {
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2),
|
||||
DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
void dns_server_packet_lost(DnsServer *s, usec_t usec) {
|
||||
assert(s);
|
||||
|
||||
if (s->resend_timeout <= usec)
|
||||
s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
|
||||
const DnsServer *s = p;
|
||||
uint64_t u;
|
||||
|
||||
@@ -46,6 +46,9 @@ struct DnsServer {
|
||||
int family;
|
||||
union in_addr_union address;
|
||||
|
||||
usec_t resend_timeout;
|
||||
usec_t max_rtt;
|
||||
|
||||
bool marked:1;
|
||||
|
||||
LIST_FIELDS(DnsServer, servers);
|
||||
@@ -62,6 +65,9 @@ int dns_server_new(
|
||||
DnsServer* dns_server_ref(DnsServer *s);
|
||||
DnsServer* dns_server_unref(DnsServer *s);
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, usec_t rtt);
|
||||
void dns_server_packet_lost(DnsServer *s, usec_t usec);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
|
||||
|
||||
extern const struct hash_ops dns_server_hash_ops;
|
||||
|
||||
@@ -319,11 +319,14 @@ static void dns_transaction_next_dns_server(DnsTransaction *t) {
|
||||
}
|
||||
|
||||
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
usec_t ts;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(p);
|
||||
assert(t->state == DNS_TRANSACTION_PENDING);
|
||||
assert(t->scope);
|
||||
assert(t->scope->manager);
|
||||
|
||||
/* Note that this call might invalidate the query. Callers
|
||||
* should hence not attempt to access the query or transaction
|
||||
@@ -369,6 +372,26 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts);
|
||||
if (r < 0)
|
||||
ts = now(clock_boottime_or_monotonic());
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
case DNS_PROTOCOL_DNS:
|
||||
assert(t->server);
|
||||
|
||||
dns_server_packet_received(t->server, ts - t->start_usec);
|
||||
|
||||
break;
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
dns_scope_packet_received(t->scope, ts - t->start_usec);
|
||||
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Invalid DNS protocol.");
|
||||
}
|
||||
|
||||
if (DNS_PACKET_TC(p)) {
|
||||
/* Response was truncated, let's try again with good old TCP */
|
||||
r = dns_transaction_open_tcp(t);
|
||||
@@ -434,9 +457,9 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
|
||||
return r;
|
||||
|
||||
if (dns_packet_validate_reply(p) > 0 &&
|
||||
DNS_PACKET_ID(p) == t->id) {
|
||||
DNS_PACKET_ID(p) == t->id)
|
||||
dns_transaction_process_reply(t, p);
|
||||
} else
|
||||
else
|
||||
log_debug("Invalid DNS packet.");
|
||||
|
||||
return 0;
|
||||
@@ -481,6 +504,12 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
|
||||
/* Timeout reached? Try again, with a new server */
|
||||
dns_transaction_next_dns_server(t);
|
||||
|
||||
/* ... and possibly increased timeout */
|
||||
if (t->server)
|
||||
dns_server_packet_lost(t->server, usec - t->start_usec);
|
||||
else
|
||||
dns_scope_packet_lost(t->scope, usec - t->start_usec);
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0)
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
|
||||
@@ -528,8 +557,26 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
|
||||
assert(t);
|
||||
assert(t->scope);
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
case DNS_PROTOCOL_DNS:
|
||||
assert(t->server);
|
||||
|
||||
return t->server->resend_timeout;
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
return t->scope->resend_timeout;
|
||||
default:
|
||||
assert_not_reached("Invalid DNS protocol.");
|
||||
}
|
||||
}
|
||||
|
||||
int dns_transaction_go(DnsTransaction *t) {
|
||||
bool had_stream;
|
||||
usec_t ts;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
@@ -555,7 +602,12 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts);
|
||||
if (r < 0)
|
||||
ts = now(clock_boottime_or_monotonic());
|
||||
|
||||
t->n_attempts++;
|
||||
t->start_usec = ts;
|
||||
t->received = dns_packet_unref(t->received);
|
||||
t->cached = dns_answer_unref(t->cached);
|
||||
t->cached_rcode = 0;
|
||||
@@ -600,7 +652,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
t->scope->manager->event,
|
||||
&t->timeout_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
now(clock_boottime_or_monotonic()) + jitter,
|
||||
ts + jitter,
|
||||
LLMNR_JITTER_INTERVAL_USEC,
|
||||
on_transaction_timeout, t);
|
||||
if (r < 0)
|
||||
@@ -660,7 +712,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
t->scope->manager->event,
|
||||
&t->timeout_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
now(clock_boottime_or_monotonic()) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 0,
|
||||
ts + transaction_get_resend_timeout(t), 0,
|
||||
on_transaction_timeout, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -58,6 +58,7 @@ struct DnsTransaction {
|
||||
DnsAnswer *cached;
|
||||
int cached_rcode;
|
||||
|
||||
usec_t start_usec;
|
||||
sd_event_source *timeout_event_source;
|
||||
unsigned n_attempts;
|
||||
|
||||
@@ -95,20 +96,13 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
|
||||
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
|
||||
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
|
||||
|
||||
/* After how much time to repeat classic DNS requests */
|
||||
#define DNS_TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
|
||||
|
||||
/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
|
||||
#define LLMNR_TRANSACTION_TIMEOUT_USEC (1 * USEC_PER_SEC)
|
||||
|
||||
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
|
||||
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
|
||||
|
||||
/* Maximum attempts to send DNS requests, across all DNS servers */
|
||||
#define DNS_TRANSACTION_ATTEMPTS_MAX 8
|
||||
#define DNS_TRANSACTION_ATTEMPTS_MAX 16
|
||||
|
||||
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
|
||||
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
#define TRANSACTION_TIMEOUT_USEC(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_TIMEOUT_USEC : DNS_TRANSACTION_TIMEOUT_USEC)
|
||||
#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
|
||||
Reference in New Issue
Block a user