免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 3589 | 回复: 12
打印 上一主题 下一主题

[网络] tcp 校验问题 [复制链接]

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-06-18 09:54 |只看该作者 |倒序浏览
本帖最后由 Fart_King 于 2015-06-18 09:54 编辑


uint16_t checksum(void *buffer, uint32_t length)
{
        uint16_t *pAddr = (uint16_t *)buffer;
        uint32_t sum = 0;

        while (length > 1)
        {
                sum += *(uint16_t *)pAddr++;
                if (sum & 0x80000000)
                {
                        sum = (sum & 0xffff) + (sum >> 16);
                }
                length -= 2;
        }

        if (length > 1)
        {
                sum += *(uint8_t *)pAddr;
        }

        while (sum >> 16)
        {
                sum = (sum & 0xffff) + (sum >> 16);
        }

        return ~sum;
}



void test_checksum(char *buf, PSDHEADER *psdHeader,unsigned char* calcBuf)
{
  
        struct ether_header *ethHdr = (struct ether_header*)buf;
        struct ip *ipHdr = (struct ip*)(buf + sizeof(struct ether_header));
        struct tcphdr *tcpHdr = (struct tcphdr*)(buf + sizeof(struct ether_header) + sizeof(struct ip));
  u_short ip_sum = 0;
  uint16_t tcp_check = 0;
        char *httpbuf = (char*)(buf + sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct tcphdr));
        //strcpy(httpbuf, request);
        int len = 0; //http + tcp len
        
  
        fprintf(stdout, "auto tcp->check = %d\n",  tcpHdr->check);
        ipHdr->ip_sum = 0;
        tcpHdr->check = 0;
        
        len = ntohs(ipHdr->ip_len)- ((ipHdr->ip_hl)&15)*4;
        fprintf(stdout, "ip len = %d http + tcp len = %d\n", ((ipHdr->ip_hl)&15)*4, len);
        memset(calcBuf, 0, sizeof(calcBuf));
        memcpy(calcBuf, (void*)ipHdr, sizeof(struct ip));
        
        
        ip_sum = checksum((uint16_t*)(buf + sizeof(struct ether_header)), sizeof(struct ip));
        
  
        
        
        psdHeader->saddr = (unsigned long)ipHdr->ip_src.s_addr;
        psdHeader->daddr = (unsigned long)ipHdr->ip_dst.s_addr;
        
        psdHeader->mbz   = 0;
        psdHeader->ptcl  = 0x06;
//        psdHeader->tcpl  = htons((u_short)(sizeof (struct tcphdr) + http_len));
        psdHeader->tcpl  = htons((u_short)(len));
        
        //tcpHdr->check        = CalcTCPSum((u_int16_t *)psdHeader, (u_int16_t *)tcpHdr,sizeof(struct tcphdr) + http_len);
        memset(calcBuf, 0, sizeof(calcBuf));
        memcpy(calcBuf, (void*)psdHeader, sizeof(PSDHEADER));
        memcpy(calcBuf + sizeof(PSDHEADER), (void*)tcpHdr, sizeof(struct tcphdr));
        //memcpy(calcBuf + sizeof(PSDHEADER), (void*)tcpHdr, len);
        memcpy(calcBuf + sizeof(PSDHEADER) + sizeof(struct tcphdr), (void*)httpbuf, len - sizeof(struct tcphdr));
        
        tcp_check = checksum((u_int16_t*)calcBuf, sizeof(PSDHEADER) + len); //
//        tcpHdr->check = tcp4_checksum (*ipHdr, *tcpHdr, (uint8_t *) httpbuf, http_len);
        
        
        fprintf(stdout, "tcp_check = %d\n",  tcp_check);
        
}  

