1============ 2SNMP counter 3============ 4 5This document explains the meaning of SNMP counters. 6 7General IPv4 counters 8===================== 9All layer 4 packets and ICMP packets will change these counters, but 10these counters won't be changed by layer 2 packets (such as STP) or 11ARP packets. 12 13* IpInReceives 14 15Defined in `RFC1213 ipInReceives`_ 16 17.. _RFC1213 ipInReceives: https://tools.ietf.org/html/rfc1213#page-26 18 19The number of packets received by the IP layer. It gets increasing at the 20beginning of ip_rcv function, always be updated together with 21IpExtInOctets. It will be increased even if the packet is dropped 22later (e.g. due to the IP header is invalid or the checksum is wrong 23and so on). It indicates the number of aggregated segments after 24GRO/LRO. 25 26* IpInDelivers 27 28Defined in `RFC1213 ipInDelivers`_ 29 30.. _RFC1213 ipInDelivers: https://tools.ietf.org/html/rfc1213#page-28 31 32The number of packets delivers to the upper layer protocols. E.g. TCP, UDP, 33ICMP and so on. If no one listens on a raw socket, only kernel 34supported protocols will be delivered, if someone listens on the raw 35socket, all valid IP packets will be delivered. 36 37* IpOutRequests 38 39Defined in `RFC1213 ipOutRequests`_ 40 41.. _RFC1213 ipOutRequests: https://tools.ietf.org/html/rfc1213#page-28 42 43The number of packets sent via IP layer, for both single cast and 44multicast packets, and would always be updated together with 45IpExtOutOctets. 46 47* IpExtInOctets and IpExtOutOctets 48 49They are Linux kernel extensions, no RFC definitions. Please note, 50RFC1213 indeed defines ifInOctets and ifOutOctets, but they 51are different things. The ifInOctets and ifOutOctets include the MAC 52layer header size but IpExtInOctets and IpExtOutOctets don't, they 53only include the IP layer header and the IP layer data. 54 55* IpExtInNoECTPkts, IpExtInECT1Pkts, IpExtInECT0Pkts, IpExtInCEPkts 56 57They indicate the number of four kinds of ECN IP packets, please refer 58`Explicit Congestion Notification`_ for more details. 59 60.. _Explicit Congestion Notification: https://tools.ietf.org/html/rfc3168#page-6 61 62These 4 counters calculate how many packets received per ECN 63status. They count the real frame number regardless the LRO/GRO. So 64for the same packet, you might find that IpInReceives count 1, but 65IpExtInNoECTPkts counts 2 or more. 66 67* IpInHdrErrors 68 69Defined in `RFC1213 ipInHdrErrors`_. It indicates the packet is 70dropped due to the IP header error. It might happen in both IP input 71and IP forward paths. 72 73.. _RFC1213 ipInHdrErrors: https://tools.ietf.org/html/rfc1213#page-27 74 75* IpInAddrErrors 76 77Defined in `RFC1213 ipInAddrErrors`_. It will be increased in two 78scenarios: (1) The IP address is invalid. (2) The destination IP 79address is not a local address and IP forwarding is not enabled 80 81.. _RFC1213 ipInAddrErrors: https://tools.ietf.org/html/rfc1213#page-27 82 83* IpExtInNoRoutes 84 85This counter means the packet is dropped when the IP stack receives a 86packet and can't find a route for it from the route table. It might 87happen when IP forwarding is enabled and the destination IP address is 88not a local address and there is no route for the destination IP 89address. 90 91* IpInUnknownProtos 92 93Defined in `RFC1213 ipInUnknownProtos`_. It will be increased if the 94layer 4 protocol is unsupported by kernel. If an application is using 95raw socket, kernel will always deliver the packet to the raw socket 96and this counter won't be increased. 97 98.. _RFC1213 ipInUnknownProtos: https://tools.ietf.org/html/rfc1213#page-27 99 100* IpExtInTruncatedPkts 101 102For IPv4 packet, it means the actual data size is smaller than the 103"Total Length" field in the IPv4 header. 104 105* IpInDiscards 106 107Defined in `RFC1213 ipInDiscards`_. It indicates the packet is dropped 108in the IP receiving path and due to kernel internal reasons (e.g. no 109enough memory). 110 111.. _RFC1213 ipInDiscards: https://tools.ietf.org/html/rfc1213#page-28 112 113* IpOutDiscards 114 115Defined in `RFC1213 ipOutDiscards`_. It indicates the packet is 116dropped in the IP sending path and due to kernel internal reasons. 117 118.. _RFC1213 ipOutDiscards: https://tools.ietf.org/html/rfc1213#page-28 119 120* IpOutNoRoutes 121 122Defined in `RFC1213 ipOutNoRoutes`_. It indicates the packet is 123dropped in the IP sending path and no route is found for it. 124 125.. _RFC1213 ipOutNoRoutes: https://tools.ietf.org/html/rfc1213#page-29 126 127ICMP counters 128============= 129* IcmpInMsgs and IcmpOutMsgs 130 131Defined by `RFC1213 icmpInMsgs`_ and `RFC1213 icmpOutMsgs`_ 132 133.. _RFC1213 icmpInMsgs: https://tools.ietf.org/html/rfc1213#page-41 134.. _RFC1213 icmpOutMsgs: https://tools.ietf.org/html/rfc1213#page-43 135 136As mentioned in the RFC1213, these two counters include errors, they 137would be increased even if the ICMP packet has an invalid type. The 138ICMP output path will check the header of a raw socket, so the 139IcmpOutMsgs would still be updated if the IP header is constructed by 140a userspace program. 141 142* ICMP named types 143 144| These counters include most of common ICMP types, they are: 145| IcmpInDestUnreachs: `RFC1213 icmpInDestUnreachs`_ 146| IcmpInTimeExcds: `RFC1213 icmpInTimeExcds`_ 147| IcmpInParmProbs: `RFC1213 icmpInParmProbs`_ 148| IcmpInSrcQuenchs: `RFC1213 icmpInSrcQuenchs`_ 149| IcmpInRedirects: `RFC1213 icmpInRedirects`_ 150| IcmpInEchos: `RFC1213 icmpInEchos`_ 151| IcmpInEchoReps: `RFC1213 icmpInEchoReps`_ 152| IcmpInTimestamps: `RFC1213 icmpInTimestamps`_ 153| IcmpInTimestampReps: `RFC1213 icmpInTimestampReps`_ 154| IcmpInAddrMasks: `RFC1213 icmpInAddrMasks`_ 155| IcmpInAddrMaskReps: `RFC1213 icmpInAddrMaskReps`_ 156| IcmpOutDestUnreachs: `RFC1213 icmpOutDestUnreachs`_ 157| IcmpOutTimeExcds: `RFC1213 icmpOutTimeExcds`_ 158| IcmpOutParmProbs: `RFC1213 icmpOutParmProbs`_ 159| IcmpOutSrcQuenchs: `RFC1213 icmpOutSrcQuenchs`_ 160| IcmpOutRedirects: `RFC1213 icmpOutRedirects`_ 161| IcmpOutEchos: `RFC1213 icmpOutEchos`_ 162| IcmpOutEchoReps: `RFC1213 icmpOutEchoReps`_ 163| IcmpOutTimestamps: `RFC1213 icmpOutTimestamps`_ 164| IcmpOutTimestampReps: `RFC1213 icmpOutTimestampReps`_ 165| IcmpOutAddrMasks: `RFC1213 icmpOutAddrMasks`_ 166| IcmpOutAddrMaskReps: `RFC1213 icmpOutAddrMaskReps`_ 167 168.. _RFC1213 icmpInDestUnreachs: https://tools.ietf.org/html/rfc1213#page-41 169.. _RFC1213 icmpInTimeExcds: https://tools.ietf.org/html/rfc1213#page-41 170.. _RFC1213 icmpInParmProbs: https://tools.ietf.org/html/rfc1213#page-42 171.. _RFC1213 icmpInSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-42 172.. _RFC1213 icmpInRedirects: https://tools.ietf.org/html/rfc1213#page-42 173.. _RFC1213 icmpInEchos: https://tools.ietf.org/html/rfc1213#page-42 174.. _RFC1213 icmpInEchoReps: https://tools.ietf.org/html/rfc1213#page-42 175.. _RFC1213 icmpInTimestamps: https://tools.ietf.org/html/rfc1213#page-42 176.. _RFC1213 icmpInTimestampReps: https://tools.ietf.org/html/rfc1213#page-43 177.. _RFC1213 icmpInAddrMasks: https://tools.ietf.org/html/rfc1213#page-43 178.. _RFC1213 icmpInAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-43 179 180.. _RFC1213 icmpOutDestUnreachs: https://tools.ietf.org/html/rfc1213#page-44 181.. _RFC1213 icmpOutTimeExcds: https://tools.ietf.org/html/rfc1213#page-44 182.. _RFC1213 icmpOutParmProbs: https://tools.ietf.org/html/rfc1213#page-44 183.. _RFC1213 icmpOutSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-44 184.. _RFC1213 icmpOutRedirects: https://tools.ietf.org/html/rfc1213#page-44 185.. _RFC1213 icmpOutEchos: https://tools.ietf.org/html/rfc1213#page-45 186.. _RFC1213 icmpOutEchoReps: https://tools.ietf.org/html/rfc1213#page-45 187.. _RFC1213 icmpOutTimestamps: https://tools.ietf.org/html/rfc1213#page-45 188.. _RFC1213 icmpOutTimestampReps: https://tools.ietf.org/html/rfc1213#page-45 189.. _RFC1213 icmpOutAddrMasks: https://tools.ietf.org/html/rfc1213#page-45 190.. _RFC1213 icmpOutAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-46 191 192Every ICMP type has two counters: 'In' and 'Out'. E.g., for the ICMP 193Echo packet, they are IcmpInEchos and IcmpOutEchos. Their meanings are 194straightforward. The 'In' counter means kernel receives such a packet 195and the 'Out' counter means kernel sends such a packet. 196 197* ICMP numeric types 198 199They are IcmpMsgInType[N] and IcmpMsgOutType[N], the [N] indicates the 200ICMP type number. These counters track all kinds of ICMP packets. The 201ICMP type number definition could be found in the `ICMP parameters`_ 202document. 203 204.. _ICMP parameters: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml 205 206For example, if the Linux kernel sends an ICMP Echo packet, the 207IcmpMsgOutType8 would increase 1. And if kernel gets an ICMP Echo Reply 208packet, IcmpMsgInType0 would increase 1. 209 210* IcmpInCsumErrors 211 212This counter indicates the checksum of the ICMP packet is 213wrong. Kernel verifies the checksum after updating the IcmpInMsgs and 214before updating IcmpMsgInType[N]. If a packet has bad checksum, the 215IcmpInMsgs would be updated but none of IcmpMsgInType[N] would be updated. 216 217* IcmpInErrors and IcmpOutErrors 218 219Defined by `RFC1213 icmpInErrors`_ and `RFC1213 icmpOutErrors`_ 220 221.. _RFC1213 icmpInErrors: https://tools.ietf.org/html/rfc1213#page-41 222.. _RFC1213 icmpOutErrors: https://tools.ietf.org/html/rfc1213#page-43 223 224When an error occurs in the ICMP packet handler path, these two 225counters would be updated. The receiving packet path use IcmpInErrors 226and the sending packet path use IcmpOutErrors. When IcmpInCsumErrors 227is increased, IcmpInErrors would always be increased too. 228 229relationship of the ICMP counters 230--------------------------------- 231The sum of IcmpMsgOutType[N] is always equal to IcmpOutMsgs, as they 232are updated at the same time. The sum of IcmpMsgInType[N] plus 233IcmpInErrors should be equal or larger than IcmpInMsgs. When kernel 234receives an ICMP packet, kernel follows below logic: 235 2361. increase IcmpInMsgs 2372. if has any error, update IcmpInErrors and finish the process 2383. update IcmpMsgOutType[N] 2394. handle the packet depending on the type, if has any error, update 240 IcmpInErrors and finish the process 241 242So if all errors occur in step (2), IcmpInMsgs should be equal to the 243sum of IcmpMsgOutType[N] plus IcmpInErrors. If all errors occur in 244step (4), IcmpInMsgs should be equal to the sum of 245IcmpMsgOutType[N]. If the errors occur in both step (2) and step (4), 246IcmpInMsgs should be less than the sum of IcmpMsgOutType[N] plus 247IcmpInErrors. 248 249General TCP counters 250==================== 251* TcpInSegs 252 253Defined in `RFC1213 tcpInSegs`_ 254 255.. _RFC1213 tcpInSegs: https://tools.ietf.org/html/rfc1213#page-48 256 257The number of packets received by the TCP layer. As mentioned in 258RFC1213, it includes the packets received in error, such as checksum 259error, invalid TCP header and so on. Only one error won't be included: 260if the layer 2 destination address is not the NIC's layer 2 261address. It might happen if the packet is a multicast or broadcast 262packet, or the NIC is in promiscuous mode. In these situations, the 263packets would be delivered to the TCP layer, but the TCP layer will discard 264these packets before increasing TcpInSegs. The TcpInSegs counter 265isn't aware of GRO. So if two packets are merged by GRO, the TcpInSegs 266counter would only increase 1. 267 268* TcpOutSegs 269 270Defined in `RFC1213 tcpOutSegs`_ 271 272.. _RFC1213 tcpOutSegs: https://tools.ietf.org/html/rfc1213#page-48 273 274The number of packets sent by the TCP layer. As mentioned in RFC1213, 275it excludes the retransmitted packets. But it includes the SYN, ACK 276and RST packets. Doesn't like TcpInSegs, the TcpOutSegs is aware of 277GSO, so if a packet would be split to 2 by GSO, TcpOutSegs will 278increase 2. 279 280* TcpActiveOpens 281 282Defined in `RFC1213 tcpActiveOpens`_ 283 284.. _RFC1213 tcpActiveOpens: https://tools.ietf.org/html/rfc1213#page-47 285 286It means the TCP layer sends a SYN, and come into the SYN-SENT 287state. Every time TcpActiveOpens increases 1, TcpOutSegs should always 288increase 1. 289 290* TcpPassiveOpens 291 292Defined in `RFC1213 tcpPassiveOpens`_ 293 294.. _RFC1213 tcpPassiveOpens: https://tools.ietf.org/html/rfc1213#page-47 295 296It means the TCP layer receives a SYN, replies a SYN+ACK, come into 297the SYN-RCVD state. 298 299* TcpExtTCPRcvCoalesce 300 301When packets are received by the TCP layer and are not be read by the 302application, the TCP layer will try to merge them. This counter 303indicate how many packets are merged in such situation. If GRO is 304enabled, lots of packets would be merged by GRO, these packets 305wouldn't be counted to TcpExtTCPRcvCoalesce. 306 307* TcpExtTCPAutoCorking 308 309When sending packets, the TCP layer will try to merge small packets to 310a bigger one. This counter increase 1 for every packet merged in such 311situation. Please refer to the LWN article for more details: 312https://lwn.net/Articles/576263/ 313 314* TcpExtTCPOrigDataSent 315 316This counter is explained by `kernel commit f19c29e3e391`_, I pasted the 317explaination below:: 318 319 TCPOrigDataSent: number of outgoing packets with original data (excluding 320 retransmission but including data-in-SYN). This counter is different from 321 TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is 322 more useful to track the TCP retransmission rate. 323 324* TCPSynRetrans 325 326This counter is explained by `kernel commit f19c29e3e391`_, I pasted the 327explaination below:: 328 329 TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down 330 retransmissions into SYN, fast-retransmits, timeout retransmits, etc. 331 332* TCPFastOpenActiveFail 333 334This counter is explained by `kernel commit f19c29e3e391`_, I pasted the 335explaination below:: 336 337 TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because 338 the remote does not accept it or the attempts timed out. 339 340.. _kernel commit f19c29e3e391: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f19c29e3e391a66a273e9afebaf01917245148cd 341 342* TcpExtListenOverflows and TcpExtListenDrops 343 344When kernel receives a SYN from a client, and if the TCP accept queue 345is full, kernel will drop the SYN and add 1 to TcpExtListenOverflows. 346At the same time kernel will also add 1 to TcpExtListenDrops. When a 347TCP socket is in LISTEN state, and kernel need to drop a packet, 348kernel would always add 1 to TcpExtListenDrops. So increase 349TcpExtListenOverflows would let TcpExtListenDrops increasing at the 350same time, but TcpExtListenDrops would also increase without 351TcpExtListenOverflows increasing, e.g. a memory allocation fail would 352also let TcpExtListenDrops increase. 353 354Note: The above explanation is based on kernel 4.10 or above version, on 355an old kernel, the TCP stack has different behavior when TCP accept 356queue is full. On the old kernel, TCP stack won't drop the SYN, it 357would complete the 3-way handshake. As the accept queue is full, TCP 358stack will keep the socket in the TCP half-open queue. As it is in the 359half open queue, TCP stack will send SYN+ACK on an exponential backoff 360timer, after client replies ACK, TCP stack checks whether the accept 361queue is still full, if it is not full, moves the socket to the accept 362queue, if it is full, keeps the socket in the half-open queue, at next 363time client replies ACK, this socket will get another chance to move 364to the accept queue. 365 366 367TCP Fast Open 368============= 369* TcpEstabResets 370Defined in `RFC1213 tcpEstabResets`_. 371 372.. _RFC1213 tcpEstabResets: https://tools.ietf.org/html/rfc1213#page-48 373 374* TcpAttemptFails 375Defined in `RFC1213 tcpAttemptFails`_. 376 377.. _RFC1213 tcpAttemptFails: https://tools.ietf.org/html/rfc1213#page-48 378 379* TcpOutRsts 380Defined in `RFC1213 tcpOutRsts`_. The RFC says this counter indicates 381the 'segments sent containing the RST flag', but in linux kernel, this 382couner indicates the segments kerenl tried to send. The sending 383process might be failed due to some errors (e.g. memory alloc failed). 384 385.. _RFC1213 tcpOutRsts: https://tools.ietf.org/html/rfc1213#page-52 386 387 388TCP Fast Path 389============ 390When kernel receives a TCP packet, it has two paths to handler the 391packet, one is fast path, another is slow path. The comment in kernel 392code provides a good explanation of them, I pasted them below:: 393 394 It is split into a fast path and a slow path. The fast path is 395 disabled when: 396 397 - A zero window was announced from us 398 - zero window probing 399 is only handled properly on the slow path. 400 - Out of order segments arrived. 401 - Urgent data is expected. 402 - There is no buffer space left 403 - Unexpected TCP flags/window values/header lengths are received 404 (detected by checking the TCP header against pred_flags) 405 - Data is sent in both directions. The fast path only supports pure senders 406 or pure receivers (this means either the sequence number or the ack 407 value must stay constant) 408 - Unexpected TCP option. 409 410Kernel will try to use fast path unless any of the above conditions 411are satisfied. If the packets are out of order, kernel will handle 412them in slow path, which means the performance might be not very 413good. Kernel would also come into slow path if the "Delayed ack" is 414used, because when using "Delayed ack", the data is sent in both 415directions. When the TCP window scale option is not used, kernel will 416try to enable fast path immediately when the connection comes into the 417established state, but if the TCP window scale option is used, kernel 418will disable the fast path at first, and try to enable it after kernel 419receives packets. 420 421* TcpExtTCPPureAcks and TcpExtTCPHPAcks 422 423If a packet set ACK flag and has no data, it is a pure ACK packet, if 424kernel handles it in the fast path, TcpExtTCPHPAcks will increase 1, 425if kernel handles it in the slow path, TcpExtTCPPureAcks will 426increase 1. 427 428* TcpExtTCPHPHits 429 430If a TCP packet has data (which means it is not a pure ACK packet), 431and this packet is handled in the fast path, TcpExtTCPHPHits will 432increase 1. 433 434 435TCP abort 436========= 437* TcpExtTCPAbortOnData 438 439It means TCP layer has data in flight, but need to close the 440connection. So TCP layer sends a RST to the other side, indicate the 441connection is not closed very graceful. An easy way to increase this 442counter is using the SO_LINGER option. Please refer to the SO_LINGER 443section of the `socket man page`_: 444 445.. _socket man page: http://man7.org/linux/man-pages/man7/socket.7.html 446 447By default, when an application closes a connection, the close function 448will return immediately and kernel will try to send the in-flight data 449async. If you use the SO_LINGER option, set l_onoff to 1, and l_linger 450to a positive number, the close function won't return immediately, but 451wait for the in-flight data are acked by the other side, the max wait 452time is l_linger seconds. If set l_onoff to 1 and set l_linger to 0, 453when the application closes a connection, kernel will send a RST 454immediately and increase the TcpExtTCPAbortOnData counter. 455 456* TcpExtTCPAbortOnClose 457 458This counter means the application has unread data in the TCP layer when 459the application wants to close the TCP connection. In such a situation, 460kernel will send a RST to the other side of the TCP connection. 461 462* TcpExtTCPAbortOnMemory 463 464When an application closes a TCP connection, kernel still need to track 465the connection, let it complete the TCP disconnect process. E.g. an 466app calls the close method of a socket, kernel sends fin to the other 467side of the connection, then the app has no relationship with the 468socket any more, but kernel need to keep the socket, this socket 469becomes an orphan socket, kernel waits for the reply of the other side, 470and would come to the TIME_WAIT state finally. When kernel has no 471enough memory to keep the orphan socket, kernel would send an RST to 472the other side, and delete the socket, in such situation, kernel will 473increase 1 to the TcpExtTCPAbortOnMemory. Two conditions would trigger 474TcpExtTCPAbortOnMemory: 475 4761. the memory used by the TCP protocol is higher than the third value of 477the tcp_mem. Please refer the tcp_mem section in the `TCP man page`_: 478 479.. _TCP man page: http://man7.org/linux/man-pages/man7/tcp.7.html 480 4812. the orphan socket count is higher than net.ipv4.tcp_max_orphans 482 483 484* TcpExtTCPAbortOnTimeout 485 486This counter will increase when any of the TCP timers expire. In such 487situation, kernel won't send RST, just give up the connection. 488 489* TcpExtTCPAbortOnLinger 490 491When a TCP connection comes into FIN_WAIT_2 state, instead of waiting 492for the fin packet from the other side, kernel could send a RST and 493delete the socket immediately. This is not the default behavior of 494Linux kernel TCP stack. By configuring the TCP_LINGER2 socket option, 495you could let kernel follow this behavior. 496 497* TcpExtTCPAbortFailed 498 499The kernel TCP layer will send RST if the `RFC2525 2.17 section`_ is 500satisfied. If an internal error occurs during this process, 501TcpExtTCPAbortFailed will be increased. 502 503.. _RFC2525 2.17 section: https://tools.ietf.org/html/rfc2525#page-50 504 505TCP Hybrid Slow Start 506===================== 507The Hybrid Slow Start algorithm is an enhancement of the traditional 508TCP congestion window Slow Start algorithm. It uses two pieces of 509information to detect whether the max bandwidth of the TCP path is 510approached. The two pieces of information are ACK train length and 511increase in packet delay. For detail information, please refer the 512`Hybrid Slow Start paper`_. Either ACK train length or packet delay 513hits a specific threshold, the congestion control algorithm will come 514into the Congestion Avoidance state. Until v4.20, two congestion 515control algorithms are using Hybrid Slow Start, they are cubic (the 516default congestion control algorithm) and cdg. Four snmp counters 517relate with the Hybrid Slow Start algorithm. 518 519.. _Hybrid Slow Start paper: https://pdfs.semanticscholar.org/25e9/ef3f03315782c7f1cbcd31b587857adae7d1.pdf 520 521* TcpExtTCPHystartTrainDetect 522 523How many times the ACK train length threshold is detected 524 525* TcpExtTCPHystartTrainCwnd 526 527The sum of CWND detected by ACK train length. Dividing this value by 528TcpExtTCPHystartTrainDetect is the average CWND which detected by the 529ACK train length. 530 531* TcpExtTCPHystartDelayDetect 532 533How many times the packet delay threshold is detected. 534 535* TcpExtTCPHystartDelayCwnd 536 537The sum of CWND detected by packet delay. Dividing this value by 538TcpExtTCPHystartDelayDetect is the average CWND which detected by the 539packet delay. 540 541TCP retransmission and congestion control 542========================================= 543The TCP protocol has two retransmission mechanisms: SACK and fast 544recovery. They are exclusive with each other. When SACK is enabled, 545the kernel TCP stack would use SACK, or kernel would use fast 546recovery. The SACK is a TCP option, which is defined in `RFC2018`_, 547the fast recovery is defined in `RFC6582`_, which is also called 548'Reno'. 549 550The TCP congestion control is a big and complex topic. To understand 551the related snmp counter, we need to know the states of the congestion 552control state machine. There are 5 states: Open, Disorder, CWR, 553Recovery and Loss. For details about these states, please refer page 5 554and page 6 of this document: 555https://pdfs.semanticscholar.org/0e9c/968d09ab2e53e24c4dca5b2d67c7f7140f8e.pdf 556 557.. _RFC2018: https://tools.ietf.org/html/rfc2018 558.. _RFC6582: https://tools.ietf.org/html/rfc6582 559 560* TcpExtTCPRenoRecovery and TcpExtTCPSackRecovery 561 562When the congestion control comes into Recovery state, if sack is 563used, TcpExtTCPSackRecovery increases 1, if sack is not used, 564TcpExtTCPRenoRecovery increases 1. These two counters mean the TCP 565stack begins to retransmit the lost packets. 566 567* TcpExtTCPSACKReneging 568 569A packet was acknowledged by SACK, but the receiver has dropped this 570packet, so the sender needs to retransmit this packet. In this 571situation, the sender adds 1 to TcpExtTCPSACKReneging. A receiver 572could drop a packet which has been acknowledged by SACK, although it is 573unusual, it is allowed by the TCP protocol. The sender doesn't really 574know what happened on the receiver side. The sender just waits until 575the RTO expires for this packet, then the sender assumes this packet 576has been dropped by the receiver. 577 578* TcpExtTCPRenoReorder 579 580The reorder packet is detected by fast recovery. It would only be used 581if SACK is disabled. The fast recovery algorithm detects recorder by 582the duplicate ACK number. E.g., if retransmission is triggered, and 583the original retransmitted packet is not lost, it is just out of 584order, the receiver would acknowledge multiple times, one for the 585retransmitted packet, another for the arriving of the original out of 586order packet. Thus the sender would find more ACks than its 587expectation, and the sender knows out of order occurs. 588 589* TcpExtTCPTSReorder 590 591The reorder packet is detected when a hole is filled. E.g., assume the 592sender sends packet 1,2,3,4,5, and the receiving order is 5931,2,4,5,3. When the sender receives the ACK of packet 3 (which will 594fill the hole), two conditions will let TcpExtTCPTSReorder increase 5951: (1) if the packet 3 is not re-retransmitted yet. (2) if the packet 5963 is retransmitted but the timestamp of the packet 3's ACK is earlier 597than the retransmission timestamp. 598 599* TcpExtTCPSACKReorder 600 601The reorder packet detected by SACK. The SACK has two methods to 602detect reorder: (1) DSACK is received by the sender. It means the 603sender sends the same packet more than one times. And the only reason 604is the sender believes an out of order packet is lost so it sends the 605packet again. (2) Assume packet 1,2,3,4,5 are sent by the sender, and 606the sender has received SACKs for packet 2 and 5, now the sender 607receives SACK for packet 4 and the sender doesn't retransmit the 608packet yet, the sender would know packet 4 is out of order. The TCP 609stack of kernel will increase TcpExtTCPSACKReorder for both of the 610above scenarios. 611 612DSACK 613===== 614The DSACK is defined in `RFC2883`_. The receiver uses DSACK to report 615duplicate packets to the sender. There are two kinds of 616duplications: (1) a packet which has been acknowledged is 617duplicate. (2) an out of order packet is duplicate. The TCP stack 618counts these two kinds of duplications on both receiver side and 619sender side. 620 621.. _RFC2883 : https://tools.ietf.org/html/rfc2883 622 623* TcpExtTCPDSACKOldSent 624 625The TCP stack receives a duplicate packet which has been acked, so it 626sends a DSACK to the sender. 627 628* TcpExtTCPDSACKOfoSent 629 630The TCP stack receives an out of order duplicate packet, so it sends a 631DSACK to the sender. 632 633* TcpExtTCPDSACKRecv 634The TCP stack receives a DSACK, which indicates an acknowledged 635duplicate packet is received. 636 637* TcpExtTCPDSACKOfoRecv 638 639The TCP stack receives a DSACK, which indicate an out of order 640duplicate packet is received. 641 642invalid SACK and DSACK 643==================== 644When a SACK (or DSACK) block is invalid, a corresponding counter would 645be updated. The validation method is base on the start/end sequence 646number of the SACK block. For more details, please refer the comment 647of the function tcp_is_sackblock_valid in the kernel source code. A 648SACK option could have up to 4 blocks, they are checked 649individually. E.g., if 3 blocks of a SACk is invalid, the 650corresponding counter would be updated 3 times. The comment of the 651`Add counters for discarded SACK blocks`_ patch has additional 652explaination: 653 654.. _Add counters for discarded SACK blocks: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=18f02545a9a16c9a89778b91a162ad16d510bb32 655 656* TcpExtTCPSACKDiscard 657This counter indicates how many SACK blocks are invalid. If the invalid 658SACK block is caused by ACK recording, the TCP stack will only ignore 659it and won't update this counter. 660 661* TcpExtTCPDSACKIgnoredOld and TcpExtTCPDSACKIgnoredNoUndo 662When a DSACK block is invalid, one of these two counters would be 663updated. Which counter will be updated depends on the undo_marker flag 664of the TCP socket. If the undo_marker is not set, the TCP stack isn't 665likely to re-transmit any packets, and we still receive an invalid 666DSACK block, the reason might be that the packet is duplicated in the 667middle of the network. In such scenario, TcpExtTCPDSACKIgnoredNoUndo 668will be updated. If the undo_marker is set, TcpExtTCPDSACKIgnoredOld 669will be updated. As implied in its name, it might be an old packet. 670 671SACK shift 672========= 673The linux networking stack stores data in sk_buff struct (skb for 674short). If a SACK block acrosses multiple skb, the TCP stack will try 675to re-arrange data in these skb. E.g. if a SACK block acknowledges seq 67610 to 15, skb1 has seq 10 to 13, skb2 has seq 14 to 20. The seq 14 and 67715 in skb2 would be moved to skb1. This operation is 'shift'. If a 678SACK block acknowledges seq 10 to 20, skb1 has seq 10 to 13, skb2 has 679seq 14 to 20. All data in skb2 will be moved to skb1, and skb2 will be 680discard, this operation is 'merge'. 681 682* TcpExtTCPSackShifted 683A skb is shifted 684 685* TcpExtTCPSackMerged 686A skb is merged 687 688* TcpExtTCPSackShiftFallback 689A skb should be shifted or merged, but the TCP stack doesn't do it for 690some reasons. 691 692TCP out of order 693================ 694* TcpExtTCPOFOQueue 695 696The TCP layer receives an out of order packet and has enough memory 697to queue it. 698 699* TcpExtTCPOFODrop 700 701The TCP layer receives an out of order packet but doesn't have enough 702memory, so drops it. Such packets won't be counted into 703TcpExtTCPOFOQueue. 704 705* TcpExtTCPOFOMerge 706 707The received out of order packet has an overlay with the previous 708packet. the overlay part will be dropped. All of TcpExtTCPOFOMerge 709packets will also be counted into TcpExtTCPOFOQueue. 710 711TCP PAWS 712======== 713PAWS (Protection Against Wrapped Sequence numbers) is an algorithm 714which is used to drop old packets. It depends on the TCP 715timestamps. For detail information, please refer the `timestamp wiki`_ 716and the `RFC of PAWS`_. 717 718.. _RFC of PAWS: https://tools.ietf.org/html/rfc1323#page-17 719.. _timestamp wiki: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_timestamps 720 721* TcpExtPAWSActive 722 723Packets are dropped by PAWS in Syn-Sent status. 724 725* TcpExtPAWSEstab 726 727Packets are dropped by PAWS in any status other than Syn-Sent. 728 729TCP ACK skip 730============ 731In some scenarios, kernel would avoid sending duplicate ACKs too 732frequently. Please find more details in the tcp_invalid_ratelimit 733section of the `sysctl document`_. When kernel decides to skip an ACK 734due to tcp_invalid_ratelimit, kernel would update one of below 735counters to indicate the ACK is skipped in which scenario. The ACK 736would only be skipped if the received packet is either a SYN packet or 737it has no data. 738 739.. _sysctl document: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt 740 741* TcpExtTCPACKSkippedSynRecv 742 743The ACK is skipped in Syn-Recv status. The Syn-Recv status means the 744TCP stack receives a SYN and replies SYN+ACK. Now the TCP stack is 745waiting for an ACK. Generally, the TCP stack doesn't need to send ACK 746in the Syn-Recv status. But in several scenarios, the TCP stack need 747to send an ACK. E.g., the TCP stack receives the same SYN packet 748repeately, the received packet does not pass the PAWS check, or the 749received packet sequence number is out of window. In these scenarios, 750the TCP stack needs to send ACK. If the ACk sending frequency is higher than 751tcp_invalid_ratelimit allows, the TCP stack will skip sending ACK and 752increase TcpExtTCPACKSkippedSynRecv. 753 754 755* TcpExtTCPACKSkippedPAWS 756 757The ACK is skipped due to PAWS (Protect Against Wrapped Sequence 758numbers) check fails. If the PAWS check fails in Syn-Recv, Fin-Wait-2 759or Time-Wait statuses, the skipped ACK would be counted to 760TcpExtTCPACKSkippedSynRecv, TcpExtTCPACKSkippedFinWait2 or 761TcpExtTCPACKSkippedTimeWait. In all other statuses, the skipped ACK 762would be counted to TcpExtTCPACKSkippedPAWS. 763 764* TcpExtTCPACKSkippedSeq 765 766The sequence number is out of window and the timestamp passes the PAWS 767check and the TCP status is not Syn-Recv, Fin-Wait-2, and Time-Wait. 768 769* TcpExtTCPACKSkippedFinWait2 770 771The ACK is skipped in Fin-Wait-2 status, the reason would be either 772PAWS check fails or the received sequence number is out of window. 773 774* TcpExtTCPACKSkippedTimeWait 775 776Tha ACK is skipped in Time-Wait status, the reason would be either 777PAWS check failed or the received sequence number is out of window. 778 779* TcpExtTCPACKSkippedChallenge 780 781The ACK is skipped if the ACK is a challenge ACK. The RFC 5961 defines 7823 kind of challenge ACK, please refer `RFC 5961 section 3.2`_, 783`RFC 5961 section 4.2`_ and `RFC 5961 section 5.2`_. Besides these 784three scenarios, In some TCP status, the linux TCP stack would also 785send challenge ACKs if the ACK number is before the first 786unacknowledged number (more strict than `RFC 5961 section 5.2`_). 787 788.. _RFC 5961 section 3.2: https://tools.ietf.org/html/rfc5961#page-7 789.. _RFC 5961 section 4.2: https://tools.ietf.org/html/rfc5961#page-9 790.. _RFC 5961 section 5.2: https://tools.ietf.org/html/rfc5961#page-11 791 792TCP receive window 793================= 794* TcpExtTCPWantZeroWindowAdv 795Depending on current memory usage, the TCP stack tries to set receive 796window to zero. But the receive window might still be a no-zero 797value. For example, if the previous window size is 10, and the TCP 798stack receives 3 bytes, the current window size would be 7 even if the 799window size calculated by the memory usage is zero. 800 801* TcpExtTCPToZeroWindowAdv 802The TCP receive window is set to zero from a no-zero value. 803 804* TcpExtTCPFromZeroWindowAdv 805The TCP receive window is set to no-zero value from zero. 806 807 808Delayed ACK 809========== 810The TCP Delayed ACK is a technique which is used for reducing the 811packet count in the network. For more details, please refer the 812`Delayed ACK wiki`_ 813 814.. _Delayed ACK wiki: https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment 815 816* TcpExtDelayedACKs 817A delayed ACK timer expires. The TCP stack will send a pure ACK packet 818and exit the delayed ACK mode. 819 820* TcpExtDelayedACKLocked 821A delayed ACK timer expires, but the TCP stack can't send an ACK 822immediately due to the socket is locked by a userspace program. The 823TCP stack will send a pure ACK later (after the userspace program 824unlock the socket). When the TCP stack sends the pure ACK later, the 825TCP stack will also update TcpExtDelayedACKs and exit the delayed ACK 826mode. 827 828* TcpExtDelayedACKLost 829It will be updated when the TCP stack receives a packet which has been 830ACKed. A Delayed ACK loss might cause this issue, but it would also be 831triggered by other reasons, such as a packet is duplicated in the 832network. 833 834Tail Loss Probe (TLP) 835=================== 836TLP is an algorithm which is used to detect TCP packet loss. For more 837details, please refer the `TLP paper`_. 838 839.. _TLP paper: https://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01 840 841* TcpExtTCPLossProbes 842A TLP probe packet is sent. 843 844* TcpExtTCPLossProbeRecovery 845A packet loss is detected and recovered by TLP. 846 847examples 848======== 849 850ping test 851--------- 852Run the ping command against the public dns server 8.8.8.8:: 853 854 nstatuser@nstat-a:~$ ping 8.8.8.8 -c 1 855 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 856 64 bytes from 8.8.8.8: icmp_seq=1 ttl=119 time=17.8 ms 857 858 --- 8.8.8.8 ping statistics --- 859 1 packets transmitted, 1 received, 0% packet loss, time 0ms 860 rtt min/avg/max/mdev = 17.875/17.875/17.875/0.000 ms 861 862The nstayt result:: 863 864 nstatuser@nstat-a:~$ nstat 865 #kernel 866 IpInReceives 1 0.0 867 IpInDelivers 1 0.0 868 IpOutRequests 1 0.0 869 IcmpInMsgs 1 0.0 870 IcmpInEchoReps 1 0.0 871 IcmpOutMsgs 1 0.0 872 IcmpOutEchos 1 0.0 873 IcmpMsgInType0 1 0.0 874 IcmpMsgOutType8 1 0.0 875 IpExtInOctets 84 0.0 876 IpExtOutOctets 84 0.0 877 IpExtInNoECTPkts 1 0.0 878 879The Linux server sent an ICMP Echo packet, so IpOutRequests, 880IcmpOutMsgs, IcmpOutEchos and IcmpMsgOutType8 were increased 1. The 881server got ICMP Echo Reply from 8.8.8.8, so IpInReceives, IcmpInMsgs, 882IcmpInEchoReps and IcmpMsgInType0 were increased 1. The ICMP Echo Reply 883was passed to the ICMP layer via IP layer, so IpInDelivers was 884increased 1. The default ping data size is 48, so an ICMP Echo packet 885and its corresponding Echo Reply packet are constructed by: 886 887* 14 bytes MAC header 888* 20 bytes IP header 889* 16 bytes ICMP header 890* 48 bytes data (default value of the ping command) 891 892So the IpExtInOctets and IpExtOutOctets are 20+16+48=84. 893 894tcp 3-way handshake 895------------------- 896On server side, we run:: 897 898 nstatuser@nstat-b:~$ nc -lknv 0.0.0.0 9000 899 Listening on [0.0.0.0] (family 0, port 9000) 900 901On client side, we run:: 902 903 nstatuser@nstat-a:~$ nc -nv 192.168.122.251 9000 904 Connection to 192.168.122.251 9000 port [tcp/*] succeeded! 905 906The server listened on tcp 9000 port, the client connected to it, they 907completed the 3-way handshake. 908 909On server side, we can find below nstat output:: 910 911 nstatuser@nstat-b:~$ nstat | grep -i tcp 912 TcpPassiveOpens 1 0.0 913 TcpInSegs 2 0.0 914 TcpOutSegs 1 0.0 915 TcpExtTCPPureAcks 1 0.0 916 917On client side, we can find below nstat output:: 918 919 nstatuser@nstat-a:~$ nstat | grep -i tcp 920 TcpActiveOpens 1 0.0 921 TcpInSegs 1 0.0 922 TcpOutSegs 2 0.0 923 924When the server received the first SYN, it replied a SYN+ACK, and came into 925SYN-RCVD state, so TcpPassiveOpens increased 1. The server received 926SYN, sent SYN+ACK, received ACK, so server sent 1 packet, received 2 927packets, TcpInSegs increased 2, TcpOutSegs increased 1. The last ACK 928of the 3-way handshake is a pure ACK without data, so 929TcpExtTCPPureAcks increased 1. 930 931When the client sent SYN, the client came into the SYN-SENT state, so 932TcpActiveOpens increased 1, the client sent SYN, received SYN+ACK, sent 933ACK, so client sent 2 packets, received 1 packet, TcpInSegs increased 9341, TcpOutSegs increased 2. 935 936TCP normal traffic 937------------------ 938Run nc on server:: 939 940 nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000 941 Listening on [0.0.0.0] (family 0, port 9000) 942 943Run nc on client:: 944 945 nstatuser@nstat-a:~$ nc -v nstat-b 9000 946 Connection to nstat-b 9000 port [tcp/*] succeeded! 947 948Input a string in the nc client ('hello' in our example):: 949 950 nstatuser@nstat-a:~$ nc -v nstat-b 9000 951 Connection to nstat-b 9000 port [tcp/*] succeeded! 952 hello 953 954The client side nstat output:: 955 956 nstatuser@nstat-a:~$ nstat 957 #kernel 958 IpInReceives 1 0.0 959 IpInDelivers 1 0.0 960 IpOutRequests 1 0.0 961 TcpInSegs 1 0.0 962 TcpOutSegs 1 0.0 963 TcpExtTCPPureAcks 1 0.0 964 TcpExtTCPOrigDataSent 1 0.0 965 IpExtInOctets 52 0.0 966 IpExtOutOctets 58 0.0 967 IpExtInNoECTPkts 1 0.0 968 969The server side nstat output:: 970 971 nstatuser@nstat-b:~$ nstat 972 #kernel 973 IpInReceives 1 0.0 974 IpInDelivers 1 0.0 975 IpOutRequests 1 0.0 976 TcpInSegs 1 0.0 977 TcpOutSegs 1 0.0 978 IpExtInOctets 58 0.0 979 IpExtOutOctets 52 0.0 980 IpExtInNoECTPkts 1 0.0 981 982Input a string in nc client side again ('world' in our exmaple):: 983 984 nstatuser@nstat-a:~$ nc -v nstat-b 9000 985 Connection to nstat-b 9000 port [tcp/*] succeeded! 986 hello 987 world 988 989Client side nstat output:: 990 991 nstatuser@nstat-a:~$ nstat 992 #kernel 993 IpInReceives 1 0.0 994 IpInDelivers 1 0.0 995 IpOutRequests 1 0.0 996 TcpInSegs 1 0.0 997 TcpOutSegs 1 0.0 998 TcpExtTCPHPAcks 1 0.0 999 TcpExtTCPOrigDataSent 1 0.0 1000 IpExtInOctets 52 0.0 1001 IpExtOutOctets 58 0.0 1002 IpExtInNoECTPkts 1 0.0 1003 1004 1005Server side nstat output:: 1006 1007 nstatuser@nstat-b:~$ nstat 1008 #kernel 1009 IpInReceives 1 0.0 1010 IpInDelivers 1 0.0 1011 IpOutRequests 1 0.0 1012 TcpInSegs 1 0.0 1013 TcpOutSegs 1 0.0 1014 TcpExtTCPHPHits 1 0.0 1015 IpExtInOctets 58 0.0 1016 IpExtOutOctets 52 0.0 1017 IpExtInNoECTPkts 1 0.0 1018 1019Compare the first client-side nstat and the second client-side nstat, 1020we could find one difference: the first one had a 'TcpExtTCPPureAcks', 1021but the second one had a 'TcpExtTCPHPAcks'. The first server-side 1022nstat and the second server-side nstat had a difference too: the 1023second server-side nstat had a TcpExtTCPHPHits, but the first 1024server-side nstat didn't have it. The network traffic patterns were 1025exactly the same: the client sent a packet to the server, the server 1026replied an ACK. But kernel handled them in different ways. When the 1027TCP window scale option is not used, kernel will try to enable fast 1028path immediately when the connection comes into the established state, 1029but if the TCP window scale option is used, kernel will disable the 1030fast path at first, and try to enable it after kerenl receives 1031packets. We could use the 'ss' command to verify whether the window 1032scale option is used. e.g. run below command on either server or 1033client:: 1034 1035 nstatuser@nstat-a:~$ ss -o state established -i '( dport = :9000 or sport = :9000 ) 1036 Netid Recv-Q Send-Q Local Address:Port Peer Address:Port 1037 tcp 0 0 192.168.122.250:40654 192.168.122.251:9000 1038 ts sack cubic wscale:7,7 rto:204 rtt:0.98/0.49 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:10 bytes_acked:1 segs_out:2 segs_in:1 send 118.2Mbps lastsnd:46572 lastrcv:46572 lastack:46572 pacing_rate 236.4Mbps rcv_space:29200 rcv_ssthresh:29200 minrtt:0.98 1039 1040The 'wscale:7,7' means both server and client set the window scale 1041option to 7. Now we could explain the nstat output in our test: 1042 1043In the first nstat output of client side, the client sent a packet, server 1044reply an ACK, when kernel handled this ACK, the fast path was not 1045enabled, so the ACK was counted into 'TcpExtTCPPureAcks'. 1046 1047In the second nstat output of client side, the client sent a packet again, 1048and received another ACK from the server, in this time, the fast path is 1049enabled, and the ACK was qualified for fast path, so it was handled by 1050the fast path, so this ACK was counted into TcpExtTCPHPAcks. 1051 1052In the first nstat output of server side, fast path was not enabled, 1053so there was no 'TcpExtTCPHPHits'. 1054 1055In the second nstat output of server side, the fast path was enabled, 1056and the packet received from client qualified for fast path, so it 1057was counted into 'TcpExtTCPHPHits'. 1058 1059TcpExtTCPAbortOnClose 1060--------------------- 1061On the server side, we run below python script:: 1062 1063 import socket 1064 import time 1065 1066 port = 9000 1067 1068 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1069 s.bind(('0.0.0.0', port)) 1070 s.listen(1) 1071 sock, addr = s.accept() 1072 while True: 1073 time.sleep(9999999) 1074 1075This python script listen on 9000 port, but doesn't read anything from 1076the connection. 1077 1078On the client side, we send the string "hello" by nc:: 1079 1080 nstatuser@nstat-a:~$ echo "hello" | nc nstat-b 9000 1081 1082Then, we come back to the server side, the server has received the "hello" 1083packet, and the TCP layer has acked this packet, but the application didn't 1084read it yet. We type Ctrl-C to terminate the server script. Then we 1085could find TcpExtTCPAbortOnClose increased 1 on the server side:: 1086 1087 nstatuser@nstat-b:~$ nstat | grep -i abort 1088 TcpExtTCPAbortOnClose 1 0.0 1089 1090If we run tcpdump on the server side, we could find the server sent a 1091RST after we type Ctrl-C. 1092 1093TcpExtTCPAbortOnMemory and TcpExtTCPAbortOnTimeout 1094--------------------------------------------------- 1095Below is an example which let the orphan socket count be higher than 1096net.ipv4.tcp_max_orphans. 1097Change tcp_max_orphans to a smaller value on client:: 1098 1099 sudo bash -c "echo 10 > /proc/sys/net/ipv4/tcp_max_orphans" 1100 1101Client code (create 64 connection to server):: 1102 1103 nstatuser@nstat-a:~$ cat client_orphan.py 1104 import socket 1105 import time 1106 1107 server = 'nstat-b' # server address 1108 port = 9000 1109 1110 count = 64 1111 1112 connection_list = [] 1113 1114 for i in range(64): 1115 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1116 s.connect((server, port)) 1117 connection_list.append(s) 1118 print("connection_count: %d" % len(connection_list)) 1119 1120 while True: 1121 time.sleep(99999) 1122 1123Server code (accept 64 connection from client):: 1124 1125 nstatuser@nstat-b:~$ cat server_orphan.py 1126 import socket 1127 import time 1128 1129 port = 9000 1130 count = 64 1131 1132 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1133 s.bind(('0.0.0.0', port)) 1134 s.listen(count) 1135 connection_list = [] 1136 while True: 1137 sock, addr = s.accept() 1138 connection_list.append((sock, addr)) 1139 print("connection_count: %d" % len(connection_list)) 1140 1141Run the python scripts on server and client. 1142 1143On server:: 1144 1145 python3 server_orphan.py 1146 1147On client:: 1148 1149 python3 client_orphan.py 1150 1151Run iptables on server:: 1152 1153 sudo iptables -A INPUT -i ens3 -p tcp --destination-port 9000 -j DROP 1154 1155Type Ctrl-C on client, stop client_orphan.py. 1156 1157Check TcpExtTCPAbortOnMemory on client:: 1158 1159 nstatuser@nstat-a:~$ nstat | grep -i abort 1160 TcpExtTCPAbortOnMemory 54 0.0 1161 1162Check orphane socket count on client:: 1163 1164 nstatuser@nstat-a:~$ ss -s 1165 Total: 131 (kernel 0) 1166 TCP: 14 (estab 1, closed 0, orphaned 10, synrecv 0, timewait 0/0), ports 0 1167 1168 Transport Total IP IPv6 1169 * 0 - - 1170 RAW 1 0 1 1171 UDP 1 1 0 1172 TCP 14 13 1 1173 INET 16 14 2 1174 FRAG 0 0 0 1175 1176The explanation of the test: after run server_orphan.py and 1177client_orphan.py, we set up 64 connections between server and 1178client. Run the iptables command, the server will drop all packets from 1179the client, type Ctrl-C on client_orphan.py, the system of the client 1180would try to close these connections, and before they are closed 1181gracefully, these connections became orphan sockets. As the iptables 1182of the server blocked packets from the client, the server won't receive fin 1183from the client, so all connection on clients would be stuck on FIN_WAIT_1 1184stage, so they will keep as orphan sockets until timeout. We have echo 118510 to /proc/sys/net/ipv4/tcp_max_orphans, so the client system would 1186only keep 10 orphan sockets, for all other orphan sockets, the client 1187system sent RST for them and delete them. We have 64 connections, so 1188the 'ss -s' command shows the system has 10 orphan sockets, and the 1189value of TcpExtTCPAbortOnMemory was 54. 1190 1191An additional explanation about orphan socket count: You could find the 1192exactly orphan socket count by the 'ss -s' command, but when kernel 1193decide whither increases TcpExtTCPAbortOnMemory and sends RST, kernel 1194doesn't always check the exactly orphan socket count. For increasing 1195performance, kernel checks an approximate count firstly, if the 1196approximate count is more than tcp_max_orphans, kernel checks the 1197exact count again. So if the approximate count is less than 1198tcp_max_orphans, but exactly count is more than tcp_max_orphans, you 1199would find TcpExtTCPAbortOnMemory is not increased at all. If 1200tcp_max_orphans is large enough, it won't occur, but if you decrease 1201tcp_max_orphans to a small value like our test, you might find this 1202issue. So in our test, the client set up 64 connections although the 1203tcp_max_orphans is 10. If the client only set up 11 connections, we 1204can't find the change of TcpExtTCPAbortOnMemory. 1205 1206Continue the previous test, we wait for several minutes. Because of the 1207iptables on the server blocked the traffic, the server wouldn't receive 1208fin, and all the client's orphan sockets would timeout on the 1209FIN_WAIT_1 state finally. So we wait for a few minutes, we could find 121010 timeout on the client:: 1211 1212 nstatuser@nstat-a:~$ nstat | grep -i abort 1213 TcpExtTCPAbortOnTimeout 10 0.0 1214 1215TcpExtTCPAbortOnLinger 1216---------------------- 1217The server side code:: 1218 1219 nstatuser@nstat-b:~$ cat server_linger.py 1220 import socket 1221 import time 1222 1223 port = 9000 1224 1225 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1226 s.bind(('0.0.0.0', port)) 1227 s.listen(1) 1228 sock, addr = s.accept() 1229 while True: 1230 time.sleep(9999999) 1231 1232The client side code:: 1233 1234 nstatuser@nstat-a:~$ cat client_linger.py 1235 import socket 1236 import struct 1237 1238 server = 'nstat-b' # server address 1239 port = 9000 1240 1241 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1242 s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 10)) 1243 s.setsockopt(socket.SOL_TCP, socket.TCP_LINGER2, struct.pack('i', -1)) 1244 s.connect((server, port)) 1245 s.close() 1246 1247Run server_linger.py on server:: 1248 1249 nstatuser@nstat-b:~$ python3 server_linger.py 1250 1251Run client_linger.py on client:: 1252 1253 nstatuser@nstat-a:~$ python3 client_linger.py 1254 1255After run client_linger.py, check the output of nstat:: 1256 1257 nstatuser@nstat-a:~$ nstat | grep -i abort 1258 TcpExtTCPAbortOnLinger 1 0.0 1259 1260TcpExtTCPRcvCoalesce 1261-------------------- 1262On the server, we run a program which listen on TCP port 9000, but 1263doesn't read any data:: 1264 1265 import socket 1266 import time 1267 port = 9000 1268 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1269 s.bind(('0.0.0.0', port)) 1270 s.listen(1) 1271 sock, addr = s.accept() 1272 while True: 1273 time.sleep(9999999) 1274 1275Save the above code as server_coalesce.py, and run:: 1276 1277 python3 server_coalesce.py 1278 1279On the client, save below code as client_coalesce.py:: 1280 1281 import socket 1282 server = 'nstat-b' 1283 port = 9000 1284 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1285 s.connect((server, port)) 1286 1287Run:: 1288 1289 nstatuser@nstat-a:~$ python3 -i client_coalesce.py 1290 1291We use '-i' to come into the interactive mode, then a packet:: 1292 1293 >>> s.send(b'foo') 1294 3 1295 1296Send a packet again:: 1297 1298 >>> s.send(b'bar') 1299 3 1300 1301On the server, run nstat:: 1302 1303 ubuntu@nstat-b:~$ nstat 1304 #kernel 1305 IpInReceives 2 0.0 1306 IpInDelivers 2 0.0 1307 IpOutRequests 2 0.0 1308 TcpInSegs 2 0.0 1309 TcpOutSegs 2 0.0 1310 TcpExtTCPRcvCoalesce 1 0.0 1311 IpExtInOctets 110 0.0 1312 IpExtOutOctets 104 0.0 1313 IpExtInNoECTPkts 2 0.0 1314 1315The client sent two packets, server didn't read any data. When 1316the second packet arrived at server, the first packet was still in 1317the receiving queue. So the TCP layer merged the two packets, and we 1318could find the TcpExtTCPRcvCoalesce increased 1. 1319 1320TcpExtListenOverflows and TcpExtListenDrops 1321------------------------------------------- 1322On server, run the nc command, listen on port 9000:: 1323 1324 nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000 1325 Listening on [0.0.0.0] (family 0, port 9000) 1326 1327On client, run 3 nc commands in different terminals:: 1328 1329 nstatuser@nstat-a:~$ nc -v nstat-b 9000 1330 Connection to nstat-b 9000 port [tcp/*] succeeded! 1331 1332The nc command only accepts 1 connection, and the accept queue length 1333is 1. On current linux implementation, set queue length to n means the 1334actual queue length is n+1. Now we create 3 connections, 1 is accepted 1335by nc, 2 in accepted queue, so the accept queue is full. 1336 1337Before running the 4th nc, we clean the nstat history on the server:: 1338 1339 nstatuser@nstat-b:~$ nstat -n 1340 1341Run the 4th nc on the client:: 1342 1343 nstatuser@nstat-a:~$ nc -v nstat-b 9000 1344 1345If the nc server is running on kernel 4.10 or higher version, you 1346won't see the "Connection to ... succeeded!" string, because kernel 1347will drop the SYN if the accept queue is full. If the nc client is running 1348on an old kernel, you would see that the connection is succeeded, 1349because kernel would complete the 3 way handshake and keep the socket 1350on half open queue. I did the test on kernel 4.15. Below is the nstat 1351on the server:: 1352 1353 nstatuser@nstat-b:~$ nstat 1354 #kernel 1355 IpInReceives 4 0.0 1356 IpInDelivers 4 0.0 1357 TcpInSegs 4 0.0 1358 TcpExtListenOverflows 4 0.0 1359 TcpExtListenDrops 4 0.0 1360 IpExtInOctets 240 0.0 1361 IpExtInNoECTPkts 4 0.0 1362 1363Both TcpExtListenOverflows and TcpExtListenDrops were 4. If the time 1364between the 4th nc and the nstat was longer, the value of 1365TcpExtListenOverflows and TcpExtListenDrops would be larger, because 1366the SYN of the 4th nc was dropped, the client was retrying. 1367 1368IpInAddrErrors, IpExtInNoRoutes and IpOutNoRoutes 1369------------------------------------------------- 1370server A IP address: 192.168.122.250 1371server B IP address: 192.168.122.251 1372Prepare on server A, add a route to server B:: 1373 1374 $ sudo ip route add 8.8.8.8/32 via 192.168.122.251 1375 1376Prepare on server B, disable send_redirects for all interfaces:: 1377 1378 $ sudo sysctl -w net.ipv4.conf.all.send_redirects=0 1379 $ sudo sysctl -w net.ipv4.conf.ens3.send_redirects=0 1380 $ sudo sysctl -w net.ipv4.conf.lo.send_redirects=0 1381 $ sudo sysctl -w net.ipv4.conf.default.send_redirects=0 1382 1383We want to let sever A send a packet to 8.8.8.8, and route the packet 1384to server B. When server B receives such packet, it might send a ICMP 1385Redirect message to server A, set send_redirects to 0 will disable 1386this behavior. 1387 1388First, generate InAddrErrors. On server B, we disable IP forwarding:: 1389 1390 $ sudo sysctl -w net.ipv4.conf.all.forwarding=0 1391 1392On server A, we send packets to 8.8.8.8:: 1393 1394 $ nc -v 8.8.8.8 53 1395 1396On server B, we check the output of nstat:: 1397 1398 $ nstat 1399 #kernel 1400 IpInReceives 3 0.0 1401 IpInAddrErrors 3 0.0 1402 IpExtInOctets 180 0.0 1403 IpExtInNoECTPkts 3 0.0 1404 1405As we have let server A route 8.8.8.8 to server B, and we disabled IP 1406forwarding on server B, Server A sent packets to server B, then server B 1407dropped packets and increased IpInAddrErrors. As the nc command would 1408re-send the SYN packet if it didn't receive a SYN+ACK, we could find 1409multiple IpInAddrErrors. 1410 1411Second, generate IpExtInNoRoutes. On server B, we enable IP 1412forwarding:: 1413 1414 $ sudo sysctl -w net.ipv4.conf.all.forwarding=1 1415 1416Check the route table of server B and remove the default route:: 1417 1418 $ ip route show 1419 default via 192.168.122.1 dev ens3 proto static 1420 192.168.122.0/24 dev ens3 proto kernel scope link src 192.168.122.251 1421 $ sudo ip route delete default via 192.168.122.1 dev ens3 proto static 1422 1423On server A, we contact 8.8.8.8 again:: 1424 1425 $ nc -v 8.8.8.8 53 1426 nc: connect to 8.8.8.8 port 53 (tcp) failed: Network is unreachable 1427 1428On server B, run nstat:: 1429 1430 $ nstat 1431 #kernel 1432 IpInReceives 1 0.0 1433 IpOutRequests 1 0.0 1434 IcmpOutMsgs 1 0.0 1435 IcmpOutDestUnreachs 1 0.0 1436 IcmpMsgOutType3 1 0.0 1437 IpExtInNoRoutes 1 0.0 1438 IpExtInOctets 60 0.0 1439 IpExtOutOctets 88 0.0 1440 IpExtInNoECTPkts 1 0.0 1441 1442We enabled IP forwarding on server B, when server B received a packet 1443which destination IP address is 8.8.8.8, server B will try to forward 1444this packet. We have deleted the default route, there was no route for 14458.8.8.8, so server B increase IpExtInNoRoutes and sent the "ICMP 1446Destination Unreachable" message to server A. 1447 1448Third, generate IpOutNoRoutes. Run ping command on server B:: 1449 1450 $ ping -c 1 8.8.8.8 1451 connect: Network is unreachable 1452 1453Run nstat on server B:: 1454 1455 $ nstat 1456 #kernel 1457 IpOutNoRoutes 1 0.0 1458 1459We have deleted the default route on server B. Server B couldn't find 1460a route for the 8.8.8.8 IP address, so server B increased 1461IpOutNoRoutes. 1462 1463TcpExtTCPACKSkippedSynRecv 1464-------------------------- 1465In this test, we send 3 same SYN packets from client to server. The 1466first SYN will let server create a socket, set it to Syn-Recv status, 1467and reply a SYN/ACK. The second SYN will let server reply the SYN/ACK 1468again, and record the reply time (the duplicate ACK reply time). The 1469third SYN will let server check the previous duplicate ACK reply time, 1470and decide to skip the duplicate ACK, then increase the 1471TcpExtTCPACKSkippedSynRecv counter. 1472 1473Run tcpdump to capture a SYN packet:: 1474 1475 nstatuser@nstat-a:~$ sudo tcpdump -c 1 -w /tmp/syn.pcap port 9000 1476 tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes 1477 1478Open another terminal, run nc command:: 1479 1480 nstatuser@nstat-a:~$ nc nstat-b 9000 1481 1482As the nstat-b didn't listen on port 9000, it should reply a RST, and 1483the nc command exited immediately. It was enough for the tcpdump 1484command to capture a SYN packet. A linux server might use hardware 1485offload for the TCP checksum, so the checksum in the /tmp/syn.pcap 1486might be not correct. We call tcprewrite to fix it:: 1487 1488 nstatuser@nstat-a:~$ tcprewrite --infile=/tmp/syn.pcap --outfile=/tmp/syn_fixcsum.pcap --fixcsum 1489 1490On nstat-b, we run nc to listen on port 9000:: 1491 1492 nstatuser@nstat-b:~$ nc -lkv 9000 1493 Listening on [0.0.0.0] (family 0, port 9000) 1494 1495On nstat-a, we blocked the packet from port 9000, or nstat-a would send 1496RST to nstat-b:: 1497 1498 nstatuser@nstat-a:~$ sudo iptables -A INPUT -p tcp --sport 9000 -j DROP 1499 1500Send 3 SYN repeatly to nstat-b:: 1501 1502 nstatuser@nstat-a:~$ for i in {1..3}; do sudo tcpreplay -i ens3 /tmp/syn_fixcsum.pcap; done 1503 1504Check snmp cunter on nstat-b:: 1505 1506 nstatuser@nstat-b:~$ nstat | grep -i skip 1507 TcpExtTCPACKSkippedSynRecv 1 0.0 1508 1509As we expected, TcpExtTCPACKSkippedSynRecv is 1. 1510 1511TcpExtTCPACKSkippedPAWS 1512----------------------- 1513To trigger PAWS, we could send an old SYN. 1514 1515On nstat-b, let nc listen on port 9000:: 1516 1517 nstatuser@nstat-b:~$ nc -lkv 9000 1518 Listening on [0.0.0.0] (family 0, port 9000) 1519 1520On nstat-a, run tcpdump to capture a SYN:: 1521 1522 nstatuser@nstat-a:~$ sudo tcpdump -w /tmp/paws_pre.pcap -c 1 port 9000 1523 tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes 1524 1525On nstat-a, run nc as a client to connect nstat-b:: 1526 1527 nstatuser@nstat-a:~$ nc -v nstat-b 9000 1528 Connection to nstat-b 9000 port [tcp/*] succeeded! 1529 1530Now the tcpdump has captured the SYN and exit. We should fix the 1531checksum:: 1532 1533 nstatuser@nstat-a:~$ tcprewrite --infile /tmp/paws_pre.pcap --outfile /tmp/paws.pcap --fixcsum 1534 1535Send the SYN packet twice:: 1536 1537 nstatuser@nstat-a:~$ for i in {1..2}; do sudo tcpreplay -i ens3 /tmp/paws.pcap; done 1538 1539On nstat-b, check the snmp counter:: 1540 1541 nstatuser@nstat-b:~$ nstat | grep -i skip 1542 TcpExtTCPACKSkippedPAWS 1 0.0 1543 1544We sent two SYN via tcpreplay, both of them would let PAWS check 1545failed, the nstat-b replied an ACK for the first SYN, skipped the ACK 1546for the second SYN, and updated TcpExtTCPACKSkippedPAWS. 1547 1548TcpExtTCPACKSkippedSeq 1549---------------------- 1550To trigger TcpExtTCPACKSkippedSeq, we send packets which have valid 1551timestamp (to pass PAWS check) but the sequence number is out of 1552window. The linux TCP stack would avoid to skip if the packet has 1553data, so we need a pure ACK packet. To generate such a packet, we 1554could create two sockets: one on port 9000, another on port 9001. Then 1555we capture an ACK on port 9001, change the source/destination port 1556numbers to match the port 9000 socket. Then we could trigger 1557TcpExtTCPACKSkippedSeq via this packet. 1558 1559On nstat-b, open two terminals, run two nc commands to listen on both 1560port 9000 and port 9001:: 1561 1562 nstatuser@nstat-b:~$ nc -lkv 9000 1563 Listening on [0.0.0.0] (family 0, port 9000) 1564 1565 nstatuser@nstat-b:~$ nc -lkv 9001 1566 Listening on [0.0.0.0] (family 0, port 9001) 1567 1568On nstat-a, run two nc clients:: 1569 1570 nstatuser@nstat-a:~$ nc -v nstat-b 9000 1571 Connection to nstat-b 9000 port [tcp/*] succeeded! 1572 1573 nstatuser@nstat-a:~$ nc -v nstat-b 9001 1574 Connection to nstat-b 9001 port [tcp/*] succeeded! 1575 1576On nstat-a, run tcpdump to capture an ACK:: 1577 1578 nstatuser@nstat-a:~$ sudo tcpdump -w /tmp/seq_pre.pcap -c 1 dst port 9001 1579 tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes 1580 1581On nstat-b, send a packet via the port 9001 socket. E.g. we sent a 1582string 'foo' in our example:: 1583 1584 nstatuser@nstat-b:~$ nc -lkv 9001 1585 Listening on [0.0.0.0] (family 0, port 9001) 1586 Connection from nstat-a 42132 received! 1587 foo 1588 1589On nstat-a, the tcpdump should have caputred the ACK. We should check 1590the source port numbers of the two nc clients:: 1591 1592 nstatuser@nstat-a:~$ ss -ta '( dport = :9000 || dport = :9001 )' | tee 1593 State Recv-Q Send-Q Local Address:Port Peer Address:Port 1594 ESTAB 0 0 192.168.122.250:50208 192.168.122.251:9000 1595 ESTAB 0 0 192.168.122.250:42132 192.168.122.251:9001 1596 1597Run tcprewrite, change port 9001 to port 9000, chagne port 42132 to 1598port 50208:: 1599 1600 nstatuser@nstat-a:~$ tcprewrite --infile /tmp/seq_pre.pcap --outfile /tmp/seq.pcap -r 9001:9000 -r 42132:50208 --fixcsum 1601 1602Now the /tmp/seq.pcap is the packet we need. Send it to nstat-b:: 1603 1604 nstatuser@nstat-a:~$ for i in {1..2}; do sudo tcpreplay -i ens3 /tmp/seq.pcap; done 1605 1606Check TcpExtTCPACKSkippedSeq on nstat-b:: 1607 1608 nstatuser@nstat-b:~$ nstat | grep -i skip 1609 TcpExtTCPACKSkippedSeq 1 0.0 1610