使用Python計算IP、TCP、UDP校驗和


1.1 ip校驗和的計算

Ip校驗是針對ip頭部的,即僅校驗ip頭部,而對於ip數據部分的校驗,則交由相應的四次協議來保證, ip 頭部中校驗和字段為16bit。

 

計算原理如下:

1.把校驗和字段設置為0

2.計算ip頭部中所有16bit的字之和

3.將2中得到的和按位取反,得到校驗和。

1.2 tcp校驗和的計算原理

   對於ip層協議來說,其校驗和只要計算ip頭即可,那相對的,對於四層協議來說,其校驗和則需要計算四層頭部與四層數據。

tcp校驗需要將ip偽首部、tcp報頭、tcp數據分為16位的字,然后進行累加(如果總長度為奇數個字節,則在最后增添一個位都為0的字節 ),最后對累加的和進行按位取反即可。

Ip偽首部包括源ip地址(4字節)、目的ip地址(4字節)、協議號(兩字節)、tcp包長(2字節) ,共12字節。

 

1.3 udp校驗和的計算原理

Udp校驗與tcp校驗基本上是一致的。

udp校驗需要將ip偽首部、udp報頭、udp數據分為16位的字,然后進行累加(如果總長度為奇數個字節,則在最后增添一個位都為0的字節 ),最后對累加的和進行按位取反即可。

Ip偽首部包括源ip地址(4字節)、目的ip地址(4字節)、協議號(兩字節)、tcp包長(2字節) ,共12字節。

 

以上就是ip、tcp、udp的校驗和的計算原理,如果是我們自己寫相應的校驗和函數,問題也不是太大,只要根據其計算原理即可。


UDP的校驗和需要計算UDP首部加數據荷載部分,但也需要加上UDP偽首部。
這個偽首部指,源地址、目的地址、UDP數據長度、協議類型(0x11),協議類型就一個字節,但需要補一個字節的0x0,構成12個字節。
偽首部+UDP首部+數據 一起計算校驗和。

UDP檢驗和的計算方法是:
1.按每16位求和得出一個32位的數;
2.如果這個32位的數,高16位不為0,則高16位加低16位再得到一個32位的數;
3.重復第2步直到高16位為0,將低16位取反,得到校驗和。


upd檢驗和的計算類似於ip首部檢驗和,udp檢驗和是一個端到端的檢驗和,由發送端計算,然后由接收端驗證。如果接收端檢測到檢驗和有差 錯,udp數據報就要被丟棄。 
下面我以本機接收到的一個udp數據包為例,來分析一下udp檢驗和的計算。 
0000: 00  e0 4c a9 28 92 00 e0 0f 7d 1e ba 08 00 45 00 
0010: 00  54 00 00 40 00 37 11 6c ea db 85 28 25 c0 a8 
0020: 12  5c 1f 40 0f a0 00 40 71 2a 02 0f  19 00 02 2e 
0030: eb 3f  34 21 51 c4 b6 cb 12 05 a3 24 b4 11 a8 d3 
0040: 93 d2 cf ac 48 b7 98 d4 83 03 1a ae 14 11 ed 24 
0050: bf c8 6f db 9a fa d8 c6 2a ca 21 08 a2 bd 25 8b 
0060: 96 03 
00 e0 4c a9 28 92 00 e0 0f 7d 1e ba 08 00----以太網幀首部(14字節) 
45 00 00 54 00 00 40 00 37 11 6c ea db 85 28 25 c0 a8 12 5c----ip首部(20字節) 
1f 40 0f a0 00 40 71 2a----udp首部(8字節);其中1f 40表示16位源端口號8000;0f a0表示16位目的端口號4000;00 40表示upd長度為64 字節;71 2a表示udp檢驗和(這是發送端計算出來的,一會兒在后面計算時還要使用到)。 
02 0f ………………96 03-----其余部分為udp數據部分。 
下面讓我們一起來計算一下檢驗和。 

