我正在编写用于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
。
任何建议都会很棒!
答案 0 :(得分:0)
TCP / IP校验和使用1的补码算法,类似于2的补码和进位反馈。即如果你添加两个16位值,并获得一个进位,你需要在总和上加1。
您的代码使用无符号16位整数(顺便说一下,我推荐使用fixed width integer types,而不是int
和short
,当大小很重要时,如本例所示),所以在添加它们时遗失了。
更好的方法是使用32位变量作为中间结果,然后反馈进位。例如:
uint16_t a, b;
...
uint32_t sum = (uint32_t)a + (uint32_t)b;
if (sum > 0x10000u) {
sum = (sum >> 16) + (sum & 0xffff);
}
答案 1 :(得分:0)
根据RFC 1071,校验和是使用16位1的补码和计算的。
在2的补码机器上,1的补码总和必须是 通过&#34;结束携带&#34;,即任何溢出来计算 从最重要的位添加到最少 重要的一点。
所以你应该&#34;反向&#34; &#34;结束携带&#34;更新校验和时。
即。每个负值进位减去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;
答案 2 :(得分: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标头的更改同样有效。