為何 cURL 總是偏好某個 IPv6 地址?

前言

同事最近被問說「為什麼 NLB 啟用 dualstack 後,從位於同 AZ 內的 EC2 使用 curl -6 去拜訪,永遠都是拜訪同 subnet 的 NLB 節點?」

這問題一直可以復現,也很確定 dig 出來的結果會有 round robin,但若從 VPC 外的 IPv6 網路測試就沒有這個問題,因此我跟其他同事們便好奇地去找原因。

過程

嘗試使用 strace 去查看,發現這段後

openat(AT_FDCWD, "/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3

過沒多久就是連線了,中途沒看到什麼可疑的地方。

connect(5, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "2600:dddd:baf:cccc:d700:bbbb:aac7:aaaa", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)

因此猜測有可能是 curl 對於地址有偏好,我跑去 curl 的 GitHub 搜尋對 DNS 等資訊搜尋看看有沒有可疑的程式碼,但只看到使用 Curl_dns_entry 結構儲存,沒看到取得 DNS 對應 IP 地址後有什麼特殊的排序、偏好設定。

接著跟另一名資深 Linux 組 H 同事去 Stack Overflow 找答案,找到有些人是問 source IP preference 的問題,底下有人留言提到 RFC 3484, 標題寫著 “Default Address Selection for Internet Protocol version 6 (IPv6)”,看了一下目錄有 “Destination Address Selection”,看來很明顯就是這份文件了。不過寫這篇文章時發現 RFC 6724 取代了 RFC 3484。

原因

當有多個 IPv6 地址可以選擇時,會先將其做各種偏好規則排序,分別有:

  • Rule 1: Avoid unusable destinations
  • Rule 2: Prefer matching scope
  • Rule 3: Avoid deprecated addresses
  • Rule 4: Prefer home addresses
  • Rule 5: Prefer matching label
  • Rule 6: Prefer higher precedence
  • Rule 7: Prefer native transport
  • Rule 8: Prefer smaller scope
  • Rule 9: Use longest matching prefix
  • Rule 10: Otherwise, leave the order unchanged

其中第 6 條讓網管人員可以根據喜好自定義偏好 prefix,根據 glibc 實作 Precedence 資訊是儲存在 /etc/gai.conf 當中,因此沒設定通常就沒有。

Amazon Linux 2 預設是沒有的,因此我們往下看到較可能的規則應該是第 9 條,這就是網路路由中常見的 Longest Prefix Matching (LPM) 規則,根據 AWS 的 IPv6 定址規則,同個 subnet 會有相同的 /64 前綴,因此解釋了該名同事的問題。

後記


comments powered by Disqus