《TCP/IP詳解卷2:實現》筆記--協議控制塊

協議層使用協議控制塊(PCB)存放各UDP和TCP插口所要求的多個信息片。Internet協議維護Internet協議控制塊

(internet protocol control block)和TCP控制塊(TCP control block)。因爲UDP是無連接的,所以一個端結點需要的

所有信息都在Internet PCB中找到,不存在UDP控制塊。

Internet PCB含有所有UDP和TCP端結點共有的信息:外部和本地IP地址、外部和本地端號、IP首部原型、該端結點使用的

IP選項以及一個指向該端結點目的地址選路表入口的指針。TCP控制塊包含了TCP爲各連接維護的所有結點信息:兩個方向

的序號、窗口大小、重傳次數等等。

下圖總結了協議控制塊以及它們與file和socket結構之間的關係。

圖中有幾個要點要考慮:
1.當socket或accept創建一個插口後,插口層生成一個file結構和一個socket結構。文件類型是DTYPE_SOCKET,UDP端結點
的插口類型是SOCK_DGRAM,TCP端結點插口類型是SOCK_STREAM。
2.然後調用協議層。UDP創建一個Internet PCB(一個inpcb結構),並把它鏈接到socket結構上:so_pcb成員指向inpcb結構,
inp_socket成員指向socket結構。

3.TCP做同樣的工作,也創建它自己的控制塊(一個inpcb結構),並用指針inp_ppcb和t_inpcb把它鏈接到inpcb上。在兩個

UDP inpcb中,inp_ppcb成員是一個空指針,因爲UDP不負責維護它自己的控制塊。
4.我們顯示的其他四個inpcb結構的成員,從inp_faddr到inp_lport,形成了該端結點的插口對:外部IP地址和端口號,以及
本地IP地址和端口號。
5.UDP和TCP用指針inp_next和inp_prev維護一個所有Internet PCB的雙向鏈表。他們在表頭分配一個全局inpcb(命名爲udb
和tcb),在該結構中使用三個成員:下一個和前一個指針,以及本地端口號。後一個成員中包含了該協議使用的下一個臨時
端口號。
Internet PCB是一個傳輸層數據結構。TCP、UDP和原始IP使用它,但是IP、ICMP或IGMP不用它。

1.inpcb的結構

下圖是inpcb結構的定義。

inp_next和inp_prev爲UDP和TCP的所有PCB形成一個雙向鏈表。另外,每個PCB都有一個指向協議鏈表表頭的指針(inp_head)。
對UDP表上的PCB,inp_head總是指向udb。
inp_faddr、inp_fport、inp_laddr和inp_lport,包含了這個IP端結點的插口對:外部IP地址和端口號,以及本地IP地址和
端口號。PCB中以網絡字節序維護這四個值。
inp_socket是一個指向該PCB的socket結構的指針,inp_ppcb是一個指針,它指向這個PCB的可選運輸層專用控制塊。inp_ppcb
和TCP一起指向對應的tcpcb,而UDP不使用它。
如果ip有一個到外部地址的路由,則它被保存在ipp_route入口處。當收到一個ICMP重定向報文時,將掃描所有Internet PCB,
找到那些外部IP地址和重定向IP地址匹配的PCB,將其inp_route入口標記成無效。當再次將該PCB用於輸出時,迫使IP重新找
一條到該外部地址的新路由。
inp_flag成員存放幾個標誌。下圖顯示了各標誌。

PCB中維護一個IP首部的備份,但它只使用其中的兩個成員,TOS和TTL。TOS被初始化成0,TTL被運輸層初始化。TCP和UDP都
把TTL默認值設爲64。進程可以用IP_TOS或IP_TTL插口選項改變這些默認值,新的記錄在inpcb-inp_ip結構中。以後,TCP和
UDP在發送IP數據報時,卻把該結構用作原型IP首部。
進程也可以用IP_OPTIONS插口選項設置外出數據報的IP選項,函數ip_pcbopts把調用方選項的備份存放在一個mbuf中,
inp_option成員是一個指向該mbuf的指針。每次TCP和UDP調用ip_output函數時,就把一個指向這些IP首部的指針傳給IP,IP
將其插到出去的IP數據報中。類似地,inp_moptions成員是一個指向用戶IP多播選項備份的指針。

2.in_pcballoc和in_pcbdetach函數

