天天看點

利用nf_conntrack機制存儲路由,省去每包路由查找

IP是無連接配接的,是以IP路由是每包一路由的,資料包通過查找路由表擷取路由,這是現代操作協定協定棧IP路由的預設處理方式。但是如果協定棧具有流識别能力,是不是可以基于流來路由呢?答案無疑是肯定的。

在Linux的實作中,nf_conntrack可以做到基于流的IP路由,大緻思想就是,僅僅針對一個流的第一個正向包和第一個反向包查找标準的IP路由表,将結果儲存在conntrack項中,後續的屬于同一流的資料包直接取出路由項來使用。背後的思想是:這可以省去查找路由表的開銷,是這樣嗎?也不全是!關鍵是,将一個資料包對應到一個資料流,這本身就需要一個查找比對的過程,如果能将路由儲存在conntrack裡面,那麼conntrack查找和路由查找就可以合并成一次查找。是以,查找是免不了的,隻是換了地方而已,如果有了conntrack,仍然進行标準的基于包的IP路由查找過程,那就是平白多了一次查找。

在實作上,很簡單,那就是盡量在資料包離開協定棧的地方設定skb的路由到conntrack。之是以可以這麼做是因為不管是POSTROUTING還是INPUT,都是在路由之後,如果前面進行了基于包的IP路由查找,此時skb上一定綁定了dst_entry,将其綁到conntrack裡面即可。另外,在資料包剛進入協定棧的地方試圖從conntrack項中取出路由,然後直接将其設定到skb上。整個處理過程類似skb-mark和conntrack mark的處理方式:

-A PREROUTING -m mark --mark 100 -j ACCEPT 

-A PREROUTING -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff 

-A PREROUTING -m mark ! --mark 0x0 -j ACCEPT 

...... 慢速比對過程

-A PREROUTING .....   -j MARK --set-mark 100

.....慢速比對過程

-A POSTROUTING -m mark ! --mark 0x0 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff  

有了以上的了解,代碼就很簡單了

在以上的實作思想的文字描述中,我使用了盡量和試圖兩個不那麼明确的詞,這就牽扯到了流路由的老化機制。老化思想

标準的路由查找是每個包都要查找,而如今引入了流路由之後,便不需要對skb進行路由查找了,取而代之的是直接從conntrack取出路由設定給skb,這個conntrack上的路由就是第一次的時候針對skb查找路由表的結果。那麼就會引入一個問題,即什麼時候再次針對skb查找路由表以便更新conntrack的路由。這個問題沒法直接回答,對于路由一直穩定的網絡,根本不需要重新查找,因為針對一個流的第一個正向包和第一個反向包的路由查找結果在該流的生命周期中将一直有效,畢竟路由沒有改變,但是如果在流的生命周期内一條相關的路由發生了改變,就需要重新更新conntrack的路由結果。

      是以可以說,引入一個通知機制就能解決這個問題。每當路由發生改變的時候,在PREROUTING的hook中,不再執行:

而這個非常容易,使用核心的Notifier機制就可以了,在任何路由改變的時候,通知上述的流路由子產品改變一個标志位,在PREROUTING的hook中,發現該标志位置位,就不執行skb_dst_set。如此一來,上述的代碼就會變為下面的:

然而,把這件事交給使用者态或許更好些。畢竟核心态發生的所有事情,使用者态都有辦法監控到,我認為用一個procfs的可寫檔案來通知flag變為1或者變為0可能更好,即flag的值由使用者來設定,這樣使用者就可以在任意時刻啟用,停用流路由機制,比如使用iproute2的monitor機制監控到了路由的改變,如果是無關路由改變了,那麼就不更新flag,隻有是相關的路由改變了,才更新,何其靈活。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1398368

繼續閱讀