当前位置: 主页 > 日志 > 其它 >

TCP,UDP完整数据包校验和通用计算

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")


完整的测试程序如下:


File: Click to Download

[日志信息]

该日志于 2009-02-26 21:13 由 redice 发表在 redice's Blog ,你除了可以发表评论外,还可以转载 “TCP,UDP完整数据包校验和通用计算” 日志到你的网站或博客,但是请保留源地址及作者信息,谢谢!!    (尊重他人劳动,你我共同努力)
   
验证(必填):   点击我更换验证码

redice's Blog  is powered by DedeCms |  Theme by Monkeii.Lee |  网站地图 |  本服务器由西安鲲之鹏网络信息技术有限公司友情提供

返回顶部