在創建插口時,TCP、UDP和原始IP會分配一個InternetPCB。通過發佈PRU_ATTACH請求後,調用in_pcballoc函數實現:函數
調用內核的內存分配器,分配一個inpcb,然後進行初始化,最後被加到協議的雙向鏈表上。
在發佈PRU_DETACH請求後,釋放一個Internet PCB,這是在關閉插口時發生的。通過把PCB從協議的雙向鏈表中移走,該PCB
使用的內存被返回給內核。

3.綁定、連接和分用

1.綁定本地IP地址和端口號。
下圖是進程在調用bind時可以指定的本地IP地址和本地端口號的六種組合。

如果客戶程序試圖綁定一個已經被其他插口使用的本地端口時會發生什麼情況。默認情況下,如果一個端口已經被使用,進程
是不能綁定它的。如果發生這種情況,則返回EADDRINUSE差錯(地址正在被使用)。正在被使用的定義很簡單,就是隻要存在
一個PCB,就把該端口作爲它的本地端口。“正在被使用”的概念是相對於綁定協議的:TCP或UDP,因爲TCP端口號與UDP端口號
無關。
Net/3允許進程指定一下兩個插口選項來改變這個默認行爲。

SO_REUSEADDR:

SO_REUSEADDR允許啓動一個監聽服務器並捆綁其衆所周知端口,即使以前建立的將此端口用做他們的本地端口的連接仍存在。

這通常是重啓監聽服務器時出現,若不設置此選項,則bind時將出錯。

SO_REUSEADDR允許在同一端口上啓動同一服務器的多個實例,只要每個實例捆綁一個不同的本地IP地址即可。對於TCP,我

們根本不可能啓動捆綁相同IP地址和相同端口號的多個服務器。

SO_REUSEADDR允許單個進程捆綁同一端口到多個套接口上,只要每個捆綁指定不同的本地IP地址即可。這一般不用於TCP服

務器。

SO_REUSEADDR允許完全重複的捆綁:當一個IP地址和端口綁定到某個套接口上時,還允許此IP地址和端口捆綁到另一個套接

口上。一般來說,這個特性僅在支持多播的系統上纔有,而且只對UDP套接口而言(TCP不支持多播)。

SO_REUSEPORT:

允許進程重用IP地址和端口號,但是包含第一個在內的各個IP地址和端口號,必須指定這個插口選項。


2.連接一個UDP插口

我們通常把connect系統調用和TCP客戶聯繫起來,但是UDP客戶或UDP服務器也可以可能調用connect,爲插口指定外部IP地址

和外部端口號,這就限制插口必須只與某個特定對方交換UDP數據報。

當連接UDP插口時,會有一個副作用:本地IP地址,如果在調用bind是沒有指定,會自動被connect設置,它被設成由IP選路指定

對方所選擇的本地接口地址。

下圖顯示了UDP插口的三種不同狀態,以及函數爲終止各狀態調用的僞代碼。


前三個狀態叫做已連接的UDP插口,後兩個叫做未連接的UDP插口,兩個沒有連接上的UDP插口的區別在於,第一個具有

一個完全指定的本地地址,而第二個具有一個通配本地IP地址。


3.分用TCP接收和IP數據報

下圖顯示了主機上三個Telnet服務器的狀態。前兩個插口處於LISTEN狀態,等待進入的連接請求。第三個連接到IP地址是

140.252.1.11的主機上的端口1500。第一個監聽插口處理在接口140.252.1.29上到達的連接請求,第二個監聽將處理所有

其他接口。


當TCP收到一個目的端口是23的報文段時,它調用in_pcblookup,搜索它的整個Internet PCB表,找到一個匹配,它由優先

權,因爲它的通配數最少。爲了確定通配匹配數,我們只考慮本地和外部IP地址,不考慮外部端口號。本地端口號必須匹配,

否則我們甚至不考慮PCB。通配數可以是0,1,2。


4.分用UDP接收的IP數據報

UDP數據報的交付比我們剛纔研究的TCP例子要複雜得多,因爲可以把UDP數據報發送到一個廣播或多播地址。Net/3的規則

是:

1)把目的地是廣播IP地址或多播IP地址的到達UDP數據報交付給所有匹配的插口。這裏沒有最好的匹配的概念。

2)把目的地是單播IP地址的到達UDP數據報只交付給一個匹配的插口,就是具有最小匹配數的插口。如果有多個插口具有相同

