TCP checksum C code returns wrong result
在假定应该为0的情况下,我得到了一个较大的TCP校验和结果。我正在通过将TCP psuedoheader复制到数组的前12个字节中,然后将TCP标头和数据复制到接下来的几个方法中来解决此问题。数组的字节,然后将该数组及其长度传递给校验和函数。我无法弄清为什么校验和与正确值显着不同(例如,其中一个数据包数据包应为0时为9180)。
这是我的IP,TCP和伪TCP标头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | typedef struct __attribute__((__packed__)) IPHeader { #if __BYTE_ORDER__ == __LITTLE_ENDIAN__ uint8_t hdrLen:4; uint8_t version:4; #else uint8_t version:4; uint8_t hdrLen:4; #endif uint8_t TOS; uint16_t totLen; uint16_t id; uint16_t offset; #define DF 0x4 #define MF 0x2 #define OFF 0 uint8_t TTL; uint8_t protocol; uint16_t checksum; struct in_addr srcIP; struct in_addr destIP; }IPHeader; typedef struct __attribute__((__packed__)) TCPHeader { uint16_t srcPort; uint16_t destPort; uint32_t seqNum; uint32_t ackNum; uint8_t offset:4; uint8_t res:4; uint8_t flags; uint16_t window; uint16_t checksum; uint16_t urg; }TCPHeader; typedef struct __attribute__((__packed__)) TCPPseudo { struct in_addr srcAddr; struct in_addr destAddr; uint8_t zeroes; uint8_t protocol; uint16_t len; }TCPPseudo; |
这是我将数据复制到数组中的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | IPHeader *iph TCPPseudo ts; TCPHeader *tcp; u_char checkData[1000]; uint16_t checkLen = 0; --- tcp = (TCPHeader *)(packet + ETHER_SIZE + iph->hdrLen * 4); ts.srcAddr = iph->srcIP; ts.destAddr = iph->destIP; ts.zeroes = 0; ts.protocol = iph->protocol; checkLen = ntohs(iph->totLen) - (iph->hdrLen * 4); ts.len = checkLen; memcpy(checkData, &ts, 12); memcpy(checkData + 12, tcp, checkLen); getTCPInfo(tcp, iph, checkData, checkLen); --- unsigned short checksum = in_cksum((unsigned short *)checkData, len + 12); printf("\\t\\tChecksum: %d, (0x%x)", checksum, htons(tcp->checksum)); |
这是我计算校验和的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | unsigned short in_cksum(unsigned short *addr,int len) { register int sum = 0; u_short answer = 0; register u_short *w = addr; register int nleft = len; /* * 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); } |
您应该保存接收到的数据包的TCP校验和,然后在计算伪报头和TCP段的校验和之前,在tcp报头中将校验和字段设置为零。然后将计算出的校验和与先前保存的值进行匹配。如果网络传输中没有错误,则两者应该相等。
这样做
1 2 | recvPktChkSum = tcp->checksum tcp->checksum = 0; |
在计算校验和之前,还应确保长度以网络字节顺序排列
1 | ts.len = htons(checkLen); |
之前
1 |
然后在
之后
1 | checksum = in_cksum((unsigned short *)checkData, len + 12); |
比较
1 2 |