我們可以在Linux上使用loopback接口來模拟兩個階段的路由抉擇,第一個階段是走一遍PRE/POST ROUTING流程,将NAT實施完畢,第二階段完成單純路由轉發。然而需要在Netfilter上挂鈎子,以便取消關聯在skb上的路由項,并且取消關聯在skb上的conntrack資訊,因為在第二階段的單純路由流程裡面,我不希望再有什麼基于conntrack的動作,是以如果需要有基于conntrack的操作,務必在第一階段内和NAT一并完成。
回過頭來看loopback的實作,不是那麼完美,因為像在Netfilter上挂載鈎子完成的這種事完全可以在虛拟網卡的xmit操作中完成,是以有必要重新寫一個虛拟網卡,之是以最終還是考慮重新寫,是因為這個子產品超級簡單,基本可以照搬loopback.c的實作,所不同的是xmit的操作:
對NVI接口的注冊也非常簡單:
為何可以使用skb的mark來儲存入接口index呢?實際上在32位的機器上它完全可以儲存原始入網卡dev的位址,強轉成net_device類型指針即可。我并不是一開始就直接把入網卡的index儲存在mark中了,因為可能Netfilter鈎子還要用這個mark,我也沒有使用mark的掩碼掩去一些位來儲存index,因為不知情者可能會誤用。我采用的方式是在“确認不可能有Netfilter鈎子使用mark的時候再将其覆寫成入網卡的index,那麼何時合适呢?精通Netfilter的都知道,在POSTROUTING的最後做這件事比較合适,是以我就把這個HOOK安置于POSTROUTING的nf_confirm之後。是否會有流控用到mark我不管,畢竟流控是在實體網卡上做的,和第一輪的路由無關。然而問題是,到了POSTROUTING的時候我還能取到原始的入網卡的index嗎?Oh,NO!:
是以,到了POSTROUTING,就再也取不到原始網卡的index了!那麼變通的辦法就是将Linux協定棧的這段代碼改了:
我個人覺得,對于将Linux作為路由BOX來用的人來講,對于FORWARDING的資料包,在POSTROUTING的時候能取到資料包從哪個網卡進入,可以實施更多的控制政策,這難道不更好嗎?好吧,如果非要說這樣改不好,我還有更加标準的做法,那就是在conntrack結構體中注冊一個新的extend,其實就是一個結構體,将原始的入網卡作為一個字段放進去,在NVI接口的xmit中,conntrack重置為nf_conntrack_untracked之前,取出這個網卡,調用eth_type_trans接口即可,這樣好了吧,我沒有觸動Linux kernel的主協定棧,還是基于Netfilter來做擴充!事實上,Netfilter的擴充能力是無限的!
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1304515