重新计算TCP校验和

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

我正在编写用于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

任何建议都会很棒!

c
3个回答
0
投票

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

你的代码使用无符号16位整数(BTW,我建议使用fixed width integer types,而不是intshort,当大小很重要时,如本例所示),所以当添加它们时,进位丢失了。

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

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


0
投票
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标头的更改同样有效。


0
投票

根据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;
© www.soinside.com 2019 - 2024. All rights reserved.