IP头TCP头的checksum校验和计算
程序员文章站
2022-07-10 14:34:15
...
校验和就是数据位按位相加之后取反所得,验证校验和的时候把数据位按位相加再加上校验和如果等于零则表示验证通过。 如:数据位相加为1010, 那么校验和为0101,验证时1010+0101=0,验证通过。
IP头校验和计算
IP头校验和固定为16位,所以计算上和上面略有不同,当数据按位相加后高16位不为0,那么则将高16位和低16位反复相加,直到高16位为零,这个低16位数的取反则为校验和。
代码摘自ecos cfe代码
1.uint16_t ip_chksum(uint16_t initcksum, uint8_t *ptr, int len)
2.{
3. unsigned int cksum;
4. int idx;
5. int odd;
6.
7. cksum = (unsigned int) initcksum;
8.
9. odd = len & 1;
10. len -= odd;
11.
12. for (idx = 0; idx < len; idx += 2) {
13. cksum += ((unsigned long) ptr[idx] << 8) + ((unsigned long) ptr[idx+1]);
14. }
15.
16. if (odd) { /* buffer is odd length */
17. cksum += ((unsigned long) ptr[idx] << 8);
18. }
19.
20. /*
21. * Fold in the carries
22. */
23.
24. while (cksum >> 16) {
25. cksum = (cksum & 0xFFFF) + (cksum >> 16);
26. }
27.
28. return cksum;
29.}
该函数考虑了传入数据长度不为偶数的情况,在计算TCP校验和时会存在数据长度为奇数的情况。另外该函数返回值并没有取反,这样可以方便调用该函数重复计算checksum(计算tcp的checksum时会用到),需要自己在函数外取反。
TCP的checksum计算
TCP的checksum计算与IP头的checksum又有不同,需要加入伪首部,伪首部12个字节,包含源地址、目的地址、协议、TCP长度信息。即像下面这样填充:
1.uint16_t tcp_chksum(uint16_t initcksum, uint8_t *tcphead, int tcplen , uint32_t *srcaddr, uint32_t *destaddr)
2.{
3. uint8_t pseudoheader[12];
4. uint16_t calccksum;
5.
6. memcpy(&pseudoheader[0],srcaddr,IP_ADDR_LEN);
7. memcpy(&pseudoheader[4],destaddr,IP_ADDR_LEN);
8. pseudoheader[8] = 0; /* 填充零 */
9. pseudoheader[9] = IPPROTO_TCP;
10. pseudoheader[10] = (tcplen >> 8) & 0xFF;
11. pseudoheader[11] = (tcplen & 0xFF);
12.
13. calccksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader));
14. calccksum = ip_chksum(calccksum,tcphead,tcplen);
15. calccksum = ~calccksum;
16. return calcchsum;
17.}
注意点:
- 其中tcplen为包含tcp头长度的len,即它的值一般为
ntohs(iph->tot_len) - (iph->ihl*4)
- 记得先将tcp头的checksum字段清零再计算