算法原理:

假定 output[2] 为输出结果,input[n]为待计算校验和的内存块。

1)所有奇数位[0,2,4……] byte 累加进 结果的奇数位内存 output[0],如果溢出,则进位给偶数位的 output[1];

2)所有偶数位[1,3,5……] byte 累加进 结果的偶数位内存 output[1],如果溢出,则进位给奇数位的 output[0];

3)最后对 output[2] 求反码即可

示例代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import struct
import sys def ip_cksum(s): a = 0
b = 0 # 偶数序号的 unsigned char 互相累加
for i in xrange(0, len(s), 2):
a += struct.unpack('B', s[i])[0] # 奇数序号的 unsigned char 互相累加
for i in xrange(1, len(s), 2):
b += struct.unpack('B', s[i])[0] # 缩小值为 unsigned char
while a > 256 or b > 256:
b += a/256 # a 超过 byte 的部分进位给 b
a = a%256 a += b/256 # b 超过 byte 的部分进位给 a
b = b%256 # 取反
a = ~a & 0xff
b = ~b & 0xff # 校验和作为字符串
v = chr(a) + chr(b) # 校验和作为 unsigned short
v = struct.unpack('H', v)[0] return v if __name__ == '__main__':
for i in sys.argv[1:]:
print ip_cksum(i)

关于TCP/IP 校验和计算的代码,网上很多,但不少都有些问题,这里作一番简单分析

1.最尾部 byte 处理依赖机序

来自 http://locklessinc.com/articles/tcp_checksum/ 的 C 代码片段:

 unsigned short checksum1(const char *buf, unsigned size)
{
unsigned sum = ;
int i; /* Accumulate checksum */
for (i = ; i < size - ; i += )
{
unsigned short word16 = *(unsigned short *) &buf[i];
sum += word16;
} /* Handle odd-sized case */
if (size & )
{
unsigned short word16 = (unsigned char) buf[i];
sum += word16;
} /* Fold to get the ones-complement result */
while (sum >> ) sum = (sum & 0xFFFF)+(sum >> ); /* Invert to get the negative in ones-complement arithmetic */
return ~sum;
}

注意第16行,对于buffer 长度非偶数情况的处理, 导致此代码只可在 Little-Endian (如x86) 机器上运行。只需对最后一个 byte 补一个’\0'的 byte,凑够两个 byte 然后转为 unsinged short 相加即可。

2.多内存块的计算

来自 python 网络包创建、解析库 dpkt 的代码 dpkt.py

 try:
import dnet
def in_cksum_add(s, buf):
return dnet.ip_cksum_add(buf, s)
def in_cksum_done(s):
return socket.ntohs(dnet.ip_cksum_carry(s))
except ImportError:
import array
def in_cksum_add(s, buf):
n = len(buf)
cnt = (n / 2) * 2
a = array.array('H', buf[:cnt])
if cnt != n:
a.append(struct.unpack('H', buf[-1] + '\x00')[0])
return s + sum(a)
def in_cksum_done(s):
s = (s >> 16) + (s & 0xffff)
s += (s >> 16)
return socket.ntohs(~s & 0xffff)

它这里会有两个实现,一个是调用dnet库的实现(见2-6行),一个是用python自己实现的版本(见8-19行)。
dnet 库是 C 实现的一个库,但和 dpkt 库是同一个作者,这里都有一个共同的问题:对于 in_cksum_add 进的内存块,如果为奇数长度,则尾部会追加一个byte '\x00' (见14行),这里就导致了问题。其实呢,尾部的那个 byte 应该留给下一个接下来的内存块一起计算,当且仅当所有的内存块都处理完毕(即 in_cksum_done 时),多余一个 byte 时才该追加 byte '\x00'。

3.经典的实现

来自 wireshark 的 in_cksum.c

 /*
* Checksum routine for Internet Protocol family headers (Portable Version).
*
* This routine is very heavily used in the network
* code and should be modified for each CPU to be as fast as possible.
*/ #define ADDCARRY(x) {if ((x) > 65535) (x) -= 65535;}
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} int
in_cksum(const vec_t *vec, int veclen)
{
register const guint16 *w;
register int sum = ;
register int mlen = ;
int byte_swapped = ; union {
guint8 c[];
guint16 s;
} s_util;
union {
guint16 s[];
guint32 l;
} l_util; for (; veclen != ; vec++, veclen--) {
if (vec->len == )
continue;
w = (const guint16 *)(const void *)vec->ptr;
if (mlen == -) {
/*
* The first byte of this chunk is the continuation
* of a word spanning between this chunk and the
* last chunk.
*
* s_util.c[0] is already saved when scanning previous
* chunk.
*/
s_util.c[] = *(const guint8 *)w;
sum += s_util.s;
w = (const guint16 *)(const void *)((const guint8 *)w + );
mlen = vec->len - ;
} else
mlen = vec->len;
/*
* Force to even boundary.
*/
if (( & (unsigned long) w) && (mlen > )) {
REDUCE;
sum <<= ;
s_util.c[] = *(const guint8 *)w;
w = (const guint16 *)(const void *)((const guint8 *)w + );
mlen--;
byte_swapped = ;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= ) >= ) {
sum += w[]; sum += w[]; sum += w[]; sum += w[];
sum += w[]; sum += w[]; sum += w[]; sum += w[];
sum += w[]; sum += w[]; sum += w[]; sum += w[];
sum += w[]; sum += w[]; sum += w[]; sum += w[];
w += ;
}
mlen += ;
while ((mlen -= ) >= ) {
sum += w[]; sum += w[]; sum += w[]; sum += w[];
w += ;
}
mlen += ;
if (mlen == && byte_swapped == )
continue;
REDUCE;
while ((mlen -= ) >= ) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= ;
byte_swapped = ;
if (mlen == -) {
s_util.c[] = *(const guint8 *)w;
sum += s_util.s;
mlen = ;
} else
mlen = -;
} else if (mlen == -)
s_util.c[] = *(const guint8 *)w;
}
if (mlen == -) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[] = ;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}

1)92行是当前内存块还余一个 byte ,则会 s_util 等待下个内存卡再处理——恰当的处理前面提到的第二个问题

2)94行是所有内存块处理完毕后,对尾部最后一个 byte 的处理 ——恰当的处理了前面提到的第一个问题

3)看点:指针非对齐的情况下处理

50行会先将未对其的1个 byte 暂存,这样可迫使指针对齐,但又为了让同奇位、同偶位内存相加,所以使 sum<<8;81行,如果前面sum是已经左移过的,则再次 sum<<8,让sum回归最初的奇偶次序

注:REDUCE 宏实现的功能是将大于 short 的值(即大于65535)转化为 short 能表示的值.

