TCP連接狀態(tài)
圖1是TCP三次握手、數(shù)據(jù)傳輸、四次揮手三個(gè)階段的狀態(tài)轉(zhuǎn)移圖,狀態(tài)說(shuō)明如下:
- LISTEN:偵聽(tīng)來(lái)自客戶端的TCP端口的連接請(qǐng)求
- SYN-SENT:再發(fā)送連接請(qǐng)求后等待匹配的連接請(qǐng)求(如果有大量這樣的狀態(tài)包,檢查是否中招了)
- SYN-RCVD:再收到和發(fā)送一個(gè)連接請(qǐng)求后等待對(duì)方對(duì)連接請(qǐng)求的確認(rèn)(如有大量此狀態(tài),估計(jì)被flood攻擊了)
- ESTABLISHED:代表一個(gè)打開(kāi)的連接
- FIN-WAIT-1:等待遠(yuǎn)程TCP連接中斷請(qǐng)求,或先前的連接中斷請(qǐng)求的確認(rèn)
- FIN-WAIT-2:從遠(yuǎn)程TCP等待連接中斷請(qǐng)求
- CLOSE-WAIT:等待從本地用戶發(fā)來(lái)的連接中斷請(qǐng)求
- LAST-ACK:等待原來(lái)的發(fā)向遠(yuǎn)程TCP的連接中斷請(qǐng)求的確認(rèn)(不是什么好東西,此項(xiàng)出現(xiàn),檢查是否被攻擊)
- TIME-WAIT:等待足夠的時(shí)間以確保遠(yuǎn)程TCP接收到連接中斷請(qǐng)求的確認(rèn)
- CLOSED:沒(méi)有任何連接狀態(tài),連接結(jié)束
本文通過(guò)實(shí)踐例子模擬每個(gè)階段的狀態(tài)變化。

本文用tcpdump抓包分析TCP連接的交互過(guò)程,其中tcpdump Flags含義如下:
- S=SYN 發(fā)起連接標(biāo)志
- P=PUSH 傳送數(shù)據(jù)標(biāo)志
- F=FIN 關(guān)閉連接標(biāo)志
- R=RESET 異常關(guān)閉連接,鏈接重置
- . 表示沒(méi)有任何標(biāo)志,表示返回ack
為什么要三次握手?
- 如果只有一次握手,Client不能確定與Server的單向連接,更加不能確定Server與Client的單向連接;
- 如果只有兩次握手,Client確定與Server的單向連接,但是Server不能確定與Client的單向連接;
- 只有三次握手,Client與Server才能相互確認(rèn)雙向連接,實(shí)現(xiàn)雙工數(shù)據(jù)傳輸。

為什么要四次揮手?
“三次握手”的第二次握手發(fā)送SYN+ACK回應(yīng)第一次握手的SYN,但是“四次揮手”的第二次揮手只能發(fā)送ACK回應(yīng)第一次揮手的FIN,因?yàn)榇藭r(shí)Server可能還有數(shù)據(jù)傳輸給Client,所以Server傳輸數(shù)據(jù)完成后才能發(fā)起第三次揮手發(fā)送FIN給Client,等待Client的第四次揮手ACK。

下面通過(guò)例子模擬TCP連接過(guò)程的狀態(tài)轉(zhuǎn)移。
情況1:Client啟動(dòng)服務(wù),Server不啟動(dòng)服務(wù)
Server(127.0.0.1:2017)未啟動(dòng)服務(wù),如圖4抓包所示,Client(127.0.0.1:32906)發(fā)送SYN(localhost.32906 > localhost.2017: Flags [S])啟動(dòng)TCP連接,Server返回localhost.2017 > localhost.32906: Flags [R.],R=RESET表示異常關(guān)閉連接,連接重置。

情況2:Server啟動(dòng)服務(wù),Client不啟動(dòng)服務(wù)
如圖5所示,Server(127.0.0.1:2017)監(jiān)聽(tīng)2017端口,TCP連接狀態(tài)是LISTEN,等待Client發(fā)起TCP連接,如圖6所示,tcpdump抓包沒(méi)有連接數(shù)據(jù)。


