![Conntrack,無法從另一個 VRF NAT 自己的 TCP 封包](https://rvso.com/image/768930/Conntrack%EF%BC%8C%E7%84%A1%E6%B3%95%E5%BE%9E%E5%8F%A6%E4%B8%80%E5%80%8B%20VRF%20NAT%20%E8%87%AA%E5%B7%B1%E7%9A%84%20TCP%20%E5%B0%81%E5%8C%85.png)
在基於 Debian 的路由器上使用多個 VRF 時,我遇到了來源 NAT 的棘手問題。解釋起來有點複雜,所以我會盡力說清楚,但不會很短,對此感到抱歉。不過這個問題應該很容易重現。
為了將路由器的「管理」部分(ssh 和其他服務)與其路由器工作(路由和 NAT 封包)隔離,我嘗試在預設 VRF 中設定「mgmt」VRF(更容易處理服務套接字)並VRF 中的路由稱為“防火牆”。
該圖可以總結如下:
「管理」網路為 192.168.100.0/24,由具有路由器「防火牆」VRF 的 L3 的 L3 交換器透過網路 10.254.5.0/24 進行路由。第三個路由器接口是「互聯網」接口,透過它的資料包經過來源 NAT。此設定對於 mgmt 子網路中的所有內容都非常有效,除了路由器自己的封包(這是 conntrack 的原因)。
關於iptables規則:
# Table filter
# chain INPUT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
(some INPUT rules, for ssh, snmp, etc)
-A INPUT -j DROP
# chain FORWARD
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -o eth2 -j ACCEPT
-A FORWARD -j DROP
# Table nat
# chain POSTROUTING
-A POSTROUTING -o eth2 -j SNAT --to-source 192.168.122.100
關於路由表:
# default VRF
default via 192.168.100.1 dev eth0 proto static metric 20
192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.90
# firewall VRF
default via 192.168.122.1 dev eth2 proto static metric 20
10.254.5.0/24 dev eth1 proto kernel scope link src 10.254.5.2
192.168.100.0/24 proto bgp metric 20 nexthop via 10.254.5.10 dev eth1 weight 1
192.168.122.0/24 dev eth2 proto kernel scope link src 192.168.122.100
因此,當來自預設 VRF 的封包嘗試存取網際網路時,它會從 eth0 發出,由 L3 交換器路由,由 eth1 進入防火牆 VRF,並透過 eth2 進行路由和 NAT。由於我追蹤 INPUT 和 FORWARD 連接,因此當封包返回時 conntrack 有點混亂,而且它無法知道如何處理該封包。
我能夠透過使用原始表中的 conntrack 區域來修復 ICMP 和 UDP 的此問題
# Table raw
# chain PREROUTING
-A PREROUTING -i eth0 -j CT --zone 5
# chain OUTPUT
-A OUTPUT -o eth0 -j CT --zone 5
根據這些規則,源自路由器並通過的封包eth0
將被標記zone 5
,並且當封包進入時eth0
它們也會被標記zone 5
。
當 ping 到 8.8.8.8 時,它看起來像這樣(使用命令conntrack -E
):
[NEW] icmp 1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 [UNREPLIED] src=8.8.8.8 dst=192.168.100.90 type=0 code=0 id=1999 zone=5
[NEW] icmp 1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 [UNREPLIED] src=8.8.8.8 dst=192.168.122.100 type=0 code=0 id=1999
[UPDATE] icmp 1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 src=8.8.8.8 dst=192.168.122.100 type=0 code=0 id=1999
[UPDATE] icmp 1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 src=8.8.8.8 dst=192.168.100.90 type=0 code=0 id=1999 zone=5
我們可以看到,NEW
當封包通過eth0
標記的封包時,會建立第一個連接,然後當封包不帶標記地zone=5
進入防火牆 VRF 時,會建立一個新連接。eth1
當答案出現時,首先更新第二個連接(因為它是面向互聯網的連接),然後是第一個連接。
這也適用於 UDP,例如對 8.8.8.8 的 DNS 查詢
[NEW] udp 17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 [UNREPLIED] src=8.8.8.8 dst=192.168.100.90 sport=53 dport=53369 zone=5
[NEW] udp 17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 [UNREPLIED] src=8.8.8.8 dst=192.168.122.100 sport=53 dport=53369
[UPDATE] udp 17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 src=8.8.8.8 dst=192.168.122.100 sport=53 dport=53369
[UPDATE] udp 17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 src=8.8.8.8 dst=192.168.100.90 sport=53 dport=53369 zone=5
但對於 TCP,它不起作用。對 172.16.10.10 連接埠 80 的 telnet 查詢如下所示:
[NEW] tcp 6 120 SYN_SENT src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 [UNREPLIED] src=172.16.10.10 dst=192.168.100.90 sport=80 dport=60234 zone=5
[NEW] tcp 6 120 SYN_SENT src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 [UNREPLIED] src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
[UPDATE] tcp 6 58 SYN_RECV src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
[UPDATE] tcp 6 57 SYN_RECV src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
(The last line repeat multiple times)
如果我 tcpdumpeth2
那裡有答案:
IP 192.168.122.100.60236 > 172.16.10.10.80: Flags [S], seq 4203590660, win 62720, options [mss 1460,sackOK,TS val 1511828881 ecr 0,nop,wscale 7], length 0
IP 172.16.10.10.80 > 192.168.122.100.60236: Flags [S.], seq 3672808466, ack 4203590661, win 65535, options [mss 1430,sackOK,TS val 2474659117 ecr 1511828881,nop,wscale 8], length 0
IP 192.168.122.100.60236 > 172.16.10.10.80: Flags [S], seq 4203590660, win 62720, options [mss 1460,sackOK,TS val 1511829887 ecr 0,nop,wscale 7], length 0
IP 172.16.10.10.80 > 192.168.122.100.60236: Flags [S.], seq 3672808466, ack 4203590661, win 65535, options [mss 1430,sackOK,TS val 2474660123 ecr 1511828881,nop,wscale 8], length 0
但由於 SIN ACK 從未被確認,因此路由器繼續發送新的 SIN。
現在,如果我 tcpdump eth1
:
IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511928806 ecr 0,nop,wscale 7], length 0
IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511929823 ecr 0,nop,wscale 7], length 0
IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511931839 ecr 0,nop,wscale 7], length 0
我們可以看到答案永遠不會路由回 192.168.100.90。
如果我禁用連接追蹤並允許 iptables 中的所有內容,它就可以工作。所以我認為 conntrack 在管理從自身到另一個區域的 TCP 連線(當它們是 NAT 時)時會遇到麻煩?如果有不清楚的地方,我很樂意回答任何相關問題。
答案1
這個問題出現在核心為 4.19.0-12-amd64 的 debian 10 上,但升級到核心為 5.10.0-11-amd64 的 debian 11 後,它可以按預期工作,即使對於 TCP 流也是如此。