ipv4、tcp、udp數據報頭的格式以及校驗和算法


一、以下各個頭文件所在的位置為

Ubuntu下目錄/usr/include/linux/

Fedora下目錄 /usr/src/kernels/2.6.35.6-45.fc14.i686/include/linux/

二、ip頭部的結構體定義如下

 1 #include <linux/ip.h>
 2 struct iphdr {
 3 #if defined(__LITTLE_ENDIAN_BITFIELD)
 4     __u8    ihl:4,
 5         version:4;
 6 #elif defined (__BIG_ENDIAN_BITFIELD)
 7     __u8    version:4,
 8         ihl:4;
 9 #else
10 #error  "Please fix <asm/byteorder.h>"
11 #endif
12     __u8    tos;
13     __be16  tot_len;
14     __be16  id;
15     __be16  frag_off;
16     __u8    ttl;
17     __u8    protocol;
18     __sum16 check;
19     __be32  saddr;
20     __be32  daddr;
21     /*The options start here. */
22 };
結構體成員對應的結構如下圖所示。

下面主要說明幾個字段:

iphdr->ihl:首部長度(4bit),首部長度指的是IP頭部占32 bit(4字節)字的數目(也就是IP頭部包含多少個4字節),包括任何選項。由於它是一個4比特字段, 因此首部最長為60個字節。

iphdr->check:IP首部檢驗和字段(16位,2字節),只計算IP頭部的的所有字段的校驗和,它不對首部后面的數據進行計算。 

發送方:計算一份數據報的IP頭部檢驗和,則需要首先把此檢驗和字段置為0。然后對首部中每個16 bit(2字節)進行二進制反碼求和(整個首部看成是由一串16 bit的字組成),然后結果存在此檢驗和字段中。

接受方:當收到一份IP數據報后,對首部中每個16 bit(2字節)進行二進制反碼的求和。由於接收方在計算過程中包含了發送方存在首部中的檢驗和,因此,如果首部在傳輸過程中沒有發生任何差錯,那么接收方計算的結果應該為全1。如果結果不是全1(即檢驗和錯誤),那么IP就丟棄收到的數據報。但是不生成差錯報文,由上層去發現丟失的數據報並進行重傳。

iphdr->tot_len:總長度字段(16位)是指整個IP數據報的長度(包含后面的協議頭和數據),以字節為單位。

利用首部長度字段和總長度字段,就可以知道 IP數據報中數據內容的起始位置和長度。由於該字段長16比特,所以IP數據報最長可達65535字節,總長度字段是IP首部中必要的內容,因為一些數據鏈路(如以太網)需要填充一些數據以達到最小長度。盡管以太網的最小幀長為46字節,但是IP數據可能會更短。如果沒有總長度字段,那么IP層就不知道46字節中有多少是IP數據報的內容。

三、tcp頭部的結構體定義如下

 1 #include <linux/tcp.h>
 2 struct tcphdr {
 3     __be16  source;
 4     __be16  dest;
 5     __be32  seq;
 6     __be32  ack_seq;
 7 #if defined(__LITTLE_ENDIAN_BITFIELD)
 8     __u16   res1:4, doff:4, fin:1, syn:1, rst:1,
 9             psh:1,  ack:1,  urg:1, ece:1, cwr:1;
10 #elif defined(__BIG_ENDIAN_BITFIELD)
11     __u16   doff:4, res1:4, cwr:1, ece:1, urg:1,
12             ack:1,  psh:1,  rst:1, syn:1, fin:1;
13 #else
14 #error  "Adjust your <asm/byteorder.h> defines"
15 #endif  
16     __be16  window;
17     __sum16 check;
18     __be16  urg_ptr;
19 };

結構體成員對應的結構如下圖所示。

tcphdr->doff:TCP頭部長度,指明了在TCP頭部包含多少個32位的字。

此信息是必須的,因為options域的長度是可變的,所以整個TCP頭部的長度也是變化的。從技術上講,這個域實際上指明了數據部分在段內部的其起始地址(以32位字作為單位進行計量),因為這個數值正好是按字為單位的TCP頭部的長度,所以,二者的效果是等同的。

