From: Damien Claisse Date: Wed, 29 Oct 2025 09:56:34 +0000 (+0000) Subject: BUG/MINOR: resolvers: ensure fair round robin iteration X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=561dc127bd3a2eb86740e8f92ba12ff076ba4944;p=thirdparty%2Fhaproxy.git BUG/MINOR: resolvers: ensure fair round robin iteration Previous fixes restored round robin iteration, but an imbalance remains when the response tree contains record types other than A or AAAA. Let's take the following example: the DNS answers two A records and a CNAME. The response "tree" (which is actually flat, more like a list) may look as follows, ordered by hash: - 1st item: first A record with IP 1 - 2nd item: second A record with IP 2 - 3rd item: CNAME record As a consequence, resolv_get_ip_from_response will iterate as follows, while the TTL is still valid: - 1st call: DNS request is done, response tree is created, iteration starts at the first item, IP 1 is returned. - 2nd call: cached response tree is used, iteration starts at the second item, IP 2 is returned. - 3rd call: cached response tree is used, iteration starts at the third item, but it's a CNAME, so we continue to the next item, which restarts iteration at the first item, and IP 1 is returned. - 4th call: cached response tree is used and iteration restarts at the beginning, returning IP 1 again. The 1-2-1-1-2-1-1-2 sequence will repeat, so IP 1 will be used twice as often as IP 2, creating a strong imbalance. Even with more IP addresses, the first one by hashing order in the tree will always receive twice the traffic of the others. To fix this, set the next iteration item to the one following the selected IP record, if any. This ensures we never use the same IP twice in a row. This commit should be backported where 3023e9819 ("BUG/MINOR: resolvers: Restore round-robin selection on records in DNS answers") is, so as far as 2.6. --- diff --git a/src/resolvers.c b/src/resolvers.c index c8be0cd98..1ce1774c1 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -1647,7 +1647,6 @@ int resolv_get_ip_from_response(struct resolv_response *r_res, */ eb32 = (!r_res->next) ? eb32_first(&r_res->answer_tree) : r_res->next; end = eb32; - r_res->next = eb32_next(eb32); /* get node for the next lookup */ do { void *ip; unsigned char ip_type; @@ -1733,6 +1732,7 @@ int resolv_get_ip_from_response(struct resolv_response *r_res, * break the parsing. Implicitly, this score is reached the ip * selected is the current ip. */ if (score > max_score) { + r_res->next = eb32_next(eb32); /* get node for the next lookup */ if (ip_type == AF_INET) newip4 = ip; else