TCP SNMP counters netstat -s 各項參數意義


轉自 roveryu.blog.chinaunix.net

 

最近在總結2.6.32與2.6.18的差異,我特別有興趣的是網絡部分,但猛然發現其實SNMP counters我也並不能准確解釋它們的含義,於是就有了以下總結,還不完整,持續總結中:
 
加粗的項代表TODO,還有一些counters沒有列出來。
 
難免有錯誤,請大家指正吧,我的codebase是linux2.6.git~
 
TCP Basic

類別 名稱 描述
Tcp ActiveOpens tcp_connect(),發送SYN時,加1
Tcp PassiveOpens tcp_create_openreq_child(), 被動三路握手完成,加1
Tcp AttemptFails
  1. tcp_done():如果在SYN_SENT/SYN_RECV狀態下結束一個連接,加1
  2. tcp_check_req():被動三路握手最后一個階段中的輸入包中如果有RST|SYN標志,加1
Tcp CurrEstab tcp_set_state(),根據ESTABLISHED是新/舊狀態,分別加減一。
Tcp EstabResets tcp_set_state(),新狀態為TCP_CLOSE,如果舊狀態是ESTABLISHED/TCP_CLOSE_WAIT就加1
Tcp ListenOverflows tcp_v4_syn_recv_sock():三路握手最后一步完全之后,Accept queue隊列超過上限時加1
Tcp ListenDrops tcp_v4_syn_recv_sock():任何原因,包括Accept queue超限,創建新連接,繼承端口失敗等,加1
Tcp MaxConn 0
Tcp InSegs tcp_v4_rcv(),收到一個skb,加1
Tcp InErrs
  1. tcp_rcv_established()->tcp_validate_incoming():如果有SYN且seq >= rcv_nxt,加1
  2. 以下函數內,如果checksum錯誤或者包長度小於TCP header,加1:
    1. tcp_v4_do_rcv()
    2. tcp_rcv_established()
    3. tcp_v4_rcv()
Tcp OutSegs
  1. tcp_v4_send_reset(), tcp_v4_send_ack(),加1
  2. tcp_transmit_skb(), tcp_make_synack(),加tcp_skb_pcount(skb)(見TCP_COOKIE_TRANSACTIONS)
Tcp OutRsts tcp_v4_send_reset(), tcp_send_active_reset()加1
 
TCP Loss & Retrans

類別 名稱 描述
Tcp TCPTimeouts
  1. 在RTO timer中,從CWR/Open狀態下第一次超時的次數,其余狀態不計入這個計數器。
  2. SYN-ACK的超時次數。
Tcp RtoAlgorithm 1,tcp_mib_init()初始化
Tcp RtoMax 120000,tcp_mib_init()初始化:TCP_RTO_MAX*1000/HZ,TCP_RTO_MAX=120*HZ
Tcp RtoMin 200,tcp_mib_init()初始化:TCP_RTO_MIN*1000/HZ,TCP_RTO_MIN=HZ/5

以下計數器,統計的是調用tcp_retransmit_skb()的次數,由於sysctl_tcp_retrans_collapse/TSO的原因,一次tcp_transmit_skb()調用可能發送多個segs

類別 名稱 描述
Tcp RetransSegs 重傳次數,包括RTO timer和常規重傳,即tcp_retransmit_skb()中調用tcp_transmit_skb(),成功返回即+1。
TcpExt TCPForwardRetrans (非RTO timer)發送新數據的次數,即在tcp_fastretrans_alert()/tcp_simple_retransmit()->tcp_xmit_retransmit_queue()中,

如果發現skb->seq > tp->retransmit_high(一般為snd_una),如果當前狀態為Recovery,啟用了SACK,並且發送條件允許就在這個函數中發送新數據。

TcpExt TCPFastRetrans

(非RTO timer)快速重傳次數,即tcp_fastretrans_alert()/tcp_simple_retransmit()->tcp_xmit_retransmit_queue()中,如果不是LOSS狀態,就加1

