unbound: Test for working EDNS buffer size and adjust accordingly

Some networks have equipment that fails to forward DNS queries
with EDNS and the DO bit set. They might even lose the replies.

This patch will adjust unbound so that it will not try to receive
too large replies and falls back to TCP earlier. This creates
some higher load on the DNS servers but at least gives us
working DNS.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
This commit is contained in:
Michael Tremer
2016-12-14 12:45:07 +00:00
parent b26b242a9c
commit 8f3034d0db

View File

@@ -86,6 +86,24 @@ update_forwarders() {
esac esac
done done
# Determine EDNS buffer size
local current_edns_buffer_size=$(unbound-control get_option edns-buffer-size)
if [ -n "${current_edns_buffer_size}" ]; then
local new_edns_buffer_size=${current_edns_buffer_size}
for ns in ${forwarders}; do
local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
if [ -n "${edns_buffer_size}" ]; then
if [ ${edns_buffer_size} -lt ${current_edns_buffer_size} ]; then
new_edns_buffer_size=${edns_buffer_size}
fi
fi
done
unbound-control -q set_option edns-buffer-size: ${new_edns_buffer_size}
fi
# Show warning for any broken upstream name servers # Show warning for any broken upstream name servers
if [ -n "${broken_forwarders}" ]; then if [ -n "${broken_forwarders}" ]; then
boot_mesg "Ignoring broken upstream name server(s): ${broken_forwarders:1}" ${WARNING} boot_mesg "Ignoring broken upstream name server(s): ${broken_forwarders:1}" ${WARNING}
@@ -249,6 +267,7 @@ get_memory_amount() {
test_name_server() { test_name_server() {
local ns=${1} local ns=${1}
local args
# Return codes: # Return codes:
# 0 DNSSEC validating # 0 DNSSEC validating
@@ -259,9 +278,15 @@ test_name_server() {
# Exit when the server is not reachable # Exit when the server is not reachable
ns_is_online ${ns} || return 1 ns_is_online ${ns} || return 1
# Determine the maximum edns buffer size that works
local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
if [ -n "${edns_buffer_size}" ]; then
args="${args} +bufsize=${edns_buffer_size}"
fi
local errors local errors
for rr in DNSKEY DS RRSIG; do for rr in DNSKEY DS RRSIG; do
if ! ns_forwards_${rr} ${ns}; then if ! ns_forwards_${rr} ${ns} ${args}; then
errors="${errors} ${rr}" errors="${errors} ${rr}"
fi fi
done done
@@ -271,7 +296,7 @@ test_name_server() {
return 3 return 3
fi fi
if ns_is_validating ${ns}; then if ns_is_validating ${ns} ${args}; then
# Return 0 if validating # Return 0 if validating
return 0 return 0
else else
@@ -283,41 +308,62 @@ test_name_server() {
# Sends an A query to the nameserver w/o DNSSEC # Sends an A query to the nameserver w/o DNSSEC
ns_is_online() { ns_is_online() {
local ns=${1} local ns=${1}
shift
dig @${ns} +nodnssec A ${TEST_DOMAIN} >/dev/null dig @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
} }
# Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating # Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
ns_is_validating() { ns_is_validating() {
local ns=${1} local ns=${1}
shift
dig @${ns} A ${TEST_DOMAIN_FAIL} | grep -q SERVFAIL dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL
} }
# Checks if we can retrieve the DNSKEY for this domain. # Checks if we can retrieve the DNSKEY for this domain.
# dig will print the SOA if nothing was found # dig will print the SOA if nothing was found
ns_forwards_DNSKEY() { ns_forwards_DNSKEY() {
local ns=${1} local ns=${1}
shift
dig @${ns} DNSKEY ${TEST_DOMAIN} | grep -qv SOA dig @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
} }
ns_forwards_DS() { ns_forwards_DS() {
local ns=${1} local ns=${1}
shift
dig @${ns} DS ${TEST_DOMAIN} | grep -qv SOA dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
} }
ns_forwards_RRSIG() { ns_forwards_RRSIG() {
local ns=${1} local ns=${1}
shift
dig @${ns} +dnssec A ${TEST_DOMAIN} | grep -q RRSIG dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
} }
ns_supports_tcp() { ns_supports_tcp() {
local ns=${1} local ns=${1}
shift
dig @${ns} +tcp A ${TEST_DOMAIN} >/dev/null || return 1 dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
}
ns_determine_edns_buffer_size() {
local ns=${1}
shift
local b
for b in 4096 2048 1500 1480 1464 1400 1280 512; do
if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
echo "${b}"
return 0
fi
done
return 1
} }
case "$1" in case "$1" in
@@ -394,6 +440,7 @@ case "$1" in
;; ;;
*) *)
echo "Test failed for an unknown reason" echo "Test failed for an unknown reason"
exit ${ret}
;; ;;
esac esac
@@ -403,6 +450,11 @@ case "$1" in
echo "${ns} does not support TCP fallback" echo "${ns} does not support TCP fallback"
fi fi
edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
if [ -n "${edns_buffer_size}" ]; then
echo "EDNS buffer size for ${ns}: ${edns_buffer_size}"
fi
exit ${ret} exit ${ret}
;; ;;