Networking

如何為從全域網路到特殊網路命名空間的兩個 UDP 連接設置無狀態 NAT?

  • March 4, 2021

需要為從全域網路命名空間中的物理網路適配器通過一對連結的虛擬網路適配器到在特殊網路命名空間中執行的服務的兩個 UDP 連接設置無狀態 NAT。這應該在執行具有核心5.9.7的 Linux (Debian) 的工業設備中的 CPU (Intel Atom) 上完成。

這是應設置的網路配置方案:

=====================    =====================================================
|| application CPU ||    ||                communication CPU                ||
||                 ||    ||                                                 ||
||                 ||    ||    global namespace    |   nsprot1 namespace    ||
||                 ||    ||                        |                        ||
||     enp4s0      ||    ||       enp1s0           |          enp3s0        ||
||    0.0.0.5/30  ==========     0.0.0.6/30        |    192.168.2.15/24    =======
||                 ||    ||                        |                        ||
|| UDP port 50001  ||    || UDP port 50001 for sv1 |  TCP port 2404 for sv2 ||
|| UDP port 50002  ||    || UDP port 50002 for sv1 |                        ||
|| UDP port 53401  ||    || UDP port 50401 for sv1 |                        ||
|| UDP port 53402  ||    || UDP port 50402 for sv1 |                        ||
||                 ||    ||                        |                        ||
||                 ||    ||      vprot0            |         vprot1         ||
||                 ||    ||     0.0.0.16/31       ---      0.0.0.17/31      ||
||                 ||    ||                        |                        ||
|| UDP port 53404  ||    || UDP port 50404 for sv2 - UDP port 50404 for sv2 ||
|| UDP port 53441  ||    || UDP port 50441 for sv2 - UDP port 50441 for sv2 ||
=====================    =====================================================

應用程序 CPU 總是首先啟動並打開幾個 UDP 埠用於與通信 CPU 上的服務sv1和服務sv2通信,通過其物理網路適配器enp4s0使用 IP 地址0.0.0.5

ss --ipv4 --all --numeric --processes --udp在應用程序 CPU 上執行的輸出是:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    UNCONN  0       0              0.0.0.0:50001        0.0.0.0:*      users:(("sva",pid=471,fd=5))
udp    UNCONN  0       0              0.0.0.0:50002        0.0.0.0:*      users:(("sva",pid=471,fd=6))
udp    ESTAB   0       0              0.0.0.5:53401        0.0.0.6:50401  users:(("sva",pid=471,fd=12))
udp    ESTAB   0       0              0.0.0.5:53402        0.0.0.6:50402  users:(("sva",pid=471,fd=13))
udp    ESTAB   0       0              0.0.0.5:53404        0.0.0.6:50404  users:(("sva",pid=471,fd=19))
udp    ESTAB   0       0              0.0.0.5:53441        0.0.0.6:50441  users:(("sva",pid=471,fd=21))

通信 CPU 第二個啟動,最後執行了兩個服務:

  • sv1在全域命名空間
  • sv2在特殊的網路命名空間nsprot1中。

ss --ipv4 --all --numeric --processes --udp在通信 CPU 的全域命名空間中執行的輸出為:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    UNCONN  0       0              0.0.0.0:50001        0.0.0.0:*      users:(("sv1",pid=812,fd=18))
udp    UNCONN  0       0              0.0.0.6:50002        0.0.0.0:*      users:(("sv1",pid=812,fd=17))
udp    UNCONN  0       0              0.0.0.6:50401        0.0.0.0:*      users:(("sv1",pid=812,fd=13))
udp    UNCONN  0       0              0.0.0.6:50402        0.0.0.0:*      users:(("sv1",pid=812,fd=15))

ip netns exec nsprot1 ss --ipv4 --all --numeric --processes --udp( nsprot1namespace)的輸出是:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    ESTAB   0       0             0.0.0.17:50404        0.0.0.5:53404  users:(("sv2",pid=2421,fd=11))
udp    ESTAB   0       0             0.0.0.17:50441        0.0.0.5:53441  users:(("sv2",pid=2421,fd=12))

IPv4 轉發通常啟用,sysctl並且適用於所有物理網路適配器。

僅廣播和多播轉發被禁用,因為不需要並且不需要。

使用以下命令在通信 CPU 上設置網路配置:

ip netns add nsprot1
ip link add vprot0 type veth peer name vprot1 netns nsprot1
ip link set dev enp3s0 netns nsprot1
ip address add 0.0.0.16/31 dev vprot0
ip netns exec nsprot1 ip address add 0.0.0.17/31 dev vprot1
ip netns exec nsprot1 ip address add 192.168.2.15/24 dev enp3s0
ip link set dev vprot0 up
ip netns exec nsprot1 ip link set vprot1 up
ip netns exec nsprot1 ip link set enp3s0 up
ip netns exec nsprot1 ip route add 0.0.0.4/30 via 0.0.0.16 dev vprot1

