tcp校验和原理和计算方法-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 752984
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:32
个人简介

小公司研发总监,既当司令也当兵!

文章分类

全部博文(144)

相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: linux

2015-09-16 11:36:34

概述

    tcp校验和是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现tcp首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则tcp段会被直接丢弃。

    tcp校验和覆盖tcp首部和tcp数据,而ip首部中的校验和只覆盖ip的首部,不覆盖ip数据报中的任何数据。
    tcp的校验和是必需的,而udp的校验和是可选的。

    tcp和udp计算校验和时,都要加上一个12字节的伪首部。

 

伪首部


    伪首部共有12字节,包含如下信息:源ip地址、目的ip地址、保留字节(置0)、传输层协议号(tcp是6)、tcp报文长度(报头 数据)。伪首部是为了增加tcp校验和的检错能力:如检查tcp报文是否收错了(目的ip地址)、传输层协议是否选对了(传输层协议号)等。

定义

(1) 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的校验和。

实现
    下面两图展示了ip头部和tcp头部内容和字节序号对应关系,方便后续代码阅读:

图一,ip头部



图二, tcp头部:



算法:

点击(此处)折叠或打开

  1. /**********************************************************************
  2. *function: computetcpchecksum
  3. *
  4. *arguments:
  5. * iphdr -- pointer to ip header
  6. * tcphdr -- pointer to tcp header
  7. *
  8. *returns:
  9. * the computed tcp checksum
  10. ***********************************************************************/
  11. uint16_t computetcpchecksum(unsigned char *iphdr, unsigned char *tcphdr)
  12. {
  13.     uint32_t sum = 0;

  14.     // ip报文中长度
  15.     uint16_t count = iphdr[2] * 256 iphdr[3];
  16.     uint16_t tmp;

  17.     unsigned char *addr = tcphdr;

  18.     // 12字节伪首部
  19.     unsigned char pseudoheader[12];

  20.     // 计算tcp头部和数据总长度(按字节)
  21.     // ip报文总长度 - ip头部长度(按4字节计算) = ip负载长度(tcp总长度)
  22.     count -= (iphdr[0] & 0x0f) * 4;

  23.     // 复制ip头部中原地址和目的地址信息(iphdr[12-19])到伪头部中
  24.     memcpy(pseudoheader, iphdr12, 8);

  25.     // 伪头部填充
  26.     pseudoheader[8] = 0;

  27.     // ip头部中的上层协议
  28.     pseudoheader[9] = iphdr[9];

  29.     // 伪头部中tcp长度
  30.     pseudoheader[10] = (count >> 8) & 0xff;
  31.     pseudoheader[11] = (count & 0xff);

  32.     // 计算伪首部累加和
  33.     sum = * (uint16_t *) pseudoheader;
  34.     sum = * ((uint16_t *) (pseudoheader2));
  35.     sum = * ((uint16_t *) (pseudoheader4));
  36.     sum = * ((uint16_t *) (pseudoheader6));
  37.     sum = * ((uint16_t *) (pseudoheader8));
  38.     sum = * ((uint16_t *) (pseudoheader10));

  39.     // 计算tcp头部和数据累加和
  40.     while (count > 1)
  41.     {
  42.         memcpy(&tmp, addr, sizeof(tmp));
  43.         sum = (uint32_t) tmp;
  44.         addr = sizeof(tmp);
  45.         count -= sizeof(tmp);
  46.     }
  47.     
  48.     // 如果tcp总长度为奇数,最后一个字节单独计算
  49.     if (count > 0)
  50.     {
  51.         sum = (unsigned char) *addr;
  52.     }

  53.     // 如果所有累加和超过16位,把进位继续类型,然后重新验证
  54.     // 知道累加和可以用16位表达
  55.     while(sum >> 16)
  56.     {
  57.         sum = (sum & 0xffff) (sum >> 16);
  58.     }

  59.     // 累加和取反
  60.     return (uint16_t) ((~sum) & 0xffff);
  61. }


阅读(4173) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图