簡體   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