算法原理:

假定 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 sysdef ip_cksum(s):a = 0b = 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 charwhile a > 256 or b > 256:b += a/256 # a 超过 byte 的部分进位给 ba = a%256a += b/256 # b 超过 byte 的部分进位给 ab = b%256# 取反a = ~a & 0xffb = ~b & 0xff# 校验和作为字符串v = chr(a) + chr(b)# 校验和作为 unsigned shortv = struct.unpack('H', v)[0]return vif __name__ == '__main__':for i in sys.argv[1:]:print ip_cksum(i)

View Code

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

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

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

 1 unsigned short checksum1(const char *buf, unsigned size)
 2 {
 3     unsigned sum = 0;
 4     int i;
 5
 6     /* Accumulate checksum */
 7     for (i = 0; i < size - 1; i += 2)
 8     {
 9         unsigned short word16 = *(unsigned short *) &buf[i];
10         sum += word16;
11     }
12
13     /* Handle odd-sized case */
14     if (size & 1)
15     {
16         unsigned short word16 = (unsigned char) buf[i];
17         sum += word16;
18     }
19
20     /* Fold to get the ones-complement result */
21     while (sum >> 16) sum = (sum & 0xFFFF)+(sum >> 16);
22
23     /* Invert to get the negative in ones-complement arithmetic */
24     return ~sum;
25 }

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

2.多内存块的计算

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

 1 try:
 2     import dnet
 3     def in_cksum_add(s, buf):
 4         return dnet.ip_cksum_add(buf, s)
 5     def in_cksum_done(s):
 6         return socket.ntohs(dnet.ip_cksum_carry(s))
 7 except ImportError:
 8     import array
 9     def in_cksum_add(s, buf):
10         n = len(buf)
11         cnt = (n / 2) * 2
12         a = array.array('H', buf[:cnt])
13         if cnt != n:
14             a.append(struct.unpack('H', buf[-1] + '\x00')[0])
15         return s + sum(a)
16     def in_cksum_done(s):
17         s = (s >> 16) + (s & 0xffff)
18         s += (s >> 16)
19         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

  1 /*
  2  * Checksum routine for Internet Protocol family headers (Portable Version).
  3  *
  4  * This routine is very heavily used in the network
  5  * code and should be modified for each CPU to be as fast as possible.
  6  */
  7
  8 #define ADDCARRY(x)  {if ((x) > 65535) (x) -= 65535;}
  9 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
 10
 11 int
 12 in_cksum(const vec_t *vec, int veclen)
 13 {
 14     register const guint16 *w;
 15     register int sum = 0;
 16     register int mlen = 0;
 17     int byte_swapped = 0;
 18
 19     union {
 20         guint8    c[2];
 21         guint16    s;
 22     } s_util;
 23     union {
 24         guint16 s[2];
 25         guint32    l;
 26     } l_util;
 27
 28     for (; veclen != 0; vec++, veclen--) {
 29         if (vec->len == 0)
 30             continue;
 31         w = (const guint16 *)(const void *)vec->ptr;
 32         if (mlen == -1) {
 33             /*
 34              * The first byte of this chunk is the continuation
 35              * of a word spanning between this chunk and the
 36              * last chunk.
 37              *
 38              * s_util.c[0] is already saved when scanning previous
 39              * chunk.
 40              */
 41             s_util.c[1] = *(const guint8 *)w;
 42             sum += s_util.s;
 43             w = (const guint16 *)(const void *)((const guint8 *)w + 1);
 44             mlen = vec->len - 1;
 45         } else
 46             mlen = vec->len;
 47         /*
 48          * Force to even boundary.
 49          */
 50         if ((1 & (unsigned long) w) && (mlen > 0)) {
 51             REDUCE;
 52             sum <<= 8;
 53             s_util.c[0] = *(const guint8 *)w;
 54             w = (const guint16 *)(const void *)((const guint8 *)w + 1);
 55             mlen--;
 56             byte_swapped = 1;
 57         }
 58         /*
 59          * Unroll the loop to make overhead from
 60          * branches &c small.
 61          */
 62         while ((mlen -= 32) >= 0) {
 63             sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
 64             sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
 65             sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
 66             sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
 67             w += 16;
 68         }
 69         mlen += 32;
 70         while ((mlen -= 8) >= 0) {
 71             sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
 72             w += 4;
 73         }
 74         mlen += 8;
 75         if (mlen == 0 && byte_swapped == 0)
 76             continue;
 77         REDUCE;
 78         while ((mlen -= 2) >= 0) {
 79             sum += *w++;
 80         }
 81         if (byte_swapped) {
 82             REDUCE;
 83             sum <<= 8;
 84             byte_swapped = 0;
 85             if (mlen == -1) {
 86                 s_util.c[1] = *(const guint8 *)w;
 87                 sum += s_util.s;
 88                 mlen = 0;
 89             } else
 90                 mlen = -1;
 91         } else if (mlen == -1)
 92             s_util.c[0] = *(const guint8 *)w;
 93     }
 94     if (mlen == -1) {
 95         /* The last mbuf has odd # of bytes. Follow the
 96            standard (the odd byte may be shifted left by 8 bits
 97            or not as determined by endian-ness of the machine) */
 98         s_util.c[1] = 0;
 99         sum += s_util.s;
100     }
101     REDUCE;
102     return (~sum & 0xffff);
103 }

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

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

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

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

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