tcphdr->check:檢驗和,覆蓋了整個的TCP報文段(包含后面的數據),這是一個強制性的字段,一定是由發送端計算和存儲,並由接收端進行驗證。

四、udp頭部的結構體定義如下

struct udphdr {        
    __be16  source;
    __be16  dest;
    __be16  len;
    __sum16 check;
};

udp協議很簡單,其數據報由udp首部和用戶數據兩部分組成,其中首部只有8字節。

1、源端口號(Source Port):長度為16位(2字節),指明發送數據的進程的端口號。

2、目的端口號(Destination Port):長度為16位(2字節),指明目的主機接收數據的進程的端口號。

3、長度(Data Length):長度為16位(2字節),該字段值為udp報頭和數據兩部分的總字節數。

4、檢驗和(Checksum):長度為16位(2字節),udp檢驗和是udp報頭和udp數據的所有數據的檢驗和。對報文中每個16 bit(2字節)進行二進制反碼的求和。由發送端計算和存儲,由接收端校驗。

5、數據

五、Ip頭和tcp頭udp頭的數據校驗和的算法函數

 1 char setIpCheck(struct iphdr* iphdrp)
 2 {
 3     iphdrp->check = 0;
 4     iphdrp->check = htons(checksum(0, (u_int8_t *)iphdrp, iphdrp->ihl << 2));
 5     return iphdrp->check == 0 ? -1 : 0;
 6 }
 7 
 8 char setTcpCheck(struct iphdr* iphdrp)
 9 {
10     struct tcphdr *tcphdrp = (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));
11     tcphdrp->check = 0;
12     
13     size_t tcplen = ntohs(iphdrp->tot_len) - (iphdrp->ihl<<2); 
14     u_int32_t cksum = 0;     
15     cksum += ntohs((iphdrp->saddr >> 16) & 0x0000ffff);   
16     cksum += ntohs(iphdrp->saddr & 0x0000ffff);   
17     cksum += ntohs((iphdrp->daddr >> 16) & 0x0000ffff);
18     cksum += ntohs(iphdrp->daddr & 0x0000ffff);
19     cksum += iphdrp->protocol & 0x00ff;
20     cksum += tcplen; 
21     tcphdrp->check = htons(checksum(cksum, (u_int8_t*)tcphdrp, tcplen));
22 
23     return tcphdrp->check == 0 ? -1 : 0;
24 }
25 
26 char setUdpCheck(struct iphdr* iphdrp)
27 {
28     struct udphdr *udphdrp = (struct udphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));
29     udphdrp->check = 0;
30 
31     size_t udplen = ntohs(iphdrp->tot_len) - (iphdrp->ihl<<2); 
32     u_int32_t cksum = 0;     
33     cksum += ntohs((iphdrp->saddr >> 16) & 0x0000ffff);   
34     cksum += ntohs(iphdrp->saddr & 0x0000ffff);   
35     cksum += ntohs((iphdrp->daddr >> 16) & 0x0000ffff);
36     cksum += ntohs(iphdrp->daddr & 0x0000ffff);
37     cksum += iphdrp->protocol & 0x00ff;
38     cksum += udplen; 
39     udphdrp->check = htons(checksum(cksum, (u_int8_t*)udphdrp, udplen));
40 
41     return udphdrp->check == 0 ? -1 : 0;
42 }
43 
44 static u_int16_t checksum(u_int32_t init, u_int8_t *addr, size_t count)
45 {
46     /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ 
47     u_int32_t sum = init; 
48     while( count > 1 )
49     { 
50         /* This is the inner loop */ 
51         sum += ntohs(* (u_int16_t*) addr);     
52         addr += 2;     
53         count -= 2; 
54     }
55     /* Add left-over byte, if any */ 
56     if( count > 0 )      
57         sum += ntohs(( *(u_int8_t *) addr ));
58     /* Fold 32-bit sum to 16 bits */ 
59     while (sum>>16)    
60         sum = (sum & 0xffff) + (sum >> 16); 
61     return (u_int16_t)~sum;
62 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM