【摘 要】 信息时代,庞大的数据集合中,信息交互的出错在所难免。对于差错处理,网际校验和算法的应用具有里程碑式的意义。算法利用网络数据使用二进制代码的特点,通过对信息串进行反码相关运算,“放大”通信两端数据出现的差异,较大程度地减少了出错概率。这一算法的思想如今已经被应用在IP、ICMP、 UDP和TCP等诸多报文的检错运算中。
本文将对网际校验的基本思想和使用情况做出简要论述,并使用python编制一个具有识别报文类型和验算校验和功能的代码示例。
技术上,在抓取报文时使用了网络抓包软件WireShark;识别报文类型时,依赖了python强大的字符串处理能力;验算校验和算法以二进制反码运算为基础。
【关键词】 网际校验和 IP UDP TCP ICMP 二进制 反码运算 互联网
一、网际校验和算法
(一)算法概述
一般的网际校验和算法过程分为两步。
第一步是,对报文分段进行二进制反码加法运算。对于需要检测的报文部分,在发送方,先将被检测报文的对应校验和字段置为全0,继而把该部分内容以16位为一个单位分组,再将这些分组分别进行二进制反码加法运算,将得到的结果存入到对应校验和字段,覆盖掉全0。发送方将这样的报文发送出去。
第二步,在接收方,将收到的报文部分以和第一步中同样方式分段进行二进制反码的加法运算。这个时候,得到结果要么是全0要么是全1。为什么呢?
我们以IP数据报为例,假设IP数据报首部只有32位也就是4个字节,经16位的划分,可分为两个16位的字序列,我们暂称其为A和B。(此时未填入数据的IP数据报首部校验和字段的值为0)则根据上述算法,此时首部校验和则为 A+B+0 = A+B,结果再求反。将A+B记为C,那则首部校验和就是 非C 。当报文传送到接收方,接收方再按照16位子序列去划分报文首部,此时首部校验和字段是 非C,则需要计算的是A+B+ 非C = A+B+ 非(A+B) = 1(数字逻辑运算)。也就是说,只要IP数据报首部未变,到此运算结果必为1。(若再求反,则结果为就为0。)
(二)不同协议的报文运算区别
网际校验和算法在不同的协议实现的时候,是有使用差别的。
主要分为两方面:1.检验字段范围;2.正确的验证结果。
对于需要检验的字段范围,IP和ICMP报文不需要附加额外的字段,其中IP只需要检验IP数据报的首部字段,ICMP则需要同时检测首部和数据部分;TCP报文段和UDP用户数据报检验范围都是首部加上数据部分,但是二者在进行校验和运算的时候,还需要在报文的首部前加上额外的12字节的被称为“伪首部”的字段,作为参与运算的一部分。但是伪首部不作为信息向上递交。
对于正确的验证结果,其中IP和ICMP对最终的正确结果要求是全0,TCP和UDP对于最终正确结果的要求是全1。其实计算的原理都是一样的,正常的经过一轮网际校验和的运算后结果为全1,不过在TCP和ICMP的检验过程中,会将结果再次求反,于是获得全0。
二、算法实现
(一)获取报文
使用抓包工具WireShark,在上网时获取到相应的MAC帧数据,并以此为基础向内解析出所含的IP数据报、TCP报文段或者UDP报文段。。
限于篇幅,测试数据在此不详细列出,请感兴趣的读者自行尝试抓包。
(二)区分报文的方法
报文的格式与包含关系请读者先了解有关资料,这里限于篇幅不再展示。
在报文都是2进制表示的情况下,若想从MAC帧中提取完整的IP数据报,取得MAC帧中(从0开始的)第14×8位往后的数据即可。在IP中,首先判断第72~79(“协议”字段)位的数据,若是6,则表示TCP,若是17,则表示UDP,否则表示ICMP。
对于首部检验和字段,IP在第80~95位,ICMP在第16~31位置,TCP在第128~143位(不含伪首部),UDP在第48~63位(不含伪首部)。
(三)算法思路
1.获取用户按指定格式输入的报文信息;
2.根据1.2中的区分报文原则,将IP数据报整体划分出来;
3.对IP首部进行校验和计算,有误则提示错误,无误则继续提取协议字段,判断其值;
1)若为6,则是TCP,
提取TCP报文段整体,进行校验和计算,有误则提示错误,无误则完成校验;
2)若为17,则为UDP,
提取UDP报文段整体,进行校验和计算,有误则提示错误,无误则完成校验;
3)不是6或17,则为ICMP,
提取ICMP报文段整体,进行校验和计算,有误则提示错误,无误则完成校验。
使用Python语言完成了本次编码。
(四)关键代码示例
1.报文分组
"""
对指定的字符串进行4位一组分组,每组二进制加法运算
"""
def get_grouped_sum(s):
s2 = s
length = len(s2)
counter = int(length / 16)
ls = []
for i in range(0, counter):
ls.append(bin(int(s2[0: 16], 2))[2:])
s2 = s2[16:]
return get_bin_sum(ls)
2.对分组进行二进制反码加法
'''
取得反码求和运算之结果
'''
def get_bin_sum(ls):
bin_sum = 0
for v in ls:
bin_sum = bin_sum + int(v, 2)
if len((bin(bin_sum))[2:]) > 16:
result = bin(bin_sum)[3:]
bin_sum = int(result, 2) + 1
return get_inverse(bin(bin_sum)[2:])
3.對结果进行求反
'''
在字符串中将原码“笨拙”地替换为反码
'''
def get_inverse(s):
inverse = ''
d = {'0': '1', '1': '0'}
for e in s:
inverse += d[e]
return bin(int(inverse, 2))[2:].zfill(16)
【参考文献】
[1] 施展. 新型互联网传输协议的差错控制设计与协议一致性测试[D]. 北京:北京交通大学,2018
[2] 谢希仁. 计算机网络(第七版)[M].北京:电子工业出版社,2017:96,128,209-210,216-217
[3] Zed A. Shaw. “笨方法”学Python3 [M].北京:中国邮电出版社,2018:98
作者简介:刘杨(1999——)男,汉族,河南信阳人,单位:河南大学计算机与信息工程学院,本科,软件工程专业,软件工程: