简体   繁体   English

重新计算TCP校验和

[英]Recalculate TCP checksum

I'm writing a userspace application for DDoS mitigation and to implement certain mitigation policies I need to be able to change TCP options and things like sequence and acknowledgement numbers in packet headers on the fly. 我正在编写一个用于DDoS缓解的用户空间应用程序,并实现某些缓解策略,我需要能够即时更改TCP选项以及数据包报头中的序列和确认号之类的内容。

The usual way to calculate the checksum would be to recreate a new TCP pseudoheader and iterate over the entire data packet to calculate the checksum but there is another way which involves just calculating the differences between only the words changed and doing a ones-complement subtraction. 计算校验和的通常方法是重新创建一个新的TCP伪报头,并在整个数据包上进行迭代以计算校验和,但是还有另一种方法,该方法涉及仅计算仅更改的字之间的差并进行单补码减法。

My current code seems to be off frequently by 1 or 2. I suspect this is a problem because I'm not handling carries/borrows correctly. 我当前的代码似乎经常被1或2关闭。我怀疑这是一个问题,因为我没有正确处理进位/借位。 I have very little idea on how I would go about fixing this: 对于如何解决此问题,我几乎一无所知:

unsigned short pseq1, pseq2, pseq3, pseq4
unsigned short sum1, sum2, sum3, prevcheck;
short pdiff1, pdiff2;

u_char *pkt_data;

prevcheck = (pkt_data[50] << 8) | pkt_data[51];

pseq1 = (pkt_data[38] << 8) | pkt_data[39]; 
pseq2 = (pkt_data[40] << 8) | pkt_data[41];

pkt_data[38] = ((seq_num - offsetResult) >> 24) & 0xFF;
pkt_data[39] = ((seq_num - offsetResult) >> 16) & 0xFF; 
pkt_data[40] = ((seq_num - offsetResult) >> 8) & 0xFF;
pkt_data[41] = (seq_num - offsetResult) & 0xFF;

pseq3 = (pkt_data[38] << 8) | pkt_data[39];
pseq4 = (pkt_data[40] << 8) | pkt_data[41];

pdiff1 = pseq1 - pseq3;

pdiff2 = pseq2 - pseq4;

sum1 = ~pdiff1 + ~pdiff2;

sum2 = ~sum1;

sum3 = sum2 + prevcheck; 

pkt_data[50] = (sum3 >> 8) & 0xFF; 
pkt_data[51] = sum3 & 0xFF;

In this instance: 68 05 ca 57 94 05 60 73 5c d0 57 bf 08 00 45 00 00 2c 00 00 40 00 3f 06 bc a5 b9 aa 2a 6a 42 f9 58 19 00 50 ed 48 fc e4 57 e5 6e c0 f6 c8 60 12 72 10 fe f3 00 00 02 04 05 b4 00 00 在这种情况下: 68 05 ca 57 94 05 60 73 5c d0 57 bf 08 00 45 00 00 2c 00 00 40 00 3f 06 bc a5 b9 aa 2a 6a 42 f9 58 19 00 50 ed 48 fc e4 57 e5 6e c0 f6 c8 60 12 72 10 fe f3 00 00 02 04 05 b4 00 00

The produced checksum is fef3 when it should be fef2 . 产生的校验和应为fef3 ,而应为fef2

Any suggestions would be amazing! 任何建议将是惊人的!

According to RFC 1071 , the checksum is calculated using a 16-bit 1's complement sum. 根据RFC 1071 ,使用16位1的补码和计算校验和。

On a 2's complement machine, the 1's complement sum must be computed by means of an "end around carry", ie, any overflows from the most significant bits are added into the least significant bits. 在2的补码机上,必须通过“结束进位”来计算1的补码和,即,将最高有效位的任何溢出都加到最低有效位中。

So you should "reverse" the "end around carry" when updating the checksum. 因此,在更新校验和时,应“反转”“随身携带”。

Ie subtract 1 for each negative carry and add 1 for each positive carry. 即,每个负进位减1,然后为每个正进位加1。

Something like this: 像这样:

int32_t sum; // or just int, but make sure it's 32-bit or more
unsigned short pseq1, pseq2, pseq3, pseq4
unsigned short prevcheck;

u_char *pkt_data;

prevcheck = (pkt_data[50] << 8) | pkt_data[51];

pseq1 = (pkt_data[38] << 8) | pkt_data[39]; 
pseq2 = (pkt_data[40] << 8) | pkt_data[41];

pkt_data[38] = ((seq_num - offsetResult) >> 24) & 0xFF;
pkt_data[39] = ((seq_num - offsetResult) >> 16) & 0xFF; 
pkt_data[40] = ((seq_num - offsetResult) >> 8) & 0xFF;
pkt_data[41] = (seq_num - offsetResult) & 0xFF;

pseq3 = (pkt_data[38] << 8) | pkt_data[39];
pseq4 = (pkt_data[40] << 8) | pkt_data[41];

sum = ~prevcheck - pseq1 - pseq2;

while (sum >> 16)
    sum = (sum & 0xFFFF) + (sum >> 16); // "end around carry"

sum += pseq3 + pseq4;

while (sum >> 16)
    sum = (sum & 0xFFFF) + (sum >> 16); // "end around carry"

sum3 = (short)~sum;

pkt_data[50] = (sum3 >> 8) & 0xFF; 
pkt_data[51] = sum3 & 0xFF;
u_int32_t sum;
u_int16_t oldSeq1, oldSeq2, newSeq1, newSeq2;
u_int16_t oldChecksum;

sum = ~oldChecksum - oldSeq1 - oldSeq2;

sum = (sum & 0xFFFF) + (sum >> 16);

sum = sum + newSeq1 + newSeq2;

sum = (sum & 0xFFFF) + (sum >> 16);

sum = (u_int16_t)~sum;

As my requirement was only to edit either sequence or acknowledge sequence numbers the above code was the solution for me using suggestions from all. 由于我只需要编辑序列号或确认序列号,因此上述代码是我的建议,可以使用所有人的建议。 This will account for all carries as the fold is done after each arithmetic operation instead of at the end where it could skew results. 这将说明所有进位,因为在每个算术运算之后进行折叠,而不是在可能导致结果倾斜的末尾进行折叠。

This will work equally for changes in the IP header or TCP header as long as you pad anything shorter than 16 bits to 16 bits. 只要填充少于16位到16位的内容,这对于IP头或TCP头的更改同样有效。

TCP/IP checksums use 1's complement arithmetic, which is similar to 2's complement plus carry feedback. TCP / IP校验和使用1的补码算法,类似于2的补码加进位反馈。 Ie if you add two 16bit values, and get a carry, you need to add 1 to the sum. 即,如果您将两个16位值相加并得到一个进位,则需要将总和加1。

Your code uses unsigned 16bit integers (BTW, I recommend using fixed width integer types , instead of int and short , when the size matters, as in this case), so when adding them the carry is lost. 您的代码使用无符号的16位整数(顺便说一句,我建议使用固定宽度的整数类型 ,而不是intshort ,因为大小很重要,在这种情况下),因此添加它们时,进位会丢失。

A better way is to use 32bit variables for the intermediate result, then feeding back the carry. 更好的方法是将32位变量用于中间结果,然后反馈进位。 For example: 例如:

uint16_t a, b; ... uint32_t sum = (uint32_t)a + (uint32_t)b; if (sum > 0x10000u) { sum = (sum >> 16) + (sum & 0xffff); }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM