Conntrack,無法從另一個 VRF NAT 自己的 TCP 封包

Conntrack,無法從另一個 VRF NAT 自己的 TCP 封包

在基於 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 流也是如此。

相關內容