计算 IPv4 数据包的校验和

问题描述 投票:0回答:3

如何计算像这样收到的示例 IPv4 数据包的校验和:

4500 062A 42A1 8001 4210 XXXX C0A8 0001 C0A8 0003

其中

xxxx
是需要与数据包一起发送的校验和。

到目前为止,这是我发现的: 转换为二进制,加法,然后是总和的补码,一旦转换回来应该是校验和?但我做不到那么远:

4500 062A 42A1 8001 4210 XXXX C0A8 0001 C0A8 0003

4500 - 0100 0101 0000 0000

062A - 0000 0110 0010 1010

42A1 - 0100 0010 1010 0001

8001 - 1000 0000 0000 0001

xxxx - 0000

C0A8 - 1100 0000 1010 1000

0003 - 0000 0000 0000 0011

将它们全部相加:

4500 - 0100 0101 0000 0000 062A - 0000 0110 0010 1010


19242 - 0100101100101010 //第一个结果 42A1 - 0100 0010 1010 0001


36299 - 01000110111001011 //第二个结果 8001 - 1000 0000 0000 0001


69068 - 010000110111001100 //第三个结果 C0A8 - 1100 0000 1010 1000


118388 - 011100111001110100 0003 - 0000 0000 0000 0011

118391 - 011100111001110111 取 118391- 100011000110001000 的补码

—> 23188 - 100011000110001000

23188 也是校验和吗??

networking ip ipv4
3个回答
2
投票

IPv4 Header Checksum 在 RFC 791, INTERNET PROTOCOL:

标头校验和:16 位

仅标头上的校验和。由于某些标头字段发生变化(例如, 生存时间),这在每个点重新计算和验证 互联网标头已处理。

校验和算法为:

校验和字段是一个的16位补码 报头中所有 16 位字的补码总和。为了 计算校验和,校验和字段的值为零。

这是一个简单的计算校验和和实验证据 表明它是足够的,但它是临时的,可以被替换 CRC 程序,取决于进一步的经验。

此算法在RFC 1071,计算互联网校验和,由RFC 1141,增量更新互联网校验和,由RFC 1624,通过增量更新计算互联网校验和更新。


2
投票

关于 IPv4 校验和计算,有四个 RFC 可供阅读:

RFC 791、RFC 1071、RFC 1141 和 RFC 1624。

我没有阅读它们,直到遇到一个非常奇怪的问题,我才打算这样做。但是还有另一个很棒的页面在谈论 IPv4 的校验和字段:Wikipedia's IPv4 Header Checksum。按照维基百科上的示例,我尝试计算校验和:

Step 1. 计算所有 IPv4 标头字段的补码和

我们可以将所有这些数字以十六进制或二进制形式相加。我会做两种方法:

步骤 1a.1:我将添加前两个字段 (4500 + 062A)。然后我将第三个字段添加到之前添加的结果 (4B2A+42A1)。从那时起,我会将下一个字段的值添加到累计总和中。

              1             1111            1       1
4500    4B2A   8DCB  10DCC  14FDC  21084  21085  2D12D
062A    42A1   8001   4210   C0A8   0001   C0A8   0003
-----  -----   ----  -----  -----  -----  -----  -----
4B2A    8DCB  10DCC  14FDC  21084  21085  2D12D  2D130

步骤 1b.1

In octave:
-------------------

octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans =  184624
octave:15> 
octave:15> dec2hex(184624)
ans = 2D130
octave:16>

Step 1a.2:step 1a.1的加法是一堆数字的简单数学加法。另一方面,在补码加法中,我们还需要做一件事。由于结果必须在 16 位以内(意味着结果应该是 4 个十六进制数字),因此这意味着我们必须处理结果的最高有效位。 0x2D130 中的“2”必须去某个地方,因为一个的补码加法必须与我们添加的所有数字具有相同的长度。在补码加法中,我们将溢出的数字加回到数字中。所以,