转载于:https://www.cnblogs.com/JesseFang/p/3150344.html

TCP/IP Checksum 吐槽相关推荐

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

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

  2. tcp/ip协议概述(8)----TCP/IP加速原理详解

    对某一个key value应用,从网卡接收数据包到应用层处理,再把数据发送出去,整个系统资源消耗情况如下: 可以看出,Sockets接口+TCP是系统瓶颈. 根据下图模型,瓶颈在于TCP(包括sock ...

  3. TCP/IP 协议栈及 OSI 参考模型详解--云平台技术栈04

    导读:之前发布了云平台技术栈(ps:点击可查看),本文主要说一下其中的tcp/ip和网络! 来源:王东裕 http://wangdy.blog.51cto.com/3845563/1588379 OS ...

  4. 通过连接实例解读TCP/IP协议

    通过连接实例解读TCP/IP协议 最近狂补基础,猛看TCP/IP协议.不过,书上的东西太抽象了,没有什么数据实例,看了不 久就忘了.于是,搬来一个sniffer,抓了数据包来看,呵呵,结合书里面得讲解 ...

  5. TCP,IP数据包结构

    TCP/IP协议中各层的数据报结构是一个比较抽象的内容,大家在日常学习过程中往往难以理解和掌握,常常是死记硬背把它记住了事.本文首先利用Sniffer工具捕获了FTP命令操作过程中的所有数据包,然后对 ...

  6. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架

    目录 文章目录 目录 netfilter 框架 netfilter 的组成模块 netfilter 的 Hook 机制实现 netfilter 的工作原理 规则(Rules) 链(Chains) 表( ...

  7. 为什么说 TCP/IP 是一个不确定性网络

    目录 文章目录 目录 为什么应用层要做数据完整性校验? 链路层强校验 网络层弱校验 传输层弱校验 安全层超强校验 应用层超强校验 总结 为什么应用层要做数据完整性校验? 以太网的 CRC32 比较强, ...

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

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

  9. 一场由nginx优化引起的tcp/ip及tcpdump研究

    一场由nginx优化引起的tcp/ip及tcpdump研究 在这里不得不再吐槽下国内整个IT粗糙浮躁,度娘下来的中文文档几尽抄袭~google下来的文档英文文档质量远高于国内中文文档. 用ie或没有安 ...

最新文章

  1. 微型计算机显卡必须插在主板的,第一章 计算机基础知识(2)
  2. js 删除数组几种方法
  3. 数据表格+弹出层的综合案例
  4. OpenCASCADE:使用扩展数据交换 XDE之剪裁平面
  5. Laravel 5 4 实现前后台登录
  6. angular8实现对象数组根据某个属性排序(多个也可以)
  7. 服务器在行例维护中,8月14日服务器例行维护公告
  8. 数据字典简单例子_Python学习100天-Day14(数据分析篇-pandas02)
  9. linux解析json指针,Linux下JSON通信协议的使用和解析
  10. 【翻译】Ext JS——高效的编码风格指南
  11. 什么?都2021年了还不会ajax嘛,来这里让您快速学会Ajax
  12. AUTOCAD——标注打断
  13. windows打开linux的vmdk,如何在VirtualBox中打开VMDK文件
  14. 用ruby写了一个简单的Gmail登陆和获取未读邮件(http协议)
  15. ArcGIS空间分析——热点分析
  16. html5 observer api,转: HTML5新特性之Mutation Observer
  17. 2019 数学建模国赛 C 题思路
  18. VS2015 还是VS2017 好用_强烈推荐:2020年12款Visual Studio 好用的工具
  19. Vue+Vuex+Axios+ECharts 画一个动态更新的中国地图
  20. 网狐大联盟启用AI功能

热门文章

  1. 【年末盛会】最值得参加的信息技术与计算机应用学术会议来了!
  2. GitHub Trending第一之后,PaddleOCR再发大招:百度自研顶会SOTA算法正式开源!
  3. CMU黑科技,手机录视频,实现人脸3D建模的高度逼真
  4. DivideMix: Salesforce提出使用半监督学习大幅改进含噪声标签的学习
  5. CV Code | 计算机视觉开源周报20190904期
  6. oracle反复查询一个集合,oracle集合查询
  7. 笔记 | 《机器学习》中计算学习理论(下)
  8. 水土保持功能评估中k值的计算公式
  9. apache 验证 php 安装,PHP的安装以及验证Apache
  10. Windows Server 2008 R2无法远程桌面解决方法