1003
技術社區[雲棲]
找出重傳較高的TCP連接
原創文章:來自找出重傳較高的TCP連接
這裏先給出某一台主機上評估tcp重傳的指標,TCP重傳率定義:
TCP重傳率 = TCP重傳的報文數量/TCP輸出的報文數量;
即tcp retransfer radio = Retrans/outSegs
在linux係統中可以通過/proc/net/snmp得到各層網絡協議收發包的情況,另外一些擴展的tcp指標可以通過tcpext在/proc/net/netstat文件中讀到。監控某台主機重傳率的通常方法:可以每隔1秒從這兩個文件中分別讀到TcpRetransSegs和TcpOutSegs和上一次記錄取差值後,再使用重傳率計算公式。
簡單的觀察方法在centos 7等3.10內核中可以使用以下命令實時觀察係統中每秒tcp重傳報文數量:
watch -n 1 'nstat -z -t 1 | grep -e TcpExtTCPSynRetrans -e TcpRetransSegs -e TcpOutSegs -e TcpInSegs'
其中TcpExtTCPSynRetrans代表syn報文和synack報文的重傳數量,TcpRetransSegs代表總的重傳數量,TcpOutSegs代表總的tcp報文發出數量,TcpInSeg代表總的入報文數量通常用於計算tcp吞吐量。
備注:TcpExtTCPSynRetrans是centos 7係統(與linux內核有關)中新加入的,在2.6.32等係統內核中沒有這個參數。
下邊考慮一個場景:如果監控到一台主機的tcp重傳率較高,如達到20%以上,且在這台主機係統上開了成百上千的tcp listen監聽端口,同時間tcp連接並發高達數十萬,此時如何得知哪些監聽端口上重傳較高? 哪些tcp連接在不斷重傳報文導致?可能的原因是什麼?下邊將解析這些問題。
方法1,如果觀察到重傳率較高時可以通過tcpdump或wireshark抓包,將主機中這段時間的所有tcp報文都抓下來保存為文件,再通過wireshark專家係統進行分析得到重傳率較高的連接。wireshark通常過濾重傳的命令是tcp.analysis.retransmission。
方法2,從內核統計重傳根源處入手,使用systemtap下探點的方式拿到重傳報文連接四元組信息。
如果對wireshark的統計與分析比較熟練使用方法1可以快速得到結果,但有興趣探究內核tcp如何實現的話我選擇使用方法2。下邊的主要說明方法2。
找到TcpRetransSegs,TcpExtTCPSynRetrans兩個參數在linux 3.10內核代碼中的位置如下:
net\ipv4\tcp_output.c
int __tcp_retransmit_skb(struct sock sk, struct sk_buff skb) 函數中也有一處:
編寫打印出重傳報文四元組信息簡要的stap腳本如下:
#! /usr/bin/env stap
#
# fenghui8611@sina.com
#sudo stap -DSTP_NO_OVERLOAD -v -g retran_tcpseg.stp
probe begin {
print ("\n")
}
#TCPHDR_SYN=0x02
function is_retran_syn:long (skb:long)
%{
struct sk_buff skb = (struct sk_buff )STAP_ARG_skb;
int ret = 0;
if (((struct tcp_skb_cb *)&(skb->cb[0]))->tcp_flags & 0x02 )
ret = 1;
STAP_RETVALUE = ret;
%}
probe kernel.statement("__tcp_retransmit_skb@net/ipv4/tcp_output.c:2554")
{
printf("tcp_transmit_skb() err=%d, syn flag=%d ", $err, is_retran_syn($skb))
saddr = format_ipaddr(__ip_sock_saddr($sk), __ip_sock_family($sk))
daddr = format_ipaddr(__ip_sock_daddr($sk), __ip_sock_family($sk))
sport = __tcp_sock_sport($sk)
dport = __tcp_sock_dport($sk)
printf("%s:%d => %s:%d\n", saddr, sport, daddr, dport)
}
probe kernel.function("tcp_v4_send_synack").return
{
err = $return
if (err == 0) {
saddr = format_ipaddr(__ip_sock_saddr($sk), __ip_sock_family($sk))
daddr = format_ipaddr(__ip_sock_daddr($sk), __ip_sock_family($sk))
sport = __tcp_sock_sport($sk)
dport = __tcp_sock_dport($sk)
printf("synack retran %s:%d => %s:%d\n", saddr, sport, daddr, dport)
}
}
probe end {
print ("\n")
}
運行命令與輸出信息示例:
#sudo stap -DSTP_NO_OVERLOAD -v -g retran_tcpseg.stp tcp_transmit_skb() err=0 syn flag=0 0.0.0.0:59738 => 100.116.2xx.16x:41239 tcp_transmit_skb() err=0 syn flag=0 0.0.0.0:59738 => 100.116.2xx.13x:44026 tcp_transmit_skb() err=0 syn flag=0 0.0.0.0:59738 => 100.116.2xx.13x:34901 tcp_transmit_skb() err=0 syn flag=0 0.0.0.0:59738 => 100.116.2xx.15x:11685
根據四元組信息可以深入的分析為什麼這條連接有問題,是鏈路問題還是軟件問題?比如拿到上述結果分析結論:因交換機出口處設置了帶寬限製,當達到限製值時很多tcp 59738端口發出的tcp報文段被丟棄,導致此台主機重傳較高;另外syn報文重傳較高時可以重點分析服務器端tcp連接隊列是否有問題等。
最後更新:2017-11-04 23:03:35