TcpExt TCPSlowStartRetrans (非RTO timer)重傳次數:即tcp_fastretrans_alert()/tcp_simple_retransmit()->tcp_xmit_retransmit_queue()中,如果是LOSS狀態,就加1
TcpExt TCPLostRetransmit

根據SACK數據推測出的重段包丟失計數器:在tcp_sacktag_write_queue()->tcp_mark_lost_retrans(), 如果發現tcp_highest_sack_seq(tp)超過某skb在重傳時的snd_nxt(TCB->ack_seq),就認為這個重傳包 已經丟失了,加1(加的不是段數)。tcp_highest_sack_seq(tp)是被SACK過的具有最高SEQ號的skb的seq。

TcpExt TCPSpuriousRTOs tcp_process_frto(),如果frto_counter !=0 && frto_counter != 1加1
 
TCP ACK & SACK

類別 名稱 描述
TcpExt DelayedACKLocked tcp_delack_timer(): delay ACK定時器因為user已經鎖住而無法發送ACK的次數。
TcpExt DelayedACKLost
  1. tcp_validate_incoming()->tcp_send_dupack():當輸入包不在接收窗口內,或者PAWS失敗后,計數器加1
  2. tcp_data_queue(): 輸入包的結束序列號< RCV_NXT時,加1
TcpExt DelayedACKs tcp_delack_timer():調用tcp_send_ack()的次數,無論是否是成功。
TcpExt TCPHPAcks tcp_ack():接收到包,進入quick path時加1
TcpExt TCPPureAcks tcp_ack():接收慢速路徑中的pure ACK數量
TcpExt TCPDSACKIgnoredNoUndo tcp_sacktag_write_queue(): undo_marker為0並且接收到非法D-SACK塊時,加1,即SACK中的序號太舊。
TcpExt TCPDSACKIgnoredOld tcp_sacktag_write_queue(): undo_marker不為0,並且接收到非法D-SACK塊時,加1,即SACK中的序號太舊。
TcpExt TCPSACKDiscard tcp_sacktag_write_queue(): 非法SACK塊(不包括D-SACK)計數,即SACK中的序號太舊。
TcpExt TCPDSACKOldSent tcp_dsack_set():如果SACK塊開始序號小於RCV.NXT,加1
TcpExt TCPDSACKOfoSent tcp_dsack_set():如果SACK塊開始序號大於等於RCV.NXT,加1
TcpExt TCPSACKReneging tcp_clean_rtx_queue(): 如果snd_una(輸入skb->ack)之后的具有最小開始序號skb(即sk_write_queue中的第一個skb)中有TCPCB_SACKED_ACKED標志,此時加1,這說明接收者已經丟掉了之前它已經SACK過的數據。
TcpExt TCPSackFailures tcp_retransmit_timer(): 在Reorder狀態下,或者sacked_out不為0時,發生RTO,並且啟用了SACK,加1
TcpExt TCPSackRecoveryFail tcp_retransmit_timer(): 在Reovery狀態下發生RTO,並且啟用了SACK,加1
TcpExt TCPDSACKRecv tcp_check_dsack(): 收到D-SACK,並且SACK0開始序號 < ACK號,加1
TcpExt TCPDSACKOfoRecv tcp_check_dsack(): 收到D-SACK,並且SACK0開始序號 >= ACK號,但SACK1包括SACK0。
TcpExt TCPSackRecovery tcp_fastretrans_alert(): SACK TCP進入Reovery狀態的次數
TcpExt TCPSackShifted tcp_ack()->tcp_sacktag_write_queue()->tcp_sacktag_walk()->tcp_shift_skb_data()->tcp_shifted_data()

在tcp_sacktag_walk()時,一個SACK可能會導致切割某skb,新切出來的skb放到被切的skb之后。
根據SACK的觀點,如果“舊的skb”(變小了)能夠與它之前的skb合並,本計數器,就加1。
這個合並過程,叫作shift

TcpExt TCPSackShiftFallback tcp_ack()->tcp_sacktag_write_queue()->tcp_sacktag_walk()->tcp_shift_skb_data()

與上相反,如果不能shift,本計數器加1。原因可能如下:

  1. 不支持GSO
  2. prev skb不完全是paged的
  3. SACK的序號已經ACK過,等等
