gt2333588
驱动牛犊
驱动牛犊
  • 注册日期2008-02-27
  • 最后登录2012-06-27
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望71点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:2787回复:7

passthru下TCP校验和的计算问题 郁闷好几天了

楼主#
更多 发布于:2010-12-30 00:56
写了一个数据包捕获转发的驱动 计算TCP校验一直不正确 IP校验和没问题 算TCP校验和的时候我已经增加了伪头啊...
源码如下
USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;
    while(size >1)
    {
        cksum+=*buffer++;
        size -=sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);

    return (USHORT)~cksum;
}

封装TCP包部分
    //计算TCP包    
    nTempPort=TcpHdr.sourcePort;
    TcpHdr.sourcePort=TcpHdr.destinationPort;
    TcpHdr.destinationPort=nTempPort;

    nTempSeq=TcpHdr.sequenceNumber;
    TcpHdr.sequenceNumber=TcpHdr.acknowledgeNumber;//上次的ACK值作为SEQ
    TcpHdr.acknowledgeNumber=nTempSeq+htons((nDataLen-sizeof(ETHeader)-sizeof(IPHeader)));//计算ACK内容
    
    TcpHdr.dataoffset=(sizeof(TCPHeader)/4<<4|0);
    TcpHdr.flags=0x18;
    TcpHdr.windows=65535;
    TcpHdr.checksum=0;

    //填充TCP伪头
    PsdHdr.saddr = IpHdr.ipSource;
    PsdHdr.daddr = IpHdr.ipDestination;
    PsdHdr.mbz = 0;
    PsdHdr.ptcl = IPPROTO_TCP;
    PsdHdr.tcpl = ntohs(sizeof(TCPHeader));

    RtlZeroMemory(szSendData,2048);//清零
    RtlCopyMemory(szSendData,&PsdHdr,sizeof(PSDHeader));
    RtlCopyMemory(szSendData+sizeof(PSDHeader),&TcpHdr,sizeof(TCPHeader));
    TempCheckNum=checksum((USHORT*)szSendData,sizeof(PSDHeader)+sizeof(TCPHeader)); //计算TCP校验和
    TcpHdr.checksum=TempCheckNum;
gt2333588
驱动牛犊
驱动牛犊
  • 注册日期2008-02-27
  • 最后登录2012-06-27
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望71点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2010-12-30 01:01
补充一句 客户端网卡已经收到包了 只不过因为校验和不对被上层丢弃了
baizebuhui
驱动牛犊
驱动牛犊
  • 注册日期2010-08-31
  • 最后登录2010-12-30
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望31点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2010-12-30 09:11
校验和不正确 应该是发送不出去的吧。。 你怎么看到的?
iihacker
论坛版主
论坛版主
  • 注册日期2010-01-07
  • 最后登录2017-08-16
  • 粉丝5
  • 关注8
  • 积分377分
  • 威望1941点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
  • 社区居民
地板#
发布于:2010-12-30 09:21
    ICMP,IP,UDP,TCP报头部分都有checksum(检验和)字段。ICMP和IP报头校验和的计算都很简单,使用RFC1071中给出的方法即可完成(如下)。
 
//计算校验和
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);
}

    UDP/TCP报头中的校验和的计算比较复杂的,要用到 UDP/TCP伪首部:先要填充伪首部各个字段,然后再将UDP/TCP报头以后(包括报头)的数据附加到伪首部的后面,再对位首部使用上述校验和计算,所得到的值才是UDP/TCP报头部分的校验和。
 
位首部可以用如下的结构体表示:
typedef struct{
 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE mbz;           //置空(0)
 BYTE ptcl;          //协议类型
 USHORT plen;        //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
}Psd_Header;
 
 
这个过程是一个很繁琐的过程,计算过几次后再也忍受不了做这样重复的工作,于是写了一个通用的计算函数。这个函数使用起来我感觉非常方便:先封装好你的数据包(完整的,包括以太头),然后将数据包的首地址作为参数,调用该函数即可。函数将帮你完成IP报头以及UDP/TCP报头部分校验和的计算。
 
//-------------------------------------------------------------------------
// PacketCheckSum
// 计算数据包的校验和
// 参数:packet-待处理数据(将封装好的数据包的指针)
//-------------------------------------------------------------------------
void PacketCheckSum(unsigned char packet[])
{
 Dlc_Header *pdlc_header=NULL; //以太头指针
 Ip_Header  *pip_header=NULL;  //IP头指针
 unsigned short attachsize=0; //传输层协议头以及附加数据的总长度
 pdlc_header=(Dlc_Header *)packet;
 //判断ethertype,如果不是IP包则不予处理
 if(ntohs(pdlc_header->ethertype)!=0x0800) return;
 pip_header=(Ip_Header  *)(packet+14);
 //TCP包
 if(0x06==pip_header->proto)
 {
  
  Tcp_Header *ptcp_header=NULL; //TCP头指针
  Tcp_Psd_Header *ptcp_psd_header=NULL;
  
  ptcp_header=(Tcp_Header *)(packet+14+((pip_header->ver_len)&15)*4);
  attachsize=ntohs(pip_header->total_len)-((pip_header->ver_len)&15)*4;
  ptcp_psd_header=(Tcp_Psd_Header *)malloc(attachsize+sizeof(Tcp_Psd_Header));
  if(!ptcp_psd_header) return;
  memset(ptcp_psd_header,0,attachsize+sizeof(Tcp_Psd_Header));
  //填充伪TCP头
  ptcp_psd_header->destip=pip_header->destIP;
  ptcp_psd_header->sourceip=pip_header->sourceIP;
  ptcp_psd_header->mbz=0;
  ptcp_psd_header->ptcl=0x06;
  ptcp_psd_header->tcpl=htons(attachsize);
  //计算TCP校验和
  ptcp_header->chksum=0;
  memcpy((unsigned char *)ptcp_psd_header+sizeof(Tcp_Psd_Header),
   (unsigned char *)ptcp_header,attachsize);
  ptcp_header->chksum=checksum((unsigned short *)ptcp_psd_header,
   attachsize+sizeof(Tcp_Psd_Header));
  
  //计算ip头的校验和
  pip_header->checksum=0;
  pip_header->checksum=checksum((unsigned short *)pip_header,20);
  return;
 }
 
 //UDP包
 if(0x11==pip_header->proto)
 {
  Udp_Header *pudp_header=NULL; //UDP头指针
  Udp_Psd_Header *pudp_psd_header=NULL;
  pudp_header=(Udp_Header *)(packet+14+((pip_header->ver_len)&15)*4);
  attachsize=ntohs(pip_header->total_len)-((pip_header->ver_len)&15)*4;
  pudp_psd_header=(Udp_Psd_Header *)malloc(attachsize+sizeof(Udp_Psd_Header));
  if(!pudp_psd_header) return;
        memset(pudp_psd_header,0,attachsize+sizeof(Udp_Psd_Header));
  //填充伪UDP头
  pudp_psd_header->destip=pip_header->destIP;
  pudp_psd_header->sourceip=pip_header->sourceIP;
  pudp_psd_header->mbz=0;
  pudp_psd_header->ptcl=0x11;
  pudp_psd_header->udpl=htons(attachsize);
  
  //计算UDP校验和
  pudp_header->chksum=0;
  memcpy((unsigned char *)pudp_psd_header+sizeof(Udp_Psd_Header),
   (unsigned char *)pudp_header,attachsize);
  pudp_header->chksum=checksum((unsigned short *)pudp_psd_header,
   attachsize+sizeof(Udp_Psd_Header));
    
  //计算ip头的校验和
  pip_header->checksum=0;
  pip_header->checksum=checksum((unsigned short *)pip_header,20);  
  return;
 }
 return;
}
 
需要几个头文件,以及库:
 
#include <winsock2.h>
#include <windows.h>
#include "packet.h"
#pragma comment(lib,"ws2_32.lib")
 
 
 
