[英]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位整數(順便說一句,我建議使用固定寬度的整數類型 ,而不是int
和short
,因為大小很重要,在這種情況下),因此添加它們時,進位會丟失。
更好的方法是將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.