TcpExt TCPSackMerged tcp_ack()->tcp_sacktag_write_queue()->tcp_sacktag_walk()->tcp_shift_skb_data()->tcp_shifted_data()

在上面介紹的shift過程中,如果發現分割之后的skb被它之前的skb完全“吃掉”,本計數器加1

 
TCP TIME_WAIT

類別 名稱 描述
TcpExt TW inet_twdr_do_twkill_work(): TIME_WAIT超時的socket數量(timeout >= 4s)
之所以按超時分別對待timewait socket,可能是考慮到長超時的socket的timeout時間分布比較分散,需要使用不同的查找方法。
TcpExt TWKilled inet_twdr_twcal_tick(): TIME_WAIT超時的socket數量.(timeout < 4s),
僅在啟用sysctl_tw_recycle,並且使用TCP timestamp option時才會有這種情況,這時使用3.5x RTO時作為timewait timeout,而默認timeout為60s
TcpExt TWRecycled tcp_v4_connect() -> __inet_check_established(): 在建立時,如果port是從TIME_WAIT socket中復用的,加1
TcpExt TCPTimeWaitOverflow tcp_time_wait(): 當系統無法分配新的tcp_timewait_socket,或者tw_count(scheduled timewait sockets)超過sysctl_max_tw_buckets時,加1

TCP Others

類別 名稱 描述
TcpExt TCPRenoRecoveryFail tcp_retransmit_timer(): 在Reovery狀態下發生RTO,並且沒有啟用SACK,加1
TcpExt TCPRenoFailures tcp_retransmit_timer(): 在Reorder狀態下,或者sacked_out不為0時,發生RTO,並且沒有啟用SACK,加1
TcpExt TCPRenoRecovery tcp_fastretrans_alert(): 不使用SACK的TCP進入Reovery狀態的次數
 
此外,可以用這個腳本收集這些counters:
 
#! /usr/bin/python

proc_files = ("/proc/net/netstat", "/proc/net/snmp")

def parse_proc_files(fn):
    stats = {}
    lines = file(fn).readlines()
    n_lines = len(lines)
    n = 0
    while n < n_lines:
        titles = lines[n].split(" ") # TcpExt: TcpXX SackXX
        values = lines[n+1].split(" ") # TcpExt: 11 23213
        kind = titles[0]
        del titles[0]
        del values[0]
        sub_stats = stats.get(kind, {})
        n_cols = len(titles)
        for i in range(n_cols):
            sub_stats[titles[i].strip()] = values[i].strip()
        stats[kind] = sub_stats
        n += 2
    return stats


def show_parsed(stats):
    kind_list = stats.keys()
    kind_list.sort()
    for kind in kind_list:
        title_list = stats[kind].keys()
        title_list.sort()
        for title in title_list:
            print "%-10s%-25s\t%20s" % (kind, title, stats[kind][title])

s = {}
for fn in proc_files:
    new_s = parse_proc_files(fn)
    for new_kind in new_s:
        if new_kind not in s: # unlikely
            s[new_kind] = new_s[new_kind]
        else:
            s[new_kind].update(new_s[new_kind])

show_parsed(s)

 

 
 
 
繼續講述/proc/net/netstat, /proc/net/snmp中TCP的故事。
 
TCP Congestion Processing

類別 名稱 描述
TcpExt TCPDSACKUndo tcp_ack() -> tcp_fastretrans_alert() -> tcp_try_undo_dsack()

Disorder狀態下,undo完成(undo_retrans == 0)的次數。

TcpExt TCPFullUndo tcp_ack() -> tcp_fastretrans_alert() -> tcp_try_undo_recovery()

Recovery狀態時,接收到到全部確認(snd_una >= high_seq)后且已經undo完成(undo_retrans == 0)的次數。

TcpExt TCPPartialUndo tcp_ack() -> tcp_fastretrans_alert() -> tcp_undo_partial()

Recovery狀態時,接收到到部分確認(snd_una < high_seq)時但已經undo完成(undo_retrans == 0)的次數。

