是查路由表快呢?還是查socket哈希表快?這不是問題的根本。問題的根本是怎麼有效利用這兩者,讓兩者成為合作者而不是競爭者。這是怎麼回事?
我們知道,如果一個數據包要到達本地,那麼它要經過兩次查找過程(暫時不考慮conntrack):IP層查找路由和傳輸層查找socket。怎麼合並這兩者。
Linux內核協議棧采用了一種辦法:在socket中增加一個dst字段作為緩存路由的手段,skb在查找路由之前首先查找socket,找到的話,就將緩存的dst設置到skb,接下來在查找路由的時候,發現已經有了dst,就省去了路由查找的過程。
問題是,socket的dst字段什麼時候設置的呢?當然是“和該socket相關的第一個skb”到來的時候設置上的,無疑即便這第一個skb找到了socket,此時的socket上的dst也是NULL,那麼它會去老老實實查找路由表,如果找到,則將找到的路由項設置到socket的dst字段。
這個特性在Linux的實現中叫做ip_early_demux,在內核文檔中,描述如下:
ip_early_demux - BOOLEAN Optimize input packet processing down to one demux for
certain kinds of local sockets. Currently we only do this
for established TCP sockets.
It may add an additional cost for pure routing workloads that
reduces overall throughput, in such case you should disable it.
Default: 1
對於forward轉發,這個特性必然要降低性能,但是我並不是想說這個顯而易見的問題,我想說的有兩點:
1.層次cache邏輯
我們知道,路由查找是一個“盡力而為”的多對一匹配過程,skb和路由entry並不是精確的一一對應關系,因此不能在路由entry中緩存socket,但是卻可以在socket中緩存路由entry,因為socket和skb是一個一一對應的關系(我說的不是TCP listen socket..),同樣,我也能在路由cache中緩存socket,因為路由cache和skb也是一一對應的關系。
然而Linux內核還是去掉了路由cache的支持,不過這沒關系,只要知道一點就夠了:一一對應的精確匹配項可以緩存更加松散的非一一對應的匹配項。如果將目光移到conntrack,就知道該怎麼做了,我曾經將路由entry緩存到了conntrack裡面,按照這個邏輯,是合理的,同樣的,socket也可以被緩存到conntrack裡面,這一點已經有了iptables相關的match和target。
2.自動還是手動
既然Linux有了ip_early_demux配置參數,問題就是什麼時候開啟它而什麼時候關閉它。特別是,在你事先不知道有多少包是到達本地,有多少包是forward的情況下,這個問題顯得更加不易回答。此時,是相信管理員的非0即1的配置呢,還是讓系統去動態自適應?
如何統計包顯得尤為重要,典型情況下,如果有超過60%的包是到達本地的,那麼就開啟它,反之則關閉它。ip_early_demux配置參數作為一個全局的參數並沒有什麼不好,因為如果不是這樣,那麼就會出現另一個問題,即如何判斷一個包是否要進行early_demux...對於邊界非七層設備,一般而言,流量是分類的,分為管理面流量和數據面流量,對於前者而言,流量的終點就是本地,而對於後者,本機僅僅做forward,如果能事先高效地將包進行平面分類,那麼兩個ip_early_demux配置會顯得更好,對於帶外管理而言,Linux的nsnamespace可以很好地完成這個任務。
基於Linux C的socket抓包程序和Package分析 http://www.linuxidc.com/Linux/2014-11/109330.htm