netstat -st输出解析(二)


netstat -st输出的两个重要信息来源分别是/proc/net/snmp和/proc/net/netstat
本文将分类整理这些counterd的含义以及一些注意事项。


在整理的过程中,发现Rover Yu前辈已经
对这些counter做过详细的整理。关于Rover Yu前辈的整理请查看参考资料中的前三篇。
本着不重复造轮子的原则。本文将尽量遵循以下原则,以期从不同的角度呈现对这些counter的理解。

a. 分类整理:根据涉及的不同TCP细节,对counter做更细致的分类
b. 结合sysctl配置:强调sysctl配置与counter之间的关联
c. 强调异常:哪些counter出现非零值,往往就意味着出现了值得关注的问题
d. 信息抽取: 如何从counter中获取有价值的信息
e. 仅关注TCP相关计数器

计数器分类

类别 涉及counters
常量 RtoAlgorithm、RtoMin、RtoMax、MaxConn
建连统计 ActiveOpens、PassiveOpens、AttemptFails、CurrEstab、EstabResets
数据包统计 InSegs、OutSegs、RetransSegs、InErrs、OutRsts、InCsumErrors、EmbryonicRsts
syncookies功能 SyncookiesSent、SyncookiesRecv、SyncookiesFailed
TIME_WAIT回收 TW、TWRecycled、TWKilled、TCPTimeWaitOverflow
RTO次数 TCPTimeouts、TCPSpuriousRTOs、TCPLossProbes、TCPLossProbeRecovery、
TCPRenoRecoveryFail、TCPSackRecoveryFail、
TCPRenoFailures、TCPSackFailures、
TCPLossFailures
Retrans数量 TCPFastRetrans、TCPForwardRetrans、
TCPSlowStartRetrans、TCPLostRetransmit、
TCPRetransFail
FastOpen TCPFastOpenActive、TCPFastOpenPassive、
TCPFastOpenPassiveFail、TCPFastOpenListenOverflow、
TCPFastOpenCookieReqd
MD5 TCPMD5NotFound、TCPMD5Unexpected
DelayedACK DelayedACKs、DelayedACKLocked、DelayedACKLost、
TCPSchedulerFailed
DSACK TCPDSACKOldSent、TCPDSACKOfoSent、
TCPDSACKRecv、TCPDSACKOfoRecv、
TCPDSACKIgnoredOld、TCPDSACKIgnoredNoUndo
Reorder TCPFACKReorder、TCPSACKReorder、
TCPRenoReorder、TCPTSReorder
Recovery TCPRenoRecovery、TCPSackRecovery、
TCPRenoRecoveryFail、TCPSackRecoveryFail
Abort TCPAbortOnData、TCPAbortOnClose、
TCPAbortOnMemory、TCPAbortOnTimeout、
TCPAbortOnLingerTCPAbortFailed

|reset相关 | |
|内存prune | PruneCalled、RcvPruned、OfoPruned、
TCPMemoryPressures |
|PAWS相关 | PAWSPassive、PAWSActive、PAWSEstab |
|Listen相关 | ListenOverflows、ListenDrops |
|undo相关 | TCPFullUndo、TCPPartialUndo、
TCPDSACKUndo、TCPLossUndo |
|快速路径与慢速路径 | TCPHPHits、TCPHPHitsToUser、
TCPPureAcks、TCPHPAcks |



常量

这些常量是Linux3.10中的默认值,仅在升级了内核版本时才需要关心一下这些值的变化。

RtoAlgorithm:
    默认为1,RTO算法与RFC2698一致
RtoMin:
    默认值为HZ/5,即200ms
RtoMax:
    默认值为120HZ,即120s
MaxConn:
    协议栈本身并不会限制TCP连接总数,默认值为-1.

建连统计

这些统计值中,只有CurrEstab反应的是系统当前状态,而其他值则是反应的历史状态
同时需要注意的是,这些计数器将ESTABLISHED和CLOSE-WAIT状态都作为当前连接数。
可以这么理解:这两个状态都以为这local=>peer方向的连接未被关闭

ActiveOpens:
    主动建连次数,CLOSE => SYN-SENT次数
PassiveOpens:
    被动建连次数,RFC原意是LISTEN => SYN-RECV次数,但Linux选择在三次握手成功后才加1
AttemptFails:
    建连失败次数
EstabResets:
    连接被reset次数,ESTABLISHED => CLOSE次数 + CLOSE-WAIT => CLOSE次数
CurrEstab:
    当前TCP连接数,ESTABLISHED个数 + CLOSE-WAIT个数

数据包统计

这些统计值也是历史值,独立的来看意义并不大。一般可统计一段时间内的变化,关注以下几个指标
a. TCP层的重传率: ΔRetransSegs / ΔOutSegs — 越小越好,如果超过20%(这个值根据实际情况而定)则应该引起注意
b. Reset发送频率: ΔOutRsts / ΔOutSegs — 越小越好,一般应该在1%以内
c. 错误包占比: ΔInErrs / ΔInSegs — 越小越好,一般应该在1%以内,同时由checksum导致的问题包应该更低

InSegs:
    收到的数据包个数,包括有错误的包个数
OutSegs:
    发送的数据包个数
RetransSegs:
    重传的包个数
InErrs:
    收到的有问题的包个数
OutRsts:
    发送的带reset标记的包个数
InCsumErrors:
    收到的checksum有问题的包个数,InErrs中应该只有*小部分*属于该类型
EmbryonicRsts:
    在SYN-RECV状态收到带RST/SYN标记的包个数

syncookies功能

syncookies一般不会被触发,只有在tcp_max_syn_backlog队列被占满时才会被触发
因此SyncookiesSent和SyncookiesRecv一般应该是0。
但是SyncookiesFailed值即使syncookies机制没有被触发,也很可能不为0。
这是因为一个处于LISTEN状态的socket收到一个不带SYN标记的数据包时,就会调
用cookie_v4_check()尝试验证cookie信息。而如果验证失败,Failed次数就加1。

SyncookiesSent:
    使用syncookie技术发送的syn/ack包个数
SyncookiesRecv
    收到携带有效syncookie信息包个数
SyncookiesFailed
    收到携带无效syncookie信息包个数

注: syncookies机制是为应对synflood攻击而被提出来的。


TIME-WAIT回收

TIME-WAIT状态是TCP协议状态机中的重要一环,服务器设备一般都有非常多处于TIME-WAIT状态的socket
如果是在主要提供HTTP服务的设备上,TW值应该接近TcpPassiveOpens值。
一般情况下,sysctl_tcp_tw_reuse和sysctl_tcp_tw_recycle都是不推荐开启的。这里解释了为什么
所以TWKilled和TWRecycled都应该是0。
同时TCPTimeWaitOverflow也应该是0,否则就意味着内存使用方面出了大问题。

TW:
    经过正常的TCP_TIMEWAIT_LEN(60s)结束TW状态的socket数量
TWKilled:
    经过更短的时间结束TW状态的socket数量。
    只有在net.ipv4.tcp_tw_recycle开启时,调度TW timer时才可能用更短的timeout值。
TWRecycled:
    Port从TIMEWAIT socket中复用的次数。
    只有在sysctl_tcp_tw_reuse开启时,才可能加1
    郁闷的是上面两个counter的命名与sysctl的命名真是超级不一致啊。囧...
TCPTimeWaitOverflow:
    如果没有内存分配TIME-WAIT结构体,则加1

RTO次数

RTO超时对TCP性能的影响是巨大的,因此关心RTO超时的次数也非常必要。
当然3.10中的TLP机制能够减少一定量的TCPTimeouts数,将其转换为快速重传。
关于TLP的原理部分,可参考我的这篇wiki

TCPTimeouts:
    RTO timer第一次超时的次数,仅包含直接超时的情况
TCPSpuriousRTOs:
    通过F-RTO机制发现的虚假超时个数
TCPLossProbes:
    Probe Timeout(PTO)导致发送Tail Loss Probe (TLP)包的次数
TCPLossProbeRecovery:
    丢失包刚好被TLP探测包修复的次数

/* 由以下计数器可以看出,进入RTO被触发时,TCP是可能处于多种不同状态的 */
TCPRenoRecoveryFail: (也放到了Recovery类别)
    先进入Recovery阶段,然后又RTO的次数,对端不支持SACK选项
TCPSackRecoveryFail:(也放到了Recovery类别)
    先进入Recovery阶段,然后又RTO的次数,对端支持SACK选项
TCPRenoFailures:
    先进TCP_CA_Disorder阶段,然后又RTO超时的次数,对端不支持SACK选项
TCPSackFailures:
    先进TCP_CA_Disorder阶段,然后又RTO超时的次数,对端支持SACK选项
TCPLossFailures:
    先进TCP_CA_Loss阶段,然后又RTO超时的次数

