歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

關於bridge-nf-call-iptables的設計問題

關於bridge-nf-call-iptables的設計問題   熟悉ebtables和iptables的都知道,後者提供的選項所能實現的功能要遠遠多於前者,但這並不是說IP層的功能要比數據鏈路層的更豐富,因為只要數據包進入網卡,協議棧代碼就能“看到”整個數據包,剩下的問題就是如何來解析和過濾的問題了,只要願意,實際上協議棧完全可以在數據鏈路層提供和IP層同樣的過濾功能,比如ip_conntrack。  www.2cto.com            然而,協議棧並沒有這麼實現,因為那樣會造成嚴重的代碼冗余,維護成本將會很高。Linux的bridge filter提供了bridge-nf-call-iptables機制來使bridge的Netfilter可以復用IP層的Netfilter代碼。Netfilter提供了強大的過濾match,流識別機制,使每一個數據包都可以和一個五元組標示的流關聯起來,這樣就可以對整個流而不是單獨的數據包進行更加人性化的操作,而對流的識別以及之後的過濾作用最大的就是mark機制,注意這個mark並不是數據包本身的,它只在本機協議棧內有效。Netfilter代碼可以識別一個流的頭包,然後會將一個mark打入該流,接下來的數據包可以直接從流中取出該mark來進行過濾而不必再遍歷整個規則鏈了,類似下面的規則是常用的:  www.2cto.com   iptables -t mangle -I PREROUTING -j CONNMARK --restore-mark iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j ACCEPT iptables -t mangle -A PREROUTING -m state --state ESTABLISHED -j ACCEPT iptables -t mangle -N mark_Policy" iptables -t mangle -A mark_Policy $matches1 -j MARK --set-mark 100 iptables -t mangle -A mark_Policy $matches2 -j MARK --set-mark 100 iptables -t mangle -A mark_Policy -m mark ! --mark 0 -j CONNMARK --save-mark 類似一種cache機制,只有一個流的第一個數據包才要遍歷整個規則鏈,其余的就可以直接restore出來mark了,接下來協議棧可以根據該mark來進行過濾或者進行Policy Routing。         如果使用bridge-nf-call-iptables的話,能否使bridge層利用上述優勢呢?比如抉擇哪些數據包需要被本地捕獲,哪些數據包需要丟棄,答案當然是模稜兩可的,並不絕對。對於上面第二個問題,抉擇哪些數據包需要丟棄是可以做到的,因為bridge-nf-call-iptables作用於bridge Netfilter的PREROUTING上,完全可以在FORWARD上做Drop or not的抉擇,這沒有任何問題,然而對於第一個問題,哪些數據包需要被本地IP層捕獲,當前的實現就無能為力,然而只需要修改不多的兩行bridge模塊的代碼,問題便迎刃而解,然而能做如此小的手術解決如此大的問題,確實需要積累很多的常識,我不是自誇,這是實話。         在給出解決辦法之前,我首先給出將本應該bridge出去的數據幀捕獲到本地IP層會在哪裡用到,如果沒有實際的需求而去修改代碼,那未免太學院派了。一個典型的需求就是透明網橋模式的VPN,VPN的加密和封裝需要在IP層進行,因此需要把感興趣流捕獲到IP層,不感興趣流直接bridge出去,這是一個實際的需求,然而現有的bridge模塊的代碼卻是解決不了,why?聽我娓娓道來。         Linux的bridge代碼中,bridge-nf-call-iptables體現在br_nf_pre_routing函數中,該函數也是一個Netfilter HOOK函數: [plain]  static struct nf_hook_ops br_nf_ops[] __read_mostly = {       {           .hook = br_nf_pre_routing,           .owner = THIS_MODULE,           .pf = PF_BRIDGE,           .hooknum = NF_BR_PRE_ROUTING,           .priority = NF_BR_PRI_BRNF,       },       ...   }     在該函數的最後: [plain]  NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,           br_nf_pre_routing_finish);     調用了IP層的Netfilter PREROUTING代碼,我希望先調用IP層的Netfilter,在其mangle表中設置好感興趣流的mark,然後在bridge的nat表中將打上mark的數據幀redirect到本地的IP層,遺憾的是,這是無法做到的,因為優先級的關系,br_nf_pre_routing的優先級是NF_BR_PRI_BRNF,它位於nat的優先級之後: [plain]  enum nf_br_hook_priorities {       NF_BR_PRI_FIRST = INT_MIN,       NF_BR_PRI_NAT_DST_BRIDGED = -300, //NAT的優先級       NF_BR_PRI_FILTER_BRIDGED = -200,       NF_BR_PRI_BRNF = 0,        //br_nf_pre_routing的優先級       NF_BR_PRI_NAT_DST_OTHER = 100,       NF_BR_PRI_FILTER_OTHER = 200,       NF_BR_PRI_NAT_SRC = 300,       NF_BR_PRI_LAST = INT_MAX,   };     因此即使IP層的Netfilter為數據幀打上了mark,該mark也不可能為NAT所用,因此此時已經執行過NAT了...如果此時你說還可以在BROUTING上將數據幀熱direct到local IP layer,那你的設備就完全成了一個IP層的設備,雖說還能保持bridge的語義(比如放過arp數據幀),然而這種設計會讓你的產品文檔很令人費解,你的心理預期也將和最終所想的謬之千裡。         最後,我們來看看應該怎麼修改代碼來解決這個問題。最本質的,那就是修改br_nf_pre_routing這個HOOK函數的優先級,使之執行於bridge的NAT之後,這比較好辦,修改br_netfilter.c代碼: [plain]  static struct nf_hook_ops br_nf_ops[] __read_mostly = {           {                   .hook = br_nf_pre_routing,                   .owner = THIS_MODULE,                   .pf = PF_BRIDGE,                   .hooknum = NF_BR_PRE_ROUTING,   #ifdef IP_FILTER_BEFORE_NAT           /**            * 2013/03/06 by 趙亞            * 使iptables的PREROUTING在ebtables的DNAT之前進行,            * 因為網橋的DNAT要使用iptables設置的mark            */                   .priority = NF_BR_PRI_NAT_DST_BRIDGED-1,   #else                   .priority = NF_BR_PRI_BRNF,   #endif   ...     另一處修改是br_nf_pre_routing_finish,問題涉及到執行完IP Netfilter之後,需要從哪裡繼續的問題,修改該函數的最後: [plain]  #ifdef IP_FILTER_BEFORE_NAT           /**            * 2013/03/06 by 趙亞            * 重新開始NF_BR_PRI_BRNF            */           NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,                          br_handle_frame_finish, NF_BR_PRI_NAT_DST_BRIDGED);   #else           NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,                          br_handle_frame_finish, 1);   #endif     NF_BR_PRI_BRNF被定義成了0,如果按照標准的現有2.6.32內核的實現,應該從優先級1開始執行,然而我們的修改版上,由於此時還沒有執行NAT,因此需要從NAT開始執行,而我們的br_nf_pre_routing優先級被設置成了NAT的優先級減去1,那麼接下來應該從NAT開始。         這個修改也不是說沒有副作用的,它使得標准的實現,即NAT位於IP Netfilter之前這個假設所帶來的收益完全失效,記住此點即可。
Copyright © Linux教程網 All Rights Reserved