udp數據報和tcp報文段在為了計算檢驗和而設置了一個12字節長的偽首部。由於udp數據報的長度可以為奇數字節,但是檢驗和算法是把若 干個16 bit字相加,因此可在填充字節字段填入0,可能增加的填充字節不被傳送。 

因此必須構造12字節的偽首部:db 85 28 25 c0 a8 12 5c 00 11 00 40 
構造完畢之后,把udp檢驗和部分置0,然后對udp每個16 bit進行二進制反碼求和,db85+2825+c0a8+125c+0011+0040 + 1f40+0fa0+0040+0000 +……+258b+9603=e8ec7 
(0xe8ec7 >> 16) + (0xe8ec7 & 0xffff)=0x8ed5 
最后,0x8ed5+0x712a=0xffff 
由此可以判斷上述udp數據報在傳輸過程中沒有發生差錯。


把十六進制的字串轉為十進制數字:
Python代碼
>>> print int('ff', 16)   
255  


 1 #原始IP報文按照字節拆分成10進制list
 2 IP_content = [69, 0, 1, 35, 127, 30, 0, 0, 64, 17, 213, 133, 10, 8, 136, 23, 10, 8, 136, 255, 214, 131, 214, 131, 1, 15, 95, 98, 0, 115, 104, 121, 121, 121, 102, 45, 103, 117, 116, 105, 110, 103, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 95, 39, 2, 0, 0, 0, 0, 48, 180, 159, 6, 0, 0, 0, 0, 51, 39, 0, 0, 0, 0, 0, 0, 16, 95, 39, 2, 0, 0, 0, 0, 192, 4, 110, 5, 0, 0, 0, 0, 124, 106, 122, 112, 0, 0, 0, 0, 152, 163, 218, 111, 0, 0, 0, 0, 89, 184, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 151, 218, 4, 0, 0, 0, 0, 164, 180, 159, 6, 0, 0, 0, 0, 192, 180, 159, 6, 0, 0, 0, 0, 168, 217, 122, 123, 97, 99, 54, 53, 100, 102, 100, 98, 45, 54, 50, 55, 52, 45, 52, 101, 101, 52, 45, 98, 99, 100, 100, 45, 52, 53, 98, 54, 97, 98, 99, 99, 55, 49, 54, 57, 125, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 160, 180, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 165, 138, 243]
 3 
 4 #原始UDP報文部分按照10進制組成list
 5 [214, 131, 214, 131, 1, 15, 95, 98, 0, 115, 104, 121, 121, 121, 102, 45, 103, 117, 116, 105, 110, 103, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 95, 39, 2, 0, 0, 0, 0, 48, 180, 159, 6, 0, 0, 0, 0, 51, 39, 0, 0, 0, 0, 0, 0, 16, 95, 39, 2, 0, 0, 0, 0, 192, 4, 110, 5, 0, 0, 0, 0, 124, 106, 122, 112, 0, 0, 0, 0, 152, 163, 218, 111, 0, 0, 0, 0, 89, 184, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 151, 218, 4, 0, 0, 0, 0, 164, 180, 159, 6, 0, 0, 0, 0, 192, 180, 159, 6, 0, 0, 0, 0, 168, 217, 122, 123, 97, 99, 54, 53, 100, 102, 100, 98, 45, 54, 50, 55, 52, 45, 52, 101, 101, 52, 45, 98, 99, 100, 100, 45, 52, 53, 98, 54, 97, 98, 99, 99, 55, 49, 54, 57, 125, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 160, 180, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 165, 138, 243]
 6 
 7 #需要組裝計算UDP校驗和部分
 8 udp_content = []
 9     
