xref: /openbmc/linux/Documentation/networking/snmp_counter.rst (revision 7fc38225363dd8f19e667ad7c77b63bc4a5c065d)
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