版權(quán)聲明:本文為博主原創(chuàng),無版權(quán),未經(jīng)博主允許可以隨意轉(zhuǎn)載,無需注明出處,隨意修改或保持可作為原創(chuàng)!
這不是一篇教你怎么可以配置nftables實現(xiàn)一個哪怕最簡單防火墻的文章,我從來不寫這種Howto,因為我覺得如果一項新技術(shù),一個人連其本身的文檔都懶得看,即便沒有文檔如果沒有一點鉆研精神將其搞懂,只靠看別人寫好的Step by step的話,那真是太失敗了。相反,這篇文章是一篇檄文,只為吹擂打鼓,目的是讓你在無感于iptables的前提下愛上nftables。
---------------------------------
關(guān)于nftables,請參見以下幾篇文字:
1.同樣一篇檄文,翻譯過來的,它是《Linux 首次引入 nftables,你可能會喜歡 nftables 的理由》
2.nftables的文檔,這個最具有權(quán)威性,它是《Nftables HOWTO 中文翻譯》,我跟朋友說,這個文檔就像當(dāng)年的iptables文檔一樣好,我給出個中文鏈接,喜歡英文的請自行搜索。
3.詳細(xì)剖析nftables語法以及內(nèi)部結(jié)構(gòu)的一篇文章,它是《What comes after 'iptables'? Its successor, of course: `nftables`》,很遺憾它沒有中文翻譯,不過看我的應(yīng)該就夠了。
4.我自己在2014年的時候?qū)懙囊黄P(guān)于nftables的文章,它是《繼iptables之后的新一代包過濾框架是nftables》,說實話,我確實自己又看了一遍這個。如果你真的完整看過了以上4篇,我相信下面的文字是很輕松的。而且,你在讀完下面文字的時候,應(yīng)該會形成一種形而上的觀點與我展開討論,熱烈歡迎!
---------------------------------
要站在歷史的延長線上觀看nftables,它是第X代Linux防火墻。它的前驅(qū)是iptables,再往前是ipchains,ipfirewall等等...如果你仔細(xì)觀察它們的名字,就會發(fā)現(xiàn),nftables中完全用nf前綴取代了ip前綴,這是不是意味著,之前類似iptables,ip6tables,arptables,ebtables等等“工具族”,完全被統(tǒng)一在nf-框架之上了呢?答案無疑是肯定的,nftables完全統(tǒng)一了所有這些,正如其名字所示,nf代表Netfilter,而不管是ipchains,iptables,ebtables,arptables,底層所依賴的機制完全就是Netfilter。nftables統(tǒng)一了所有這一切!伴隨著這個事實,理所當(dāng)然會有下面的疑問:
nftables的外部語法和內(nèi)部架構(gòu)該如何變化以適應(yīng)這個大一統(tǒng)的名字呢?
我們從名字開始,逐步開始向下挖。
---------------------------------
首先我們先從離我們最近的地方,即用戶接口看起。這部分側(cè)重表現(xiàn)在命令或者說語法方面。請注意,iptables的規(guī)則是沒有語法的,它只是一系列命令行參數(shù)的組合,而nftables的語法也并不是完善的,起碼現(xiàn)在還很弱,不過我們要向前看有所期待。
有文章說nftables是借鑒了tcpdump的語法,將-a $a或者--abc $abc這種語法改成了類似a $a之類的語法,我不太認(rèn)同nftables是借鑒了tcpdump,因為這是nftables自然而然的做法。
如果是iptables時代,我們使用下面的規(guī)則:
iptables -A OUTPUT -p tcp --dport 80 -j DROP到了nftables時代,同樣的規(guī)則,會變成下面的樣子:
nft add rule ip filter output tcp dport 80 drop雖然長了些,但你會發(fā)現(xiàn),所有的字詞都完整地構(gòu)成了一個個的句子構(gòu)成元素,比如主語,謂語,賓語,狀語,定語等,然后回看iptables的命令行參數(shù),幾乎全部都是限定性的,比如出現(xiàn)-p,就意味著后面將要出現(xiàn)的就是協(xié)議...在nftables中,沒有這種限制了。映射到程序中,iptables可以用getopt這種方式處理“命令行參數(shù)”,請注意,iptables將所有的子命令都作為了命令行參數(shù)來看待,而nftables則完全不同,它處理子命令的過程完全是語法分析和詞法分析的過程,所有你敲打進(jìn)去的每一個單詞都將構(gòu)成一顆樹上的節(jié)點,nftables是有“真正的語法”的,所以說,你配置nftables規(guī)則的過程,就是一個編程的過程。編程的過程是一個直觀且簡潔的過程,就像人們自然而然的寫下一個句子一樣。
外部語法的升華背后,那是內(nèi)部結(jié)構(gòu)的調(diào)整。
---------------------------------
nftables的內(nèi)部結(jié)構(gòu)與iptables相比,有哪些變化呢?
首先,我們先看下規(guī)則的布局。
我們知道,iptables規(guī)則的布局是基于連續(xù)的大塊內(nèi)存的,我之前曾經(jīng)稱之為數(shù)組式布局,寫到這里,我想你應(yīng)該知道我想說什么了,既然iptables規(guī)則是數(shù)組式布局,那么nftables規(guī)則會不會是鏈表式布局呢?答案無疑是肯定的。那么iptables規(guī)則的布局和nftables規(guī)則的布局之間的區(qū)別,其實就是數(shù)組和鏈表的區(qū)別了。
舉個最簡單的例子,數(shù)組的元素要是被刪除了,保證順序不變的前提下,后面所有的元素都要往前移動,試想10000個元素的數(shù)組刪除array[1]的情況,鏈表就不存在這個問題,只需要修改一個或者幾個指針即可。當(dāng)然,數(shù)組還是有好處的,內(nèi)存緊湊,cache命中率高,可以索引定位什么的...
當(dāng)然,我并不曉得iptables當(dāng)初為什么要采用這種數(shù)組式的布局,這很值得深挖一下,但卻不是本文的主題。離開這個話題前,我必須要說的是,對于我個人來說,在快速實現(xiàn)一個可以測試的功能的編程過程中,如果需要容器,我首選的就是數(shù)組,因為在實驗階段它更直接,且不會遇到O(n)問題...最后等真的遇到問題且有替換數(shù)組為鏈表的十足理由了,我才會去重構(gòu)成鏈表式的實現(xiàn),否則它就在那里了。
看完了規(guī)則的布局差異,我們繼續(xù)往下看。
如果你熟悉iptables的內(nèi)核機制(如果不知道,請Stop,本文不是介紹這方面的Howto),你就會知道它實際上是一個非常規(guī)則且簡單的“機械裝置”,這套裝置明令要求所有的規(guī)則必須由若干的matches和一個target組成,我們經(jīng)常在肯德基或者老式工廠(比如老式機床廠)的車間見到類似的這種裝置。這類裝置的特點是“工序是固定的”!我以煎蛋為例,如果我想煎三個雞蛋,并且我有一把足夠大的平底鍋,我會一次性把三個雞蛋全部打在刷了足夠植物油的鍋底,這也是一種自然而然的做法,然而如果用類似iptables內(nèi)核機制那樣的煎鍋,不管鍋有多大,每次只能煎一個雞蛋,如果你想煎三個雞蛋,很簡單,重復(fù)三次且只能重復(fù)三次。這種裝置是“不可編程的”!
我們生活的世界不是這樣子的!試想你去一趟超市只能買一樣?xùn)|西,并且不能再去別的地方,現(xiàn)在需要你買來做午飯的所有食材,鑒于超市的蔬菜可能不太新鮮,你要去路對面的蔬菜店單獨購買蔬菜,怎么辦?使用iptables裝置是“可以實現(xiàn)的”,但也僅僅是可以實現(xiàn)而已,除非我們是機器人,否則這種世界是不令人舒適的!我們更容易適應(yīng)的世界應(yīng)該是這樣子:大早上起床先去海鮮市場買條魚,然后拎著魚去蔬菜店買蔬菜,然后拎著魚和蔬菜寄存在超市,進(jìn)入超市購買各種肉類,酒水飲料以及家里需要的日用品,結(jié)完帳取出寄存的魚和蔬菜,拎回家或者一起放入汽車的后備箱。
幸運的是,這就是nftables的方式!
nftables不再固定“機械裝置的行為”,行為完全由你自己來確定,它只是實現(xiàn)了幾個足夠獨立的動作,組合這些動作你幾乎可以實現(xiàn)任何操作,事實上,這種新的裝置解構(gòu)了行為,將行為分解成了更加基礎(chǔ)的動作,你讓它執(zhí)行什么動作它就執(zhí)行什么動作,怎么組合這些動作以及最終完成什么樣的行為,全在你。這裝置是什么?這好像跟電腦很像。其實跟我們?nèi)吮旧砀?!稍微術(shù)語一點,這就是一部虛擬機。nftables竟然在Linux內(nèi)核協(xié)議棧里實現(xiàn)了一個虛擬機!其實不止一個,而是5個!Netfilter的5個HOOK點上均有一部這樣的虛擬機。
和以往固定的機械裝置不同,你灌輸給虛擬機的不光是數(shù)據(jù),還有程序,而固定的機械裝置只能灌輸給其數(shù)據(jù),因為程序是固定的。
回過頭來我們再來看一下nftables的語法特點,一條規(guī)則其實就是一個程序,一個簡單的程序就是一個描述性的祈使句,誰,在什么地點,什么時候,用什么,做什么事:
nft add rule ip filter output tcp dport 80 drop如果想看個究竟,那么加上
--debug=netlink,我們看下其輸出:
ip filter output
[ payload load 1b @ network header + 9 => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ cmp eq reg 1 0x00005000 ]
[ immediate reg 0 drop ]非常明確的一個程序,簡直就是另一個Arch上的匯編程序代碼,用人話描述這個程序,就是:
從數(shù)據(jù)包IP頭開始算,取出數(shù)據(jù)包的第10個字節(jié)開始的1字節(jié)并把它放入reg 1,比較reg 1的值是不是0x06,如果是的話,以數(shù)據(jù)包TCP頭開始算,取出第3個字節(jié)開始的2個字節(jié)放入reg 1,看看它是不是80,如果是,就指示這個數(shù)據(jù)包應(yīng)該丟掉。關(guān)于這個論點,我覺得再繼續(xù)說下去就有點拖沓了,用一下nftables便知,如果不知,看下其內(nèi)核代碼,比iptables的要簡單。
現(xiàn)在,說點關(guān)于人的。
---------------------------------
程序員一般都不懂怎么配iptables規(guī)則,甚至都不懂網(wǎng)絡(luò),而會配iptables規(guī)則的或者會配網(wǎng)絡(luò)的人一般也不會編程,他們可能是運維管理員,我一直都徘徊在兩個陣營之間,最終我自己就死皮賴臉刀槍不入了。我就是個墻頭草,跟程序員在一起的時候,說那幫運維只懂搬機器,只會鍵盤上敲命令,根本就無力修改命令的行為,而編程才是最高貴的隨心所欲。然而跟運維在一起的時候,我會說,編程者只會寫代碼,以為網(wǎng)絡(luò)就是Socket,充其量就是TCP,根本就不懂IP路由協(xié)議,更別提硬件特征比如網(wǎng)卡指示燈,電源了,出了問題他們根本就無力定位,只會擼代碼。....雖然說這種自嘲其實是一種自我吹捧,以顯得自己什么都會,但如果大家都用nftables的話,我便無力吐槽了。
nftables的運維者成了編程者。
因為nftables規(guī)則的編寫過程就是一個編程的過程,你可以對不服者如下吶喊:
匯編語言和C語言寫的程序喂的是CPU這個虛擬機;
Java寫的程序喂的是JVM這個Java虛擬機;
nftables編寫的規(guī)則喂的是Netfilter鉤子點上內(nèi)置的虛擬機。只要想象一下就興奮,Netfilter鉤子點竟然可以內(nèi)置虛擬機了,那么如此一來,所有的完成特定功能的Netfilter內(nèi)核模塊都可以扔掉了,在用戶態(tài)寫nftables規(guī)則即可。這個特性
可以吸引iptables使用者去使用nftables,因為后者可以“隨心所有編程”了!我個人也是比較期待nftables在BSD的Netgraph上有所為的。
但是如此的想法后只能呵呵,其實nftables還遠(yuǎn)遠(yuǎn)沒有進(jìn)化到如此地步。不過基本思想在,我們可以期待nftables的進(jìn)化。
以前,你是拿著原材料到傳統(tǒng)的代工廠生產(chǎn)了一條規(guī)則,如今你是用自家的家伙自己“烹飪”了一條規(guī)則。
nftables引入了運算符,變量和數(shù)據(jù)結(jié)構(gòu),這意味著nftables規(guī)則的設(shè)置者從此成了“編程者”,內(nèi)核僅僅執(zhí)行nftables管理員編制的程序,不再做任何假定。
接近尾聲,關(guān)于性能相關(guān)的話題,我再辯護(hù)幾句。
---------------------------------
iptables曾經(jīng)飽受詬病,iptables和Netfilter只能為此承擔(dān)了責(zé)任。因為iptables和其依賴的Netfilter內(nèi)核模塊為用戶做的太多了!我們來看看這是為什么。
iptables規(guī)則集由N條規(guī)則組成,這些規(guī)則和Netfilter的5和HOOK點相關(guān)聯(lián),在每一個HOOK點上,進(jìn)入的IP數(shù)據(jù)報文要遍歷所有關(guān)聯(lián)與此點的規(guī)則,直到某條規(guī)則明確返回ACCEPT或者DROP之類。如果有10000條規(guī)則,那就要最多去匹配10000次。除此之外,進(jìn)入內(nèi)部,我剛才說過,iptables的內(nèi)核設(shè)施明令規(guī)定了規(guī)則的結(jié)構(gòu)以及執(zhí)行過程,寫規(guī)則的“運維人員”根本就無力像“程序員”那般去修改其行為以便優(yōu)化,一切都是固定的,如果一個iptables內(nèi)核模塊實現(xiàn)的不好,比如數(shù)據(jù)結(jié)構(gòu)組織的不好,那么iptables規(guī)則的編寫者只能興嘆。
正是因為這種無力,除非必須要用,人們一提起Netfilter/iptables就想到它會影響性能,這其實不是Netfilter的問題,這是iptables的問題,nf-HiPAC也是基于Netfilter的,但為什么就不影響性能呢?
nftables作為iptables的后繼者,擺脫了本不該自己承擔(dān)的責(zé)任。
從此以后隨著nftables版本的進(jìn)化,它的行為和性能只與“nftables編程者”有關(guān),你再也不能抱怨nftables的效率低下了,如果它的性能低下,那只能怪你編程編的不好。nftables作為擁有自己獨立語法的“一種編程語言”,和C語言是類似的,如果你的C語言代碼性能很低,你會怪C語言本身嗎?不光如此,你不能怪C語言,你也不能怪CPU,所以你不能怪nftables,也不能怪Netfilter。
包分類這個性能攸關(guān)的話題一直以來真的是性能攸關(guān),一般情況下,人們不敢用iptables來搞包分類,這也是事實。后來出現(xiàn)了nf-HiPAC,這玩意兒性能非常高,但是其背后卻是復(fù)雜的內(nèi)核代碼,伴隨著nf-HiPAC作者被招安以及其作品的商業(yè)化,你能看到的那個性能比iptables高很多的nf-HiPAC版本其實只是個低級版本。使用nftables的話,你甚至可以“烹飪”出一條基于多維樹匹配的規(guī)則來,在iptables中,你無力放棄線性的逐條matches匹配,但是在nftables中,你卻有能力將其由線性匹配優(yōu)化成一顆多維匹配樹。
---------------------------------
如果你能看到這里,那么下面的內(nèi)容也就不重要了。但是雖不重要有可能還是比較有用的。最后來點Howto。
和xtables-addons一樣,安裝nftables需要先安裝以下的包:libmnl-1.0.4.tar.bz2,libnftnl-1.0.6.tar.bz2,readline-6.3.tar.gz,gmp-6.1.2.tar.bz2。這些都很容易找到,我就不給鏈接了。然后就可以編譯nftables了。內(nèi)核方面的升級并沒有難度,我在CentOS 6.7(內(nèi)核版本為2.6.32)上成功編譯了4.9版本內(nèi)核(將關(guān)于nftables的編譯選項全部打開)并安裝,隨后成功安裝了nftable并順利運行,難度并不大。