最小通配匹配數,那麼具體由哪個插口來接收到達數據報依賴與不同的實現。


4.in_pcblookup函數

in_pcblookup函數有幾個作用:

1.當TCP或UDP收到一個IP數據報時,in_pcblookup掃描協議的Internet PCB表,尋找一個匹配的PCB,來接收該數據報。

這是運輸層對收到的數據報的分用。

2.當進程執行bind系統調用,爲某個插口分配一個本地IP地址和本地端口號,協議調用in_pcbbind,驗證請求的本地地址

對沒有被使用。

3.當進程執行bind系統調用,請求給它的插口分配一個臨時端口時,內核選了一個臨時端口,並調用in_pcbbind檢查該端

口是否正在被使用,如果正在被使用,就試下一個端口,以此類推,直到找到一個沒有被使用的端口號。

4.當進程顯式或隱式地執行connect系統調用時,in_pcbbind驗證請求的插口對是唯一的。

在第2,3,4種情況下,in_pcbbind調用in_pcblookup。


5.in_pcbbind函數

in_pcbbind把一個本地地址和端口號綁定到一個插口上。從五個函數中調用它:

1.bind爲某個TCP插口調用(通常綁定到服務器的一個知名端口上)

2.bind爲某個UDP插口調用(綁定到服務器的一個知名端口上,或者綁定到客戶插口的一個臨時端口上)

3.connect爲某個TCP插口調用,如果該插口還沒有綁定到一個非零端口上(對TCP客戶來說,這是一種典型情況)

4.listen爲某個TCP插口調用,如果該插口還沒有綁定到一個非零端口(這很少見,因爲是TCP服務器調用listen,TCP服務器

通常綁定到一個知名端口上,而不是臨時端口)

5.從in_pcbconnect調用,如果本地IP地址和本地端口號被置位。

其中1,2爲顯示綁定,3,4,5爲隱式綁定。


6.in_pcbconnect函數

函數in_pcbconnect爲插口指定IP地址和外部端口號。有四個函數調用它:

1.connect爲某個TCP插口(某個TCP客戶的請求)調用。

2.connect爲某個UDP插口(對UDP客戶是可選的,UDP服務器很少見)調用。

3.當在一個沒有連接上的UDP插口上輸出數據報時從sendto調用。

4.當一個連接請求(一個SYN報文段)到達一個處於LISTEN狀態(對TCP服務器是標準的)的TCP插口時,tcp_input調用。


7.in_pcbdisconnect函數

in_pcbdisconnect把UDP插口斷連。把外部IP地址設成全0,外部端口設成0,就把外部相關內容刪除了。

這是在已經在一個未連接的UDP插口上發送一個數據報後,在一個連接上的UDP插口上調用connect時做的。在第一種情況

下,調用sendto的次序是:UDP調用in_pcbconnect把插口臨時連接到目的地,udp_output發送數據報,然後in_pcbdisconnect

刪除臨時連接。

當關閉插口時,不調用in_pcbdisconnect,因爲in_pcbdetach處理釋放PCB。只有當一個不同的地址或端口號要求重用該PCB

時,才斷連。


8.in_pcbnotify、in_rtchange和in_losing函數

當收到一個ICMP差錯時,調用in_pcbnotify函數,把差錯通知給合適的進程。把差錯通知給合適的進程。通過對所有的PCB

搜索一個協議(TCP或UDP),並把本地和外部IP地址及端口號與ICMP差錯返回的值進行比較,找到合適的進程。例如,當

因爲一些路由器丟掉了某個TCP報文段而收到ICMP源抑制差錯時,TCP必須找到產生該差錯的連接的PCB,放慢在該連接

上的傳輸速度。

下圖顯示了處理ICMP差錯時調用的函數。


當收到一個ICMP報文時,調用icmp_input。icmp的五種報文按照差錯來劃分:

目的主機不可達

參數問題

重定向

源抑制

超時

每個協議都定義了它的控制輸入函數,即protosw機構中pr_ctlinput。對TCP和UDP,他們分別是tcp_ctlinput和udp_cltinput。

因爲收到的ICMP差錯中包含了引起差錯的數據報的IP首部,所有引起該差錯的協議是已知的。這五個ICMP差錯中的四個將引起

對協議的控制輸入函數的調用。

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