知乎千贊的 TCP 文章,我寫錯了一個點(diǎn)。。。
實(shí)驗(yàn)一:模擬 TCP 第一次握手的 SYN 丟包;
實(shí)驗(yàn)二:模擬 TCP 第二次握手的 SYN、ACK 丟包;
實(shí)驗(yàn)三:模擬 TCP 第三次握手的 ACK 包丟;




TCP 三次握手異常情況實(shí)戰(zhàn)分析
TCP 第一次握手的 SYN 丟包了,會發(fā)生了什么?
TCP 第二次握手的 SYN、ACK 丟包了,會發(fā)生什么?
TCP 第三次握手的 ACK 包丟了,會發(fā)生什么?
那會重傳幾次?
超時重傳的時間 RTO 會如何變化?
在 Linux 下如何設(shè)置重傳次數(shù)?
….
實(shí)驗(yàn)場景

客戶端和服務(wù)端都是 CentOs 6.5 Linux,Linux 內(nèi)核版本 2.6.32
服務(wù)端 192.168.12.36,apache web 服務(wù)
客戶端 192.168.12.37
實(shí)驗(yàn)一:TCP 第一次握手 SYN 丟包



date 返回的時間,可以發(fā)現(xiàn)在超時接近 1 分鐘的時間后,curl 返回了錯誤。
第一次是在 1 秒超時重傳
第二次是在 3 秒超時重傳
第三次是在 7 秒超時重傳
第四次是在 15 秒超時重傳
第五次是在 31 秒超時重傳
SYN 超時重傳次數(shù),是如下內(nèi)核參數(shù)指定的:$?cat?/proc/sys/net/ipv4/tcp_syn_retries
5
tcp_syn_retries 默認(rèn)值為 5,也就是 SYN 最大重傳次數(shù)是 5 次。tcp_syn_retries 設(shè)置為 2 次:$?echo?2?>?/proc/sys/net/ipv4/tcp_syn_retries

實(shí)驗(yàn)一的實(shí)驗(yàn)小結(jié)
tcp_syn_retries 值后,客戶端不再發(fā)送 SYN 包。
實(shí)驗(yàn)二:TCP 第二次握手 SYN、ACK 丟包


date 返回的時間前后,可以算出大概 1 分鐘后,curl 報錯退出了。
客戶端發(fā)起 SYN 后,由于防火墻屏蔽了服務(wù)端的所有數(shù)據(jù)包,所以 curl 是無法收到服務(wù)端的 SYN、ACK 包,當(dāng)發(fā)生超時后,就會重傳 SYN 包
服務(wù)端收到客戶的 SYN 包后,就會回 SYN、ACK 包,但是客戶端一直沒有回 ACK,服務(wù)端在超時后,重傳了 SYN、ACK 包,接著一會,客戶端超時重傳的 SYN 包又抵達(dá)了服務(wù)端,服務(wù)端收到后,然后回了 SYN、ACK 包,但是SYN、ACK包的重傳定時器并沒有重置,還持續(xù)在重傳,因?yàn)榈诙挝帐衷跊]收到第三次握手的 ACK 確認(rèn)報文時,就會重傳到最大次數(shù)。
最后,客戶端 SYN 超時重傳次數(shù)達(dá)到了 5 次(tcp_syn_retries 默認(rèn)值 5 次),就不再繼續(xù)發(fā)送 SYN 包了。
咦?客戶端設(shè)置了防火墻,屏蔽了服務(wù)端的網(wǎng)絡(luò)包,為什么 tcpdump 還能抓到服務(wù)端的網(wǎng)絡(luò)包?
如果添加的是
INPUT規(guī)則,則可以抓得到包如果添加的是
OUTPUT規(guī)則,則抓不到包
進(jìn)來的順序 Wire -> NIC -> tcpdump -> netfilter/iptables
出去的順序 iptables -> tcpdump -> NIC -> Wire
tcp_syn_retries 是限制 SYN 重傳次數(shù),那第二次握手 SYN、ACK 限制最大重傳次數(shù)是多少?
tcp_synack_retries 內(nèi)核參數(shù)限制的,其默認(rèn)值如下:$?cat?/proc/sys/net/ipv4/tcp_synack_retries
5
5 次。tcp_syn_retries 設(shè)置為 1,表示客戶端 SYN 最大超時次數(shù)是 1 次,目的是為了防止多次重傳 SYN,把服務(wù)端 SYN、ACK 超時定時器重置。客戶端配置防火墻屏蔽服務(wù)端的數(shù)據(jù)包
客戶端 tcpdump 抓取 curl 執(zhí)行時的數(shù)據(jù)包

客戶端的 SYN 只超時重傳了 1 次,因?yàn)?
tcp_syn_retries值為 1服務(wù)端應(yīng)答了客戶端超時重傳的 SYN 包后,由于一直收不到客戶端的 ACK 包,所以服務(wù)端一直在超時重傳 SYN、ACK 包,每次的 RTO 也是指數(shù)上漲的,一共超時重傳了 5 次,因?yàn)?
tcp_synack_retries值為 5
tcp_syn_retries 依然設(shè)置為 1:$?echo?2?>?/proc/sys/net/ipv4/tcp_synack_retries
$?echo?1?>?/proc/sys/net/ipv4/tcp_syn_retries