10 #IP源地址為IP報文的13、14、15、16字節
11 udp_content.append(IP_content[12])
12 udp_content.append(IP_content[13])
13 udp_content.append(IP_content[14])
14 udp_content.append(IP_content[15])
15 
16 #IP目的地址為IP報文的17、18、19、20字節
17 udp_content.append(IP_content[16])
18 udp_content.append(IP_content[17])
19 udp_content.append(IP_content[18])
20 udp_content.append(IP_content[19])
21 
22 #UDP數據長度是UDP報文中的第5、6字節
23 udp_content.append(IP_content[IHL + 4])
24 udp_content.append(IP_content[IHL + 5])
25 
26 #協議類型是IP報文的第10字節
27 udp_content.append(0x00)
28 udp_content.append(IP_content[9])
29 
30 for i in range(UDP_Len):
31     udp_content.append(IP_content[IHL + i])
32 
33 udp_content[18] = 0    #把原來的校驗和設置為0
34 udp_content[19] = 0
35 
36 if UDP_Len % 2 == 1: #整個報文長度為奇數需要補充0
37     udp_content.append(0x00)
38 
39 print('需要計算的UDP校驗和內容為:' + str(udp_content))
40 manual_check = calc_checksum(udp_content)
41 print(manual_check)
42 
43 #組裝后的結果為,按照10進制顯示
44 udp_content = [10, 8, 136, 23, 10, 8, 136, 255, 1, 15, 0, 17, 214,131, 214, 131, 1, 15, 0, 0, 0, 115, 104, 121, 121, 121, 102, 45, 103, 117, 116,105, 110, 103, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 95, 39, 2, 0, 0, 0, 0, 48, 180, 159, 6, 0, 0, 0, 0, 51, 39, 0, 0, 0, 0, 0, 0, 16, 95, 39, 2, 0, 0, 0, 0, 192, 4, 110, 5, 0, 0, 0, 0, 124, 106, 122, 112, 0, 0, 0, 0,152, 163, 218, 111, 0, 0, 0, 0, 89, 184, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 151, 218, 4, 0, 0, 0, 0, 164, 180, 159, 6, 0, 0, 0, 0, 192, 180, 159,6, 0, 0, 0, 0, 168, 217, 122, 123, 97, 99, 54, 53, 100, 102, 100, 98, 45, 54, 50, 55, 52, 45, 52, 101, 101, 52, 45, 98, 99, 100, 100, 45, 52, 53, 98, 54, 97, 98, 99, 99, 55, 49, 54, 57, 125, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 160, 180, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 165, 138, 243, 0]
45 
46 def calc_checksum(sum_data):
47     join_sum_data = []
48     for i in range(0, len(sum_data), 2):    #先需要將前后二個數合並成16位長度的16進制的數
49         first_part = str(hex(sum_data[i]))[2:]    #10進制轉換為16進制,長度為8位
50         if len(first_part) < 2:    #如果轉換為16進制后只有1位需要高位補0操作
51             first_part = '0' + first_part
52         
53         second_part = str(hex(sum_data[i + 1]))[2:]    #10進制轉換為16進制,長度為8位
54         if len(second_part) < 2:    #如果轉換為16進制后只有1位需要高位補0操作
55             second_part = '0' + second_part
56         
57         total_part = first_part + second_part    #合並成16位長度
58         
59         join_sum_data.append(int(total_part, 16))    #重新把16進制轉換為10進制
60         #join_sum_data.append(total_part)
61     
62     #print(join_sum_data)
63     
64     sum_result = 0
65     for single_value in join_sum_data:
66         sum_result = sum_result + single_value    #計算所有數的和
67         
68     hex_sum_result = str(hex(sum_result))[2:]    #轉變為4字節32位的十六進制格式
69     
70     len_hex_sum = len(hex_sum_result)    #取得字節數
71     
72     if len_hex_sum > 4:    #求和的結果大於2個字節16位的話,分割成二個2字節16位數
73         first_part = int(hex_sum_result[:len_hex_sum - 4], 16)    #分割第一、二字節的十六進制數字,轉換為10進制
74         
75         second_part = int(hex_sum_result[len_hex_sum - 4:], 16)    #分割第三、四字節的十六進制數字,轉換為10進制
76         
77         last_check_sum = str(hex(0xffff - (first_part + second_part)))[2:]    #二個字節的十六進制數之和取反
78         return last_check_sum
79     else:
80         last_check_sum = str(hex(65535 - sum_result))[2:]    #只有二個字節的十六進制數直接取反就好了
81         return last_check_sum

 


免責聲明!

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



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