Retrans数量

这些计数器统计的重传包,都不是由于RTO超时导致进行的重传
如果结合RetransSegs统计来看,如果这些非RTO导致的重传占比较大的话,也算是不幸中的万幸。
另外LostRetransmit的数量应该偏低比较好,重传包如果都大量被丢弃,则真的要注意了。

TCPLostRetransmit:
    丢失的重传SBK数量,没有TSO时,等于丢失的重传包数量
TCPFastRetrans:
    成功快速重传的SKB数量
TCPForwardRetrans:
    成功ForwardRetrans的SKB数量,Forward Retrans重传的序号高于retransmit_high的数据
    TODO: retransmit_high目前的理解是被标记为lost的SKB中,最大的end_seq值
TCPSlowStartRetrans:
    成功在Loss状态发送的重传SKB数量,而且这里仅记录非RTO超时进入Loss状态下的重传数量
    目前找到的一种非RTO进入Loss状态的情况就是:tcp_check_sack_reneging()函数发现
    接收端违反(renege)了之前的SACK信息时,会进入Loss状态
TCPRetransFail:
    尝试FastRetrans、ForwardRetrans、SlowStartRetrans重传失败的次数

FastOpen

TCP FastOpen(TFO)技术是Google提出来减少三次握手开销的技术,
核心原理就是在第一次建连时server计算一个cookies发给client,之后client向
server再次发起建连请求时就可以携带cookies信息验明正身。如果cookies验证通过,
server可以不等三次握手的最后一个ACK包就将client放在SYN包里面的数据传递给application layer。

在3.10内核中,TFO由sysctl_tcp_fastopen开关控制,默认值为0(关闭)。
而且sysctl_tcp_fastopen目前也是推荐关闭的,因为网络中有些middlebox会丢弃那些带有不认识的option的SYN包.
所以正常情况下这些值也应该都是0,当然如果收到过某些不怀好意带TFO cookies信息的SYN包,
TCPFastOpenPassive计数器就可能不为0。

TCPFastOpenActive:
    发送的带TFO cookie的SYN包个数
TCPFastOpenPassive:
    收到的带TFO cookie的SYN包个数
TCPFastOpenPassiveFail:
    使用TFO技术建连失败的次数
TCPFastOpenListenOverflow:
    TFO请求数超过listener queue设置的上限则加1
TCPFastOpenCookieReqd:
    收到一个请求TFO cookies的SYN包时加1

MD5

TCP MD5 Signature选项是为提高BGP Session的安全性而提出的,详见RFC 2385
因此内核中是以编译选项,而不是sysctl接口来配置是否使用该功能的。
如果内核编译是的CONFIG_TCP_MD5SIG选项未配置,则不会支持TCPMD5Sig,下面两个计数器也就只能是0

TCPMD5NotFound:
    希望收到带MD5选项的包,但是包里面没有MD5选项
TCPMD5Unexpected:
    不希望收到带MD5选项的包,但是包里面有MD5选项

DelayedACK

DelayedACK是内核中默认支持的,但即使使用DelayedACKs,每收到两个数据包也
必须发送一个ACK。所以DelayedACKs可以估算为发送出去的ACK数量的一半。
同时DelayedACKLocked反应的是应用与内核争抢socket的次数,
如果占DelayedACKs比例过大可能就需要看看应用程序是否有问题了。

DelayedACKs:
    尝试发送delayed ack的次数,包括未成功发送的次数
DelayedACKLocked:
    由于usr锁住了sock,而无法发送delayed ack的次数
DelayedACKLost:
    TODO 暂时不理解准确含义
TCPSchedulerFailed:
    如果在delay ack处理函数中发现prequeue还有数据,就加1。
    数据放到prequeue,就是想user能尽快处理。如果任由数据,
    则可能user行为调度效果不好
    这个值应该非常接近于零才正常

DSACK

该类型计数器统计的是收/发DSACK信息次数。
DSACKOldSent + DSACKOfoSent可以当做是发送出的DSACK信息的次数,而且概率上来讲
OldSent应该占比更大。
同理DSACKRecv的数量也应该远多于DSACKOfoRecv的数量。
另外DSACK信息的发送是需要sysctl_tcp_dsack开启的,如果发现sent两个计数器为零,则要检查一下了。
一般还是建议开启dsack选项

TCPDSACKOldSent:
    如果收到的重复数据包序号比rcv_nxt(接收端想收到的下一个序号)小,则增加oldsent
TCPDSACKOfoSent:
    如果收到的重复数据包序号比rcv_nxt大,则是一个乱序的重复数据包,增加ofosent
TCPDSACKRecv:
    收到的old dsack信息次数,判断old的方法:dsack序号小于ACK号
TCPDSACKOfoRecv:
    收到的Ofo dsack信息次数
TCPDSACKIgnoredOld:
    当一个dsack block被判定为无效,且设置过undo_marker,则加1
TCPDSACKIgnoredNoUndo:
    当一个dsack block被判定为无效,且未设置undo_marker,则加1

Reorder

当发现了需要更新某条TCP流的reordering值(乱序值)时,以下计数器可能被使用到。
不过下面四个计数器为互斥关系,最少见的应该是TCPRenoReorder,毕竟sack已经被
广泛部署使用了。
TODO: 什么情况下能准确的判断出要更新reorder值呢?

TCPTSReorder:
    如果是被一个partial ack确认后需要更新reorder值,则加1
    这个地方取个TS的名字,还真是费解。不知道是什么的缩写表示了partial ack的含义。
TCPRenoReorder:
    如果被不支持SACK的dupack确认后,需要更新reorder值,则加1
TCPFACKReorder:
    如果在需要更新时判断支持FACK,则加1
TCPSACKReorder:
    如果仅支持SACK,则该计数器加1

关于partial ack的完整内容可参考RFC6582,这里摘要定义部分

In the case of multiple packets dropped from a single window of data,
the first new information available to the sender comes when the
sender receives an acknowledgment for the retransmitted packet (that
is, the packet retransmitted when fast retransmit was first entered).
If there is a single packet drop and no reordering, then the
acknowledgment for this packet will acknowledge all of the packets
transmitted before fast retransmit was entered.  However, if there
are multiple packet drops, then the acknowledgment for the
retransmitted packet will acknowledge some but not all of the packets
transmitted before the fast retransmit.  We call this acknowledgment
a partial acknowledgment.

Recovery

该类型计数器统计的进入快速重传阶段的总次数及失败次数,失败次数是指先进入了
recovery阶段,然后有RTO超时了。Fast Recovery没有成功。
首先由于SACK选项已经大面积使用,RenoRecovery的次数应该远小于SackRecovery的次数
另外fail的次数应该比例较小才比较理想

TCPRenoRecovery:
    进入Recovery阶段的次数,对端不支持SACK选项
TCPSackRecovery:
    进入Recovery阶段的次数,对端支持SACK选项
TCPRenoRecoveryFail: (也放到了RTO次数类别)
    先进入Recovery阶段,然后又RTO的次数,对端不支持SACK选项
TCPSackRecoveryFail:(也放到了RTO次数类别)
    先进入Recovery阶段,然后又RTO的次数,对端支持SACK选项

Abort

abort本身是一种很严重的问题,因此是否有必要关心这些计数器
后三个计数器如果不为0,则往往意味着系统发生了较为严重的问题,需要格外注意

TCPAbortOnClose:
    如果调用tcp_close()关闭socket时,recv buffer中还有数据,则加1
    此时会主动发送一个reset包给对端
TCPAbortOnData:
    如果在FIN_WAIT_1和FIN_WAIT_2状态下收到后续数据,或TCP_LINGER2设置小于0,则计数器加1
TCPAbortOnTimeout:
    因各种计时器(RTO/PTO/keepalive)的重传次数超过上限,而关闭连接时,计数器加1
TCPAbortOnMemory:
    如果orphan socket数量或者tcp_memory_allocated超过上限,则加1
    一般值为0
TCPAbortOnLinger:
    tcp_close()中,因tp->linger2被设置小于0,导致FIN_WAIT_2立即切换到CLOSE状态的次数
    一般值为0
TCPAbortFailed:
    如果在准备发送reset时,分配SKB或者发送SKB失败,则加1
    一般值为0

c. 当rcv_buf不足时可能需要prune ofo queue, 这种情况就会导致PruneCalled计数器增加;
   当一般都应该通过collapse节省内存就可以了,并不需要真正的prune掉被SACK的数据。
   所以OfoPruned和更严重的RcvPruned都应该计数为0

参考资料

TCP SNMP counters一
TCP SNMP counters二
TCP SNMP counters三
RFC 2012: SNMPv2 Management Information Base for the Transmission Control Protocol using SMIv2
TCP Fast Open: expediting web services