1. 程式人生 > >關於bridge-nf-call-iptables的設計問題

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

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                熟悉ebtables和iptables的都知道,後者提供的選項所能實現的功能要遠遠多於前者,但這並不是說IP層的功能要比資料鏈路層的更豐富,因為只要資料包進入網絡卡,協議棧程式碼就能“看到”整個資料包,剩下的問題就是如何來解析和過濾的問題了,只要願意,實際上協議棧完全可以在資料鏈路層提供和IP層同樣的過濾功能,比如ip_conntrack。
         然而,協議棧並沒有這麼實現,因為那樣會造成嚴重的程式碼冗餘,維護成本將會很高。Linux的bridge filter提供了bridge-nf-call-iptables機制來使bridge的Netfilter可以複用IP層的Netfilter程式碼。Netfilter提供了強大的過濾match,流識別機制,使每一個數據包都可以和一個五元組標示的流關聯起來,這樣就可以對整個流而不是單獨的資料包進行更加人性化的操作,而對流的識別以及之後的過濾作用最大的就是mark機制,注意這個mark並不是資料包本身的,它只在本機協議棧內有效。Netfilter程式碼可以識別一個流的頭包,然後會將一個mark打入該流,接下來的資料包可以直接從流中取出該mark來進行過濾而不必再遍歷整個規則鏈了,類似下面的規則是常用的:
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函式:
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,    },    ...}

在該函式的最後:
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的優先順序之後:
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程式碼:
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之後,需要從哪裡繼續的問題,修改該函式的最後:
#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之前這個假設所帶來的收益完全失效,記住此點即可。
           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述