《TCP/IP詳解卷2:實現》筆記--原始IP

應用進程在Internet域中創建一個SOCK_RAW類型的插口,就可以利用原始IP層。一般有下列3種用法:

1.應用進程可利用原始插口發送和接收ICMP和IGMP報文。

有些選路守護程序,利用這一特徵跟蹤通常由內核處理的ICMP重定向報文段。

這個特徵還用於實現基於ICMP的協議,如路由通告和路由請求,他們需用到ICMP,不過最好由應用進程,而不是內核完成

相應處理。

多播路由守護程序利用原始IGMP插口,發送和接收IGMP報文。

2.應用進程可利用原始插口構造自己的IP首部。

3.應用進程可利用原始插口讀寫內核不支持的IP協議的IP數據報。


1.原始IP的protosw結構

與所有其他協議不同,inetsw數組有多條記錄都可以讀寫原始IP。inetsw結構中有4個記錄的插口類型都等於SOCK_RAW,

但協議類型則各不相同。

IPPROTO_ICMP(協議值1)

IPPROTO_IGMP(協議值2)

IPPROTO_RAW(協議值255)

原始IP通配記錄(協議值0)

四項記錄間的區別總結如下:

如果應用進程創建了一個原始插口(SOCK_RAW),協議值非0,並且如果協議值等於IPPROTO_ICMP、IPPROTO__GMP

或IPPROTO_RAW,則會使用對應的protosw記錄。

如果應用進程創建了一個原始插口(SOCK_RAW),協議值非0,但內核不支持該協議,pffindproto會返回協議值爲0的通配

記錄,從而允許應用進程處理內核不支持的IP協議,無需修改內核代碼。

下圖爲原始IP的protosw結構:

上圖中並沒有說明其他協議(ICMP和IGMP),在它們自己的protosw結構中也用到了一些原始IP函數,下圖對4個SOCK_RAW

協議各自protosw結構的相關成員變量做了一個比較。


2.rip_init函數

系統初始化時,domaininit函數調用原始IP初始化函數rip_init。這個函數的唯一操作是令PCB首部中的前向和後向指針都

指向自己,實現一個空的雙向鏈表。


3.rip_input函數

因爲ip_protox數組中保存的所有關於未知協議記錄都指向IPPROTO_RAW,且後者的pr_input函數指向rip_input,所以只要

某個接收IP數據報的協議號內核無法識別,就會調用此函數,ICMP和IGMP都可能調用rip_input,只要滿足下列條件:

1.icmp_input調用rip_input處理所有未知的ICMP報文類型和所有非響應的ICMP報文。

2.igmp_input調用rip_input處理所有IGMP分組。

上述兩種情況下,調用rip_input的一個原因是允許創建了原始插口的應用進程處理新增的ICMP和IGMP報文,內核可能不

支持它們。

rip_input函數的大概處理流程如下:

1.在所有原始IP PCB中尋找一個或多個匹配的記錄。

2.協議比較。如果PCB中的協議字段非0,並且與IP首部的協議字段不匹配,則PCB被忽略。

3.比較本地和遠端IP。如果PCB中的本地地址非0,並且與IP首部的目的IP地址不匹配,則PCB被忽略。如果PCB中的遠端

地址非0,並且與IP首部的源IP地址不匹配,PCB被忽略。

4.提交接收數據報的複製報文以備處理。

5.無法上交的數據報被釋放mbuf。


4.rip_output函數

ICMP、IGMP和原始IP都調用rip_output實現原始IP輸出。應用進程調用5個寫函數之一:send、sendto、sendmsg、wirte

和writev,系統將輸出報文段。如果插口已建立連接就可以任何調用上述5個函數,儘管sendto和sendmsg中不能規定目的

地址,如果插口沒有建立連接,則只能調用sendto和sendmsg,且必須規定目的地址。

rip_output函數的大概處理流程如下:

1.內核填充IP首部。

2.調用者填充IP首部:IP_HDRINCR插口選項。


5.rip_usrreq函數

協議的用戶請求處理函數能夠完成多種操作。與UDP和TCP的用戶請求處理函數類似,rip_usrreq是一個很大的switch語句,

每個PRU_xxx請求,都有一個對應的case子句。下面對每個請求進行說明。

1.PRU_ATTACH請求,來自socket系統調用。

每次socket函數被調用時,都會創建新的socket結構,此時還沒有指向某個Internet PCB。

確認超級用戶,只有超級用戶才能創建原始插口,這是爲了防止普通用戶想網絡發送自己的IP數據報。

創建Internet PCB,保留緩存空間。

2.PRU_DISCONNECT請求。下面兩種情況會發送PRU_DISCONNECT請求。

1)關閉建立連接的原始插口時,在PRU_DETACH之前先發送PRU_DISCONNECT請求。

2)如果對一個已建立連接的原始插口調用connect,soconnect在發送PRU_CONNECT請求前會先發送PRU_DISCONNECT

請求。

如果處理PRU_DISCONNECT請求的插口沒有進入連接狀態,則返回錯誤。否則調用soisdisconnected函數。

3.PRU_ABORT請求。禁止在一個原始接口上發送PRU_ABORT請求。

4.PRU_DETACH請求。close系統調用發送PRU_DISCONNECT請求,如果socket接口用於多播選路,則取消多播選路,

然後調用in_pcbdetach釋放Internet PCB,並從原始IP PCB表中刪除。

5.PRU_BIND請求。通過該請求,可以把原始IP插口綁定到某個本地IP地址上,下面3個條件必須全真,否則返回錯誤。

1)至少配置了一個IP接口。

2)地址族等於AF_INET

3)如果綁定的IP地址不等於0.0.0.0,它必須對應於某個本地接口。

6.PRU_CONNECT請求。應用進程在原始IP插口與某個特定遠端IP地址間建立連接。

7.PRU_CONNECT2請求。原始IP插口不支持該請求。

8.PRU_SHUTDOWN請求。應用進程結束髮送數據後,調用shutdown,生成該請求。該請求調用socantsendmore函數

置位插口標誌,禁止所有輸出。

9.PRU_SEND請求。5個寫函數發送該請求。最終調用rip_output函數向ip_output提交mbuf鏈。

10.PRU_SOCKADDR和PRU_PEERADDR請求分別由getsockname和getpeername系統調用生成。

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