情況3:Client連接Server
如圖7所示,Server(127.0.0.1:2017)啟動(dòng)2017端口監(jiān)聽(tīng)連接,Client(127.0.0.1:55702)啟動(dòng)55702端口訪問(wèn)2017端口的Server,由圖7、圖1和圖2比對(duì),兩端的雙向連接已經(jīng)建立,TCP狀態(tài)轉(zhuǎn)移到ESTABLISHED。正常情況下,SYN-SENT和SYN-RCVD轉(zhuǎn)移非常快,netstat難以捕獲,捕獲場(chǎng)景請(qǐng)查看下面異常情況1和異常情況2。
如圖8抓包所示,三行數(shù)據(jù)對(duì)應(yīng)圖2的三次握手,說(shuō)明雙向連接已經(jīng)建立。


情況4:Client傳輸數(shù)據(jù)
從圖1和圖9看,數(shù)據(jù)傳輸過(guò)程中Server(127.0.0.1:2017)和Client(127.0.0.1:55866)的TCP連接狀態(tài)均為ESTABLISHED。

從圖10紅色方框看,Client(localhost:55866)向Server(localhost:2017)發(fā)送數(shù)據(jù)(localhost.55866 > localhost.2017: Flags [P.]),Server返回ack(localhost.2017 > localhost.55866: Flags [.])確認(rèn)。

情況5:Client斷開(kāi)連接,Server保持連接
Client(localhost.41416)主動(dòng)斷掉TCP連接,進(jìn)入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT狀態(tài),分別順序?qū)?yīng)圖11紅色方框的三行記錄,對(duì)比圖3,第一行表示第一次揮手,第二行表示第二/三次揮手,第三行表示第四次揮手。

對(duì)比12和圖3,Client(127.0.0.1:41416)主動(dòng)斷掉TCP連接,進(jìn)入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT狀態(tài),等待2MSL,如果2MSL內(nèi)Server(127.0.0.1:2017)沒(méi)有發(fā)送FIN,意味著Server已經(jīng)接收到Client的FIN ACK,1分鐘(/proc/sys/net/ipv4/tcp_fin_timeout:60)超時(shí)后Client TCP狀態(tài)轉(zhuǎn)移為CLOSED,符合TCP四次揮手邏輯。

情況6:Server斷開(kāi)連接,Client保持連接
Server(localhost:2017)主動(dòng)斷掉TCP連接,根據(jù)圖13倒數(shù)第二行,對(duì)比圖3(Server/Client反著對(duì)比),Server發(fā)送FIN后TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移到FIN-WAIT-1,根據(jù)圖13倒數(shù)第一行,Server接收到Client(localhost:40277)發(fā)送的FIN ACK,由FIN-WAIT-1轉(zhuǎn)移到FIN-WAIT-2狀態(tài),如圖14所示,超時(shí)后轉(zhuǎn)移到CLOSED。
如圖14所示,Client(127.0.0.1:40277)回應(yīng)Server FIN ACK,但是Client未斷開(kāi)連接,TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移為CLOSE_WAIT,不會(huì)由CLOSE_WAIT轉(zhuǎn)移為L(zhǎng)AST-ACK。
如圖15所示,Client(localhost:40277)關(guān)閉連接,發(fā)送FIN,Client由CLOSE_WAIT -> LAST-ACK -> CLOSED。Server(localhost:2017)已經(jīng)關(guān)閉,故回復(fù)localhost.2017 > localhost.40277: Flags [R],R=RESET表示異常關(guān)閉連接,連接重置。



正常情況下,SYN-SENT、SYN-RCVD、LAST-ACK這些狀態(tài)轉(zhuǎn)移非???,netstat很難查看到,如果netstat出現(xiàn)這些狀態(tài),有可能受到攻擊,下面將模擬這些場(chǎng)景。
異常情況1:模擬出現(xiàn)SYN-SENT
從圖2看到,正常情況,三次握手Client的TCP狀態(tài)很快轉(zhuǎn)移到ESTABLISHED,不會(huì)長(zhǎng)時(shí)間停留在SYN_SENT。但是如果Server不返回SYN ACK,Client通過(guò)netstat可以查看到SYN_SENT。
1.設(shè)置防火墻iptables丟棄發(fā)送給Server(127.0.0.1:2017)的數(shù)據(jù)包,這樣Server不會(huì)響應(yīng)Client發(fā)送的SYN而返回SYN ACK。如圖16,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client發(fā)送給Server的SYN。
iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP

2.Client(localhost:35996)發(fā)送SYN給Server(localhost:2017),如圖17所示,從localhost.35996 > localhost.2017: Flags [S]行記錄看,Client TCP連接狀態(tài)轉(zhuǎn)移到SYN_SENT,防火墻丟棄發(fā)送給Server的SYN,Server端也就不會(huì)發(fā)送SYN ACK給Client,結(jié)合圖16和圖18,Client TCP連接狀態(tài)不會(huì)由SYN_SENT轉(zhuǎn)移到ESTABLISHED,Server TCP連接狀態(tài)不會(huì)由LISTEN轉(zhuǎn)移到SYN_RCVD。
如圖17紅色方框所示,Client端以2的倍數(shù)遞增的間隔重試6次,重試時(shí)間間隔:1s、2s、4s、8s、16s、32s。如圖18、19所示,Client TCP連接狀態(tài)保持在SYN_SENT,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖17的重試時(shí)間。



3.客戶端握手超時(shí)報(bào)錯(cuò)信息:Connection timed out
Exception in thread "main" java.net.ConnectException: Connection timed out (Connection timed out)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at java.net.Socket.<init>(Socket.java:434)
at java.net.Socket.<init>(Socket.java:211)
at io.socket.Client1.main(Client1.java:17)
異常情況2:模擬出現(xiàn)SYN-RCVD
從圖2看到,正常情況,Server的TCP連接狀態(tài)很快轉(zhuǎn)移到ESTABLISHED,不會(huì)長(zhǎng)時(shí)間停留在SYN_RCVD。但是如果Client不返回SYN ACK,Server通過(guò)netstat可以查看到SYN_RCVD。
1.設(shè)置防火墻丟棄Server(127.0.0.1:2017)發(fā)送出去的數(shù)據(jù)包,這樣Client不會(huì)響應(yīng)Server發(fā)送的SYN而返回SYN ACK。如圖20,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Server發(fā)送的SYN&ACK。
iptables -I INPUT -s 127.0.0.1 -p tcp --sport 2017 -j DROP

2.Client(localhost:36436)發(fā)送SYN給Server(localhost:2017),Server發(fā)送SYN ACK給Client,但是被防火墻丟棄Server發(fā)送Client端的SYN&ACK,Client也就不會(huì)發(fā)送SYN ACK給Server,結(jié)合圖20和圖22,Client TCP連接狀態(tài)轉(zhuǎn)移為SYN_SENT,但是不會(huì)轉(zhuǎn)移到ESTABLISHED,Server TCP連接狀態(tài)由LISTEN轉(zhuǎn)移到SYN_RCVD,但是不會(huì)轉(zhuǎn)移到ESTABLISHED。
如圖21紅色方框所示,Client收不到Server 的SYN ACK,從localhost.36436 > localhost.2017: Flags [S.]的行記錄看,Client以2的倍數(shù)遞增的間隔重試6次,重試時(shí)間間隔:1s、2s、4s、8s、16s、32s。
Server收不到Client發(fā)送的SYN ACK,從localhost.2017 > localhost.36436: Flags [S.]的行記錄看,Server端以2的倍數(shù)遞增的間隔重試5次,重試時(shí)間間隔:1s、2s、4s、8s、16s。
如圖22所示,Client TCP連接狀態(tài)保持在SYN_SENT,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖21的重試時(shí)間。Server TCP連接狀態(tài)保持在SYN_RCVD,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖21的重試時(shí)間。


異常情況3:模擬出現(xiàn)FIN_WAIT1
從圖3看到,正常情況,四次握手Client斷開(kāi)連接后,TCP連接狀態(tài)很快轉(zhuǎn)移到FIN_WAIT2,不會(huì)長(zhǎng)時(shí)間停留在FIN_WAIT1。但是如果Server不返回FIN ACK,Client通過(guò)netstat可以查看到FIN_WAIT1。
1.Client在斷開(kāi)連接之前,設(shè)置防火墻丟棄Client發(fā)送給Server(127.0.0.1:2017)的數(shù)據(jù)包,這樣Server不會(huì)響應(yīng)Client發(fā)送的FIN而返回FIN ACK。如圖23,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client發(fā)送的FIN。
iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP

2.設(shè)置防火墻丟棄Client(127.0.0.1:40604)發(fā)送出去的包,Client主動(dòng)斷開(kāi)連接,此時(shí)Server保持連接,如圖24所示, 從localhost.40604 -> localhost.2017 Flags [F.]的行記錄,Client發(fā)送FIN給Server,但是被防火墻丟棄,結(jié)合圖23和圖25,Client的TCP狀態(tài)轉(zhuǎn)移過(guò)程:ESTABLISHED -> FIN_WAIT1,Client收不到Server發(fā)送的FIN ACK, TCP狀態(tài)不能由FIN_WAIT1轉(zhuǎn)移到FIN_WAIT2;Server TCP狀態(tài)為ESTABLISHED,Server接收不到Client的FIN而沒(méi)有發(fā)送FIN ACK, TCP狀態(tài)不能由ESTABLISHED轉(zhuǎn)移到CLOSE_WAIT。
如圖24紅色方框所示,Client收不到Server發(fā)送的FIN ACK,從localhost.40604 -> localhost.2017 Flags [F.]行記錄看,Client重試6次,重試時(shí)間間隔:1s、1s、2s、3s、6s、14s、26s。
如圖25所示,Client TCP連接狀態(tài)保持在FIN_WAIT1,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖24的重試時(shí)間。


異常情況4:模擬出現(xiàn)LAST-ACK
從圖3看,Server主動(dòng)斷開(kāi)連接,實(shí)際上此時(shí)Server變成客戶端,Client和Server的TCP狀態(tài)反著查看TCP四次揮手,Client發(fā)送FIN&ACK后CLOSE-WAIT -> LAST-ACK,接收到Server的FIN ACK后LAST-ACK -> CLOSED。
1.Server(127.0.0.1:2017)主動(dòng)斷開(kāi)連接后,Client(127.0.0.1:40541)主動(dòng)斷開(kāi)連接前,設(shè)置防火墻丟棄Client發(fā)送出去的包,這樣Server不能接收到Client發(fā)送的FIN,Client(127.0.0.1:40541)TCP狀態(tài)由CLOSE-WAIT過(guò)渡到LAST-ACK,超時(shí)由LAST-ACK轉(zhuǎn)移為CLOSED。如圖26,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client FIN。
iptables -I INPUT -s 127.0.0.1 -p tcp --sport 40541 -j DROP

2.Server(127.0.0.1:2017)主動(dòng)斷開(kāi)連接,此時(shí)Client(127.0.0.1:40541)保持連接,如圖27所示, localhost.2017 > localhost.40541 Flags [F.],Server發(fā)送FIN給Client,結(jié)合圖26和圖28,Server的TCP狀態(tài)轉(zhuǎn)移過(guò)程:ESTABLISHED -> FIN_WAIT1 -> FIN_WAIT2,Client的TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移為CLOSE_WAIT。
如圖27紅色方框所示,因?yàn)镃lient(localhost:40541)發(fā)給Server的FIN被防火墻丟棄,所以Client收不到Server發(fā)送的FIN ACK,從localhost.40541 > localhost.2017 Flags [F.]行記錄看,Client重試7次,重試時(shí)間間隔:1s、1s、1s、4s、6s、13s、26s。
設(shè)置防火墻丟棄Client(127.0.0.1:40541)發(fā)送出去的包,緊接著Client主動(dòng)斷開(kāi)連接,結(jié)合圖26和圖28,Client的TCP狀態(tài)由CLOSE_WAIT轉(zhuǎn)移為L(zhǎng)AST_ACK,因?yàn)镾erver(127.0.0.1:2017)接收不到Client發(fā)送的FIN(已被防火墻丟棄),也就不能給Client發(fā)送FIN ACK,LAST_ACK不能直接轉(zhuǎn)移為CLOSED,超時(shí)后LAST_ACK -> CLOSED,超時(shí)時(shí)長(zhǎng)對(duì)應(yīng)圖27的重試時(shí)長(zhǎng)。