使用以下命令設置網路地址轉換:

nft add table ip prot1
nft add chain ip prot1 prerouting '{ type nat hook prerouting priority -100; policy accept; }'
nft add rule prot1 prerouting iif enp1s0 udp dport '{ 50404, 50441 }' dnat 0.0.0.17
nft add chain ip prot1 postrouting '{ type nat hook postrouting priority 100; policy accept; }'
nft add rule prot1 postrouting ip saddr 0.0.0.16/31 oif enp1s0 snat 0.0.0.6

的輸出nft list table ip prot1是:

table ip prot1 {
   chain prerouting {
       type nat hook prerouting priority -100; policy accept;
       iif "enp1s0" udp dport { 50404, 50441 } dnat to 0.0.0.17
   }

   chain postrouting {
       type nat hook postrouting priority 100; policy accept;
       ip saddr 0.0.0.16/31 oif "enp1s0" snat to 0.0.0.6
   }
}

在全域命名空間中定義了具有以下內容的表inet filter

table inet filter {
   chain input {
       type filter hook input priority 0; policy accept;
   }

   chain forward {
       type filter hook forward priority 0; policy accept;
   }

   chain output {
       type filter hook output priority 0; policy accept;
   }
}

該 NAT 配置用於有狀態 NAT。它適用於具有埠號的 UDP 通道,50404 並且53404由於sv2最後開始打開0.0.0.17:50404並發送一個 UDP 數據包, 在全域命名空間中的鉤子中0.0.0.5:53404應用源網路地址轉換。應用 CPU的服務發回一個 UDP 數據包,從那裡到達。UDP 數據包沒有通過to的規則。正如我後來發現的那樣,它是通過連接跟踪直接發送到的。postrouting``enp1s0``sva``0.0.0.5:53404``0.0.0.6:50404``0.0.0.17:50404``prerouting``dnat``0.0.0.17``0.0.0.17

但是這種有狀態的 NAT 配置不適用於具有埠號50441534441. 看起來原因是應用程序 CPU在服務啟動之前sva已經發送了幾個 UDP 數據包0.0.0.5:53441,並且目標埠在網路命名空間中打開。ICMP 返回目的埠不可達。考慮到目標埠還沒有打開,這並不奇怪。不幸的是,在服務啟動並打開兩個 UDP 埠之前,無法阻止服務中發送的 UDP 數據包。服務會定期發送自發的 UDP 數據包,有時還會額外觸發0.0.0.6:50441``sv2``nsprot1``sva``sv2``sva``0.0.0.5:53441``0.0.0.6:50441獨立於連接狀態。

所以這個配置的問題似乎是有狀態的 NAT,因為鉤子中的dnat規則prerouting仍然沒有用於最終在網路命名空間中打開的目標埠nsprot1。仍然繼續路由 UDP 數據包0.0.0.6:50441,導致丟棄 UDP 數據包並返回目標埠不可達的 ICMP。

因此,解決方案可能是使用無狀態 NAT。因此,還執行了以下命令:

nft add table ip raw
nft add chain ip raw prerouting '{ type filter hook prerouting priority -300; policy accept; }'
nft add rule ip raw prerouting udp dport '{ 50404, 50441, 53404, 53441 }' notrack

但結果並不如預期。將來自輸入介面的 UDP 數據包prerouting的目標地址從到更改為0.0.0.6目標埠的規則, 仍然沒有被考慮在內。0.0.0.17``enp1s0``50404``50441

接下來被我執行了:

nft add table ip filter
nft add chain filter trace_in '{ type filter hook prerouting priority -301; }'
nft add rule filter trace_in meta nftrace set 1
nft add chain filter trace_out '{ type filter hook postrouting priority 99; }'
nft add rule filter trace_out meta nftrace set 1
nft monitor trace

我查看了跟踪,可以看到該notrack規則被考慮在內,但是帶有目標埠的 UDP 數據包50441直接傳遞給了input鉤子。我不知道為什麼。

我非常仔細地研究了很多很多小時以下頁面:

我嘗試了很多不同的配置,使用過 Wireshark,使用過nft monitor trace,但我找不到適用於帶有埠的 UDP 通道50441以及53441在完全打開sva目標埠之前就已經發送 UDP 數據包的解決方案。0.0.0.17:50441

如果我在應用程序 CPU 上手動終止服務sva,在通信 CPU 上設置網路配置並啟動兩個服務sv1,然後在通信 CPU 上已打開的所有 UDP 埠上再次sv2手動啟動服務,則有狀態 NAT 配置有效。sva但是這種啟動服務的順序在工業設備中預設是無法做到的。應用程序服務sva必須獨立於通信服務是否準備好通信而執行。

