由問題驅(qū)動几缭,看一下TCP連接的本質(zhì)吧慰技。
1. TCP如何保證消息的有序和不丟包贵少?
Sequence Number就是SYN——包的序號,用來解決網(wǎng)絡(luò)包亂序(reordering)問題铆惑。
Acknowledgement Number就是ACK——用于確認(rèn)收到范嘱,用來解決不丟包的問題。
2. 三次握手的目的员魏?
為了建立可靠的數(shù)據(jù)傳輸丑蛤,TCP通信雙方相互告知初始化序列號(ISN),并確定對方已經(jīng)收到ISN的(使用ACK機(jī)制)撕阎。
3. 三次握手的過程受裹?
- 客戶端發(fā)送一個
SYN
段,并指明客戶端的初始序列號闻书,即ISN(c)
. - 服務(wù)端發(fā)送自己的
SYN
段作為應(yīng)答名斟,同樣指明自己的ISN(s)
脑慧。為了確認(rèn)客戶端的SYN
魄眉,將ISN(c)+1
作為ACK
數(shù)值。這樣闷袒,每發(fā)送一個SYN
坑律,序列號就會加1. 如果有丟失的情況,則會重傳囊骤。 - 為了確認(rèn)服務(wù)器端的
SYN
晃择,客戶端將ISN(s)+1
作為返回的ACK
數(shù)值。
4. 為什么不能用兩次握手進(jìn)行連接也物?
三次握手的目的就是為了建立可靠的連接宫屠,TCP通信雙方都必須維護(hù)一個序列號,以標(biāo)識發(fā)送的包哪些是被對方收到的滑蚯。三次握手的過程中通信雙方要相互告知初始化序列號浪蹂,并確定對方已經(jīng)收到抵栈。
如果只是兩次握手,至多只有連接發(fā)起方的初始化序列號(ISN)能夠被確認(rèn)坤次,另一方的序列化得不到確認(rèn)古劲。
5. 為什么建立連接是三次握手,而關(guān)閉連接卻是四次揮手呢缰猴?
四次揮手過程:
主機(jī)A發(fā)送
FIN
后产艾,進(jìn)入終止等待狀態(tài),服務(wù)器B收到主機(jī)A連接的釋放報文滑绒,就立即給主機(jī)A發(fā)送ACK
闷堡。然后服務(wù)器B就進(jìn)入了close-wait
狀態(tài)。并且服務(wù)器B再次發(fā)送
FIN
通知主機(jī)A關(guān)閉連接疑故,服務(wù)器B進(jìn)入最后確定狀態(tài)缚窿。主機(jī)A收到服務(wù)器B
FIN
請求后,會發(fā)送一個ACK
告訴服務(wù)器B收到焰扳,于是客戶端和服務(wù)器都關(guān)閉了倦零。
FIN —— 該報文段的發(fā)送方已經(jīng)結(jié)束向?qū)Ψ桨l(fā)送數(shù)據(jù)。
這是因為服務(wù)端在LISTEN狀態(tài)下吨悍,收到建立連接請求的SYN報文后扫茅,把ACK和SYN放在一個報文里發(fā)送給客戶端。而關(guān)閉連接時育瓜,當(dāng)收到對方的FIN報文時葫隙,僅僅表示對方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),己方是否現(xiàn)在關(guān)閉發(fā)送數(shù)據(jù)通道躏仇,需要上層應(yīng)用來決定恋脚,因此,己方ACK和FIN一般都會分開發(fā)送焰手。
一句話總結(jié):是否關(guān)閉通道糟描,是上層應(yīng)用決定的的,TCP無權(quán)將FIN和ACK一同發(fā)送书妻。
6. 三次握手建立連接時SYN超時船响?
server端接到了clien發(fā)的SYN后回了SYN-ACK后client掉線了,server端沒有收到client回來的ACK躲履,那么见间,這個連接處于一個中間狀態(tài),即沒成功工猜,也沒失敗米诉。于是,server端如果在一定時間內(nèi)沒有收到的TCP會重發(fā)SYN-ACK
篷帅。在Linux下史侣,默認(rèn)重試次數(shù)為5次汗销,重試的間隔時間從1s開始每次都翻售,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s抵窒,總共31s弛针,第5次發(fā)出后還要等32s都知道第5次也超時了,所以李皇,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s削茁,TCP才會把斷開這個連接。
一句話總結(jié):服務(wù)器未收到客戶端的確定ACK
掉房,便會一直重試茧跋。
7. 關(guān)于SYN攻擊?
我們說過卓囚,在建立連接時瘾杭,server未收到client端的ACK通知,便開始了長達(dá)63s的重試機(jī)制哪亿。
7.1 什么叫做SYN Flood:
一些惡意的人就為此制造了SYN Flood
攻擊——給服務(wù)器發(fā)了一個SYN后粥烁,就下線了,于是服務(wù)器需要默認(rèn)等63s才會斷開連接蝇棉,這樣讨阻,攻擊者就可以把服務(wù)器的syn連接的隊列耗盡,讓正常的連接請求不能處理篡殷。
7.2 妥協(xié)版的TCP協(xié)議:synccookies:
于是钝吮,Linux下給了一個叫tcp_syncookies
的參數(shù)來應(yīng)對這個事——當(dāng)SYN
隊列滿了后,TCP會通過源地址端口板辽、目標(biāo)地址端口和時間戳打造出一個特別的Sequence Number
發(fā)回去(又叫cookie
)奇瘦,如果是攻擊者則不會有響應(yīng),如果是正常連接劲弦,則會把這個 SYN Cookie發(fā)回來耳标,然后服務(wù)端可以通過cookie建連接(即使你不在SYN隊列中)。請注意瓶您,請先千萬別用tcp_syncookies來處理正常的大負(fù)載的連接的情況麻捻。因為纲仍,synccookies
是妥協(xié)版的TCP協(xié)議呀袱,并不嚴(yán)謹(jǐn)。
7.3 對于正常的請求郑叠,你應(yīng)該調(diào)整三個TCP參數(shù)可供你選擇:
第一個是:tcp_synack_retries
可以用他來減少重試次數(shù)夜赵;
第二個是:tcp_max_syn_backlog
,可以增大SYN連接數(shù)乡革;
第三個是:tcp_abort_on_overflow
處理不過來干脆就直接拒絕連接了寇僧。
8. 如何設(shè)置ISN的值摊腋?
三次握手的一個重要功能是客戶端和服務(wù)端交換ISN(Initial Sequence Number)
, 以便讓對方知道接下來接收數(shù)據(jù)的時候如何按序列號組裝數(shù)據(jù)。
如果ISN是固定的嘁傀,攻擊者很容易猜出后續(xù)的確認(rèn)號兴蒸。
ISN = M + F(localhost, localport, remotehost, remoteport
)
M是一個計時器,每隔4微秒加1细办。 F是一個Hash算法橙凳,根據(jù)源IP、目的IP笑撞、源端口岛啸、目的端口生成一個隨機(jī)數(shù)值。要保證hash算法不能被外部輕易推算得出茴肥。
一句話總結(jié):ISN不能是固定不變的坚踩,一般是計時器(每4微妙+1)+隨機(jī)hash值設(shè)置的,防止被攻擊瓤狐。
9瞬铸、序列號回繞
因為ISN是隨機(jī)的,所以序列號容易就會超過2^31-1. 而tcp對于丟包和亂序等問題的判斷都是依賴于序列號大小比較的础锐。此時就出現(xiàn)了所謂的tcp序列號回繞(sequence wraparound)問題赴捞。怎么解決?
內(nèi)核代碼:
/** The next routines deal with comparing 32 bit unsigned ints
* and worry about wraparound (automatic with unsigned arithmetic).*/
static inline int before(__u32 seq1, __u32 seq2){
return (__s32)(seq1-seq2) < 0;}
序列號發(fā)生回繞后郁稍,序列號變小赦政,相減之后,把結(jié)果變成有符號數(shù)了耀怜,因此結(jié)果成了負(fù)數(shù)恢着。
假設(shè)seq1=255, seq2=1(發(fā)生了回繞)财破。
seq1 = 1111 1111 seq2 = 0000 0001
我們希望比較結(jié)果是
seq1 - seq2=
1111 1111
-0000 0001
————————————
1111 1110
由于我們將結(jié)果轉(zhuǎn)化成了有符號數(shù)掰派,由于最高位是1,因此結(jié)果是一個負(fù)數(shù)左痢,負(fù)數(shù)的絕對值為
0000 0001 + 1 = 0000 0010 = 2
因此seq1 - seq2 < 0
文章參考: