概說《TCP/IP詳解 卷2》第8章 IP:網際協議

原文鏈接: https://mp.weixin.qq.com/s/aLggUYeTeo9JyNjDnNb74A

本文要點

  • 引言

  • IP分組

  • 輸入處理:ipintr函數

    • ipintr概述

    • 驗證

    • 轉發或不轉發

    • 重裝和分用

  • 轉發:ip_forward函數

  • 轉出處理:ip_output函數

    • 首部初始化

    • 路由選擇

    • 源地址選擇和分片

  • Internet檢驗和:in_cksum函數

  • setsockopt和getsockopt系統調用

  • ip_sysctl函數

 

引言

    本文主要介紹IP分組的結構和基本的IP處理過程,包括輸入、轉發和輸出。圖1顯示了IP層常見的組織形式。

圖1 IP層的處理

    在第4章中,我們已經介紹網絡接口如何把到達的IP分組放到IP輸入隊列ipintrq中,並如何調用一個軟件中斷。因爲硬件中斷的優先級比軟件中斷的要高,所以在發生一次軟件中斷之前,有的分組可能會被放到隊列中。在軟件中斷處理中,ipintr函數不斷從ipintrq中移走和處理分組,直到隊列爲空。在最終的目的地,IP把分組重裝爲數據報,並通過函數調用把該數據報傳給適當的運輸層協議。如果分組沒有到達最後的目的地,並且如果主機被配置成一個路由器,則IP把分組傳給ip_forward。傳輸協議和ip_forward把要輸出的分組傳給ip_output,由ip_output完成IP首部、選擇輸出接口以及在必要時對分組分片。最終的分組被傳給合適的網絡接口輸出函數。

    當產生差錯時,IP丟棄分組,並在某些條件下向分組的源站發出一個差錯報文。這些報文是ICMP的一部分。Net/3通過調用icmp_error發出ICMP差錯報文,icmp_error接收一個mbuf,其中包括差錯分組、發現的差錯類型以及一個選項碼,提供依賴於差錯類型的附加信息。

 

IP分組

    爲了更準確的討論Internet協議處理,首先必須理解一些名詞定義,圖2顯示了在不同Internet層之間傳遞數據時用來描述數據的名詞。

圖2 幀、分組、分片、數據報和報文

    我們把傳輸層協議交給IP網絡層的數據稱爲報文。典型的報文包含一個運輸層首部和應用程序數據。圖2所示的傳輸協議是UDP。IP在報文的首部前加上它自己的首部形成一個數據報。如果在選定的網絡中,數據報的長度太大,IP就把數據報分裂成幾個分片,每個分片中含有它自己的IP首部和一段原來數據報的數據。圖2顯示了一個數據報被分成三個分片。

    當提交給數據鏈路層進行傳送時,一個IP分片或者一個很小的無需分片的IP數據報稱爲分組。數據鏈路層在分組前面加上它自己的首部,併發送得到的幀。

    IP只考慮它自己加上IP首部,對報文本身既不檢查也不修改(除非進行分片)。圖3顯示了IP首部的結構。

圖3 IP數據報

    圖3包括ip結構(圖4)中各成員的名字,Net/3通過該結構訪問IP首部。

圖4 ip結構

47~67 因爲在存儲器中,比特字段的物理順序根據機器和編譯器的不同而不同,所以由#ifs保證編譯器按照IP標準排列結構成員。從而,當Net/3把一個ip結構覆蓋到存儲器中的一個IP分組上時,結構成員能夠訪問到分組中正確的比特。

    IP分組的格式由版本ip_v指定,通常爲4;首部長度ip_hl,通常以4字節單元度量;分組長度ip_len以字節爲單位度量;傳輸協議ip_p生成分組內數據;ip_sum是檢驗和,檢測在發送中首部的變化。

    標準的IP首部長度是20字節,所以ip_hl必須大於或者等於5。大於5表示IP選項緊跟在標準首部後。ip_hl的最大值爲15(2的4次方減1),允許最多40個字節的選項(20+40=60)。IP數據報的最大長度爲65535(2的16次方減1),因爲ip_len是一個16bit的字段。圖5顯示了一個完整的IP分組構成。

