繁体   English   中英

重新计算TCP校验和

[英]Recalculate TCP checksum

我正在编写一个用于DDoS缓解的用户空间应用程序,并实现某些缓解策略,我需要能够即时更改TCP选项以及数据包报头中的序列和确认号之类的内容。

计算校验和的通常方法是重新创建一个新的TCP伪报头,并在整个数据包上进行迭代以计算校验和,但是还有另一种方法,该方法涉及仅计算仅更改的字之间的差并进行单补码减法。

我当前的代码似乎经常被1或2关闭。我怀疑这是一个问题,因为我没有正确处理进位/借位。 对于如何解决此问题,我几乎一无所知:

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;

在这种情况下: 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

产生的校验和应为fef3 ,而应为fef2

任何建议将是惊人的!

根据RFC 1071 ,使用16位1的补码和计算校验和。

在2的补码机上,必须通过“结束进位”来计算1的补码和,即,将最高有效位的任何溢出都加到最低有效位中。

因此,在更新校验和时,应“反转”“随身携带”。

即,每个负进位减1,然后为每个正进位加1。

像这样:

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;

由于我只需要编辑序列号或确认序列号,因此上述代码是我的建议,可以使用所有人的建议。 这将说明所有进位,因为在每个算术运算之后进行折叠,而不是在可能导致结果倾斜的末尾进行折叠。

只要填充少于16位到16位的内容,这对于IP头或TCP头的更改同样有效。

TCP / IP校验和使用1的补码算法,类似于2的补码加进位反馈。 即,如果您将两个16位值相加并得到一个进位,则需要将总和加1。

您的代码使用无符号的16位整数(顺便说一句,我建议使用固定宽度的整数类型 ,而不是intshort ,因为大小很重要,在这种情况下),因此添加它们时,进位会丢失。

更好的方法是将32位变量用于中间结果,然后反馈进位。 例如:

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