衆所周知,通過socket編程,我們能夠實現機器之間的通信.在TCP/IP協議
簇(PF_INET)中,可以建立面向連接的SOCK_STREAM類型的socket,非連接的
SOCK_DGRAM類型的socket.事實上,在所有的網絡程序中,也是這兩種socket用
的最爲廣泛.除此之外,還有一些不常用的socket類型,它們卻是在某些網絡通
信中擔當重要的角色.這裏要講的就是這麼一種socket,稱之爲raw socket.
raw socket的作用主要在三個方面:
1.通過raw socket來接受發向本機的ICMP,IGMP協議包,或者用來發送這些
協議包.
2.接受發向本機的但TCP/IP棧不能夠處理的IP包.
3.用來發送一些自己制定源地址特殊作用的IP包(自己寫IP頭,TCP頭等等)
我們知道,平時我們想看一看網絡是否通達,就用ping命令測試一些.ping
命令用的是ICMP協議.因此,我們不能夠通過建立一個SOCK_STREAM或SOCK_DGRAM
來發送這個包,只能夠自己親自來構建ICMP包來發送.這是一種情況.另一種情況
是:現在許多操作系統在實現網絡部分的時候,通常只實現了常用的幾種協議,
如tcp,udp,icmp等,但象其它的如ospf,ggp等協議,操作系統往往沒有實現,如果
自己有必要編寫位於其上的應用,就必須藉助raw socket來實現,這是因爲操作
系統遇到自己不能夠處理的數據包(ip頭中的protocol所指定的上層協議不能處
理).就將這個包交給raw socket.而最後一種使用raw socket的目的主要是用來
構建一些特殊的協議頭,比如我們想對某臺機器進行denial of service類型的
攻擊,但是有不想留下痕跡,讓別人知道IP包的來源,這時候就可以使用raw
socket來發送這些僞造源地址信息的包,這其實也是這種攻擊所採用的主要技術
手段.當然了,我說的是HACKER行爲,之所以想要處理這些特殊的IP包,通常也是
爲了診斷網絡的目的.
raw socket的建立是通過如下方式的:
sockfd = socket(PF_INET, SOCK_RAW, protocol);
第一個參數就不必講了,第二個參數說明建立的是一個raw socket,第三個
參數倒是需要詳細解說一下.這裏分三種情況:
1.參數protocol用來指明所要接收的協議包,如果是象IPPROTO_TCP(6)這
種非0,非255的協議,則內核碰到ip頭中protocol域和創建socket所使用參
數protocol相同的IP包,就會交給這個rawsocket來處理.因此,一般說來,
要想接收什麼樣的數據包,就應該在參數protocol裏來指定相應的協議.當
內核向此raw socket交付數據包的時候,是包括整個IP頭的,並且已經是重
組好的IP包. 如下:
---------------------------------------------------------------
|ip header|tcp header(or x header)| data |
---------------------------------------------------------------
用recvfrom收到的數據包括一個IP頭,一個相應的協議頭,然後是數據(數
據也可以爲空,就看實際情況了). 但當我們發送IP包的時候,卻不用親自
處理IP包頭,只需要填 充參數protocol所指定的相應的協議頭即可.也就
是說,用sendto的時候,我們提供給它的緩衝區數據是從IP包頭的第一個字
節開始,如下,只需要構造這麼一個緩衝區就可以了.
--------------------------------------------------------------
|tcp header(or udp header or x header)| data |
--------------------------------------------------------------
如果想自己也想親自處理IP頭,則需要IP_HDRINCL的socket選項.如下:
int flag = 1;
setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(int));
這樣,發送時所要提供的緩衝區有成如下形式:
---------------------------------------------------------------
|ip header|tcp header(or x header)| data |
---------------------------------------------------------------
但是,即時是這種情況,在我們發送IP包的時候.也不是填充ip頭的所有字
段,而是應該將ip頭的id(identification)字段設置爲0,表示讓內核來處
理這個字段.同時,內核還幫你完成ip頭的校驗和的計算,並隨後填充check
字段.
2.如果protocol是IPPROTO_RAW(255),這時候,這個socket只能用來發送
IP包,而不能接收任何數據.發送的數據需要自己填充IP包頭,並且自己計
算校驗和.
3.對於protocol爲0(IPPROTO_IP)的raw socket. 在linux和sco unix上
是不允許建立的.我沒有再試過其他操作系統,誰能給我一個答案:)
對於raw socket,只有root權限才能夠創建.
這裏對raw socket總結一下: 當內核接收到IP包的時候,首先檢查ip包的
protocol域,當存在與此域匹配的raw socket時,就將包先傳給此raw socket,
然後交給相應的上層協議處理.交給raw socket的數據是包括IP頭並且已經重
組完成後的.當使用raw socket發送包的時候,如果raw socket創建時的protocol
不是0或255,並且沒有設置IP_HDRINCL選項,則發送的數據不包括IP頭.如果此
選項置位,則需要自己構件IP頭.如果創建protocol爲255的raw socket,此raw
socket只能用來發送包括IP頭,自己構建IP包.
附錄:
IP頭結構:
struct iphdr
|-------|--------|---------------|-------------------------------|
|version| ihl | tos | tot_len |
|-------|--------|---------------|-------------------------------|
| id | | frag_off |
|----------------|---------------|-------------------------------|
| ttl | protocol | check |
|----------------|---------------|-------------------------------|
| saddr |
|----------------------------------------------------------------|
| daddr |
|----------------------------------------------------------------|