圖5 有選項的IP分組構成

    因爲ip_hl是以4字節爲單元計算的,所以IP選項必須常常被填充成4字節的倍數。

 

輸入處理:ipintr函數

    當接口把分組放到ipintrq上排隊後,通過schednetisr調用一個軟中斷。當該軟中斷髮生時,如果IP處理過程已經由schednetisr調度,則內核調用ipintr。在調用ipintr之前,CPU的優先級被改變成splnet。

1. ipintr概述

ipintr是一個比較大的函數,我們將分4個部分討論:a. 對到達分組驗證;b. 選項處理及轉發;c. 分組重裝;d. 分用。在ipintr中發生分組的重裝,比較複雜,將在後續章節討論。圖6顯示了ipintr的整體結構。

圖6 ipintr函數整體結構

100~117 標號next標識主要的分組處理循環開始。ipintr從ipintrq中移走分組,並對之加以處理直到整個隊列爲空。如果到函數最後控制失敗,goto把控制權傳回給next中最上面的函數。ipintr把分組阻塞在splimp內,避免當它訪問隊列時,運行網絡的中斷程序(例如slinput和ether_input)。

332~336 標號bad標識由於釋放相關mbuf並且返回到next中處理循環開始而自動丟棄分組。在整個ipintr中,都是跳到bad來處理差錯。

2. 驗證

    從圖7開始,把分組從ipintrq中取出,驗證它們的內容,損壞和有差錯的分組自動被丟棄。

圖7 ipintr函數:驗證

    a. IP版本

118~134 如果in_ifaddr表爲空,則該網絡接口沒有指派IP地址,ipintr必須丟棄所有的IP分組;沒有地址,ipintr就無法決定該分組是否要到該系統。

    在ipintr訪問任何IP首部之前,它必須證實ip_v是4。當m的數據長度小於結構ip的長度時,調用m_pullup函數,將ip首部放在同一個mbuf中,即一段連續的存儲區。

135~146 下面的步驟保證IP首部(包括選項)位於一段連續的存儲器緩存區上:

  • 如果在第一個mbuf中的數據小於一個標準的IP首部(20字節),m_pullup會重新把標準商務部放到一個連續的存儲器緩存區上去。鏈路層不太可能把最大的(60字節)IP首部分在兩個mbuf中從而使用上面的m_pullup。

  • ip_hl通過乘以4得到首部字節長度,並將其保存在hlen中。

  • 如果IP分組首部的字節數長度hlen小於標準首部(20字節),將是無效分組並被丟棄。

  • 如果整個首部仍不在第一個mbuf中(也就是說,分組包含了IP選項),則由m_pullup完成其任務。同樣,這不一定是必須的。

    檢驗和計算是所有Internet協議的重要組成。所有的協議均使用相同的算法(由函數in_cksum完成),但應用於分組的不同部分。對於IP來說,檢驗和只保證IP首部(以及選項)。對於傳輸協議,如UDP或TCP,檢驗和覆蓋了分組的數據部分和運輸層首部。

    b. IP檢驗和

147~150 ipintr把由in_cksum計算出來的檢驗和保存首部的ip_sum字段中。一個未被破壞的首部應該具有0檢驗和。如果結構非0,則該分組被丟棄。

    c. 字節順序

151~160 Internet標準在指定協議首部中多字節整數值的字節順序時非常小心。NTOHS把IP首部中所有16bit的值從網絡字節序列轉換主機字節序列:分組長度(ip_len),數據報標識(ip_id)和分片偏移(ip_off)。如果兩種格式相同,則NTOHS是一個空的宏。在這裏就轉換成主機字節序列,以避免Net/3每次檢驗該字段時必須進行一次轉換。

    d. 分組長度