0xD130+ 0x2 = 0xD132

header字段的补码是:

0xD132
.

步骤 1b.2:

In octave:
-------------------

octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans =  184624
octave:15> dec2hex(184624)
ans = 2D130
octave:16> 16^4
ans =  65536
octave:17> 184624-(2*65536)
ans =  53552
octave:18> 184624-(2*65536)+2
ans =  53554
octave:19> dec2hex(184624-(2*65536)+2)
ans = D132
octave:20> 

步骤 1c.1:

将字段转换为二进制:

4500: 0100 0101 0000 0000
062A: 0000 0110 0010 1010 
42A1: 0100 0010 1010 0001
8001: 1000 0000 0000 0001
4210: 0100 0010 0001 0000
C0A8: 1100 0000 1010 1000
0001: 0000 0000 0000 0001
C0A8: 1100 0000 1010 1000
0003: 0000 0000 0000 0011

4500+062A:
00 0100 0101 0000 0000+
00 0000 0110 0010 1010
-----------------------
00 0100 1011 0010 1010 (04B2A)

+42A1
00 0100 1011 0010 1010+
00 0100 0010 1010 0001
-----------------------
00 1000 1101 1100 1011 (08DCB)    

+8001

00 1000 1101 1100 1011+
00 1000 0000 0000 0001
------------------------
01 0000 1101 1100 1100 (10DCC)

+4210
01 0000 1101 1100 1100+
00 0100 0010 0001 0000
------------------------
01 0100 1111 1101 1100 (14FDC)

+C0A8
01 0100 1111 1101 1100+
00 1100 0000 1010 1000
-----------------------
10 0001 0000 1000 0100 (21084)

+0001
10 0001 0000 1000 0100+
00 0000 0000 0000 0001
-----------------------
10 0001 0000 1000 0101 (21085)

+C0A8
10 0001 0000 1000 0101+
00 1100 0000 1010 1000
-----------------------
10 1101 0001 0010 1101 (2D12D)

+0003
10 1101 0001 0010 1101+
00 0000 0000 0000 0011
-----------------------
10 1101 0001 0011 0000 (2D130)

步骤 1c.2: 将 10(最左边的位)添加到数字:

1101 0001 0011 0000+
0000 0000 0000 0010
--------------------
1101 0001 0011 0010 (D132)

步骤 2.

无论您如何进行补码加法,您现在都必须对结果进行补码。任何二进制数的补码只是“翻转数字中所有位”的奇特名称:

1101 0001 0011 0010 -> 0010 1110 1100 1101 (2ECD)

如果你想在不转换为二进制的情况下取一个十六进制数字的补码,下面是一个方便的表格:

n 1'
0 F
1 E
2 D
3 C
4
5 A
6 9
7 8
8 7
9 6
A 5
4
C 3
D 2
E 1
F 0

所以取 D132 的补码是:

D->2
1->E
3->C
2->D

所以 IPv4 标头

4500 062A 42A1 8001 4210 XXXX C0A8 0001 C0A8 0003
的校验和是
0x2ECD
.

一个演示步骤的小程序:

https://go.dev/play/p/DOj28mjuqtP


0
投票

我在 C 中使用了下一个算法

uint16_t swap_uint16(uint16_t val)
{
    return (val << 8) | (val >> 8 );
}

unsigned short in_cksum(const void* ip)
{
    const unsigned short* ptr = (const unsigned short *)ip;
    unsigned int sum = 0;
    for (int i = 0; i < 10; i++)
    {
        if (i != 5) // skip IP packet checksum value.
        {
            unsigned short value = *ptr;
            sum += swap_uint16(value);
        }
        ptr++;
    }
    if (sum > UINT16_MAX)
    {
        sum -= UINT16_MAX;
    }
    sum = ~sum;
    sum = swap_uint16(sum);

    return sum;
}
© www.soinside.com 2019 - 2024. All rights reserved.