TCP/IP协议族校验和的计算方法相同, 差别在于进行校验的数据.
一 校验和计算方法
1. 将数据流按16bit求和, 存放在32Bit及更大的空间(eg. u_int32_t cs).
2. 如果数据流为单字节(留8bit数据), 将之前求和的值与最后8bit求和.
3. 如cs高16bit不为0, 则将高16bit与低16bit相加, 直到高16bit为0.
4. 将低16bit的值取反, 得到校验和.
1. 将数据流按16bit求和, 存放在32Bit及更大的空间(eg. u_int32_t cs).
2. 如果数据流为单字节(留8bit数据), 将之前求和的值与最后8bit求和.
3. 如cs高16bit不为0, 则将高16bit与低16bit相加, 直到高16bit为0.
4. 将低16bit的值取反, 得到校验和.
这种校验和的计算方法有两个特点:
1 把数据流按16bit化分为若干u_int16_t的字段. 计算结果并不依赖u_int16_t的顺序.
2 在检验和计算中加上全0值对结果没有影响.
算法如下:
1 把数据流按16bit化分为若干u_int16_t的字段. 计算结果并不依赖u_int16_t的顺序.
2 在检验和计算中加上全0值对结果没有影响.
算法如下:
- u_int16_t check_sum(u_int16_t *buffer, int size)
- {
- //建议将变量放入寄存器, 提高处理效率.
- register int len = size;
- register u_int16_t *p = buffer;
- register u_int32_t cs = 0;
- //16bit求和
- while( len >= 2)
- {
- cs += *(p++);
- len -= 2;
- }
- //最后的单字节直接求和
- if( len == 1)
- cs += *((u_int8_t *)buffer);
- //高16bit与低16bit求和, 直到高16bit为0
- while( (cs&0xffff0000) != 0)
- cs = (cs>>16) + (cs&0xffff);
- //取反
- return (u_int16_t)(~cs);
- }
二 TCP/UDP的校验和计算方法
TCP/UDP的校验和计算方法相同, 都包括了以下数据的计算: 1) 12字节的伪首部(包含了IP层信息). 2) TCP/UDP首部. 3) TCP/UDP数据.
我们用IP首部/UDP首部为例(1 示例不包含IP选项. 2 引用的图来自于《TCP/IP详解-卷二》)
1. 首先看在缓冲区内的UDP/IP数据, 灰色部份IP填充, 白色UDP填充
2. 伪首部的数据
考虑到校验和算法的两个特点 顺序无关/全0无关, 我们可以认为: 缓冲区内, 8字节以后的数据与伪部首完全一至, 可以直接进行校验和计算.
注: 1 TCP数据校验方法和UDP完全相同.
2 也可以另外建立一个缓冲区, 将数据拷贝过去进行计算, 但效率太低.
以下是上一篇文章《网络协议及应用之一:socket构造数据包及DOS(SYNflood)攻击》中应用到的代码及详细解释.
- {
- //ip首部中的ttl. 在伪首部计算时, 要先设置为0
- ip->ip_ttl = 0;
-
- //ip首部的校验和字段, 作为伪首部时, 存放tcp长度
- ip->ip_sum = htons(sizeof(struct tcphdr));
- //因为是攻击包, 源地址随机产生
- ip->ip_src.s_addr = random();
- //校验前, 将校验和字段设置为0
- tcp->check = 0;
-
- //计算tcp校验和时, 避开ip首部的8个字节. 并设置需要校验的长度
- tcp->check = check_sum((u_int16_t *)buffer + 4, sizeof(buffer) - 8);
-
- //恢复ip的ttl
- ip->ip_ttl = MAXTTL;
-
- sendto( sockfd, buffer, len, 0, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in));
- }
三 计算结果
1 校验和错误时, 没有服务器的SYN+ACK包
3 正确的tcp校验和结果