最后附上我使用的数据包的结构体(比较多):
 
 
//数据包结构体
#pragma pack(1)  
/*物理帧头结构*/
typedef struct {
   BYTE  desmac[6];      //目的MAC地址
   BYTE  srcmac[6];      //源MAC地址
   USHORT  ethertype;    //帧类型
}Dlc_Header;
/*Arp帧结构*/
typedef struct {
   USHORT hw_type;       //硬件类型Ethernet:0x1
   USHORT prot_type;     //上层协议类型IP:0x0800
   BYTE hw_addr_len;     //硬件地址长度:6
   BYTE prot_addr_len;   //协议地址(IP地址)的长度:4
   USHORT flag;          //1表示请求,2表示应答
   BYTE send_hw_addr[6]; //源MAC地址
   UINT send_prot_addr;  //源IP地址
   BYTE targ_hw_addr[6]; //目的MAC地址
   UINT targ_prot_addr;  //目的IP地址
   BYTE padding[18];     //填充数据  
}Arp_Frame;
/*ARP包=DLC头+ARP帧*/
typedef struct {
 Dlc_Header dlcheader;//DLC头
 Arp_Frame arpframe;  //ARP帧
}ARP_Packet;
/*IP报头结构*/
typedef struct {
 BYTE  ver_len;       //IP包头部长度,单位:4字节
 BYTE  tos;           //服务类型TOS
 USHORT total_len;    //IP包总长度  
 USHORT ident;        //标识
 USHORT frag_and_flags;  //标志位
 BYTE ttl;           //生存时间
 BYTE proto;         //协议
 USHORT checksum;    //IP首部校验和
 UINT  sourceIP;  //源IP地址(32位)
 UINT  destIP;    //目的IP地址(32位)
}Ip_Header;
/*TCP报头结构*/
typedef struct {
 USHORT srcport;   // 源端口
 USHORT dstport;   // 目的端口
 UINT seqnum;      // 顺序号
 UINT acknum;      // 确认号
 BYTE dataoff;     // TCP头长
 BYTE flags;       // 标志(URG、ACK等)
 USHORT window;    // 窗口大小
 USHORT chksum;    // 校验和
 USHORT urgptr;    // 紧急指针
}Tcp_Header;
//TCP伪首部 用于进行TCP校验和的计算,保证TCP效验的有效性
typedef struct{
 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE mbz;           //置空(0)
 BYTE ptcl;          //协议类型(IPPROTO_TCP)
 USHORT tcpl;        //TCP包的总长度(单位:字节)
}Tcp_Psd_Header;
/*UDP报头*/
typedef struct  {  
 USHORT srcport;     // 源端口
 USHORT dstport;     // 目的端口
 USHORT total_len;   // 包括UDP报头及UDP数据的长度(单位:字节)
 USHORT chksum;      // 校验和
}Udp_Header;
/*UDP伪首部-仅用于计算校验和*/
typedef struct tsd_hdr  
{  
 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE  mbz;           //置空(0)
 BYTE  ptcl;          //协议类型(IPPROTO_UDP)
 USHORT udpl;         //UDP包总长度(单位:字节)  
}Udp_Psd_Header;
/*ICMP报头*/
typedef struct{
 BYTE i_type;     //类型 类型是关键:0->回送应答(Ping应答) 8->回送请求(Ping请求)
 BYTE i_code;     //代码 这个与类型有关 当类型为0或8时这里都是0
 USHORT i_cksum;  //ICMP包校验和
 USHORT i_id;     //识别号(一般用进程ID作为标识号)
 USHORT i_seq;    //报文序列号(一般设置为0)
 //UINT timestamp;  //时间戳
 BYTE padding[32];//填充数据
}Icmp_Header;
/*ICMP数据包*/
typedef struct
{
 Dlc_Header dlc_header;  //以太帧
 Ip_Header  ip_header;   //IP头
 Icmp_Header icmp_header;//ICMP帧
}Icmp_Packet;
/*攻击信息*/
typedef struct
{
 unsigned char flag;     //攻击数据包类型1-arp,2-tcp,3-udp
 unsigned int srcip;     //攻击者IP
 unsigned char code[33]; //攻击特征码
}Attack_Infor;
#pragma pack()  



网上代码,可以参考
NDIS 1群74755180 NDIS 2群182802097 交换机软硬件技术群 187471475 FPGA PCI PCIE 群187471817
gt2333588
驱动牛犊
驱动牛犊
  • 注册日期2008-02-27
  • 最后登录2012-06-27
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望71点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2010-12-30 11:15
  没用 我已经加了伪头了
gt2333588
驱动牛犊
驱动牛犊
  • 注册日期2008-02-27
  • 最后登录2012-06-27
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望71点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2010-12-30 18:52
自己解决了 因为不在TCP握手阶段 校验和要校验数据包大小 感谢两位好心的朋友帮忙 困扰了好几天了也
iihacker
论坛版主
论坛版主
  • 注册日期2010-01-07
  • 最后登录2017-08-16
  • 粉丝5
  • 关注8
  • 积分377分
  • 威望1941点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
  • 社区居民
6楼#
发布于:2010-12-31 09:09
回 5楼(gt2333588) 的帖子
我粘贴的代码可以在不同阶段通用,可以参考一下
NDIS 1群74755180 NDIS 2群182802097 交换机软硬件技术群 187471475 FPGA PCI PCIE 群187471817
TANG_XIAO_BIN
驱动牛犊
驱动牛犊
  • 注册日期2011-06-21
  • 最后登录2014-04-25
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望141点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2011-10-17 14:49

游客

返回顶部