TCP/IP Checksum 吐槽的更多相关文章

  1. TOE(TCP/IP Offload Engine)网卡与一般网卡的区别

    TCP减压引擎,第一次听说这个名词,但是并不是一个新的概念了,若干年前听说过设备厂商在研究在FPGA之中实现TCP Stack,但是后来没有听到任何的产品出来,应该是路由设备to host的traff ...

  2. WireShark抓包时TCP数据包出现may be caused by ip checksum offload

    最近用WireShark抓包时发现TCP数据包有报错:IP Checksum Offload,经过查阅资料终于找到了原因 总结下来就是wireshark抓到的数据包提示Checksum错误,是因为它截 ...

  3. 【转】TCP/IP协议栈及OSI参考模型详解

    OSI参考模型 OSI RM:开放系统互连参考模型(open systeminterconnection reference model) OSI参考模型具有以下优点: 简化了相关的网络操作: 提供设 ...

  4. linux下TCP/IP及内核参数优化调优(转)

    Linux下TCP/IP及内核参数优化有多种方式,参数配置得当可以大大提高系统的性能,也可以根据特定场景进行专门的优化,如TIME_WAIT过高,DDOS攻击等等. 如下配置是写在sysctl.con ...

  5. 渣渣小本求职复习之路每天一博客系列——TCP/IP协议栈(5)

    前情回顾:一篇短短的博客明显不能满足TCP和UDP这两个饥渴的汉子,而且还被应用协议占了一小半的篇幅.在昨天结束之后,相信大家都基本对TCP/IP协议栈的轮廓有一个大概的印象了,能够对整体有所把握. ...

  6. 云计算之路-阿里云上:消灭“黑色n秒”第三招——禁用网卡的TCP/IP Offload

    程咬金有三板斧,我们有三招.在这篇博文中我们要出第三招,同时也意味着昨天在“希望的田野”上的第二招失败了. 前两招打头(CPU)不凑效,这一招要换一个部位,但依然要坚持攻击敌人最弱(最忙最累)部位的原 ...

  7. TCP/IP详解

    第一篇 TCPIP协议详解 第1章 TCPIP协议族 第2章 IP协议详解 第3章 TCP协议详解 第4章 TCP/IP通信案例:访问Internet上的Web服务器 一.TCP/IP协议族 TCP/ ...

  8. TCP/IP Four Layer Protocol Format Learning

    相关学习资料 tcp-ip详解卷1:协议.pdf 目录 . 引言 . 应用层 . 传输层 . 网络层 0. 引言 协议中的网络字节序问题 在学习协议格式之前,有一点必须明白,否则我们在观察抓包数据的时 ...

  9. TCP/IP之TCP的建立与终止

    TCP协议简介 tcp/ip协议族中传输层最重要的两种协议是UDP和TCP协议,上一篇文章用很短的篇幅介绍完了UDP协议相关的内容,但相对于UDP而言的TCP协议,是种更复杂,应用更广的协议.在接下来 ...

随机推荐

  1. python核心编程-第三章-习题

    1.这是python的语言特性,python先创建对象,在给变量赋值时,不需要定义变量的名称和类型,它实际是用变量引用对象.变量类型在给变量赋值时自动声明 2.原因类似变量无须声明类型 3.pytho ...

  2. linux case 语句

    #!/bin/bash #$ 表示脚本名 #$n 表示第n个参数(n>) in ") echo '--=> A' ;; ") echo '--=> B' ;; * ...

  3. STM32F10xxx启动模式分析(详细)

    STM32的启动模式: STM32有三种启动模式,对应的存储介质均是芯片内置的:      1. User Flash Memory(Main Memory)  = 芯片内置的Flash,用户程序存放 ...

  4. 无法关闭的QT程序——思路开阔一下,原来这么简单!

    做一个无法关闭的QT程序(想关闭时要在任务管理器里关闭),看似很难, 其实它并不难,只要让程序在关闭时启动它自身就可以了. 上代码: #include <QtGui> class Temp ...

  5. Java常见序列化与反序列方法总结

    很多商业项目用到数据库.内存映射文件和普通文件来完成项目中的序列化处理的需求,但是这些方法很少会依靠于Java序列化.本文也不是用来解释序列化的,而是一起来看看面试中有关序列化的问题,这些问题你很有可 ...

  6. 数据指令MOV

    MOV分成三类,第一类不需要拓展(MOV),第二类做符号拓展(MOVS),第三类做零拓展(MOVZ),拓展类型根据源操作数决定. 这三类根据操作的数据类型其后可加l,w,b. MOV操作的操作数可以是 ...

  7. mac 上配置sublime text3插件

    1.安装MAC 版 sublime text 3 安装插件管理器 打开Sublime,按下Control + `(Mac)或者Ctrl + `(Windows),然后粘贴上下面的代码: import ...

  8. TestWriter自动化测试介绍

    简介: TestWriter是上海博为峰结合多年为企业做测试服务的经验所研发的一款具有自主知识产权的自动化测试工具,为企业用户提供真正的低成本.高效率的自动化测试,引领软件测试自动化运用由技术层面向业 ...

  9. .NET系统架构改造的经验和教训

    转自: http://robbinfan.com/blog/43/rid-off-dotnet-experience 在互联网行业,基于Unix/Linux的网站系统架构毫无疑问是当今主流的架构解决方 ...

  10. C#关于事件的几个好例子

    #region // 定义BoiledEventArgs类,传递给Observer所感兴趣的信息 public class BoiledEventArgs : EventArgs { public r ...