客戶端的 SYN 包只超時重傳了 1 次,符合 tcp_syn_retries 設(shè)置的值;
服務(wù)端的 SYN、ACK 超時重傳了 2 次,符合 tcp_synack_retries 設(shè)置的值
實(shí)驗(yàn)二的實(shí)驗(yàn)小結(jié)
實(shí)驗(yàn)三:TCP 第三次握手 ACK 丟包



SYN_RECV 狀態(tài):
ESTABLISHED 狀態(tài):

ESTABLISHED 狀態(tài):


為什么服務(wù)端原本處于
SYN_RECV狀態(tài)的連接,過 1 分鐘后就消失了?為什么客戶端 telnet 輸入 123456 字符后,過了好長一段時間,telnet 才斷開連接?

客戶端發(fā)送 SYN 包給服務(wù)端,服務(wù)端收到后,回了個 SYN、ACK 包給客戶端,此時服務(wù)端的 TCP 連接處于
SYN_RECV狀態(tài);客戶端收到服務(wù)端的 ?SYN、ACK 包后,給服務(wù)端回了個 ACK 包,此時客戶端的 TCP 連接處于
ESTABLISHED狀態(tài);由于服務(wù)端配置了防火墻,屏蔽了客戶端的 ACK 包,所以服務(wù)端一直處于
SYN_RECV狀態(tài),沒有進(jìn)入 ?ESTABLISHED狀態(tài),tcpdump 之所以能抓到客戶端的 ACK 包,是因?yàn)閿?shù)據(jù)包進(jìn)入系統(tǒng)的順序是先進(jìn)入 tcpudmp,后經(jīng)過 iptables;接著,服務(wù)端超時重傳了 SYN、ACK 包,重傳了 5 次后,也就是超過 tcp_synack_retries 的值(默認(rèn)值是 5),然后就沒有繼續(xù)重傳了,此時服務(wù)端的 TCP 連接主動中止了,所以剛才處于 SYN_RECV 狀態(tài)的 TCP 連接斷開了,而客戶端依然處于
ESTABLISHED狀態(tài);雖然服務(wù)端 TCP 斷開了,但過了一段時間,發(fā)現(xiàn)客戶端依然處于
ESTABLISHED狀態(tài),于是就在客戶端的 telnet 會話輸入了 123456 字符;此時由于服務(wù)端已經(jīng)斷開連接,客戶端發(fā)送的數(shù)據(jù)報文,一直在超時重傳,每一次重傳,RTO 的值是指數(shù)增長的,所以持續(xù)了好長一段時間,客戶端的 telnet 才報錯退出了,此時共重傳了 15 次。
服務(wù)端在重傳 SYN、ACK 包時,超過了最大重傳次數(shù)
tcp_synack_retries,于是服務(wù)端的 TCP 連接主動斷開了。客戶端向服務(wù)端發(fā)送數(shù)據(jù)包時,由于服務(wù)端的 TCP 連接已經(jīng)退出了,所以數(shù)據(jù)包一直在超時重傳,共重傳了 15 次, telnet 就斷開了連接。
TCP 第一次握手的 SYN 包超時重傳最大次數(shù)是由 tcp_syn_retries 指定,TCP 第二次握手的 SYN、ACK 包超時重傳最大次數(shù)是由 tcp_synack_retries 指定,那 TCP 建立連接后的數(shù)據(jù)包最大超時重傳次數(shù)是由什么參數(shù)指定呢?
tcp_retries2 指定,默認(rèn)值是 15 次,如下:$?cat?/proc/sys/net/ipv4/tcp_retries2
15
那如果客戶端不發(fā)送數(shù)據(jù),什么時候才會斷開處于 ESTABLISHED 狀態(tài)的連接?
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75??
net.ipv4.tcp_keepalive_probes=9
tcp_keepalive_time=7200:表示?;顣r間是 7200 秒(2小時),也就 2 小時內(nèi)如果沒有任何連接相關(guān)的活動,則會啟動保活機(jī)制
tcp_keepalive_intvl=75:表示每次檢測間隔 75 秒;
tcp_keepalive_probes=9:表示檢測 9 次無響應(yīng),認(rèn)為對方是不可達(dá)的,從而中斷本次的連接。

實(shí)驗(yàn)三的實(shí)驗(yàn)小結(jié)
SYN_RECV 狀態(tài),而客戶端會處于 ESTABLISHED 狀態(tài)。tcp_synack_retries 值(默認(rèn)值 5 次)后,服務(wù)端就會斷開 TCP 連接。如果客戶端沒發(fā)送數(shù)據(jù)包,一直處于
ESTABLISHED狀態(tài),然后經(jīng)過 2 小時 11 分 15 秒才可以發(fā)現(xiàn)一個「死亡」連接,于是客戶端連接就會斷開連接。如果客戶端發(fā)送了數(shù)據(jù)包,一直沒有收到服務(wù)端對該數(shù)據(jù)包的確認(rèn)報文,則會一直重傳該數(shù)據(jù)包,直到重傳次數(shù)超過
tcp_retries2值(默認(rèn)值 15 次)后,客戶端就會斷開 TCP 連接。
