LKT*_*LKT 1 c ip checksum network-programming tcp
我正在编写一个简单的程序来发送/接收TCP数据包并将其合并到一个更大的项目中.我卡在校验和部分,我计算的数字与wireshark数字不匹配.
对于校验和功能,我重新使用Mike Muss的代码如下:
static int
in_cksum(u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
Run Code Online (Sandbox Code Playgroud)
我收到了一个数据包并存储在char缓冲区[2048].为了获得IP头,我做:
struct iphdr* ip;
ip = (struct iphdr*) buffer;
Run Code Online (Sandbox Code Playgroud)
从这里,我可以正确读取信息,如ip-> protocol,ip-> saddr等,甚至打印出正确的校验和,如wireshark中所示
printf("Print checksum = 0x%x\n",ntohs(ip->check));
Run Code Online (Sandbox Code Playgroud)
然后我尝试使用上面的函数计算校验和并将其打印出来
printf("My calculated checksum =0x%x\n",in_cksum ((unsigned short*) ip, sizeof(struct iphdr)));
Run Code Online (Sandbox Code Playgroud)
我得到的是"我的计算校验值= 0x0",似乎IP头中没有任何内容.我想我可能没有正确传递in_cksum函数参数,但我不确定如何修复或者我正在做的方式是否存在其他问题.
IP cksum问题已在下面解决.但是,我在尝试计算TCP校验和时遇到了类似的问题.以下是我获取tcp标头的方法:
tcp=(struct tcphdr*) (buffer+sizeof(struct iphdr);
Run Code Online (Sandbox Code Playgroud)
在此之后,我再次可以读取有关tcp头的正确信息,例如tcp-> source,tcp-> dest,甚至tcp-> check
然后我尝试重新计算校验和,如下所示:
tcp->check=0;
tcp->check=in_cksum((unsigned short*)tcp, ntohs(ip->tot_length)-(4*ip->ihl));
Run Code Online (Sandbox Code Playgroud)
我在这里得到的结果与我之前打印的结果不同.我认为我的问题可以在cksum函数中传递,但我不太确定如何解决它.
任何帮助将不胜感激.先感谢您!
IP校验和的计算不得包含包含该校验和的字段 - 应该为该字段使用零.来自RFC 791:
校验和字段是报头中所有16位字的一个补码和的16位补码.出于计算校验和的目的,校验和字段的值为零.
我没有完成数学计算,但我怀疑得到零是在整个标题上重新计算校验和的预期结果,即它导致计算出的总和自行取消.
[我检查过 - 维基百科同意:"如果没有腐败,整个IP头的总和,包括校验和,应该为零".因此,在验证校验和时,零实际上是正确的结果]
请注意,IP标头只能是4个字节长的倍数,因此在这种特定情况下您的填充代码是不必要的,尽管如果想要计算其他数据的通用补码校验和,则可能很有用.
此外,sizeof(struct iphdr)
您应该4 * ip->ihl
首先确保您的缓冲区中至少有这么多字节,而不是传递您应该传递到标题中可能包含的任何IP选项的帐户.