TcpExt TCPLossUndo tcp_ack() -> tcp_fastretrans_alert() -> tcp_try_undo_loss()

Loss狀態時,接收到到全部確認(snd_una >= high_seq)后且已經undo完成(undo_retrans == 0)的次數。

TcpExt TCPRenoReorder

在tcp_update_reordering()中更新,當metric > tp->reordering並且沒有啟用SACK,本計數器加1 綜合來說,在sacked_out“不可靠”時,tp->reordering被更新為當前窗口中的“已用seg”個數,同時包括未確認(和已確認的?)數據,但不包括lost_out。

A. tcp_ack() -> tcp_fastretrans_alert() -> tcp_add_reno_sack() -> tcp_check_reno_reordering() -> tcp_update_reordering(): 
在Open/Recovery/Disorder/CWR狀態下接收到dupACK時:
如果sacked_out + lost_out > packets_out,
用metric( = packets_out)調用tcp_update_reordering()
B. tcp_ack() -> tcp_clean_rtx_queue() -> tcp_remove_reno_sacks() -> tcp_check_reno_reordering() -> tcp_update_reordering() : 
在清理rtx queue時,會從packets_out, lost_out, sacked_out中減去確認了的seg數量,
如果sacked_out + lost_out > packets_out,
用metric( = packets_out + acked_pcount)調用tcp_update_reordering()

注:

  1. sacked_out : 接收到的dupACK數量
  2. lost_out : 限制最小值為1,最大值為packets_out
  3. tp->reordering: 創建socket時,被動建立連接時,進入Loss狀態時,初始化為sysctl_tcp_reordering

 

TcpExt TCPSACKReorder

在tcp_update_reordering()中更新,當metric > tp->reordering並且啟用SACK但關閉FACK時,本計數器加1
A. tcp_ack() -> tcp_sacktag_write_queue() -> tcp_update_reordering() 
在tcp_sacktag_walk()中會計算fackets_out(通過累加state.fack_count),這個值即從snd_una開始到已經SACK的最高序號間的seg數量(包括沒有被SACK覆蓋的)。判斷發生亂序的條件是: (1)發現針對重傳報文的D-SACK;(2)當前接收到SACK序號比以前接收到的最大SACK序號小。state.reord是發生亂序時的最小fack_count,即在“snd_una + fack_count”處發生了亂序。
metric = tp->fackets_out - state.reord,即可能發生亂序的最多報文數。
B. tcp_ack() -> tcp_clean_rtx_queue() -> tcp_update_reordering() 
與A.類似,tcp_clean_rtx_queue()計算rtx queue中被SACK過的數據(非重傳)中空洞,reord保存“最小號”空洞的位置(在重傳隊列中的“座次”)。而prior_fackets - reord即可能發生亂序的TCP segments數量。如果沒有SACK,reorder = prior_fackets = 0
metric = prior_fackets - reord

TcpExt TCPFACKReorder 與TCPSACKReorder類似,如果同時啟用了SACK和FACK,就增加本計數器。
TcpExt TCPTSReorder tcp_ack() -> tcp_fastretrans_alert() -> tcp_undo_partial() -> tcp_update_reordering() 

Recovery狀態時,接收到到部分確認(snd_una < high_seq)時但已經undo完成(undo_retrans == 0)的次數。 數量上與TCPPartialUndo相等。

 
 
 
TCP Others

類別 名稱 描述
TcpExt TCPRenoRecoveryFail tcp_retransmit_timer(): 在Reovery狀態下發生RTO,並且沒有啟用SACK,加1
TcpExt TCPRenoFailures tcp_retransmit_timer(): 在Reorder狀態下,或者sacked_out不為0時,發生RTO,並且沒有啟用SACK,加1
TcpExt TCPRenoRecovery tcp_fastretrans_alert(): 不使用SACK的TCP進入Reovery狀態的次數
TcpExt ArpFilter arp_rcv() -> NETFILTER(ARP_IN) -> arp_process()

與TCP無關,接收到ARP packet時做一次輸出路由查找(sip, tip),如果找到的路由項的device與輸入device的不同,計數器加1

