TCP校验和计算原理与实现

1. 概述

TCP首部校验和计算三部分:TCP首部+TCP数据+TCP伪首部。

TCP校验和覆盖TCP首部和TCP数据,而IP首部中的校验和只覆盖IP的首部,不覆盖IP数据报中的任何数据。

伪首部是为了增加TCP校验和的检错能力:如检查TCP报文是否收错了(目的IP地址)、传输层协议是否选对了(传输层协议号)等。伪首部来自IP首部。

RFC 793的TCP校验和定义

The checksum field is the 16 bit one‘s complement of the one‘s complement sum of all 16-bit words in the header and text.

If a segment contains an odd number of header and text octets to be checksummed, the last octet is padded on the right

with zeros to form a 16-bit word for checksum purposes. The pad is not transmitted as part of the segment. While computing

the checksum, the checksum field itself is replaced with zeros.

上述的定义说得很明确:

首先,把伪首部、TCP报头、TCP数据分为16位的字,如果总长度为奇数个字节,则在最后增添一个位都为0的字节。

把TCP报头中的校验和字段置为0。

其次,用反码相加法累加所有的16位字(进位也要累加)。

最后,将上述结果作为TCP的校验和。

验证示例:

校验和  反码求和过程

以4bit 为例

发送端计算:

数据:   1000  0100   校验和  0000

则反码:0111  1011               1111

叠加:   0111+1011+1111 = 0010 0001   高于4bit的, 叠加到低4位      0001 + 0010 = 0011 即为校验和

接收端计算:

数据:  1000   0100   检验和  0011

反码:  0111   1011                1100

叠加:  0111 + 1011 +1100 = 0001 1110  叠加为4bit为1111.   全为1,则正确

2.  校验和反码求和  的实现

发送方:原码相加 ,并将高位叠加到低位,取反 ,得到反码求和结果,放入校验和

接收方:将所有原码 相加,高位叠加, 如全为1,则正确

代码如下:


    USHORT checksum (USHORT *buffer,int size)

    {

        Unsigned long cksum=0;

        While (size>1)

        {

            Cksum +=*buffer++;

            size -=sizeof(USHORT);

        }

        If (size)

        {

            Cksum +=*(UCHAR *) buffer;

        }

        //将32位转换为16位

        While (cksum>>16)

            Cksum = (cksum>>16) + (cksum & 0xffff);

        return (USHORT) (~cksum);

    }

buffer是指向需要校验数据缓冲区的指针,size是需要检验数据的总长度(字节为单位)。

4-8行代码是对数据按16bit累加求和,由于最高位的进位需要加在最低位上,所以cksum必须是32位的unsigned long型,高16bit用于保存累加过程中的进位;

9-11行是对size为奇数情况的处理。

14~15行代码的作用是将cksum高16bit的值加到低16bit上,即把累加中最高位的进位加到最低位上。这里使用了 while循环,判断cksum高16bit是否非零,因为第16行代码执行的时候,还是可能向cksum的高16bit进位。

有些地方是通过下面两条代码实现的:

Cksum = (cksum >> 16) + (cksum & 0xffff);

Cksum = (cksum >> 16);

这里只进行了两次相加,即可保证相加后cksum的高16位为0,两种方式的效果是一样,事实上,上面的循环也最多执行两次!

16行代码即对16bit数据累加的结果取反,得到二进制反码求和的结果,然后函数返回该值。

3. Linux kernel中实现:

csum为32bit ,存储原码相加的结果, 将原码高16bit叠加到低16bit,然后求反,即可得到 反码求和的结果


static inline __sum16 csum_fold(__wsum csum)

{

    u32 sum = (__force u32)csum;

    sum = (sum & 0xffff) + (sum >> 16);      //将高16 叠加到低16

    sum = (sum & 0xffff) + (sum >> 16);      //将产生的进位   叠加到 低16

    return (__force __sum16)~sum;               //求反,   得到二进制反码求和的结果

}

其中上述csum的计算如下:

skb_add_data -> csum_partial     计算  添加数据段的校验和 ,将其放置在 skb->csum 中,完成tcp数据部分计算。

tcp_transmit_skb(传输层统一出口函数) 为 skb添加 tcp首部,并在其中调用  tcp_v4_send_check 来完成 tcp首部的最终校验和计算( tcp首部+tcp数据+tcp伪首部 )。

源码分析:

tcp_transmit_skb

{

icsk->icsk_af_ops->send_check(sk, skb->len, skb); //完成 tcp首部校验和的计算

...

...

err = icsk->icsk_af_ops->queue_xmit(skb, 0);  //将其发往 IP层     ip_queue_xmit

}

其中 tcp_transmit_skb-->icsk->icsk_af_ops->send_check(tcp_v4_send_check)

tcp_v4_send_check

{

.....

th->check = tcp_v4_check(len, inet->inet_saddr, inet->inet_daddr,

csum_partial(th, th->doff << 2, skb->csum));  //将 th首部 校验和 和 tcp数据段校验和相加

....

}

//NETIF_F_ALL_CSUM

static inline __sum16 tcp_v4_check(int len, __be32 saddr,

__be32 daddr, __wsum base)

{

return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);// 将tcp首部+tcp数据+tcp伪首部 三部分校验和相加,并将 32bit的 csum (skb->csum),折叠计算为16bit的 check,存放在 th->check (tcp首部校验和字段)中

即产生最终校验和

}

//对伪头 计算校验和

static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,

unsigned short proto, __wsum sum)

{

return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));

}

//将32位值的高16位折叠到低16位中,然后取反输出值,即为发送端应填入的校验和

static inline __sum16 csum_fold(__wsum csum)

{

u32 sum = (__force u32)csum;

sum = (sum & 0xffff) + (sum >> 16);      //将高16 叠加到低16

sum = (sum & 0xffff) + (sum >> 16);      //将产生的进位   叠加到 低16

return (__force __sum16)~sum;               //求反,   得到二进制反码求和的结果

}

时间: 2024-12-13 20:48:28

TCP校验和计算原理与实现的相关文章

TCP校验和的原理和实现

http://blog.csdn.net/zhangskd/article/details/11770647 分类: Linux TCP/IP Linux Kernel 2013-09-24 18:29 7853人阅读 评论(0) 收藏 举报 目录(?)[+] 概述 TCP校验和是一个端到端的校验和,由发送端计算,然后由接收端验证.其目的是为了发现TCP首部和数据在发送端到 接收端之间发生的任何改动.如果接收方检测到校验和有差错,则TCP段会被直接丢弃. TCP校验和覆盖TCP首部和TCP数据,

ipv4校验和计算

ipv4校验和的计算 原理: 计算方法一:除去校验和的两位,将其他的位相加:45+00+00+3c+55+81+00+00+40+01+ac+1c 0f+0d+ac+1c+0f+0e= 计算方法二: 校验和(checksum)算法,简单的说就是16位累加的反码运算: 计算函数如下: 我们在计算时是主机字节序,计算的结果封装成IP包时是网络字节序,注意这两者之间的区别,我们在从IP包里读取要转化为主机字节序,往IP包里存入时要转化为网络字节序在存入. UINT32 Checksum(UINT32

TCP传输工作原理

引言 在TCP/IP体系结构中,IP协议只管将数据包尽力传送到目的主机,无论数据传输正确与否,它都不做验证,不发确认,也不保证数据包的顺序,因而不具有可靠性.这一问题要由传输层TCP协议来解决,TCP协议为Internet提供了可靠的无差错的通信服务. 一.OSI参考模型和TCP/IP参考模型 OSI模型(open system interconnection reference model)是基于国际标准化组织(ISO)的建议而发展起来的,它分为如图1所示的七层. TCP/IP最初是为ARPA

ip首部校验和计算

IP首部校验和的计算方法: 1.把校验和字段清零. 2.然后对每16位(2字节)进行二进制反码求和,反码求和的意思是先对每16位求和,再将得到的和转为反码. 接下来详细描述反码求和的步骤:看下面的代码 算法: SHORT checksum(USHORT* buffer, int size){    unsigned long cksum = 0;    while(size>1)    {        cksum += *buffer++;        size -= sizeof(USHO

TCP/IP协议原理与应用笔记17:IP编址之 IP地址简介(重点)

1. IP地址(通用标识符) 对于同一个网络设备(主机或路由器)的不同网络连接,需要不同的IP地址进行标识 2. 主机标识符 主要有下面三种方式的主机标识方式: (1)Name:是什么,可读性强(了解主机的属性或者用途) (2)Address:在哪里,软件效率高. (3)Route(路由的标识):怎样到达 3. IP地址结构 (1)32 bit二进制地址 它模拟物理网的编址机制,如下示意图: (2)IP地址分类,如下: TCP/IP协议原理与应用笔记03:IP地址分类

OpenGL中摄像机矩阵的计算原理

熟悉OpenGL|ES的朋友,可能会经常设置摄像机的view矩阵,iOS中相对较好,已经封装了方向,只需要设置摄像机位置,目标点位置以及UP向量即可.下面先介绍下摄像机view矩阵的计算原理.此处假设知道摄像机位置eye,目标点位置target以及UP向量. 主要是u,v,w三个向量的计算: 1.w向量: (1)计算向量eyeToTarget = eye - target; (2)向量w 等于 向量eyeToTarget与向量UP的叉乘. 2.u向量:向量w与向量UP的叉乘 3.v向量:向量w与

mapreducer计算原理

mapreducer计算原理 InputFormat InputFormat的默认实现是TextInputFormat InputSplit 概念 是mapreducer对文件进行处理和运算的输入单位.只是一个逻辑概念.每一个InputSplit并没有对文件进行实际的切割.只是记录了要处理文件的位置信息(包括文件的path和 hosts.长度(length)).在默认情况下,InputSplit和Block的数目是一样的. getLength 得到一个InputSplit的长度 getLocat

TCP/IP协议原理学习笔记

昨天学习了杨宁老师的TCP/IP协议原理第一讲和第二讲,主要介绍了OSI模型,整理如下: OSI是open system innerconnection的简称,即开放式系统互联参考模型,它把网络协议从逻辑上分为了7层.即如上图所示: 1.物理层:主要定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介质的传输速率等.它的主要作用是传输比特流(就是由1.0转化为电流强弱来进行传输,到达目的地后在转化为1.0,也就是我们常说的数模转换与模数转换).这一层的数据叫做比特. 2.数据链路层:定

前端移动端的rem适配计算原理

rem是什么? rem(font size of the root element)是指相对于根元素的字体大小的单位.简单的说它就是一个相对单位.看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位.它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算. 计算原理: 1 屏幕宽为 clientWidth(px). 设计稿宽度为 750 (px), 假设 n = clientWidth(px)/750(px)