最近要在单片机上实现一个TCP/IP协议,遇到一个怪问题我的电脑发出的TCP包,没带数据时(三握手),用这段代码能校验成功...
volatile U16 crc;
volatile U16 length, temp; RxdNetBuff.IpFrame.ttl = 0;//伪首部0字段
temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长
//length=(RxdNetBuff.IpFrame.VerandIphLen&0x0f)<<2;//IP首部长度
length=20 ;//不考虑IP首部选项,直接作为20字节计算
RxdNetBuff.IpFrame.Crc =htons(temp-length);//IP包CRC位置转换为伪首的TCP包数据长度
crc = CheckSum(&RxdNetBuff.IpPacket.IpPacket[4], temp-8);//从IP包第8个字节开始计算
UartPrintfCh1("VerifyTcpCrc:temp;%u,length:%u crc:%u\r\n",temp,length,crc);当发出带有数据的包时,得到的CRC就不为0了...
更奇怪的是,我用一个sniffer软件叫Iris截取发出的数据包时,他居然也说我电脑网卡发出的带数据的TCP校验和错误,不带数据的正确
但我用另外一台电脑测试能收到这个包,又说明这个包是没问题的...
我现在糊涂了...
volatile U16 crc;
volatile U16 length, temp; RxdNetBuff.IpFrame.ttl = 0;//伪首部0字段
temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长
//length=(RxdNetBuff.IpFrame.VerandIphLen&0x0f)<<2;//IP首部长度
length=20 ;//不考虑IP首部选项,直接作为20字节计算
RxdNetBuff.IpFrame.Crc =htons(temp-length);//IP包CRC位置转换为伪首的TCP包数据长度
crc = CheckSum(&RxdNetBuff.IpPacket.IpPacket[4], temp-8);//从IP包第8个字节开始计算
UartPrintfCh1("VerifyTcpCrc:temp;%u,length:%u crc:%u\r\n",temp,length,crc);当发出带有数据的包时,得到的CRC就不为0了...
更奇怪的是,我用一个sniffer软件叫Iris截取发出的数据包时,他居然也说我电脑网卡发出的带数据的TCP校验和错误,不带数据的正确
但我用另外一台电脑测试能收到这个包,又说明这个包是没问题的...
我现在糊涂了...
解决方案 »
- do{}while(0)
- 如何屏蔽单文档视图打印预览中的缩放功能
- 关于Media Encoder 和 AsfRecord 及 DirectShow
- 创建CSocketFile对象时,断言失败!
- 求助,ODBC添加数据,但添加进去的数据为什么后面会出现多余空格????
- 请教一个关于dll文件中加载资源的问题?
- 关于ICOPYHOOK接口的问题
- 请教程序如何响应用户按CTRL+C
- 请教BCGControlBar Library安装好后,我想在源码级调试,我该怎么办???用F11跟踪不到~
- 如何屏蔽Dialog的syscommand的菜单项?
- 头痛问题:多线程里 如何触发时钟
- 用winspool.h里的东西,有哪些功能和技巧?
这是TCP较验和计算相关的一些代码:/* TCP 包头 */
typedef struct tcp_header
{
u_16 sport; // 16位源端口号
u_16 dport; // 16位目地端口号
u_32 serial; // 32位序号
u_32 serial_2; // 32位确认序号
u_16 multi; // 4 位首部长度 保留6位 URG ACK PSH PST SYN FIN
u_16 win_size; // 16位窗口大小
u_16 crc; // 16位TCP较验和
u_16 urgent; // 16位紧急指针
}tcp_header,* ptcp_header;/* IP 伪首部 */
typedef struct psd_header
{
u_32 saddr; //源地址
u_32 daddr; //目的地址
u_char mbz; //置空
u_8 protocol; //协议类型
u_16 tcpl; //TCP长度
}psd_header;/* 计算 TCP 头较验值 */
u_16 tcp_set_checksum(
u_16 *buffer, //从TCP头开始
u_16 size , //TCP头长度+数据长度
in_addr ia_src_addr , //源地址
in_addr ia_dst_addr , //目的地址
u_8 c_protocol //应用协议类型
)
{
tcp_header *tcphead = (tcp_header*)( buffer);
u_32 checksum = 0;
u_16 *p = NULL;
psd_header tcpsd_header = { 0 }; //填充 IP 伪首部
tcpsd_header.saddr = ia_src_addr.s_addr;
tcpsd_header.daddr = ia_dst_addr.s_addr;
tcpsd_header.protocol = c_protocol;
tcpsd_header.tcpl = htons( size );
tcphead->crc = 0; p = (u_16*)&tcpsd_header;
checksum += *p++;
checksum += *p++;
checksum += *p++;
checksum += *p++;
checksum += *p++;
checksum += *p++; p = (u_16*)tcphead; while( size > 1 )
{
checksum += *p++;
size -= 2;
}
if( size > 0 )
{
checksum += *(u_char*)p;
} checksum = (checksum>>16) + (checksum&0xFFFF);
checksum += (checksum>>16);
tcphead->crc = (u_16)(~checksum);
return tcphead->crc;
}
TCP伪头就是源IP(4字节),目标IP(4字节),协议htons(0x0006),TCP数据长度(4字节)
计算checksum时,TCP头里的checksum默认为0x0000
我用的函数是
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);
} 假设我要计算一个包
#pragma pack(push) // 将当前pack设置压栈保存
#pragma pack(1)typedef struct Packet
{
unsigned char DestMAC[6];
unsigned char SourMAC[6];
unsigned short EthType;
unsigned char IPVersion;
unsigned char IPType;
unsigned short Len1;
unsigned short IDCode;
unsigned char IPFlags;
unsigned char Fragment_Offest;
unsigned char Time_to_alive;
unsigned char Protocol;
unsigned short IP_Checksum;
unsigned long SourIP;
unsigned long DestIP;
unsigned short SourPort;
unsigned short DestPort;
unsigned long Sn;
unsigned long An;
unsigned char Data_Offest;
unsigned char flags;
unsigned short window;
unsigned short cheksum;
unsigned short u_point;
unsigned char padding[16];
}PACKET, *PPACKET;
#pragma pack(pop) // 恢复先前的pack设置
PACKET buf;
buf.cheksum=htons(0);UCHAR tempbuf[100];
memset(tempbuf,0,100);
memcpy(tempbuf,(UCHAR*)&buf+0x1a,44); //从SourIP开始复制
tempbuf[45]=36; //长度
tempbuf[47]=6; //协议
buf.cheksum=checksum((USHORT*)tempbuf,48);
volatile U16 length, temp; RxdNetBuff.IpFrame.ttl = 0;//伪首部0字段
temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长这里的temp是一个network byte order
//length=(RxdNetBuff.IpFrame.VerandIphLen&0x0f)<<2;//IP首部长度
length=20 ;//不考虑IP首部选项,直接作为20字节计算这里的Length是一个Host byte order
RxdNetBuff.IpFrame.Crc =htons(temp-length/*一个network byte order减去一个Host byte order,结果是什么东西?*/);//IP包CRC位置转换为伪首的TCP包数据长度
crc = CheckSum(&RxdNetBuff.IpPacket.IpPacket[4], temp-8);//从IP包第8个字节开始计算
UartPrintfCh1("VerifyTcpCrc:temp;%u,length:%u crc:%u\r\n",temp,length,crc);
temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长这里的temp是一个network byte order
---------------------------------
这是网卡取出来的数据,因此要换一下顺序才能得到IP包真正的长度,所以要经过 htons 才转成主机顺序
这个htons是我写的一个可逆宏...换过来换过去是一样的,别看字面意思请大家帮我考虑下,为什么我的代码对不带数据的包能校验成功,
带数据的包则错误.那个软件也这么说
原因肯定出在这个长度上...IP包总长度-IP首部长度 == TCP包长度
TCP包长度 + 伪首部12字节 == 需要校验数据总长
伪首部里存的长度 == TCP包总长 还是 TCP首部长度 ? 查了很多资料都没有结果
就把他的TTL当作伪首0值
CRC当作长度,协议类型与两IP地址不变,
正好连在一起12字节
=========
谁告诉你htons是转换成主机字节序的?
也能把 BB AA 转成 AA BB啊,
都说这是个可逆的过程了,不要看字面意思
你一定要改成ntohs吧那加条宏就是
#define ntohs(x) htons(x)
如果是像普通那样,是把TCP分片成若干IP小包进行传输,在路由上还会验证如果是这样。
我没有使用另外的伪首部结构是因为IP包头这时候已经没有用了,
就把他的TTL当作伪首0值
CRC当作长度,协议类型与两IP地址不变,
正好连在一起12字节 这段就有问题了
A: If the packets that have incorrect TCP checksums are all being sent
by the machine on which Ethereal is running, this is probably because
the network interface on which you're capturing does TCP checksum
offloading. That means that the TCP checksum is added to the packet by
the network interface, not by the OS's TCP/IP stack; when capturing on
an interface, packets being sent by the host on which you're capturing
are directly handed to the capture interface by the OS, which means
that they are handed to the capture interface without a TCP checksum
being added to them.看来12楼的兄弟思路对了,
可能当包有数据时,网卡或者路由器已经将包重新弄过了,检验和也重新计算过
但这种包怎么检验,仍然没解决..
实在没办法只好是个TCP就收下,不抛掉检验和不对的包了to 11楼
TCP算检验和用的伪首根本不真正发送,所以抓包软件也抓不到,
再说抓包软件也只能抓到操作系统TCP栈内的数据,抓不到网卡真正发出的数据
再说谁做这个,电脑里不安几个sniffer软件的to 13楼,
我桌子上就摆着一本<TCP/IP详解 卷1 协议>
请帮我翻一下哪页讲了伪首内长度字节的真正含义站着说话不腰痛的,你们的回复说明你们很不熟悉TCP的检验和,请看清我的帖子标题再进来,谢谢
另外http://topic.csdn.net/t/20050614/15/4081732.html
的14楼也提到这个同样的问题,已经PM请教了
个人理解啊,别当真!
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. The checksum also covers a 96 bit pseudo header conceptually
prefixed to the TCP header. This pseudo header contains the Source
Address, the Destination Address, the Protocol, and TCP length.
This gives the TCP protection against misrouted segments. This
information is carried in the Internet Protocol and is transferred
across the TCP/Network interface in the arguments or results of
calls by the TCP on the IP. +--------+--------+--------+--------+
| Source Address |
+--------+--------+--------+--------+
| Destination Address |
+--------+--------+--------+--------+
| zero | PTCL | TCP Length |
+--------+--------+--------+--------+ ===> The TCP Length is the TCP header length plus the data length in
octets (this is not an explicitly transmitted quantity, but is
computed), and it does not count the 12 octets of the pseudo
header.See RFC793