TcpExt EmbryonicRsts tcp_v4_do_rcv() -> tcp_v4_hnd_req() -> tcp_check_req(): 在三手握手時的SYN_RECV狀態中接收到RST或者SYN的次數。
TcpExt LockDroppedIcmps tcp_v4_err(): 接收到ICMP錯誤報文,但tcp socket被user鎖住
TcpEx OfoPruned tcp_data_queue() -> tcp_try_rmem_schedule()

慢速路徑中,如果不能將數據直接復制到user space,需要加入到sk_receive_queue前,會檢查receiver side memory是否允許,如果rcv_buf不足就可能prune ofo queue。此時計數器加1

TcpExt OutOfWindowIcmps tcp_v4_err(): 接收到的ICMP,但ICMP中的TCP頭序號不在接收窗口之內的次數,有兩個可能情況:(1)LISTEN狀態時,序號不等待ISN;(2)其他狀態時,序號不在SND_UNA .. SND_NXT之間
TcpExt PAWSActive tcp_rcv_synsent_state_process(): 在發送SYN后,接收到ACK,但PAWS檢查失敗的次數。
TcpExt PAWSEstab tcp_validate_incoming()

tcp_timewait_state_process()
tcp_check_req()
輸入包PAWS失敗次數。

TcpExt PAWSPassive tcp_v4_conn_request(): 三路握手最后一個ACK的PAWS檢查失敗次數。
TcpExt PruneCalled tcp_data_queue() -> tcp_try_rmem_schedule()

慢速路徑中,如果不能將數據直接復制到user space,需要加入到sk_receive_queue前,會檢查receiver side memory是否允許,如果rcv_buf不足就可能prune ofo queue。此時計數器加1

TcpExt RcvPruned tcp_data_queue() -> tcp_try_rmem_schedule()

慢速路徑中,如果不能將數據直接復制到user space,需要加入到sk_receive_queue前,會檢查receiver side memory是否允許,如果rcv_buf不足就可能prune receive queue,如果prune失敗了,此計數器加1。

TcpExt SyncookiesFailed cookie_v4_check(): SYN cookie檢查失敗次數。
TcpExt SyncookiesRecv cookie_v4_check(): 接收SYN cookie次數。
TcpExt SyncookiesSent cookie_v4_init_sequence(): 生成SYN cookie次數。
TcpExt TCPAbortFailed tcp_send_active_reset(): alloc_skb()或者tcp_transmit_skb()失敗。
TcpExt TCPAbortOnClose tcp_close(): sk_receive_queue中仍有數據的次數。
TcpExt TCPAbortOnData tcp_rcv_state_process(): 在FIN_WAIT_1/FIN_WAIT_2狀態下接收到后續數據(序號>RCV_NXT);或者,TCP_LINGER2設置值<0,計數器加1

tcp_close(): 沒有未讀數據,但設置了SO_LINGER並且linger timeout=0, 計數器加1,此時TCP正常斷開連接sk_prot->disconnect()。

TcpExt TCPAbortOnLinger tcp_close(): 因TCP_LINGER2設置值<0,FIN_WAIT_2立即切換到CLOSE的次數。
TcpExt TCPAbortOnMemory 在執行tcp_close()/probe timer/keepalive timer時,orphan sockets數量和tcp_memory_allocated是否超過最大值的次數。
TcpExt TCPAbortOnSyn tcp_validate_incoming(): 出現SYN,並且序號大於RCV_NXT的次數。
TcpExt TCPAbortOnTimeout RTO/probe/keepalive timer到達最大重試次數或者最長重試時間的次數

 
 

TCP Others Others

類別 名稱 描述
TcpExt TCPBacklogDrop tcp_v4_rcv() : 如果socket被user鎖住,后退一步內核會把包加到sk_backlog_queue,但如果因為sk_rcv_buf不足的原因入隊失敗,計數器加1
TcpExt TCPDeferAcceptDrop tcp_check_req(): 如果啟用TCP_DEFER_ACCEPT,這個計數器統計了被丟掉了“Pure ACK”個數。