161~177 如果分組的邏輯長度(ip_len)比儲存在mbuf中的數據量(m_pkthdr.len)大,說明有些字節被丟失了,此時丟棄該分組。如果mbuf比分組大,則去掉多餘的字節。

    現在,有了完整的IP首部,分組的邏輯長度和物理長度相同,檢驗和表明分組的首部無損地到達。

3. 轉發或不轉發

    圖8顯示了ipintr的下一部分,調用ip_dooptions來處理IP選項,然後決定分組是否到達它最後的目的地。如果分組沒有到達最後的目的地,Net/3會嘗試轉發該分組(如果系統被配置成路由器)。如果分組到達最後目的地,就被交付給合適的運輸層協議。

圖8 ipintr函數:是否轉發

    a. 選項處理

178~186 通過對ip_nhops清零,丟掉前一個分組的原路由。如果分組首部大於默認首部長度,則它必然包含由ip_dooptions處理的選項。如果ip_dooptions返回0,ipintr將繼續處理該分組;否則,ip_dooptions通過轉發或丟棄分組完成對該分組的處理,ipintr可以處理輸入隊列中的下一個分組。我們將在第9章進一步討論選項處理。

    處理完選項後,ipintr通過把IP首部內的ip_dst與配置的所有本接口的IP地址比較,以決定分組是否已到達最終目的地。ipintr必須考慮與接口相關的幾個廣播地址、一個或多個單播地址以及做生意個多播地址。

    b. 最終目的地

187~261 ipintr通過遍歷in_ifaddr(概說《TCP/IP詳解 卷2》第6章 IP編址圖3所示),由配置好的Internet地址表,來決定是否與分組的目的地址匹配。對in_ifaddr列表中的每個in_ifaddr結構進行一系列的比較。要考慮4種常見的情況:

  • 與某個接口地址完全匹配(圖9第一行)

  • 與某個接口相關的廣播地址匹配(圖9中間四行)

  • 與某個接口相關的多播組之一的匹配

  • 與兩個受限的廣播地址之一匹配(圖9最後兩行)

    圖9顯示的是當分組到達我們的示例網絡裏的主機sun上的以太網接口時要測試的地址(ip多播地址除外)。

圖9 爲判斷分組是否到達最終目的地進行的比較

    c. 轉發

262~271 如果ip_dst與所有地址都不匹配,分組還沒到達最終目的地。如果還沒有設置ipforwarding,就丟棄分組。否則,ip_forward嘗試把分組路由到它的最終目的地。

    當分組到達的某個地址不是目的地址指定的接口時,主機會丟掉該分組。在這種情況下,Net/3將搜索整個in_ifaddr列表;只考慮那些分配給接收接口地地址,這種方式稱爲強端系統模型。與之相對的是稱爲弱端系統模型。

4. 重裝和分用

    最後,ipintr的最後一部分代碼如圖10所示,在這裏進行重裝和分用。我們略去了重裝代碼,推遲到第10章討論。當無法重裝完全的數據報時,略去的代碼將指針ip設爲空;否則ip指向一個已經到達目的地的完整數據報。

圖10 ipintr函數

325~332 數據報中指定的協議被ip_p用ip_protox數組映射到inetsw數組的下標。ipintr調用選定的protosw結構中的pr_input函數來處理數據報包含的運輸報文。當pr_input返回時,ipintr繼續處理ipintrq中的下一個分組。

    注意,運輸層對分組的處理髮生在ipintr處理循環內部。在IP和傳輸協議之間沒有到達分組的排隊。

 

轉發:ip_forward函數

    到達非最終目的地的系統的分組需要被轉發。只有當ipforwarding非零或者當分組中包含源路由(第9章介紹)時,ipintr才調用實現轉發算法的ip_forward函數。當分組中包含源路由時,ip_dooptions調用ip_forward,並且第2個參數srcrt設爲1.

    ip_forward通過圖11顯示了route結構與路由表接口。

圖11 route結構

