應用進程在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系統調用生成。