linux下ip層的一些概念

首先來看這個ip層的結構: 




這裏看到非常多的netfilter hook,這是因爲netfilter主要是針對ip層的。 

ip層的主要任務有下面5個方面: 

1 ip數據包的校驗 

2 防火牆的處理(也就是netfilter子系統) 

3 處理options(這裏的options包含了一些可選的信息。比如時間戳或者源路由option). 

4 切包和組包(由於mtu的存在,因此我們需要切包和組包). 

5 接收,輸出和轉發操作。 


接下來我們來看ip頭的結構: 


 


Java代碼  收藏代碼
  1. struct iphdr {  
  2. #if defined(__LITTLE_ENDIAN_BITFIELD)  
  3.     __u8    ihl:4,  
  4.         version:4;  
  5. #elif defined (__BIG_ENDIAN_BITFIELD)  
  6.     __u8    version:4,  
  7.         ihl:4;  
  8. #else  
  9. #error  "Please fix <asm/byteorder.h>"  
  10. #endif  
  11.     __u8    tos;  
  12.     __be16  tot_len;  
  13.     __be16  id;  
  14. ///這裏要注意DF,MF和Fragment offset表示爲frag_off一個域.  
  15.     __be16  frag_off;  
  16.     __u8    ttl;  
  17.     __u8    protocol;  
  18.     __sum16 check;  
  19.     __be32  saddr;  
  20.     __be32  daddr;  
  21.     /*The options start here. */  
  22. };  


這裏每個字段的意思也就沒什麼好解釋得了,基本上對網絡協議有些瞭解的都會知道。我下面會介紹幾個主要的域。這裏可以看到在 

首先來看options域。 

前面已經說過了,這個域也就是包含一些其他程序(比如路由)需要的信息。這個域的大小爲0到40位,一般不會超過40個位,再大的options基本上很罕見。 

options被分爲2種,一種是單字節的,一種是多字節的。下面我們就來看這兩種的結構: 

 

先來看type字段,它的結構: 


 


clas也就是這個options的類型了,copied如果被設置,則當數據包切片的時候,這個option必須被複制到每一個切好的包。而number則是類型所對應的值:下面這個圖就是ip option的一些類型值: 




主要的類型定義在linux/ip.h裏面。 

接下來length表示這個option的長度。poionter這個指針表示options的起始位移。option data存儲一些需要的數據。 

隨便舉個ip options的例子,比如record route option,這個option用來保存輸出接口的ip地址,但它有大小限制,只能保存9個地址們如果超過九個就會忽略,接下來看下面的圖,a主機發送包到b主機: 

 


最後我們來看一下數據報的切片和組包。 

首先來看相關的ip頭裏面的域: 


DF,表示不切片,因爲有時切片並組包耗時太長,影響性能。可是如何解決mtu等問題呢,這裏linux採用了Path MTU Discovery算法,來取得所能傳輸的最大mtu,而不是根據輸入幀的頭來判斷。 

MF表示需要更多的切好的片。當一個包被切片之後,它設置mg爲true,知道最後一個分片,而這個分片的MF會被設置爲false(也就是0).當接收端接收到這最後一個切片時,就會開始組包,哪怕其他的切片還沒到達。 

Fragment offset表示這個分片的數據包在原來數據包中的位移,只有憑藉這個才能正確組包。 

ID,ip包的id,一個ip包的所有幀切片的id都是相同的。通過這個域,接受者能知道那些切片是屬於同一個包的。 




我們再來看切片和組包有可能會出現的問題。 

先來看丟包的問題,首先我們要知道只有當ip包全部接收到(也就是被切片的包)之後,纔會組包並將此數據包發送給高層。 

丟包有3種情況: 

1 有可能被路由器丟掉。 

2 有可能由於crc校驗不通過而被丟掉。 

3 有可能被防火牆過濾掉。 

解決方法就是,如果一些切片沒有在給定的時間內到達的話,每一個路由器和主機都有一個定時器來清理髮送過的ip包的切片。 

這裏要注意,ip層是沒有重傳機制的(ip協議是無連接的),因此必須等待高層來告訴它重傳整個數據包。 

重傳的數據包不能重新使用未傳輸成功的數據報的id,也就是id不能相同。 

由於kernel不能交換數據到硬盤,因此handling切片在內存中,會影響路由器的性能,因此linux對於切片的內存有了一個限制。 

由於ip是一個無連接的協議,因此沒有流量控制什麼的,所以這些都交給上層去做。 

標識每一個切片是屬於哪一個數據包,在linux中使用4個域來確定: 

源地址,目的地址,ip包id,l4協議類型。 

可是這有一個問題,那就是有可能不同的包,這四個參數都是相同的,比如經過nat轉發後的數據包。比如下面的例子,當pc1,pc2發出去的包被路由r修改掉源地址,並切片後,然後同時抵達S,這時就會出問題: 


 

而ipv6就會更好的處理這個問題,他將只允許在原始的host進行切片。 

最後看一下packet ID,在linux中,是每個目的地址一個ip packet id,每個id是一個16位的整數。這樣的話就降低了數據包的id有可能老的還沒到,新的就要重新使用老的的id。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章