哪些命令(鏈/規則)對於兩個 UDP 通道具有無狀態 NAT 是必需的,0.0.0.5:53404 - 0.0.0.17:50404並且0.0.0.5:53441 - 0.0.0.17:50441獨立於目標埠的打開狀態以及哪個服務首先向另一個服務發送 UDP 數據包?

PS:服務sv2可以根據設備的配置啟動,也可以在全域命名空間中使用不同的物理網路適配器啟動,不需要 NAT 和網路命名空間。在此網路配置中,三個服務之間的 UDP 通信絕對沒有問題。

經過無數小時的閱讀文件、教程、各種網頁上的建議,進行大量試驗,並對網路和 netfilter 進行深入而全面的監控和分析,我終於自己找到了解決方案。

nft add table ip prot1
nft add chain ip prot1 prerouting '{ type filter hook prerouting priority -300; policy accept; }'
nft add rule ip prot1 prerouting iif enp1s0 udp dport '{ 50404, 50441 }' ip daddr set 0.0.0.17 notrack accept
nft add rule ip prot1 prerouting iif vprot0 ip saddr 0.0.0.17 notrack accept
nft add chain ip prot1 postrouting '{ type filter hook postrouting priority 100; policy accept; }'
nft add rule ip prot1 postrouting oif enp1s0 ip saddr 0.0.0.17 ip saddr set 0.0.0.6 accept

應先打開netfilter hooks頁面並閱讀以了解以下說明。

使用命令說明:

  1. 為帶有 name的協議(IPv4)添加了一個netfilter ip``prot1
  2. 一個被添加到表中prot1,其名稱為具有優先級的鉤子prerouting的類型。使用低於能夠繞過連接跟踪的優先級編號非常重要。這不包括使用具有更低優先級的目標網路地址轉換的類型鏈。filter``prerouting``-300``-200``conntrack``nat
  3. 過濾 規則被添加到prot1鍊錶,該規則僅適用於在協議類型的輸入介面prerouting上接收到的 IPv4 數據包,該協議類型具有作為estination或者修改數據包的estination from to並啟動此 UDP 數據包的連接。判決明確指定,儘管實際上沒有必要將從應用程序 CPU 的服務接收到的 UDP 數據包盡快傳遞給通信 CPU 服務的下一個鉤子,在這種情況下是鉤子。i``i``f``enp1s0``udp``d``port``50404``50441``ip d``addr``0.0.0.6``0.0.0.17``no track``accept``sva``sv2``forward
  4. 將第二個過濾 規則添加到鍊錶prot1,該規則prerouting僅適用於在輸入介面上接收的所有IPv4 數據包,獨立於協議類型(… 當然也可以只過濾具有適當源或目標埠號的 UDP 數據包,但這裡不需要這個額外的限制,而且這個規則也適用於 ICMP 數據包從尚未打開的目標埠發送回,因為服務i``i``f``vprot0``udp``icmp``ip s``addr``0.0.0.17``no track``0.0.0.17``0.0.0.5``sv2目前沒有執行。再次明確指定判決,accept而不是使用隱式預設值continue以盡可能快地將數據包傳遞給forward鉤子。
  5. 將第二個添加到表中prot1,其名稱為具有優先級的鉤子postrouting的類型。重要的是使用類型鏈而不是類型鏈,以便能夠對繞過連接跟踪的 UDP(和 ICMP)數據包應用源地址轉換。filter``postrouting``100``filter``nat
  6. 過濾 規則添加到prot1第二個鏈的表中,該規則僅適用於在輸出介面postrouting上發送的 IPv4 數據包,而與協議類型 ( , , …)無關,該協議類型將數據包的來源從修改為。再次明確指定該判定,儘管並非真的有必要盡快將從通信 CPU 的服務接收到的 UDP 數據包傳遞給應用 CPU 的服務。此規則還將ICMP 數據包的源地址更改為o``i``f``enp1s0``udp``icmp``s``addr``0.0.0.17``ip s``addr``0.0.0.17``0.0.0.6``accept``sv2``sva``0.0.0.6``0.0.0.17``sv2由於服務尚未執行,無法訪問目標埠。因此,應用程序 CPU 永遠不會注意到它為兩個具有不同介面的 UDP 通道進行通信,0.0.0.6這是第二個要滿足的要求,儘管這並不重要。

很難發現這種非常特殊的網路配置和服務之間的通信需要無狀態網路轉換,sva並且sv2必須在使用nat鉤子的情況下完成 NAT。

引用自:https://unix.stackexchange.com/questions/637228