我有一個主bind9
DNS 伺服器和 2 個在 IPv4 (Debian Jessie) 上運行的從屬伺服器,使用/etc/bind/named.conf
:
listen-on-v6 { none; };
當我嘗試從不同的伺服器連接時,每個連接至少需要 5 秒(我正在使用約瑟夫的時間訊息用於調試):
$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com
time_namelookup: 5.512
time_connect: 5.512
time_appconnect: 5.529
time_pretransfer: 5.529
time_redirect: 0.000
time_starttransfer: 5.531
----------
time_total: 5.531
根據curl
,查找花費了大部分時間,但是標準nslookup
非常快:
$ time nslookup example.com > /dev/null 2>&1
real 0m0.018s
user 0m0.016s
sys 0m0.000s
強制curl
使用 IPv4 後,情況好多了:
$ curl -4 -w "@curl-format.txt" -o /dev/null -s https://example.com
time_namelookup: 0.004
time_connect: 0.005
time_appconnect: 0.020
time_pretransfer: 0.020
time_redirect: 0.000
time_starttransfer: 0.022
----------
time_total: 0.022
我已在主機上停用 IPv6:
echo 1 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6
儘管問題仍然存在。我嘗試運行strace
看看超時的原因是什麼:
write(2, "*", 1*) = 1
write(2, " ", 1 ) = 1
write(2, "Hostname was NOT found in DNS ca"..., 36Hostname was NOT found in DNS cache
) = 36
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 4
close(4) = 0
mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f220bcf8000
mprotect(0x7f220bcf8000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f220c4f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f220c4f89d0, tls=0x7f220c4f8700, child_tidptr=0x7f220c4f89d0) = 2004
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 4) = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 8) = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 16) = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 32) = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 64) = 0 (Timeout)
這似乎不是防火牆問題,因為nslookup
(或curl -4
) 使用相同的 DNS 伺服器。知道可能出什麼問題嗎?
以下tcpdump
來自樓主tcpdump -vvv -s 0 -l -n port 53
:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:14:52.542526 IP (tos 0x0, ttl 64, id 35839, offset 0, flags [DF], proto UDP (17), length 63)
192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:52.542540 IP (tos 0x0, ttl 64, id 35840, offset 0, flags [DF], proto UDP (17), length 63)
192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:52.543281 IP (tos 0x0, ttl 61, id 63674, offset 0, flags [none], proto UDP (17), length 158)
192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
20:14:57.547439 IP (tos 0x0, ttl 64, id 36868, offset 0, flags [DF], proto UDP (17), length 63)
192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:57.548188 IP (tos 0x0, ttl 61, id 64567, offset 0, flags [none], proto UDP (17), length 184)
192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 39535* q: A? example.com. 2/2/2 example.com. [1h] CNAME s01.example.com., s01.example.com. [1h] A 136.243.154.168 ns: example.com. [30m] NS ns01.example.com., example.com. [30m] NS ns02.example.com. ar: ns01.example.com. [1h] A 136.243.154.168, ns02.example.com. [1h] A 192.168.1.2 (156)
20:14:57.548250 IP (tos 0x0, ttl 64, id 36869, offset 0, flags [DF], proto UDP (17), length 63)
192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:57.548934 IP (tos 0x0, ttl 61, id 64568, offset 0, flags [none], proto UDP (17), length 158)
192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
編輯: 在綁定日誌中經常出現以下訊息:
error sending response: host unreachable
不過,每個查詢最終都會得到答案(只需要 5 秒)。所有機器都是實體伺服器(這不是 NAT 的錯),更有可能的是封包被路由器阻止。這是很可能相關的問題:DNS 查找有時需要 5 秒。
答案1
簡短回答:
解決方法是透過新增一行來強制glibc
重複使用套接字來尋找AAAA
和記錄:A
/etc/resolv.conf
options single-request-reopen
這個問題的真正原因可能是:
- 設定錯誤的防火牆或路由器(例如此處所述的 Juniper 防火牆配置) 這會導致丟棄
AAAA
DNS 封包 - DNS 伺服器中的錯誤
長答案:
curl
類似或wget
使用glibc的功能的程序取得地址資訊(),它嘗試透過並行查找 DNS 記錄來相容 IPv4 和 IPv6。在收到兩筆記錄之前它不會回傳結果(有與這種行為相關的幾個問題) - 這解釋了strace
上面的內容。當強制使用 IPv4 時,例如curl -4
在內部僅gethostbyname()
查詢A
記錄。
從中tcpdump
我們可以看出:
-> A?
開頭發送兩個請求-> AAAA?
(請求 IPv6 位址)<- AAAA
回覆-> A?
再次請求 IPv4 位址<- A
收到回覆-> AAAA?
再次請求 IPv6<- AAAA
回覆
A
由於某種原因,一個回覆被刪除,這是一條錯誤訊息:
error sending response: host unreachable
但我不清楚為什麼需要第二次AAAA
查詢。
要驗證您是否遇到相同的問題,您可以在以下位置更新逾時/etc/resolv.conf
:
options timeout:3
首先建立一個文字文件自訂時間報告配置:
cat >./curl-format.txt <<-EOF
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_redirect: %{time_redirect}\n
time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n
EOF
然後發送請求:
$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com
time_namelookup: 3.511
time_connect: 3.511
time_appconnect: 3.528
time_pretransfer: 3.528
time_redirect: 0.000
time_starttransfer: 3.531
----------
time_total: 3.531
還有另外兩個相關選項man resolv.conf
:
單一請求(自 glibc 2.10 起)設定
RES_SNGLKUP
在_res.options
.預設情況下,glibc 從 2.9 版本開始並行執行 IPv4 和 IPv6 查找。某些設備 DNS 伺服器無法正確處理這些查詢並使請求逾時。此選項會停用該行為並使 glibc 依序執行 IPv6 和 IPv4 請求(代價是解析過程會減慢)。單一請求重新開啟(自 glibc 2.9 起) 解析器對 A 和 AAAA 請求使用相同的套接字。某些硬體錯誤地只發回一個回應。當這種情況發生時,客戶端系統將坐下來等待第二個回應。開啟此選項會變更此行為,以便如果來自相同連接埠的兩個請求未正確處理,它將關閉套接字並在發送第二個請求之前開啟一個新套接字。
相關問題:
答案2
正如@Tombart 所說,延遲是由於等待 IPv6 解析逾時造成的。
另一個可能的做法是在 /etc/gai.conf 中優先考慮 IPv4
來自 /etc/gai.conf 中的註釋
# For sites which prefer IPv4 connections change the last line to # precedence ::ffff:0:0/96 100
更改後gai.conf
,您需要重新啟動任何使用 DNS 解析器庫的應用程式才能使更改生效。
請注意,如果您使用沒有 IPv6 連接的 BIND 伺服器,我建議停用 IPv6named
並從根提示中取得 IPv6 位址。顯然它仍然會嘗試解析 AAAA 位址。
所以對於 BIND 配置,
在 /etc/default/bind9 中,為 IPv4 位址新增 -4:
OPTIONS="-4 -u bind"
並在 中/etc/bind/db.root
刪除所有具有 AAAA DNS 根的行。
答案3
答案4
如果有人正在尋找curl-format.txt。將其貼到您的 shell 中,它將為您建立格式檔案。原來的連結對我不起作用。找到這個例子這裡
cat >./curl-format.txt <<-EOF
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_redirect: %{time_redirect}\n
time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n
EOF