IP服務的特點
IP協議是TCP/IP協議得動力,它為上層協議提供無狀態,無連接,不可靠得服務。
- 無狀態:是指IP通信雙方不同步傳輸數據的狀態信息,因此所有IP數據報的發送,傳輸,和接收都是相互獨立的,沒有山下文關系的。發送、傳輸和接收都是相互獨立、沒有上下文關系的。這種服務最大的缺點是無法處理亂序和重復的IP數據報。比如發送端發送出的第N個IP數據報可能比第N+1個IP數據報后到達接收端,而同一個IP數據報也可能經過不同的路徑多次到達接收端。在這兩種情況下,接收端的IP模塊無法檢測到亂序和重復,因為這些IP數據報之間沒有任何上下文關系。接收端的IP模塊只要收到了完整的IP數據報(如果是IP分片的話,IP 模塊將先執行重組),就將其數據部分(TCP報文段、UDP數據報或者1CMP報文),上交給上層協議。那么從上層協議來看,這些數據就可能是亂序的、重復的。面向連接的協議,比如TCP協議,則能夠自己處理亂序的、重復的報文段,它遞交給上層協議的內容絕對是有序的、正確的。雖然IP數據報頭部提供了一個標識字段(見后文)用以唯-標識-一個IP數據報,但它是被用來處理IP分片和重組的,而不是用來指示接收順序的。無狀態服務的優點也很明顯:簡單、高效。我們無須為保持通信的狀態而分配一些內核資源,也無須每次傳輸數據時都攜帶狀態信息。在網絡協議中,無狀態是很常見的,比如UDP協議和HTTP協議都是無狀態協議。以HTTP協議為例,一個瀏覽器的連續兩次網頁請求之間沒有任何關聯,它們將被Web服務器獨立地處理。
- 無連接(connectionless)是指IP通信雙方都不長久地維持對方的任何信息。這樣,上層協議每次發送數據的時候,都必須明確指定對方的IP地址。
- 不可靠是指IP協議不能保證IP數據報准確地到達接收端,它只是承諾盡最大努力(best effort)。 很多種情況都能導致IP數據報發送失敗。比如,某個中轉路由器發現IP數據報在網絡上存活的時間太長(根據IP數據報頭部字段TTL判斷,見后文),那么它將丟棄之,並返回一個ICMP錯誤消息(超時錯誤)給發送端。又比如,接收端發現收到的IP數據報不正確(通過校驗機制),它也將丟棄之,並返回一個ICMP錯誤消息(IP 頭部參數錯誤)給發送端。無論哪種情況,發送端的IP模塊一旦檢測到IP數據報發送失敗,就通知上層協議發送失敗,而不會試圖重傳。因此,使用IP服務的上層協議(比如TCP協議)需要自己實現數據確認、超時重傳等機制以達到可靠傳輸的目的。
IPv4頭部結構
- 4位版本號(version) 指定IP協議的版本。對IPv4來說,其值是4。
- 4位頭部長度(header length)標識該IP頭部有多少個32 bit字(4字節)。因為4位最大能表示15,所以IP頭部最長是60字節。
- 8位服務類型(Type Of Service, TOS)包括- -個 3位的優先權字段(現在已經被忽略), 4位的TOS字段和1位保留字段(必須置0)。4位的TOS字段分別表示:最小延時,最大吞吐量,最高可靠性和最小費用。其中最多有一個能置為1,應用程序應該根據實際需要來設置它。比如像ssh和telnet這樣的登錄程序需要的是最小延時的服務,而文件傳輸程序ftp則需要最大吞吐量的服務。
- 16位總長度(total length) 是指整個IP數據報的長度,以字節為單位,因此IP數據報的最大長度為65535 (2l6-1) 字節。但由於MTU的限制,長度超過MTU的數據報都將被分片傳輸,所以實際傳輸的IP數據報(或分片)的長度都遠遠沒有達到最大值。接下來的3個字段則描述了如何實現分片。
- 16位標識(identif.cation) 唯一地標識主機發送的每個一數據報。其初始值由系統隨機生成:每發送一個數據報,其值就加1。該值在數據報分片時被復制到每個分片中,因此同一個數據報的所有分片都具有相同的標識值。
- 3位標志字段的第- -位保留。第二位(Don't Fragment,DF)表示“禁止分片”。如果設置了這個位,IP 模塊將不對數據報進行分片。在這種情況下,如果IP數據報長度超過MTU的話,IP 模塊將丟棄該數據報並返回一ICMP差錯報文。第三位(More Fragment, MF)表示“更多分片”。除了數據報的最后- -個分片外,其他分片都要把它置1。
- 13位分片偏移(fragmentationoffset)是分片相對原始IP數據報開始處(僅指數據部分)的偏移。實際的偏移值是該值左移3位(乘8)后得到的。由於這個原因,除了最后- -個IP分片外,每個IP分片的數據部分的長度必須是8的整數倍(這樣才能保證后面的IP分片擁有一個合適的偏移值)。
- 8位生存時間(TimeToLive,TTL)是數據報到達目的地之前允許經過的路由器跳數。TTL值被發送端設置(常見的值是64)。數據報在轉發過程中每經過一個路由,該值就被路由器減1。當TTL值減為0時,路由器將丟棄數據報,並向源端發送-一個ICMP差錯報文。TTL值可以防止數據報陷人路由循環。
- 8位協議( protocol)用來區分上層協議。/etc/protocols 文件定義了所有上層協議對應的protocol字段的數值。其中,ICMP是1, TCP是6,UDP是17。/etc/protocols文件是RFC 1700的-一個子集。
- 16位頭部校驗和(header checksum)由發送端填充,接收端對其使用CRC算法以檢驗IP數據報頭部(注意,僅檢驗頭部)在傳輸過程中是否損壞。
- 32位的源端IP地址和目的端IP地址用來標識數據報的發送端和接收端。一般情況下,這兩個地址在整個數據報的傳遞過程中保持不變,而不論它中間經過多少個中轉路由器。
- IPv4最后一個選項字段(option) 是可變長的可選信息。這部分最多包含40字節,因為IP頭部最長是60字節(其中還包含前面討論的20字節的固定部分)。可用的IP選項包括:
- 記錄路由(recordroute),告訴數據報途經的所有路由器都將自己的IP地址填人IP頭部的選項部分,這樣我們就可以跟蹤數據報的傳遞路徑。
- 時間戳(timestamp),告訴每個路由器都將數據報被轉發的時間(或時間與IP地址對)填人IP頭部的選項部分,這樣就可以測量途經路由之間數據報傳輸的時間。
- 松散源路由選擇(loose source routing),指定- -個路由 器IP地址列表,數據報發送過程中必須經過其中所有的路由器。
- 嚴格源路由選擇(strict source routing),和松散源路由選擇類似,不過數據報只能經過被指定的路由器。
IP分片
當IP數據報的長度超過幀的MtU時,它將被分片傳輸。分片可能發生在發送端,也可能發生在中轉路由器上,而且可能在傳輸過程中被多次分片,但只有在最終的目標機器上,這些分片才會被內核中的IP模塊重新組裝。
IP頭部中的如下三個字段給IP的分片和重組提供了足夠的信息:數據報標識、標志和片偏移。一個IP數據報的每個分片都具有自己的IP頭部,它們具有相同的標識值,但具有不同的片偏移。並且除了最后一個分片外,其他分片都將設置MP標志。此外,每個分片的IP頭部的總長度字段將被設置為該分片的長度。
以太網幀的MTU是1500字節( 可以通過ifconfig命令或者netstat命令查看),因此它攜帶的IP數據報的數據部分最多是1480 字節(IP 頭部占用20字節)。考慮用IP數據報封裝一個長度為1481字節的ICMP報文(包括8字節的ICMP頭部,所以其數據部分長度為1473字節),則該數據報在使用以太網幀傳輸時必須被分片。
長度為1501字節的IP數據報被拆分成兩個IP分片,第一個IP分片長度為1500字節,第二個IP分片的長度為21字節。每個IP分片都包含自己的IP頭部(20 字節),且第--個IP分片的IP頭部設置了MF標志,而第二個IP分片的IP頭部則沒有設置該標志,因為它已經是最后-一個分片了。原始IP數據報中的ICMP頭部內容被完整地復制到了第一個IP分片中。第二個IP分片不包含ICMP頭部信息,因為IP模塊重組該ICMP報文的時候只需要- .份ICMP頭部信息,重復傳送這個信息沒有任何益處。1473 字節的ICMP報文數據的前1472字節被IP模塊復制到第-一個IP分片中,使其總長度為1500字節,從而滿足MTU的要求;而多出的最后1字節則被復制到第二個IP分片中。
IP路由
IP模塊工作流程
我們從右往左來分析圖。當IP模塊接收到來自數據鏈路層的IP數據報時,它首先對該數據報的頭部做CRC校驗,確認無誤之后就分析其頭部的具體信息。
如果該IP數據報的頭部設置了源站選路選項(松散源路由選擇或嚴格源路由選擇),則IP模塊調用數據報轉發子模塊來處理該數據報。如果該IP數據報的頭部中目標IP地址是本機的某個IP地址,或者是廣播地址,即該數據報是發送給本機的,則IP模塊就根據數據報頭部中的協議字段來決定將它派發給哪個上層應用(分用)。如果IP模塊發現這個數據報不是發送給本機的,則也調用數據報轉發子模塊來處理該數據報。
數據報轉發子模塊將首先檢測系統是否允許轉發,如果不允許, IP模塊就將數據報丟棄。如果允許,數據報轉發子模塊將對該數據報執行一些操作,然后將它交給IP數據報輸出子模塊。我們將在后面討論數據報轉發的具體過程。
IP數據報應該發送至哪個下一跳路由(或者目標機器),以及經過哪個網卡來發送,就是IP路由過程,即圖中“計算下一-跳路由”子模塊。IP 模塊實現數據報路由的核心數據結構是路由表。這個表按照數據報的目標IP地址分類,同--類型的IP數據報將被發往相同的下一跳路由器(或者目標機器)。我們將在后面討論IP路由過程。
IP輸出隊列中存放的是所有等待發送的IP數據報,其中除了需要轉發的IP數據報外,還包括封裝了本機上層數據(ICMP報文、TCP報文段和UDP數據報)的IP數據報。
圖中的虛線箭頭顯示了路由表更新的過程。這一過程是指通過路由協議或者route:命令調整路由表,使之更適應最新的網絡拓撲結構,稱為IP路由策略。
路由機制
cmf@cmf-virtual-machine:~$ route
內核 IP 路由表
目標 網關 子網掩碼 標志 躍點 引用 使用 接口
default 192.168.26.2 0.0.0.0 UG 100 0 0 ens33
link-local 0.0.0.0 255.255.0.0 U 1000 0 0 ens33
192.168.26.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
路由表中,第一項的目標地址是default,即所謂的默認路由項。該項包含一個“G"標志,說明路由的下一跳目標是網關,其地址是192.168.26.2 (這是測試網絡中路由器的本地IP地址)。另外一個路由項的目標地址是192.168.26.0, 它指的是本地局域網。如果該路由項的網關地址為*,說明數據報不需要路由中轉,可以直接發送到目標機器。
那么路由表是如何按照IP地址分類的呢?或者說給定數據報的目標IP地址,它將匹配路由表中的哪-項呢?這就是IP的路由機制,分為3個步驟:
- 1)查找路由表中和數據報的目標IP地址完全匹配的主機IP地址。如果找到,就使用該路由項,沒找到則轉步驟2。
- 2)查找路由表中和數據報的目標IP地址具有相同網路ID的網絡IP地址所示的路由表中的第二項)。如果找到,就使用該路由項:沒找到則轉步驟3。
- 3)選擇默認路由項,這通常意味着數據報的下一跳路由是網關。因此,對於測試而言,所有發送到IP地址為192.168.1.*的機器的IP數據報都可以直接發送到目標機器(匹配路由表第二項),而所有訪問因特網的請求都將通過網關來轉發(匹配默認路由項)。
IP轉發
不是發送給本機的IP數據報將由數據報轉發子模塊來處理。路由器都能執行數據報的轉發操作,而主機一般只發送和接收數據報,這是因為主機上/proc/sys/ne/ipv4/ip_forward內核參數默認被設置為0。我們可以通過修改它來使能主機的數據報轉發功能
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
對於允許IP數據報轉發的系統( 主機或路由器),數據報轉發子模塊將對期望轉發的數據報執行以下操作:
- 檢查數據報頭部的TTL值。如果TTL值已經是0,則丟棄該數據報。
- 查看數據報頭部的嚴格源路由選擇選項。如果該選項被設置,則檢測數據報的目標IP地址是否是本機的某個IP地址。如果不是,則發送-一個ICMP源站選路失敗報文給發送端。
- 如果有必要,則給源端發送一個ICMP重定向報文,以告訴它-一個更合理的下一跳路由器。
- 將TTL值減1。
- 處理IP頭部選項。
- 如果有必要,則執行IP分片操作。
重定向
ICMP重定向報文的類型值是5,代碼字段有4個可選值,用來區分不同的重定向類型。主機重定向,其代碼值為1。ICMP重定向報文的數據部分含義很明確,它給接收方提供了如下兩個信息:
- 引起重定向的IP數據報(即圖2-4中的原始IP數據報)的源端IP地址。
- 應該使用的路由器的IP地址。
接收主機根據這兩個信息就可以斷定引起重定向的IP數據報應該使用哪個路由器來轉發,並且以此來更新路由表(通常是更新路由表緩沖,而不是直接更改路由表)。/proc/sys/net/ipv4/confall/send_redirects 內核參數指定是否允許發送ICMP重定向報文,而/proc/sys/netipv4/confal/accept_redirects 內核參數則指定是否允許接收ICMP重定向報文。一般來說,主機只能接收ICMP重定向報文,而路由器只能發送ICMP重定向報文。
IPv6頭部結構
- 4位版本號(version) 指定IP協議的版本。對IPv6來說,其值是6。
- 8位通信類型(raffic class)指示數據流通信類型或優先級,和IPv4中的Tos類似。
- 20位流標簽(flow label)是IPv6新增加的字段,用於某些對連接的服務質量有特殊要求的通信,比如音頻或視頻等實時數據傳輸。
- 16位凈荷長度(payload length)指的是IPv6 擴展頭部和應用程序數據長度之和,不包括固定頭部長度。
- 8位下一一個包頭(next header)指出緊跟IPv6固定頭部后的包頭類型,如擴展頭(如果有的話)或某個上層協議頭(比如TCP, UDP或ICMP)。它類似於IPv4頭部中的協議字段,且相同的取值有相同的含義。
- 8位跳數限制(hop limit)和IPv4中的TTL含義相同。
- IPv6用128位(16字節)來表示IP地址,使得IP地址的總量達到了28個。所以有人說,“IPv6 使得地球上的每粒沙子都有一-個IP地址”。
- 32位表示的IPv4地址一般用點分十進制來表示,而IPv6地址則用十六進制字符串表示,比如“FE80:000:000:0000: 1234:56780000:0012”。可見,IPv6地址用“:”分割成8組,每組包含2字節。但這種表示方法過於麻煩,通常可以使用所謂的零壓縮法來將其簡寫,也就是省略連續的、全零的組。比如,上 面的例子使用零壓縮法可表示為“FE80: 1234:56780000:0012”.不過零壓縮法對一個IPv6地址只能使用一次, 比如上面的例子中,字節組“5678"后面的全零組就不能再省略,否則我們就無法計算每個“:”之間省略了多少個全零組。
IPv6拓展頭部
可變長的擴展頭部使得IPv6能支持更多的選項,並且很便於將來的擴展需要。它的長度可以是0,表示數據報沒使用任何擴展頭部。一個數據報可以包含多個擴展頭部,每個擴展頭部的類型由前一個頭部(固定頭部或擴展頭部)中的下一-個報頭字段指定。目前可以使用的擴展頭部如表所示。