TCP_DEFER_ACCEPT:允許listener只有在連接上有數據才創建新的socket,以抵御syn-flood攻擊。

TcpExt TCPDirectCopyFromBacklog tcp_recvmsg(): 如果有數據在softirq里面從backlog queue中直接復制到userland memory上,計數器加1
TcpExt TCPDirectCopyFromPrequeue tcp_recvmsg(): 如果有數據在這個syscall里從prequeue中直接復制到userland memory上,計數器加1
TcpExt TCPHPHits tcp_rcv_established(): 如果有skb通過“快速路徑”進入到sk_receive_queue上,計數器加1。

特別地,Pure ACK以及直接復制到user space上的都不算在這個計數器上。

TcpExt TCPHPHitsToUser tcp_rcv_established(): 如果有skb通過“快速路徑”直接復制到user space上,計數器加1。
TcpExt TCPLossFailures tcp_retransmit_timer(): icsk_retransmit==0(第一次進入重傳狀態)並且處於Loss狀態下,計數器加1

可能情況是:因為partial ACK中從Loss中undo了一些狀態,但還有完全離開Loss

TcpExt TCPMD5NotFound tcp_v4_do_rcv() -> tcp_v4_inbound_md5_hash() : 配置了md5檢查,但在輸入skb中沒有找到對應TCP選項。
TcpExt TCPMD5Unexpected tcp_v4_do_rcv() -> tcp_v4_inbound_md5_hash() : 未配置md5檢查,但在輸入skb中找到了對應TCP選項。
TcpExt TCPMemoryPressures tcp_enter_memory_pressure()在從“非壓力狀態”切換到“有壓力狀態”時計數器加1,可能的觸發點有:
  • tcp_sendmsg()
  • tcp_sendpage()
  • tcp_fragment()
  • tso_fragment()
  • tcp_mtu_probe()
  • tcp_data_queue()
TcpExt TCPMinTTLDrop tcp_v4_err() / tcp_v4_rcv(): 在接收到TCP報文或者TCP相關的ICMP報文時,檢查IP TTL,如果小於socket option設置的一個閥值,就丟包。這個功能是RFC5082(The Generalized TTL Security Mechanism, GTSM)規定的,使用GTSM的通信雙方,都將TTL設置成最大值255,雙方假定了解之間的鏈路情況,這樣可以通過檢查最小TTL值隔離攻擊。
TcpExt TCPPrequeueDropped tcp_v4_rcv() -> tcp_prequeue() : 如果因為內存不足(ucopy.memory < sk->rcv_buf)而加入到prequeue失敗,重新由backlog處理,計數器加1
TcpExt TCPPrequeued tcp_recvmsg() -> tcp_prequeue_process() : tcp_recvmsg()發現可以從prequeue接收到報文,計數器加1(不是每個skb加1)
TcpExt TCPRcvCollapsed tcp_prune_queue() -> tcp_collapse() -> tcp_collapse_one()

tcp_prune_ofo_queue() -> tcp_collapse() 
每當合並sk_receive_queue(ofo_queue)中的連續報文時,計數器加1

TcpExt TCPReqQFullDoCookies tcp_rcv_state_process() -> tcp_v4_conn_request() -> tcp_syn_flood_action()

syn_table過載,進行SYN cookie的次數(取決於是否打開sysctl_tcp_syncookies)。

TcpExt TCPReqQFullDrop tcp_rcv_state_process() -> tcp_v4_conn_request() -> tcp_syn_flood_action()

syn_table過載,丟掉SYN的次數。

TcpExt TCPSchedulerFailed tcp_delack_timer(): 在delay ACK處理功能內,如果prequeue中仍有數據,計數器就加1

加入到prequeue,本來是期待着userspace(使用tcp_recvmsg()之類的系統調用)盡快處理之。其中仍有數據,可能隱含着userspace行為不佳。

TcpExt IPReversePathFilter ip_rcv_finish() -> ip_route_input_noref(): 反向路徑過濾掉的IP分組數量:要么反向路由查找失敗,要么是找到的輸出接口與輸入接口不同。
 
全劇終,如有錯誤,敬請指正。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM