使用iptables配置stateless NAT?我沒有搞錯。
可能你根本不知道這麼多NAT的實作細節,或者說根本不在乎,那麼本文就當是一個“如何編寫iptables子產品”的練習了。
實 際上,我已經實作了一個可以配置stateless NAT的核心子產品了,但是它的接口是基于procfs的,并不是說這個接口不好用,而是我覺得如果能內建到iptables就更加perfect了,難道 不應該這樣嗎?有誰能忍受通過iptables和echo的方式配置兩種NAT呢?難道不應該在一個iptables -L或者iptables-save指令中展示所有的配置嗎?
想法是一回事,實作是另一回事,二者之間需要有一個促成的動力,這個動力來自于一個陳年的文章,大概是2004年的吧,那是我還在上學,那個文章就是在讨 論關于Linux下stateless NAT的問題,時隔多年,如果你搜尋相關的主題,依然沒有太好的答案。也許是沒有這方面的需求,但真的沒有嗎?我覺得不。在2.4核心時期,還可以通過 iproute2來配置stateless NAT,在2.6/3.X核心時期就隻能用tc來配置了,不管怎麼說,總是可以做到的,但是你不覺得太麻煩了嗎?難道就不能像下面這樣子配置一條 stateless NAT嗎?
iptables -t nat -A PREROUTING -j STATIC-2-WAY-NAT --mapaddr 192.168.184.250-192.168.184.154 --type src
不 過請注意,iptables本身有一個預設的事實,那就是它是基于match和target的,它的句法是“如果...那麼...”,這個句法在 static staleless NAT便不适合了,因為對于這種NAT而言,每添加一條規則,就會自動生成一條反轉的規則,這樣的話,“如果...那麼...”就不行了,要想用 iptables配置stateless NAT,就必須僅僅将規則設定進核心,其它的都由獨立的子產品來做,換句話說,我們僅僅利用iptables的接口,而不使用它的match機制,是以我隻 需要注冊一個target,在這個target的checkentry回調中完成“設定規則(正反兩個方向)”的操作,在destroy回調中完成“删除 規則”的操作,而target回調本身則什麼都不做。幸虧target有這麼兩個回調函數,否則的話估計玩的還真的有點大。
但是,千萬别把checkentry/destroy這兩個回調函數想得太簡單,事實上,它們的調用機制遠比你想象的要複雜得多,如果我說每添加一條規 則,該target的所有的規則都要重新checkentry一次,這可能有點抽象又不合情理,那麼下面我就先說一下iptables規則的添加機制。 iptables添加/删除規則事實上操作的是兩個規則集,即新的規則集replace舊的規則集,新舊規則集的差別在于對于添加操作新的規則集中多了一 條要添加的規則,對于删除操作新的規則集中少了一條要删除的規則。每一個規則集都是一塊連續的記憶體,正是因為記憶體的連續性是一個要求(記憶體連續可以完全基 于offset來尋址,不必引入指針,完美利用局部性原理),才會出現上述那種replace機制,因為每添加/删除一條規則規則集的大小就會發生變化。 以下的文字摘自我的代碼注釋:
給出代碼前,先看一下用法,這個STATIC-2-WAY-NAT子產品目前一共以下幾個參數:
--mapaddr a.b.c.d-A.B.C.D
必須參數。它将位址a.b.c.d轉換為A.B.C.D,至于是轉換源位址還是目标位址,就看該配置的類型是src還是dst以及資料的方向了。
--type [src|dst]
必須參數。訓示轉換的類型,src表示源位址轉換,它将--mapaddr參數中的源位址為a.b.c.d的資料包的源位址轉換為A.B.C.D,目标位址為A.B.C.D的資料包的目标位址轉換為a.b.c.d;dst表示目标位址轉換,解釋類似src。
--proto [udp|tcp]
可選參數。訓示轉換的資料包協定類型,如果缺失這個參數,則代表所有協定。
--dev [ethX|...]
可選參數。訓示參與位址轉換的資料包的接收網卡和發送網卡。
--mapport p1-p2
可選參數。訓示第四層協定的端口轉換規則,僅僅針對udp和tcp,即如果有這個參數,則必須指定proto為udp或者tcp。
目 前的參數就以上這麼多,日後會完善。使用方法很簡單,為了好看那麼一點點,我将我的NAT實作放在了nat表的PREROUTING和 POSTROUTING上,在寫規則的時候,随便哪個ROUTING都行,實作并不關心,matches在理論上是沒有用的,但是它可以對精确比對到的數 據包屏蔽基于conntrack的原生NAT操作,這也算是一種副作用吧。
代碼一共三個檔案,可用但不完美,一條日志都沒有打,也算一種拆彈方式...:
xt_STATIC-2-WAY-NAT.c:這是一個核心子產品,實作了NAT的核心邏輯和iptables target接口
libxt_STATIC-2-WAY-NAT.c:這是使用者态的iptables target子產品的實作檔案
xt_STATIC-2-WAY.h:這是一個頭檔案,核心子產品和使用者态檔案公用
最後是一個Makefile:
關 于備份不得不多說幾句,我現在有時候在工作中碰到問題的時候,參考的最多的就是我自己的部落格,因為總是隐隐約約覺得自己曾經搞定過某件事,但隻是曾經而 已,隻要有迹可尋,找到那個曾經的方案即可,當然,如果現在重新從零開始最終也是可以搞定的,但是那将浪費很多時間。以前我喜歡在紙上做筆記,但是幾乎不 做索引,随着本子越來越多越來越厚,就很難找到要找的東西了,後來就改成了在電腦裡用Word,OneNote甚至記事本做筆記,可是最終的結果和用紙和 筆的效果一樣,後來我覺得網際網路上現成的索引做的不錯,為何不讓搜尋引擎替我搜尋呢?于是就改成部落格的方式了,至于代碼,我倒不是很看重,自娛自樂而已, 我主要想記錄的是當時想了些什麼而不是怎麼做的。其實你有沒有想過,你參考的最多還是自己以往的經驗,而不是别人的,是以幹嘛不把自己以往的想法錄下來 呢?以前是寫日記,現在是寫部落格,零散的想法以前可以随身帶個小本子寫随筆,現在有微網誌和朋友圈,其實萬變不離其中。
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1596647