Seiry

Seiry

在非桥接拨号的OpenWrt中存在着NAT回环问题。

前言#

在翻译过程中,需要特别注意保持所有 Markdown 语法和标签的完整性,不改变 HTML 标签的功能,以确保翻译后的内容不会影响任何语法或标签的渲染。请按照以下规则进行翻译:

  1. 识别和翻译文本内容:只识别和翻译 Markdown 中的纯文本内容,包括标题、段落和列表项中的文本。

  2. 保留标签和属性:遇到 HTML 标签(如

  3. 特殊语法处理:对于 Markdown 特定的语法(如链接、图像标签),只翻译描述性文本部分(如 alt text),不改变链接或语法结构。

  4. 保持格式不变:确保所有 Markdown 格式(如粗体、斜体、代码块)在翻译过程中保持不变。

  5. 你的任务是确保翻译内容准确无误,并且不破坏原始 Markdown 结构和 HTML 标签的功能。请在翻译过程中仔细检查,以确保语法和标签的正确渲染。

  6. 你只能返回翻译后的文本,不能返回其他内容。

重要提示:只返回翻译后的文本,不要返回其他内容。

将以下文本翻译为繁体中文:

前言#

當我想要寫這篇文章時,突然覺得哪裡不對。為什麼我在 xlog 寫的文章,都是日網絡的?

其實我並不擅長計算機網絡基礎,這是我知識領域裡很薄弱的一塊。日站也好,網絡業務也好,雖然我會做,但是那是很頂層的應用,自頂到下,其實我是並不擅長的。

但是這個,既是原因,也是結果。正因為我不擅長,所以就會遇到「很罕見」的問題,然後這個問題其實對大家來說並不是問題。

這也是一種非舒適區悖論。

nat loopback#

nat loopback,也叫 hairpinning。它解決的問題很簡單,就是。。。就是。。。。就是 nat 的 loopback!

你的公網 ipv4 假設是 1.2.3.4,你配置了一個端口轉發,從 1.2.3.4:1000,轉發到 192.168.1.2:1000,你的(其中一台)路由器是 192.168.1.1。如果你身處內網,以 / 24 為例,你的那網 ip 是 192.168.1.200。這個時候,你訪問 1.2.3.4:1000,是會遇到問題的。

通常上,路由器的端口轉發,背後對應的 iptable,是從外部(wan 側)作為開始,向內(lan)側轉發。換句話,wan 側監聽,任意的(外部)ip 請求端口 1000,都會被轉發到 192.168.1.2:1000。

對應規則

-A zone_wan_prerouting -p tcp -m tcp --dport 1000 -j DNAT --to-destination 192.168.1.2:1000

那麼問題來了,如果你並不在外網,而在內網,去訪問 1.2.3.4:1000 會怎麼樣呢?如果不做額外的設置(但是其實這個額外設置在很多情況下是默認存在的),那麼單憑這一條 dnat 規則是無法連通的。因為限制了入口鏈在 wan 側。

nat loopback?#

那麼,上邊說這個問題,是個什麼問題呢?nat loopback?

如果你不知道這個問題,那麼你就無法提出這個問題。

幸好科技帶給了我們新東西。在今天,我們可以通過直接問 chatgpt,來得到我們的問題是什麼。

問問問題。而不是問問題。Ask what the question is, instead of how to solve it.

這是我對於 chatgpt 的典型用法。在浩瀚的技術門類中,傳統的查詢和技術學習路線,去空中樓閣地做一件事情,太需要運氣了。你掌握了 ABCEFG 六門技術,但是你無法將他們聯結起來,曾經很多時候只能漫無目的地再嘗試 / 積累 HIGK,但是你真正缺少的是 D。大模型的生產力其實很有限,很多未知的東西她現在還很難做到,但是這個場景很典型。

nat loopback!#

其實在現代的路由器系統裡,nat loopback 早就給你考慮好了。

openwrt 裡,其背後的設計為 reflection。

你會得到這樣一個規則。

-A zone_lan_prerouting -s 192.168.1.0/24 -d 192.168.0.3/32 -p tcp -m tcp --dport 1000 -m comment --comment "!fw3: 1000 (reflection)" -j DNAT --to-destination 192.168.1.2:1000

注意標題裡的話,因為是非橋接環境,所以此處 openwrt 的 wan 環境 ip 是 192.168.0.3

問題!#

橋接環境,openwrt 直接 pppoe 撥號,並不會導致 nat loopback 失效。但是在非橋接環境下,也就是光猫路由模式下,會的。

這裡的問題在於,openwrt 的 nat loopback,實際上是斷斷續續出過問題的,加上版本的碎片化,你在嘈雜的互聯網裡,很難找到你的錯誤究竟是哪個。

這直接導致了,問題很多,答案也很多,但是大家的插口並不兼容。

你會搜索到各種的,比如說這個某某版本的 openwrt 的 bug 呀,你要配置什麼內核 flag 呀,種種「對」的方法。對,但是解答不了你的問題。

這個問題,大模型目前也幫不了你。非但幫不了,她可太擅長在這個問題上擦邊捣亂了。

她最擅長的事情是,搭建草台班子。

問題的根本原因是,這條規則,僅設置了 192.168.0.3 為入口,而實際上的入口,應該也包括 public wan。

-A zone_lan_prerouting -s 192.168.1.0/24 -d 192.168.0.3/32 -p tcp -m tcp --dport 1000 -m comment --comment "!fw3: 1000 (reflection)" -j DNAT --to-destination 192.168.1.2:1000

也就是額外需要

-A zone_lan_prerouting -s 192.168.1.0/24 -d 1.2.3.4/32 -p tcp -m tcp --dport 1000 -j DNAT --to-destination 192.168.1.2:1000

從 lan 側,訪問 pub ip,那麼直接轉發到目的主機。

但是這樣不夠優雅,對吧

完成#

IP=$(curl -s 4.ipw.cn | grep -oE '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' | head -n 1)

iptables -t nat -A PREROUTING -s 192.168.1.0/24 -d $IP/32 -p tcp -m tcp --dport 1000 -j zone_wan_prerouting

所以我們就可以直接添加一個自定義的防火墻規則,如果使用 nft,那麼大模型可以幫你。其實這個兩行,也是 chatgpt 寫的。

獲取到 public ip 之後,其實把請求扔回到zone_wan_prerouting即可,不需要再重複寫一次目的主機。

其實可以更簡化 / 更通用為,不過這樣就和 openwrt 的 uci 綁定了,畢竟還有 padavan 呢。

LAN=$(uci show network.lan.ipaddr | cut -d"'" -f2)
IP=$(curl -s 4.ipw.cn | grep -oE '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' | head -n 1)

iptables -t nat -A PREROUTING -s $LAN/24 -d $IP/32 -p tcp -m tcp --dport 1000 -j zone_wan_prerouting

padavan 下,其實處理的更優雅,並沒有 flection,而是

-A PREROUTING -d 1.2.3.4/32 -j vserver
-A PREROUTING -d 192.168.0.1/32 -j vserver

定義了vserver作為外部輸入鏈。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。