我正在编写用于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
。
任何建议都会很棒!
TCP / IP校验和使用1的补码算法,类似于2的补码和进位反馈。即如果你添加两个16位值,并得到一个进位,你需要在总和上加1。
你的代码使用无符号16位整数(BTW,我建议使用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);
}
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标头的更改同样有效。
根据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;