这个函数是我用来测试tcp checksum是否正确的function。
参数 buf 就是raw sock 接收的以太网帧 psdheader 是tcp 伪首部 。
为什么一旦tcp包中带了数据tcp->check 就不正确了呢。求大神指点

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
2 [报告]
发表于 2015-06-18 22:15 |只看该作者
回复 1# Fart_King


    你的这个校验算法有问题吧?从while跳出后,if那句肯定执行不到,算法原意是如果是奇数个字节,落单的那个字节也要加上去,废话少说,网上另找的一段代码,你看看,跟你的就不一样了:
  1. USHORT checksum(USHORT *buffer,int size)  
  2. {  
  3.     unsigned long cksum=0;  
  4.     while(size>1)  
  5.     {  
  6.         cksum+=*buffer++;  
  7.         size-=sizeof(USHORT);  
  8.     }  
  9.     if(size)  
  10.     {  
  11.         cksum+=*(UCHAR *)buffer;  
  12.     }  
  13.     //将32位数转换成16  
  14.     while (cksum>>16)  
  15.         cksum=(cksum>>16)+(cksum & 0xffff);  
  16.     return (USHORT) (~cksum);  
  17. }  
复制代码

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
3 [报告]
发表于 2015-06-23 12:47 |只看该作者
回复 2# 羽剑天涯

这是应对自构造的包的函数,当包比较大的时候会进入if
   

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
4 [报告]
发表于 2015-06-23 12:48 |只看该作者
所以这个函数应该没有问题

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2015-06-23 12:59 |只看该作者
回复 4# Fart_King


    while 跳出的条件只有(length > 1)不满足,此时怎么都不会进入if,请仔细看一下,或者举个例子,while跳出后可以进入if。
    这个就是个IP、TCP等的标准校验算法。

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
6 [报告]
发表于 2015-06-23 14:06 |只看该作者
回复 5# 羽剑天涯


                if (sum & 0x80000000)
                {
                        sum = (sum & 0xffff) + (sum >> 16);
                }

    如果发现整形最高位已经是1时,就把它先转化成16bit数据,这样就不会溢出,导致计算错误。

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
7 [报告]
发表于 2015-06-23 14:07 |只看该作者
本帖最后由 Fart_King 于 2015-06-23 14:09 编辑

这个问题还是没有解决,可能是这个函数的带参问题,有没有人能告知一下。
tcp 伪首部 + tcp首部 + data 计算出来的校验和为何有时对有时错呢..

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
8 [报告]
发表于 2015-06-23 14:49 |只看该作者
回复 7# Fart_King


    我真要晕到点了,大哥,有没有仔细看我的发帖呢? 我是说你这个校验算法写得有问题,if那一句执行不到,导致有可能算出来的校验和是错的,举个简单例子:
    例如:需要计算校验和的数据是11个字节(checksum传递的length是11),那么while跳出时,length为1,此时不会执行if,那么buffer中最后一个字节没有加到sum中去,计算出来的sum就是错的,你如果按照校验的方法加起来,结果将不会是0。
    又如,需要计算校验和的数据是10个字节,那么if那句不执行也没关系,这样算出来的sum就是对的。
    你可以试试,将每次计算校验和的长度打印出来,看看正确与否是否跟数据长度的奇偶有关!

论坛徽章:
1
天蝎座
日期:2015-03-09 10:14:22
9 [报告]
发表于 2015-06-23 15:11 |只看该作者
回复 8# 羽剑天涯


   ..疏忽了,改过来之后tcp校验和 还是会偶尔错误,带http数据的校验和计算有过经验吗?能否赐教一下

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
10 [报告]
发表于 2015-06-23 15:44 |只看该作者
回复 9# Fart_King


    不管是负载的http、https还是telnet等,都是基于tcp协议的,仅仅是使用的端口,和负载的内容格式不同罢了,TCP校验和的算法是不变的,你这个问题问得比较大,你可以贴一些结果出来,比如原始数据是什么(tcpdump抓到的是什么,你程序中抓到的是什么),计算出来错误的校验和是什么,然后我们再分析你这段代码为什么会算错。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP