IP实验一:首部校验和算法

2015年11月04日

一、实验目的

通过简单的实验和代码来了解首部校验和算法(适用于IP/ICMP/IGMP/TCP/UDP等协议)

二、IP首部简介

  • IP数据包格式

图片失效

  • 字段说明
    • 版本:目前是4
    • 首部长度:首部长度,包含选项,单位为4 Bytes。因此首部最长为60 Bytes(15 * 4)
    • 服务类型
      • 3 bit:优先权(现已忽略)
      • 4 bit:TOS子字段,最多只能设置其中1 bit为1,全0表示一般服务
        • 1000:最小时延
        • 0100:最大吞吐量
        • 0010:最高可靠性
        • 0001:最小费用
      • 1 bit:未使用,必须为0
    • 总长度:整个IP数据报的长度,单位为1 Byte,因此IP数据报最长为65535 Bytes(2^16-1)
    • TTL(time to live):数据报可以经过的最多路由数,指定了数据报的生存时间。初始值由源主机设置(32 or 64),每经过一个路由器,数值减一,当值为0时,数据报酒会被丢弃,并发送ICMP报文通知源主机
    • 协议:标识哪个协议向IP传送数据
    • 首部校验和:下面将介绍首部校验和的相关运算

三、实验环境

表1 IP实验环境
机器名称 网卡名称 IP地址 硬件平台
CentOS V1 lo 127.0.0.1 X86(小端)

四、实验步骤

  • 在CentOS V1上监听本身的网卡lo上的十六进制包内容(不包括链路层)
1
2
# 命令
tcpdump -nxe -i lo port 23
  • 在CentOS V1对本机(lo)进行telnet操作
1
2
# 命令
telnet 127.0.0.1
  • CentOS V1上监听到eth0上的网络内容如下
1
2
3
4
5
00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 74: 127.0.0.1.53133 > 127.0.0.1.telnet: Flags [S], seq 3979419414, win 43690, options [mss 65495,sackOK,TS val 5423297 ecr 0,nop,wscale 7], length 0
	0x0000:  4510 003c dfd7 4000 4006 5cd2 7f00 0001
	0x0010:  7f00 0001 cf8d 0017 ed31 1f16 0000 0000
	0x0020:  a002 aaaa fe30 0000 0204 ffd7 0402 080a
	0x0030:  0052 c0c1 0000 0000 0103 0307

五、实验结果

  • 对比IP数据报格式,易得:
表2 IP数据报解析
版本 首部长度 服务类型 总长度 标识 标志 片偏移 TTL 协议 首部校验和 源IP地址 目的IP地址
0x4 0x5 0x10 0x003c 0xdfd7 0x2 0x0 0x40 0x06 0x5cd2 0x7f000001 0x7f000001
IPV4 20 Bytes 最小时延 60 Bytes 64 TCP 置为0x0000 127.0.0.1 127.0.0.1

六、相关代码

  • 内核汇编源码:CentOS V1内核源码(内核版本为3.10.0-229.el7.x86_64)关于首部校验和的代码(/arch/x86/include/asm/checksum_64.h)时用汇编代码,不直观。

  • C代码实现:引用网上一段首部校验和的C代码,具体可见http://xfocus.net/tools/200405/synflood.c.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# unsigned long	: 32 bit
# USHORT		: 16 bit
USHORT checksum(USHORT *buffer, int size)
{
	unsigned long cksum = 0;
	while(size >1) {
		cksum += *buffer++;
		size  -= sizeof(USHORT);
	}
	if(size) cksum += *(UCHAR*)buffer;
(1)	cksum =  (cksum >> 16) + ( cksum & 0xffff);
(2)	cksum += (cksum >> 16);
(3)	return (USHORT)(~cksum);
}

七、首部校验和的运算

  • 发送方:计算首部校验和(将首部校验和设置为0x0000)
1
2
3
4
cksum = 0x4510 + 0x003c + 0xdfd7 + 0x4000 + 0x4006 + 0x0000 + 0x7f00 + 0x0001 = 0x2a32b
cksum = 0x2a32b >> 16 + 0x2a32b & 0xffff = 0xa32d
cksum = 0xa32d + 0xa32d >> 16 = 0xa32d
cksum = (0xffffffff - 0xa32d) & 0xffff = 0x5cd2 # 发送端计算得到的校验和
  • 接收方:校验首部校验和(首部校验和为0x5cd2)
1
2
3
4
cksum = 0x4510 + 0x003c + 0xdfd7 + 0x4000 + 0x4006 + 0x5cd2 + 0x7f00 + 0x0001 = 0x2fffd
cksum = 0x2fffd >> 16 + 0x2fffd & 0xffff = 0xffff
cksum = 0xffff + 0xffff >> 16 = 0xffff
cksum = (0xffffffff - 0xffff) & 0xffff = 0x0000 # 全为0 ,校验通过(各个协议栈实现不一) 

八、算法合理性的证明(个人推导,仅供参考)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 假设变量如下:
#	S1:发送方首部和(16 bit为单位,首部校验和预置为0)
#	C1:发送方计算得到的首部校验和
#	S2:发送方首部和(16 bit为单位,易得 S2 = S1 + C1)
#	C2:接收方计算得到的首部校验和
#
# 约定:
#	运算过程采用迭代的方式,即 n = f(n)


# 发送方计算首部校验和
(1
C1	= S1>>16 + S1&0xFFFF

(2)
C1	= (C1>>16) + C1
	= (S1>>16 + S1&0xFFFF) >> 16 + S1>>16 + S1&0xFFFF
	= S1>>32 + 0x0000 + S1>>16 + S1&0xFFFF
	= S1>>16 + S1>>32 + S1&0xFFFF

(3)
C1	= (0xFFFFFFFF - C1) & 0xFFFF
	= (0xFFFFFFFF - S1>>16 - S1>>32 - S1&0xFFFF) & 0xFFFF
	= 0xFFFF - (S1 + S1>>16 + S1>>32) & 0xFFFF


# 接收方校验首部校验和
(1)
S2	= S1 + C1
	= S1 + 0xFFFF - (S1 + S1>>16 + S1>>32) & 0xFFFF
C2	= S2>>16 + S2&0xFFFF
	= [S1 + 0xFFFF - (S1 + S1>>16 + S1>>32) & 0xFFFF] >> 16 + [S1 + 0xFFFF - (S1 + S1>>16 + S1>>32) & 0xFFFF] & 0xFFFF
	= [S1>>16 + 0x0000 - 0x0000] + [S1&0xFFFF + 0xFFFF - (S1 + S1>>16 + S1>>32) & 0xFFFF]
	= S1>>16 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF

(2)
C2	= (C2>>16) + C2
	= [S1>>16 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF] >> 16 + S1>>16 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF
	= [S1>>32 + 0x0000 - 0x00000] + S1>>16 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF
	= S1>>16 + S1>>32 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF

(3)
C2	= (0xFFFFFFFF - C2) & 0xFFFF
	= {0xFFFFFFFF - [S1>>16 + S1>>32 + 0xFFFF - (S1>>16 + S1>>32) & 0xFFFF]} & 0xFFFF
	= [0xFFFF0000 - S1>>16 - S1>>32 + (S1>>16 + S1>>32) & 0xFFFF] & 0xFFFF
	= -(S1>>16 + S1>>32) & 0xFFFF + (S1>>16 + S1>>32) & 0xFFFF
	= 0x0000

# 得证

九、参考内容