46~49 route結構有兩個成員:ro_rt,指向rtentry結構的指針;ro_dst,一個socketaddr結構,指定與ro_rt所指的路由項相關的目的地。目的地是在內核的路由表中用來查找路由信息的關鍵字。第18章對rtentry結構和路由表進行詳細討論。

    我們分兩個部分討論ip_forward。第一部分確定允許系統轉發分組,修改IP首部,併爲分組選擇路由;第二部分處理ICMP重定向報文,並把分組交給ip_output進行發送。代碼如圖12所示。

 

圖12 ip_forward函數:路由選擇

    a. 分組適合轉發嗎

867~871 ip_forward的第1個參數是指向一個mbuf鏈的指針,該mubf中包含了要被轉發的分組。如果第2個參數非0,則分組由於源路由選項正在被轉發。

879~884 if語句識別並丟棄以下分組

  • 鏈路層廣播

    任何支持廣播的網絡接口驅動器必須爲收到的廣播分組把M_BCASH標誌置位。如果分組尋址到以太網廣播地址,則ether_input就把M_CAST置位。不轉發鏈路層的廣播分組。

  • 環回分組

    對尋址到環回網絡的分組,in_canforward返回0。這些分組將被ipintr提交給ip_forward,因爲沒有正確配置反饋接口。

  • 網絡0和E類地址

    對於這些分組,in_canforward返回0。這些目的地址是無效的,而且因爲沒有主機接收這些分組,所以它們不應該繼續在網絡中流動。

  • D類地址

    尋址到D類地址的分組應該由多播函數ip_mforward而不是由ip_forward處理。in_canforward拒絕D類(多播)地址。

    處理分組的所有系統都必須把生存時間(TTL)字段至少減去1,即使TTL是以秒計算的。由於這個要求,TTL通常被認爲是對IP分組在被丟掉之前能經過的跳的個數。

    b. 減小TTL

885~890 由於轉發時不再需要分組的標識符,所以標識符又被轉換回網絡字節序列。但是當ip_forward發送包含無效IP首部的ICMP差錯報文時,分組的標識符又應該是正確的順序。

    如果ip_ttl達到1(IPTTLDEC),則向發送發返回一個ICMP超時報文,並丟掉該分組。否則,ip_forward把ip_ttl減去IPTTLDEC(先判斷後減1)。

    c. 定位下一跳

891~907 IP轉發算法把最近的路由緩存在全局route結構的ipforward_rt中,有可能時應用於當前分組。研究表明連續分組趨向於同一目的地址,所以這種向後一個的緩存使得路由查詢的次數減少。如果緩存爲空(ipforward_rt)或者當前分組的目的地不是ipforward_rt中的路由,就取消前面的路由,ro_dst被初始化成新的目的地,rtalloc爲當前的目的地找一個新路由。如果找不到路由,則返回一個ICMP主機不可達差錯,並丟棄該分組。

908~914 由於在產生差錯時,ip_output要丟掉分組,所以m_copy複製分組的前64個字節,以便ip_forward發送ICMP差錯報文。如果調用m_copy失敗,ip_forward並不終止。在這個情況下不發送差錯報文。ip_ifmatrix記錄在接口之間進行路由選擇的分組的個數。具有接收和發送接口索引的計數器是遞增的。

 

    當主機錯誤地選擇某個路由器作爲分組的第一跳路由器時,該路由器向源主機返回一個ICMP重定向報文。IP網絡互連模型假定主機相對地並不知道整個互聯網的拓撲結構,把維護正確路由選擇的責任交給路由器。路由器發出重定向報文是向主機表明它爲分組選擇了一個不正確的路由。圖13說明重定向報文。

圖13 路由器R1重定向主機HS使用路由器R2到達HD

    通常,管理員對主機的配置是:把遠程網絡的分組發送到某個默認路由器上。在圖13中,主機HS上R1被配置成它的默認路由器。當HS首次向HD發送分組時,它不知道R2是合適的選擇,所以把分組轉發給R1。R1識別出差錯,就把分組轉發給R2,並向HS發回一個重定向報文。接收到重定向報文後,HS更新它的路由表,下一次發往HD的分組就直接發給R2。

    在圖14中,ip_forward決定是否發送重定向報文。

圖14 ipforward函數:是否發送重定向報文

    a. 在接收接口上離開嗎

915~929 路由器識別重定向情況的規則很複雜。首先,只有在同一接口(rt_ifp和rcvif)上接收或重發分組時,才能應用重定向。其次,被選擇的路由本身必須沒有被ICMP重定向報文創建或者修改過(RTF_DYNAMIC|RTF_MODIFIED),而且該路由也不能是默認目的地(0.0.0.0)。

    全局整數ipsendredirects指定系統是否被授權發送重定向,它的默認值爲1。當傳給ip_forward的參數srcrt指明系統是對分組路由選擇的源時,禁止系統重定向,因爲假定源主機要覆蓋中間路由器的選擇。

    b. 發送重定向嗎

930~931 這個測試決定分組是否產生於本地子網。如果源地址的子網掩碼位和輸出接口的相同,則這兩個地址位於同一IP網絡中。如果源接口和輸出的接口位於同一網絡中,則該系統就不應該接收這個分組,因爲源站可能已經把分組發給正確的第一跳路由器了。ICMP重定向報文告訴主機正確的第一跳目的地。如果分組產生於其它子網,則前一系統是個路由器,這個系統就不應該發送重定向報文;差錯路由由選擇協議糾正。

    c. 選擇合適的路由器

932~940 ICMP重定向報文中包含下一個系統的地址,如果目的主機不在直接相連的網絡上,該地址是一個路由器地址;當目的主機在直接相連的網絡中時,該地址就是主機地址。

圖15 ip_forward

    d. 轉發分組

941~954 現在,ip_forward有一個路由,並決定是否需要ICMP重定向報文。ip_output把分組發送到路由ipforward_rt所指定的下一跳。IP_ALLOWBROADCAST標誌位允許被轉發分組是到某個局域網的廣播。如果ip_output成功,並且不需要發送任何重定向報文,則丟掉分組的前64字節,if_forward返回。

    e. 發送ICMP差錯報文?

955~983 ip_forward可能會由於ip_output失敗或者重定向而發送ICMP報文。如果沒有原始分組的複製(可能當時複製時,緩存不足而複製失敗),則無法發送重定向報文,ip_forward返回。如果有重定向,type和code以前被置位,但是如果ip_output失敗,switch語句基於從ip_output返回的值重新設置新的ICMP類型和錯誤碼。icmp_error發送該報文。來自失敗的ip_outputICMP報文將覆蓋任何重定向報文。

    處理來自ip_output的差錯的switch語句非常重要。它把本地差錯翻譯成適當的ICMP差錯報文,並返回給分組的源站,如果是ICMP重定向報文,則dest則是告訴源站對於當前報文,下次應該跳轉的目的地。對於圖16對差錯作了總結。

圖16 來自ip_output的差錯

 

輸出處理:ip_output函數

    IP輸出代碼從兩處接收分組:ip_forward和運輸協議(圖1)。讓inetsw[0].pr_output能訪問到IP輸出操作似乎很有道理,但事情並非如此。標準Internet傳輸協議(ICMP、IGMP、UDP和TCP)直接調用ip_output,而不查詢inetsw表。對標準Internet傳輸協議而言,protosw結構不必具有一般性,因爲調用函數並不是在與協議無關的情況下接入IP的。對於協議無關的路由選擇插口可以調用pr_output接入IP。

    下面分三個部分描述ip_output:

  • 首部初始化

  • 路由選擇

  • 源地址選擇和分片

1. 首部初始化

    圖17顯示了ip_output的第一部分,把選項與外出的分組合並,完成傳輸協議提交(不是ip_forward提交的)的分組首部。

圖17 函數ip_output

44~59 傳給ip_output的參數包括:m0,要發送的分組;opt,包含的IP選項;ro,緩存的到目的地的路由;flags,見圖18;imo,指向多播選項的指針。

圖18 ip_output:flag值

    IP_FORWARDING被 ip_forward和ip_mforward(多播分組轉發)設置,並禁止ip_output重新設置任何IP首部字段。

    a. 構造IP首部

60~73 如果調用程序提供任何IP選項,它們將被ip_insertoptions與分組合並,並把返回新的首部長度。

    進程可以設置IP_OPTIONS插口選項來爲一個插口指定IP選項。插口的運輸層(TCP或UDP)總是把這些選項提交給ip_output。

    被轉發的分組(IP_FORWARDING)或有預構首部(IP_RAWOUTPUT)分組的IP首部不能被ip_output修改。任何其它分組需要有幾個IP首部字段被初始化。ip_output把ip_v設置成4,把DF位需要的ip_off清零,並設置成調用程序提供的值,給來自全局整數的ip->ip_id賦一個唯一的標識符,把ip_id加1。ip_id是在協議初始化時由系統時鐘設置。ip_hl被設置成用32bit字節度量的首部長度。

    IP首部的其它字段長度、偏移、TTL、TOS和目的地址已經被傳輸協議初始化了。源地址可能沒被設置,因爲是確定了到目的地的路由後選擇的(圖20)。

    b. 分組已經包括首部

74~76 對一個已轉發的分組(或一個有首部的原始IP分組),首部長度被保存在hlen中,留給將來分片算法使用。

2. 路由選擇

    在完成IP首部後,ip_output的下一個任務就是確定一條到目的地的路由。如圖19所示。

圖19 ip_output:選擇路由

    a. 驗證調整緩存中的路由

77~99 ip_output可能把一條高速緩存中的路由作爲ro參數來提供。如果沒有路由,則ip_output把ro設置成臨時route結構iproute。

    如果高速緩存中的目的地不是去當前分組的目的地,就把該路由丟掉,新的目的地址放在dst中。

    b. 旁路路由選擇

100~114 調用方可以通過設置IP_ROUTETOI標誌禁止對分組進行路由選擇。ip_output必須找到一個與分組中指定的目的地網絡直接相連的接口。ifa_ifwithdstaddr搜索點到點接口,而in_ifwithnet搜索其它接口。如果任一函數均未找到與目的網絡相連的接口,就返回ENETUNREACH;否則,ifp指向選定接口。

    c. 本地路由

115~122 如果分組正被路由選擇,並且沒有其他緩存的路由,則rtalloc找到一條到dst指定的地址的路由。如果rtalloc沒找到路由,則ip_output返回EHOSTUNREACH。如果ip_forward調用ip_output,就把EHOSTUNREACH轉換成ICMP差錯。如果某個傳輸協議調用ip_output,就把差錯傳回給進程。

123~122 ia被設置成指向選定接口的地址(ifaddr結構),而ifp指向接口的ifnet結構。如果下一跳不是分組的最終目的地,則把dst改成下一跳路由器地址,而不再是分組最終的目的地址。IP首部內部的目的地址不變,但接口層必須把分組提交給dst,即下一跳路由器。

3. 源地址選擇和分片

    ip_output的最後一部分如圖20所示,保證IP首部有一個有效源地址,然後把分組提交給與路由相關的接口。如果分組比接口的MTU大,就必須對分組分片,然後一片一片地發送。關於分片的代碼,將在第10章討論。

圖20 ip_output函數:源地址選擇和分片

    a. 選擇源地址

212~239 如果沒有指定ip_src,則ip_output選擇輸出接口的IP地址ia作爲源地址。這不能在早期填充其它IP首部字段時一起初始化,因爲那裏還沒選定路由。轉發分組通常都有一個源地址,但是,如果發送進程沒有明確指定源地址,產生於本地主機的分組可能沒有源地址。

    如果目的IP地址是一個廣播地址,則接口必須支持廣播(IFF_BROADCAST),調用方必須明確能廣播(IP_ALLOWBROADCAST),而且分組必須足夠小,無需分片。

    如果這些條件都不滿足,就扔掉該分組,把相應錯誤碼返回給調用方。否則,設置輸出分組的M_BCAST,告訴接口輸出函數把該分組作爲鏈路級廣播發送。

    如果目的地址不是廣播地址,則ip_output把M_BCAST清零。

    b. 發送分組

240~252 如果分組對所選擇的接口足夠小,ip_len和ip_off被轉換成網絡字節序列,IP檢驗和與in_cksum一起計算,把分組提交給所選接口的if_output函數。

    c. 分片分組

253~338 大分組在被髮送之前必須分片。這裏暫時不予討論,將在第10章討論。

    d. 清零

339~346 對第一路由入口都有一個引用計數。如果參數ro爲空,ip_output可能會使用一個臨時的route結構(iproute)。如果需要,RTFREE發佈iproute內的路由入口,並把引用計數減1。Bad處的代碼在返回前扔掉當前分組。

 

Internet檢驗和:in_cksum函數

    檢驗和的目的是檢驗數據是否正確,沒有被修改。對於檢驗和函數的設計和實現如下所示:

  1. 把被檢驗的相鄰字節成對配成16bit整數,然後進行二進制反碼求和。

  2. 爲生成檢驗和,把檢驗和字段本身清零,把這個16bit的和的二進制反碼放到檢驗和字段。

  3. 爲了檢驗檢驗和,依然按16bit的方式進行二進制反碼求和。如果結果爲全1(在二進制反碼運算中爲0),則檢驗成功。

    二進制反碼運算:當對用二進制反碼錶示的整數進行加法運算時,把兩個整數相加後再加上最高位的進位從而得到加法的結果。在二進制反碼運算中,只要把每一位求補就得到一個數的反;所以在二進制反碼運算中,0的表示方法有兩種:全0和全1。

    檢驗和算法在發送分組之前計算出要放在IP首部檢驗和字段的值。爲了計算這個值,先把首部的檢驗和字段設爲0,然後把首部作爲一個16bit的整數數組來處理,計算整個首部(包括選項)的二進制反碼的和。暫且把這個計算結果稱爲a,因爲檢驗和字段被明確設爲0,所以a是除了檢驗和字段外所有IP首部字段的各。a的二進制反碼,用-a表示,被放在檢驗和字段中,發送該分組。

    如果在傳輸過程中沒有比特位被改變,則在目的地計算的檢驗和應該等於(a+-a)的二進制反碼。在二進制反碼運算中(a+-a)的和是-0(全1),而它的二進制反碼應該等於0(全0)。所以在目的地,一個沒有損壞分組計算出來的檢驗和應該總是爲0,正如圖7中看到的檢驗和判斷部分。

    圖21是這個算法的一種原始的實現:

圖21 IP檢驗和計算的一種原始的實現

1~16 這裏唯一提高性能之處在於累計sum高16bit的進位。當循環結束時,累計的進位被加在低16bit上,直到沒有其它進位發生。

    圖22顯示的是Net/3的可移植C版本。它使用了延遲進位技術,作用於存儲在一個mbuf鏈中的分組。

圖22 IP檢驗和計算的一個優化

42~140 我們的新檢驗和實現假定所有的被檢驗字節存儲在一個連續緩存而不是mbuf中。這個版本的檢驗和計算採用相同的底層算法來正確地處理mbuf:用32bit整數的延遲進位對16bit字節作加法。對奇數個字節的mbuf,多出來的一個字節保存起來,並與下一個mbuf的第一個字節配對。因爲大多數體系結構中,對16bit字的不對齊訪問是無效的,甚至會產生嚴重差錯,所以不對齊字節被保存,in_cksum繼續加上下一個對齊的字。當這種情況發生時,in_cksum總是很小心地交換字節,保證位於奇數和偶數位置的字節被放在單獨的和字節中,以滿足檢驗和算法的要求。

93~115 函數中的三個while循環在每次迭代中分別在和中加上16個字、4個字和1個字。展開的循環減小了循環的耗費,在某些體系結構中可能比一個直接循環要快很多,但代價是代碼長度和複雜性增加。

 

