Merge pull request #841 from teg/resolved-exponential-resend

resolved: exponential backoff of packet resend
This commit is contained in:
Daniel Mack
2015-08-03 16:32:40 +02:00
7 changed files with 116 additions and 14 deletions
+2 -2
View File
@@ -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++) {
+22
View File
@@ -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;
+6
View File
@@ -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);
+22
View File
@@ -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;
+6
View File
@@ -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;
+56 -4
View File
@@ -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;
+2 -8
View File
@@ -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)