setsockopt和getsockopt系統調用

    Net/3提供setsockopt和getsockopt兩個系統調用來訪問一些網絡互連的性質。這兩個系統調用支持一個動態接口,進程可用該動態接口訪問某種網絡互連協議的一些性質,而標準的系統調用通常不支持該協議。這兩個調用的原型是:

    int setsockopt(int s, int level, int optname, 

                             void *optval, int opt len);

    int getsockopt(int s, int level, int optname, 

                        const void * optval, int optlen)

    大多數插口選項隻影響它們在其上發佈的插口。與sysctl參數相比,後者影響整個系統。

    setsockopt和getsockopt設置和獲取通信棧所有層上的選項。Net/3按照與s相關的協議和由level指定的標識處理選項。圖23列出了在我們討論的協議中level可能取得的值。

圖23 sosetopt和sogetopt參數

    圖24顯示了所有插口選項的總結。該圖顯示了IPPROTO_IP級選項。選項出現在第1列,optval指向變量的數據類型出現在第2列,第3列顯示的是處理該選項的函數。

圖24 IPPROTO_IP級的插口選項

    圖25顯示了用於處理大部分IPPROT_IP選項的ip_ctloutput函數的整個結構。

圖25 ip_ctloutput函數整體結構

431~447 ip_ctloutput的第一個參數op,可以是PRCO_SETOPT或者PRCO_GETOPT。第二個參數so,指向向其發佈請求的插口。levlel必須是IPROTO_IP。optname是要改變或者檢索的選項,mp間接指向一個含有與該選項相關數據的mbuf,m被初始化爲指向由*mp引用的mbuf。

448~500 如果在調用setsockopt時指定了一個無法識別的選項,ip_ctloutput釋放掉所有調用方傳來的緩存,並返回EINVAL。

501~553 getsockopt傳來的無法識別的選項導致ip_ctloutput返回ENOPROTOOPT,調用方釋放mbuf。

1. PRCO_SETOPT的處理

    對於PRCO_SETOPT的處理如圖26所示。

圖26 ip_ctloutput函數:處理PRCO_SETOPT

450~451 IP_OPTIONS是由ip_pcbopts處理的(第9章討論)。

452~484 IP_TOS、IP_TTL、IP_RECVOPTS、IP_RECVERTOPTS以及IP_RECVDSTADDR選項都需要在由m指向的mbuf中有一個整數。該整數存儲在optval中,用來改變與插口有關的ip_tos和ip_ttl的值,或者用來設置或復位與插口相關的INP_RECVOPTS、INT_RECVERTOPTS和INP_RECVDSTADDR標誌位。如果optval是非0,則宏OPTSET設置或復位指定的比特。

2. PRCO_GETOPT的處理

    圖27顯示了對PRCO_SETOPT的處理。

圖27 ip_ctloutput函數:處理PRCO_GETOPT

503~538 對IP_OPTIONS,ip_ctloutput返回一個緩存,該緩存中包含了與該插口相關的選項的備份。對其他選項,ip_ctloutput返回ip_tos和ip_ttl的值,或與該選項相關的標誌的狀態。返回的值放在由m指向的mbuf中。如果在inp_flags中的bit是打開的,則宏OPTBIT返回1,否則返回0。

 

ip_sysctl函數

    概說《TCP/IP詳解 卷2》第7章 域和協議圖24中顯示,在調用sysctl中,當協議和協議族的標識符是0時,就調用ip_sysctl函數。圖28顯示了ip_sysctl支持的三個參數。

圖28 sysctl參數

    圖29顯示了ip_sysctl函數。

圖29 ip_sysctl函數

    因爲ip_sysctl並不把sysctl請求轉發給其他函數,所以在name中只能有一個成員,否則返回ENOTDIR。

    switch語句選擇的調用sysctl_int,它訪問或修改ipforwarding、ipsendredirects或者ip_defttl。對無法識別的選項返回EOPNOTSUPP。

 

更多最新文章盡在公衆號:大白愛爬山,歡迎關注!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章