mirror of
https://github.com/torvalds/linux
synced 2024-09-06 18:01:05 +00:00
![Vegard Nossum](/assets/img/avatar_default.png)
There aren't a ton of references to commits in the documentation, but they do exist, and we can use automarkup to linkify them to make them easier to follow. Use something like this to find references to commits: git grep -P 'commit.*[0-9a-f]{8,}' Documentation/ Also fix a few of these to standardize on the exact format that is already used in changelogs. Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Link: https://lore.kernel.org/r/20231027115420.205279-1-vegard.nossum@oracle.com
1790 lines
65 KiB
ReStructuredText
1790 lines
65 KiB
ReStructuredText
============
|
|
SNMP counter
|
|
============
|
|
|
|
This document explains the meaning of SNMP counters.
|
|
|
|
General IPv4 counters
|
|
=====================
|
|
All layer 4 packets and ICMP packets will change these counters, but
|
|
these counters won't be changed by layer 2 packets (such as STP) or
|
|
ARP packets.
|
|
|
|
* IpInReceives
|
|
|
|
Defined in `RFC1213 ipInReceives`_
|
|
|
|
.. _RFC1213 ipInReceives: https://tools.ietf.org/html/rfc1213#page-26
|
|
|
|
The number of packets received by the IP layer. It gets increasing at the
|
|
beginning of ip_rcv function, always be updated together with
|
|
IpExtInOctets. It will be increased even if the packet is dropped
|
|
later (e.g. due to the IP header is invalid or the checksum is wrong
|
|
and so on). It indicates the number of aggregated segments after
|
|
GRO/LRO.
|
|
|
|
* IpInDelivers
|
|
|
|
Defined in `RFC1213 ipInDelivers`_
|
|
|
|
.. _RFC1213 ipInDelivers: https://tools.ietf.org/html/rfc1213#page-28
|
|
|
|
The number of packets delivers to the upper layer protocols. E.g. TCP, UDP,
|
|
ICMP and so on. If no one listens on a raw socket, only kernel
|
|
supported protocols will be delivered, if someone listens on the raw
|
|
socket, all valid IP packets will be delivered.
|
|
|
|
* IpOutRequests
|
|
|
|
Defined in `RFC1213 ipOutRequests`_
|
|
|
|
.. _RFC1213 ipOutRequests: https://tools.ietf.org/html/rfc1213#page-28
|
|
|
|
The number of packets sent via IP layer, for both single cast and
|
|
multicast packets, and would always be updated together with
|
|
IpExtOutOctets.
|
|
|
|
* IpExtInOctets and IpExtOutOctets
|
|
|
|
They are Linux kernel extensions, no RFC definitions. Please note,
|
|
RFC1213 indeed defines ifInOctets and ifOutOctets, but they
|
|
are different things. The ifInOctets and ifOutOctets include the MAC
|
|
layer header size but IpExtInOctets and IpExtOutOctets don't, they
|
|
only include the IP layer header and the IP layer data.
|
|
|
|
* IpExtInNoECTPkts, IpExtInECT1Pkts, IpExtInECT0Pkts, IpExtInCEPkts
|
|
|
|
They indicate the number of four kinds of ECN IP packets, please refer
|
|
`Explicit Congestion Notification`_ for more details.
|
|
|
|
.. _Explicit Congestion Notification: https://tools.ietf.org/html/rfc3168#page-6
|
|
|
|
These 4 counters calculate how many packets received per ECN
|
|
status. They count the real frame number regardless the LRO/GRO. So
|
|
for the same packet, you might find that IpInReceives count 1, but
|
|
IpExtInNoECTPkts counts 2 or more.
|
|
|
|
* IpInHdrErrors
|
|
|
|
Defined in `RFC1213 ipInHdrErrors`_. It indicates the packet is
|
|
dropped due to the IP header error. It might happen in both IP input
|
|
and IP forward paths.
|
|
|
|
.. _RFC1213 ipInHdrErrors: https://tools.ietf.org/html/rfc1213#page-27
|
|
|
|
* IpInAddrErrors
|
|
|
|
Defined in `RFC1213 ipInAddrErrors`_. It will be increased in two
|
|
scenarios: (1) The IP address is invalid. (2) The destination IP
|
|
address is not a local address and IP forwarding is not enabled
|
|
|
|
.. _RFC1213 ipInAddrErrors: https://tools.ietf.org/html/rfc1213#page-27
|
|
|
|
* IpExtInNoRoutes
|
|
|
|
This counter means the packet is dropped when the IP stack receives a
|
|
packet and can't find a route for it from the route table. It might
|
|
happen when IP forwarding is enabled and the destination IP address is
|
|
not a local address and there is no route for the destination IP
|
|
address.
|
|
|
|
* IpInUnknownProtos
|
|
|
|
Defined in `RFC1213 ipInUnknownProtos`_. It will be increased if the
|
|
layer 4 protocol is unsupported by kernel. If an application is using
|
|
raw socket, kernel will always deliver the packet to the raw socket
|
|
and this counter won't be increased.
|
|
|
|
.. _RFC1213 ipInUnknownProtos: https://tools.ietf.org/html/rfc1213#page-27
|
|
|
|
* IpExtInTruncatedPkts
|
|
|
|
For IPv4 packet, it means the actual data size is smaller than the
|
|
"Total Length" field in the IPv4 header.
|
|
|
|
* IpInDiscards
|
|
|
|
Defined in `RFC1213 ipInDiscards`_. It indicates the packet is dropped
|
|
in the IP receiving path and due to kernel internal reasons (e.g. no
|
|
enough memory).
|
|
|
|
.. _RFC1213 ipInDiscards: https://tools.ietf.org/html/rfc1213#page-28
|
|
|
|
* IpOutDiscards
|
|
|
|
Defined in `RFC1213 ipOutDiscards`_. It indicates the packet is
|
|
dropped in the IP sending path and due to kernel internal reasons.
|
|
|
|
.. _RFC1213 ipOutDiscards: https://tools.ietf.org/html/rfc1213#page-28
|
|
|
|
* IpOutNoRoutes
|
|
|
|
Defined in `RFC1213 ipOutNoRoutes`_. It indicates the packet is
|
|
dropped in the IP sending path and no route is found for it.
|
|
|
|
.. _RFC1213 ipOutNoRoutes: https://tools.ietf.org/html/rfc1213#page-29
|
|
|
|
ICMP counters
|
|
=============
|
|
* IcmpInMsgs and IcmpOutMsgs
|
|
|
|
Defined by `RFC1213 icmpInMsgs`_ and `RFC1213 icmpOutMsgs`_
|
|
|
|
.. _RFC1213 icmpInMsgs: https://tools.ietf.org/html/rfc1213#page-41
|
|
.. _RFC1213 icmpOutMsgs: https://tools.ietf.org/html/rfc1213#page-43
|
|
|
|
As mentioned in the RFC1213, these two counters include errors, they
|
|
would be increased even if the ICMP packet has an invalid type. The
|
|
ICMP output path will check the header of a raw socket, so the
|
|
IcmpOutMsgs would still be updated if the IP header is constructed by
|
|
a userspace program.
|
|
|
|
* ICMP named types
|
|
|
|
| These counters include most of common ICMP types, they are:
|
|
| IcmpInDestUnreachs: `RFC1213 icmpInDestUnreachs`_
|
|
| IcmpInTimeExcds: `RFC1213 icmpInTimeExcds`_
|
|
| IcmpInParmProbs: `RFC1213 icmpInParmProbs`_
|
|
| IcmpInSrcQuenchs: `RFC1213 icmpInSrcQuenchs`_
|
|
| IcmpInRedirects: `RFC1213 icmpInRedirects`_
|
|
| IcmpInEchos: `RFC1213 icmpInEchos`_
|
|
| IcmpInEchoReps: `RFC1213 icmpInEchoReps`_
|
|
| IcmpInTimestamps: `RFC1213 icmpInTimestamps`_
|
|
| IcmpInTimestampReps: `RFC1213 icmpInTimestampReps`_
|
|
| IcmpInAddrMasks: `RFC1213 icmpInAddrMasks`_
|
|
| IcmpInAddrMaskReps: `RFC1213 icmpInAddrMaskReps`_
|
|
| IcmpOutDestUnreachs: `RFC1213 icmpOutDestUnreachs`_
|
|
| IcmpOutTimeExcds: `RFC1213 icmpOutTimeExcds`_
|
|
| IcmpOutParmProbs: `RFC1213 icmpOutParmProbs`_
|
|
| IcmpOutSrcQuenchs: `RFC1213 icmpOutSrcQuenchs`_
|
|
| IcmpOutRedirects: `RFC1213 icmpOutRedirects`_
|
|
| IcmpOutEchos: `RFC1213 icmpOutEchos`_
|
|
| IcmpOutEchoReps: `RFC1213 icmpOutEchoReps`_
|
|
| IcmpOutTimestamps: `RFC1213 icmpOutTimestamps`_
|
|
| IcmpOutTimestampReps: `RFC1213 icmpOutTimestampReps`_
|
|
| IcmpOutAddrMasks: `RFC1213 icmpOutAddrMasks`_
|
|
| IcmpOutAddrMaskReps: `RFC1213 icmpOutAddrMaskReps`_
|
|
|
|
.. _RFC1213 icmpInDestUnreachs: https://tools.ietf.org/html/rfc1213#page-41
|
|
.. _RFC1213 icmpInTimeExcds: https://tools.ietf.org/html/rfc1213#page-41
|
|
.. _RFC1213 icmpInParmProbs: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInRedirects: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInEchos: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInEchoReps: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInTimestamps: https://tools.ietf.org/html/rfc1213#page-42
|
|
.. _RFC1213 icmpInTimestampReps: https://tools.ietf.org/html/rfc1213#page-43
|
|
.. _RFC1213 icmpInAddrMasks: https://tools.ietf.org/html/rfc1213#page-43
|
|
.. _RFC1213 icmpInAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-43
|
|
|
|
.. _RFC1213 icmpOutDestUnreachs: https://tools.ietf.org/html/rfc1213#page-44
|
|
.. _RFC1213 icmpOutTimeExcds: https://tools.ietf.org/html/rfc1213#page-44
|
|
.. _RFC1213 icmpOutParmProbs: https://tools.ietf.org/html/rfc1213#page-44
|
|
.. _RFC1213 icmpOutSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-44
|
|
.. _RFC1213 icmpOutRedirects: https://tools.ietf.org/html/rfc1213#page-44
|
|
.. _RFC1213 icmpOutEchos: https://tools.ietf.org/html/rfc1213#page-45
|
|
.. _RFC1213 icmpOutEchoReps: https://tools.ietf.org/html/rfc1213#page-45
|
|
.. _RFC1213 icmpOutTimestamps: https://tools.ietf.org/html/rfc1213#page-45
|
|
.. _RFC1213 icmpOutTimestampReps: https://tools.ietf.org/html/rfc1213#page-45
|
|
.. _RFC1213 icmpOutAddrMasks: https://tools.ietf.org/html/rfc1213#page-45
|
|
.. _RFC1213 icmpOutAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-46
|
|
|
|
Every ICMP type has two counters: 'In' and 'Out'. E.g., for the ICMP
|
|
Echo packet, they are IcmpInEchos and IcmpOutEchos. Their meanings are
|
|
straightforward. The 'In' counter means kernel receives such a packet
|
|
and the 'Out' counter means kernel sends such a packet.
|
|
|
|
* ICMP numeric types
|
|
|
|
They are IcmpMsgInType[N] and IcmpMsgOutType[N], the [N] indicates the
|
|
ICMP type number. These counters track all kinds of ICMP packets. The
|
|
ICMP type number definition could be found in the `ICMP parameters`_
|
|
document.
|
|
|
|
.. _ICMP parameters: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
|
|
|
|
For example, if the Linux kernel sends an ICMP Echo packet, the
|
|
IcmpMsgOutType8 would increase 1. And if kernel gets an ICMP Echo Reply
|
|
packet, IcmpMsgInType0 would increase 1.
|
|
|
|
* IcmpInCsumErrors
|
|
|
|
This counter indicates the checksum of the ICMP packet is
|
|
wrong. Kernel verifies the checksum after updating the IcmpInMsgs and
|
|
before updating IcmpMsgInType[N]. If a packet has bad checksum, the
|
|
IcmpInMsgs would be updated but none of IcmpMsgInType[N] would be updated.
|
|
|
|
* IcmpInErrors and IcmpOutErrors
|
|
|
|
Defined by `RFC1213 icmpInErrors`_ and `RFC1213 icmpOutErrors`_
|
|
|
|
.. _RFC1213 icmpInErrors: https://tools.ietf.org/html/rfc1213#page-41
|
|
.. _RFC1213 icmpOutErrors: https://tools.ietf.org/html/rfc1213#page-43
|
|
|
|
When an error occurs in the ICMP packet handler path, these two
|
|
counters would be updated. The receiving packet path use IcmpInErrors
|
|
and the sending packet path use IcmpOutErrors. When IcmpInCsumErrors
|
|
is increased, IcmpInErrors would always be increased too.
|
|
|
|
relationship of the ICMP counters
|
|
---------------------------------
|
|
The sum of IcmpMsgOutType[N] is always equal to IcmpOutMsgs, as they
|
|
are updated at the same time. The sum of IcmpMsgInType[N] plus
|
|
IcmpInErrors should be equal or larger than IcmpInMsgs. When kernel
|
|
receives an ICMP packet, kernel follows below logic:
|
|
|
|
1. increase IcmpInMsgs
|
|
2. if has any error, update IcmpInErrors and finish the process
|
|
3. update IcmpMsgOutType[N]
|
|
4. handle the packet depending on the type, if has any error, update
|
|
IcmpInErrors and finish the process
|
|
|
|
So if all errors occur in step (2), IcmpInMsgs should be equal to the
|
|
sum of IcmpMsgOutType[N] plus IcmpInErrors. If all errors occur in
|
|
step (4), IcmpInMsgs should be equal to the sum of
|
|
IcmpMsgOutType[N]. If the errors occur in both step (2) and step (4),
|
|
IcmpInMsgs should be less than the sum of IcmpMsgOutType[N] plus
|
|
IcmpInErrors.
|
|
|
|
General TCP counters
|
|
====================
|
|
* TcpInSegs
|
|
|
|
Defined in `RFC1213 tcpInSegs`_
|
|
|
|
.. _RFC1213 tcpInSegs: https://tools.ietf.org/html/rfc1213#page-48
|
|
|
|
The number of packets received by the TCP layer. As mentioned in
|
|
RFC1213, it includes the packets received in error, such as checksum
|
|
error, invalid TCP header and so on. Only one error won't be included:
|
|
if the layer 2 destination address is not the NIC's layer 2
|
|
address. It might happen if the packet is a multicast or broadcast
|
|
packet, or the NIC is in promiscuous mode. In these situations, the
|
|
packets would be delivered to the TCP layer, but the TCP layer will discard
|
|
these packets before increasing TcpInSegs. The TcpInSegs counter
|
|
isn't aware of GRO. So if two packets are merged by GRO, the TcpInSegs
|
|
counter would only increase 1.
|
|
|
|
* TcpOutSegs
|
|
|
|
Defined in `RFC1213 tcpOutSegs`_
|
|
|
|
.. _RFC1213 tcpOutSegs: https://tools.ietf.org/html/rfc1213#page-48
|
|
|
|
The number of packets sent by the TCP layer. As mentioned in RFC1213,
|
|
it excludes the retransmitted packets. But it includes the SYN, ACK
|
|
and RST packets. Doesn't like TcpInSegs, the TcpOutSegs is aware of
|
|
GSO, so if a packet would be split to 2 by GSO, TcpOutSegs will
|
|
increase 2.
|
|
|
|
* TcpActiveOpens
|
|
|
|
Defined in `RFC1213 tcpActiveOpens`_
|
|
|
|
.. _RFC1213 tcpActiveOpens: https://tools.ietf.org/html/rfc1213#page-47
|
|
|
|
It means the TCP layer sends a SYN, and come into the SYN-SENT
|
|
state. Every time TcpActiveOpens increases 1, TcpOutSegs should always
|
|
increase 1.
|
|
|
|
* TcpPassiveOpens
|
|
|
|
Defined in `RFC1213 tcpPassiveOpens`_
|
|
|
|
.. _RFC1213 tcpPassiveOpens: https://tools.ietf.org/html/rfc1213#page-47
|
|
|
|
It means the TCP layer receives a SYN, replies a SYN+ACK, come into
|
|
the SYN-RCVD state.
|
|
|
|
* TcpExtTCPRcvCoalesce
|
|
|
|
When packets are received by the TCP layer and are not be read by the
|
|
application, the TCP layer will try to merge them. This counter
|
|
indicate how many packets are merged in such situation. If GRO is
|
|
enabled, lots of packets would be merged by GRO, these packets
|
|
wouldn't be counted to TcpExtTCPRcvCoalesce.
|
|
|
|
* TcpExtTCPAutoCorking
|
|
|
|
When sending packets, the TCP layer will try to merge small packets to
|
|
a bigger one. This counter increase 1 for every packet merged in such
|
|
situation. Please refer to the LWN article for more details:
|
|
https://lwn.net/Articles/576263/
|
|
|
|
* TcpExtTCPOrigDataSent
|
|
|
|
This counter is explained by kernel commit f19c29e3e391, I pasted the
|
|
explanation below::
|
|
|
|
TCPOrigDataSent: number of outgoing packets with original data (excluding
|
|
retransmission but including data-in-SYN). This counter is different from
|
|
TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is
|
|
more useful to track the TCP retransmission rate.
|
|
|
|
* TCPSynRetrans
|
|
|
|
This counter is explained by kernel commit f19c29e3e391, I pasted the
|
|
explanation below::
|
|
|
|
TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down
|
|
retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
|
|
|
|
* TCPFastOpenActiveFail
|
|
|
|
This counter is explained by kernel commit f19c29e3e391, I pasted the
|
|
explanation below::
|
|
|
|
TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because
|
|
the remote does not accept it or the attempts timed out.
|
|
|
|
* TcpExtListenOverflows and TcpExtListenDrops
|
|
|
|
When kernel receives a SYN from a client, and if the TCP accept queue
|
|
is full, kernel will drop the SYN and add 1 to TcpExtListenOverflows.
|
|
At the same time kernel will also add 1 to TcpExtListenDrops. When a
|
|
TCP socket is in LISTEN state, and kernel need to drop a packet,
|
|
kernel would always add 1 to TcpExtListenDrops. So increase
|
|
TcpExtListenOverflows would let TcpExtListenDrops increasing at the
|
|
same time, but TcpExtListenDrops would also increase without
|
|
TcpExtListenOverflows increasing, e.g. a memory allocation fail would
|
|
also let TcpExtListenDrops increase.
|
|
|
|
Note: The above explanation is based on kernel 4.10 or above version, on
|
|
an old kernel, the TCP stack has different behavior when TCP accept
|
|
queue is full. On the old kernel, TCP stack won't drop the SYN, it
|
|
would complete the 3-way handshake. As the accept queue is full, TCP
|
|
stack will keep the socket in the TCP half-open queue. As it is in the
|
|
half open queue, TCP stack will send SYN+ACK on an exponential backoff
|
|
timer, after client replies ACK, TCP stack checks whether the accept
|
|
queue is still full, if it is not full, moves the socket to the accept
|
|
queue, if it is full, keeps the socket in the half-open queue, at next
|
|
time client replies ACK, this socket will get another chance to move
|
|
to the accept queue.
|
|
|
|
|
|
TCP Fast Open
|
|
=============
|
|
* TcpEstabResets
|
|
|
|
Defined in `RFC1213 tcpEstabResets`_.
|
|
|
|
.. _RFC1213 tcpEstabResets: https://tools.ietf.org/html/rfc1213#page-48
|
|
|
|
* TcpAttemptFails
|
|
|
|
Defined in `RFC1213 tcpAttemptFails`_.
|
|
|
|
.. _RFC1213 tcpAttemptFails: https://tools.ietf.org/html/rfc1213#page-48
|
|
|
|
* TcpOutRsts
|
|
|
|
Defined in `RFC1213 tcpOutRsts`_. The RFC says this counter indicates
|
|
the 'segments sent containing the RST flag', but in linux kernel, this
|
|
counter indicates the segments kernel tried to send. The sending
|
|
process might be failed due to some errors (e.g. memory alloc failed).
|
|
|
|
.. _RFC1213 tcpOutRsts: https://tools.ietf.org/html/rfc1213#page-52
|
|
|
|
* TcpExtTCPSpuriousRtxHostQueues
|
|
|
|
When the TCP stack wants to retransmit a packet, and finds that packet
|
|
is not lost in the network, but the packet is not sent yet, the TCP
|
|
stack would give up the retransmission and update this counter. It
|
|
might happen if a packet stays too long time in a qdisc or driver
|
|
queue.
|
|
|
|
* TcpEstabResets
|
|
|
|
The socket receives a RST packet in Establish or CloseWait state.
|
|
|
|
* TcpExtTCPKeepAlive
|
|
|
|
This counter indicates many keepalive packets were sent. The keepalive
|
|
won't be enabled by default. A userspace program could enable it by
|
|
setting the SO_KEEPALIVE socket option.
|
|
|
|
* TcpExtTCPSpuriousRTOs
|
|
|
|
The spurious retransmission timeout detected by the `F-RTO`_
|
|
algorithm.
|
|
|
|
.. _F-RTO: https://tools.ietf.org/html/rfc5682
|
|
|
|
TCP Fast Path
|
|
=============
|
|
When kernel receives a TCP packet, it has two paths to handler the
|
|
packet, one is fast path, another is slow path. The comment in kernel
|
|
code provides a good explanation of them, I pasted them below::
|
|
|
|
It is split into a fast path and a slow path. The fast path is
|
|
disabled when:
|
|
|
|
- A zero window was announced from us
|
|
- zero window probing
|
|
is only handled properly on the slow path.
|
|
- Out of order segments arrived.
|
|
- Urgent data is expected.
|
|
- There is no buffer space left
|
|
- Unexpected TCP flags/window values/header lengths are received
|
|
(detected by checking the TCP header against pred_flags)
|
|
- Data is sent in both directions. The fast path only supports pure senders
|
|
or pure receivers (this means either the sequence number or the ack
|
|
value must stay constant)
|
|
- Unexpected TCP option.
|
|
|
|
Kernel will try to use fast path unless any of the above conditions
|
|
are satisfied. If the packets are out of order, kernel will handle
|
|
them in slow path, which means the performance might be not very
|
|
good. Kernel would also come into slow path if the "Delayed ack" is
|
|
used, because when using "Delayed ack", the data is sent in both
|
|
directions. When the TCP window scale option is not used, kernel will
|
|
try to enable fast path immediately when the connection comes into the
|
|
established state, but if the TCP window scale option is used, kernel
|
|
will disable the fast path at first, and try to enable it after kernel
|
|
receives packets.
|
|
|
|
* TcpExtTCPPureAcks and TcpExtTCPHPAcks
|
|
|
|
If a packet set ACK flag and has no data, it is a pure ACK packet, if
|
|
kernel handles it in the fast path, TcpExtTCPHPAcks will increase 1,
|
|
if kernel handles it in the slow path, TcpExtTCPPureAcks will
|
|
increase 1.
|
|
|
|
* TcpExtTCPHPHits
|
|
|
|
If a TCP packet has data (which means it is not a pure ACK packet),
|
|
and this packet is handled in the fast path, TcpExtTCPHPHits will
|
|
increase 1.
|
|
|
|
|
|
TCP abort
|
|
=========
|
|
* TcpExtTCPAbortOnData
|
|
|
|
It means TCP layer has data in flight, but need to close the
|
|
connection. So TCP layer sends a RST to the other side, indicate the
|
|
connection is not closed very graceful. An easy way to increase this
|
|
counter is using the SO_LINGER option. Please refer to the SO_LINGER
|
|
section of the `socket man page`_:
|
|
|
|
.. _socket man page: http://man7.org/linux/man-pages/man7/socket.7.html
|
|
|
|
By default, when an application closes a connection, the close function
|
|
will return immediately and kernel will try to send the in-flight data
|
|
async. If you use the SO_LINGER option, set l_onoff to 1, and l_linger
|
|
to a positive number, the close function won't return immediately, but
|
|
wait for the in-flight data are acked by the other side, the max wait
|
|
time is l_linger seconds. If set l_onoff to 1 and set l_linger to 0,
|
|
when the application closes a connection, kernel will send a RST
|
|
immediately and increase the TcpExtTCPAbortOnData counter.
|
|
|
|
* TcpExtTCPAbortOnClose
|
|
|
|
This counter means the application has unread data in the TCP layer when
|
|
the application wants to close the TCP connection. In such a situation,
|
|
kernel will send a RST to the other side of the TCP connection.
|
|
|
|
* TcpExtTCPAbortOnMemory
|
|
|
|
When an application closes a TCP connection, kernel still need to track
|
|
the connection, let it complete the TCP disconnect process. E.g. an
|
|
app calls the close method of a socket, kernel sends fin to the other
|
|
side of the connection, then the app has no relationship with the
|
|
socket any more, but kernel need to keep the socket, this socket
|
|
becomes an orphan socket, kernel waits for the reply of the other side,
|
|
and would come to the TIME_WAIT state finally. When kernel has no
|
|
enough memory to keep the orphan socket, kernel would send an RST to
|
|
the other side, and delete the socket, in such situation, kernel will
|
|
increase 1 to the TcpExtTCPAbortOnMemory. Two conditions would trigger
|
|
TcpExtTCPAbortOnMemory:
|
|
|
|
1. the memory used by the TCP protocol is higher than the third value of
|
|
the tcp_mem. Please refer the tcp_mem section in the `TCP man page`_:
|
|
|
|
.. _TCP man page: http://man7.org/linux/man-pages/man7/tcp.7.html
|
|
|
|
2. the orphan socket count is higher than net.ipv4.tcp_max_orphans
|
|
|
|
|
|
* TcpExtTCPAbortOnTimeout
|
|
|
|
This counter will increase when any of the TCP timers expire. In such
|
|
situation, kernel won't send RST, just give up the connection.
|
|
|
|
* TcpExtTCPAbortOnLinger
|
|
|
|
When a TCP connection comes into FIN_WAIT_2 state, instead of waiting
|
|
for the fin packet from the other side, kernel could send a RST and
|
|
delete the socket immediately. This is not the default behavior of
|
|
Linux kernel TCP stack. By configuring the TCP_LINGER2 socket option,
|
|
you could let kernel follow this behavior.
|
|
|
|
* TcpExtTCPAbortFailed
|
|
|
|
The kernel TCP layer will send RST if the `RFC2525 2.17 section`_ is
|
|
satisfied. If an internal error occurs during this process,
|
|
TcpExtTCPAbortFailed will be increased.
|
|
|
|
.. _RFC2525 2.17 section: https://tools.ietf.org/html/rfc2525#page-50
|
|
|
|
TCP Hybrid Slow Start
|
|
=====================
|
|
The Hybrid Slow Start algorithm is an enhancement of the traditional
|
|
TCP congestion window Slow Start algorithm. It uses two pieces of
|
|
information to detect whether the max bandwidth of the TCP path is
|
|
approached. The two pieces of information are ACK train length and
|
|
increase in packet delay. For detail information, please refer the
|
|
`Hybrid Slow Start paper`_. Either ACK train length or packet delay
|
|
hits a specific threshold, the congestion control algorithm will come
|
|
into the Congestion Avoidance state. Until v4.20, two congestion
|
|
control algorithms are using Hybrid Slow Start, they are cubic (the
|
|
default congestion control algorithm) and cdg. Four snmp counters
|
|
relate with the Hybrid Slow Start algorithm.
|
|
|
|
.. _Hybrid Slow Start paper: https://pdfs.semanticscholar.org/25e9/ef3f03315782c7f1cbcd31b587857adae7d1.pdf
|
|
|
|
* TcpExtTCPHystartTrainDetect
|
|
|
|
How many times the ACK train length threshold is detected
|
|
|
|
* TcpExtTCPHystartTrainCwnd
|
|
|
|
The sum of CWND detected by ACK train length. Dividing this value by
|
|
TcpExtTCPHystartTrainDetect is the average CWND which detected by the
|
|
ACK train length.
|
|
|
|
* TcpExtTCPHystartDelayDetect
|
|
|
|
How many times the packet delay threshold is detected.
|
|
|
|
* TcpExtTCPHystartDelayCwnd
|
|
|
|
The sum of CWND detected by packet delay. Dividing this value by
|
|
TcpExtTCPHystartDelayDetect is the average CWND which detected by the
|
|
packet delay.
|
|
|
|
TCP retransmission and congestion control
|
|
=========================================
|
|
The TCP protocol has two retransmission mechanisms: SACK and fast
|
|
recovery. They are exclusive with each other. When SACK is enabled,
|
|
the kernel TCP stack would use SACK, or kernel would use fast
|
|
recovery. The SACK is a TCP option, which is defined in `RFC2018`_,
|
|
the fast recovery is defined in `RFC6582`_, which is also called
|
|
'Reno'.
|
|
|
|
The TCP congestion control is a big and complex topic. To understand
|
|
the related snmp counter, we need to know the states of the congestion
|
|
control state machine. There are 5 states: Open, Disorder, CWR,
|
|
Recovery and Loss. For details about these states, please refer page 5
|
|
and page 6 of this document:
|
|
https://pdfs.semanticscholar.org/0e9c/968d09ab2e53e24c4dca5b2d67c7f7140f8e.pdf
|
|
|
|
.. _RFC2018: https://tools.ietf.org/html/rfc2018
|
|
.. _RFC6582: https://tools.ietf.org/html/rfc6582
|
|
|
|
* TcpExtTCPRenoRecovery and TcpExtTCPSackRecovery
|
|
|
|
When the congestion control comes into Recovery state, if sack is
|
|
used, TcpExtTCPSackRecovery increases 1, if sack is not used,
|
|
TcpExtTCPRenoRecovery increases 1. These two counters mean the TCP
|
|
stack begins to retransmit the lost packets.
|
|
|
|
* TcpExtTCPSACKReneging
|
|
|
|
A packet was acknowledged by SACK, but the receiver has dropped this
|
|
packet, so the sender needs to retransmit this packet. In this
|
|
situation, the sender adds 1 to TcpExtTCPSACKReneging. A receiver
|
|
could drop a packet which has been acknowledged by SACK, although it is
|
|
unusual, it is allowed by the TCP protocol. The sender doesn't really
|
|
know what happened on the receiver side. The sender just waits until
|
|
the RTO expires for this packet, then the sender assumes this packet
|
|
has been dropped by the receiver.
|
|
|
|
* TcpExtTCPRenoReorder
|
|
|
|
The reorder packet is detected by fast recovery. It would only be used
|
|
if SACK is disabled. The fast recovery algorithm detects recorder by
|
|
the duplicate ACK number. E.g., if retransmission is triggered, and
|
|
the original retransmitted packet is not lost, it is just out of
|
|
order, the receiver would acknowledge multiple times, one for the
|
|
retransmitted packet, another for the arriving of the original out of
|
|
order packet. Thus the sender would find more ACks than its
|
|
expectation, and the sender knows out of order occurs.
|
|
|
|
* TcpExtTCPTSReorder
|
|
|
|
The reorder packet is detected when a hole is filled. E.g., assume the
|
|
sender sends packet 1,2,3,4,5, and the receiving order is
|
|
1,2,4,5,3. When the sender receives the ACK of packet 3 (which will
|
|
fill the hole), two conditions will let TcpExtTCPTSReorder increase
|
|
1: (1) if the packet 3 is not re-retransmitted yet. (2) if the packet
|
|
3 is retransmitted but the timestamp of the packet 3's ACK is earlier
|
|
than the retransmission timestamp.
|
|
|
|
* TcpExtTCPSACKReorder
|
|
|
|
The reorder packet detected by SACK. The SACK has two methods to
|
|
detect reorder: (1) DSACK is received by the sender. It means the
|
|
sender sends the same packet more than one times. And the only reason
|
|
is the sender believes an out of order packet is lost so it sends the
|
|
packet again. (2) Assume packet 1,2,3,4,5 are sent by the sender, and
|
|
the sender has received SACKs for packet 2 and 5, now the sender
|
|
receives SACK for packet 4 and the sender doesn't retransmit the
|
|
packet yet, the sender would know packet 4 is out of order. The TCP
|
|
stack of kernel will increase TcpExtTCPSACKReorder for both of the
|
|
above scenarios.
|
|
|
|
* TcpExtTCPSlowStartRetrans
|
|
|
|
The TCP stack wants to retransmit a packet and the congestion control
|
|
state is 'Loss'.
|
|
|
|
* TcpExtTCPFastRetrans
|
|
|
|
The TCP stack wants to retransmit a packet and the congestion control
|
|
state is not 'Loss'.
|
|
|
|
* TcpExtTCPLostRetransmit
|
|
|
|
A SACK points out that a retransmission packet is lost again.
|
|
|
|
* TcpExtTCPRetransFail
|
|
|
|
The TCP stack tries to deliver a retransmission packet to lower layers
|
|
but the lower layers return an error.
|
|
|
|
* TcpExtTCPSynRetrans
|
|
|
|
The TCP stack retransmits a SYN packet.
|
|
|
|
DSACK
|
|
=====
|
|
The DSACK is defined in `RFC2883`_. The receiver uses DSACK to report
|
|
duplicate packets to the sender. There are two kinds of
|
|
duplications: (1) a packet which has been acknowledged is
|
|
duplicate. (2) an out of order packet is duplicate. The TCP stack
|
|
counts these two kinds of duplications on both receiver side and
|
|
sender side.
|
|
|
|
.. _RFC2883 : https://tools.ietf.org/html/rfc2883
|
|
|
|
* TcpExtTCPDSACKOldSent
|
|
|
|
The TCP stack receives a duplicate packet which has been acked, so it
|
|
sends a DSACK to the sender.
|
|
|
|
* TcpExtTCPDSACKOfoSent
|
|
|
|
The TCP stack receives an out of order duplicate packet, so it sends a
|
|
DSACK to the sender.
|
|
|
|
* TcpExtTCPDSACKRecv
|
|
|
|
The TCP stack receives a DSACK, which indicates an acknowledged
|
|
duplicate packet is received.
|
|
|
|
* TcpExtTCPDSACKOfoRecv
|
|
|
|
The TCP stack receives a DSACK, which indicate an out of order
|
|
duplicate packet is received.
|
|
|
|
invalid SACK and DSACK
|
|
======================
|
|
When a SACK (or DSACK) block is invalid, a corresponding counter would
|
|
be updated. The validation method is base on the start/end sequence
|
|
number of the SACK block. For more details, please refer the comment
|
|
of the function tcp_is_sackblock_valid in the kernel source code. A
|
|
SACK option could have up to 4 blocks, they are checked
|
|
individually. E.g., if 3 blocks of a SACk is invalid, the
|
|
corresponding counter would be updated 3 times. The comment of commit
|
|
18f02545a9a1 ("[TCP] MIB: Add counters for discarded SACK blocks")
|
|
has additional explanation:
|
|
|
|
* TcpExtTCPSACKDiscard
|
|
|
|
This counter indicates how many SACK blocks are invalid. If the invalid
|
|
SACK block is caused by ACK recording, the TCP stack will only ignore
|
|
it and won't update this counter.
|
|
|
|
* TcpExtTCPDSACKIgnoredOld and TcpExtTCPDSACKIgnoredNoUndo
|
|
|
|
When a DSACK block is invalid, one of these two counters would be
|
|
updated. Which counter will be updated depends on the undo_marker flag
|
|
of the TCP socket. If the undo_marker is not set, the TCP stack isn't
|
|
likely to re-transmit any packets, and we still receive an invalid
|
|
DSACK block, the reason might be that the packet is duplicated in the
|
|
middle of the network. In such scenario, TcpExtTCPDSACKIgnoredNoUndo
|
|
will be updated. If the undo_marker is set, TcpExtTCPDSACKIgnoredOld
|
|
will be updated. As implied in its name, it might be an old packet.
|
|
|
|
SACK shift
|
|
==========
|
|
The linux networking stack stores data in sk_buff struct (skb for
|
|
short). If a SACK block acrosses multiple skb, the TCP stack will try
|
|
to re-arrange data in these skb. E.g. if a SACK block acknowledges seq
|
|
10 to 15, skb1 has seq 10 to 13, skb2 has seq 14 to 20. The seq 14 and
|
|
15 in skb2 would be moved to skb1. This operation is 'shift'. If a
|
|
SACK block acknowledges seq 10 to 20, skb1 has seq 10 to 13, skb2 has
|
|
seq 14 to 20. All data in skb2 will be moved to skb1, and skb2 will be
|
|
discard, this operation is 'merge'.
|
|
|
|
* TcpExtTCPSackShifted
|
|
|
|
A skb is shifted
|
|
|
|
* TcpExtTCPSackMerged
|
|
|
|
A skb is merged
|
|
|
|
* TcpExtTCPSackShiftFallback
|
|
|
|
A skb should be shifted or merged, but the TCP stack doesn't do it for
|
|
some reasons.
|
|
|
|
TCP out of order
|
|
================
|
|
* TcpExtTCPOFOQueue
|
|
|
|
The TCP layer receives an out of order packet and has enough memory
|
|
to queue it.
|
|
|
|
* TcpExtTCPOFODrop
|
|
|
|
The TCP layer receives an out of order packet but doesn't have enough
|
|
memory, so drops it. Such packets won't be counted into
|
|
TcpExtTCPOFOQueue.
|
|
|
|
* TcpExtTCPOFOMerge
|
|
|
|
The received out of order packet has an overlay with the previous
|
|
packet. the overlay part will be dropped. All of TcpExtTCPOFOMerge
|
|
packets will also be counted into TcpExtTCPOFOQueue.
|
|
|
|
TCP PAWS
|
|
========
|
|
PAWS (Protection Against Wrapped Sequence numbers) is an algorithm
|
|
which is used to drop old packets. It depends on the TCP
|
|
timestamps. For detail information, please refer the `timestamp wiki`_
|
|
and the `RFC of PAWS`_.
|
|
|
|
.. _RFC of PAWS: https://tools.ietf.org/html/rfc1323#page-17
|
|
.. _timestamp wiki: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_timestamps
|
|
|
|
* TcpExtPAWSActive
|
|
|
|
Packets are dropped by PAWS in Syn-Sent status.
|
|
|
|
* TcpExtPAWSEstab
|
|
|
|
Packets are dropped by PAWS in any status other than Syn-Sent.
|
|
|
|
TCP ACK skip
|
|
============
|
|
In some scenarios, kernel would avoid sending duplicate ACKs too
|
|
frequently. Please find more details in the tcp_invalid_ratelimit
|
|
section of the `sysctl document`_. When kernel decides to skip an ACK
|
|
due to tcp_invalid_ratelimit, kernel would update one of below
|
|
counters to indicate the ACK is skipped in which scenario. The ACK
|
|
would only be skipped if the received packet is either a SYN packet or
|
|
it has no data.
|
|
|
|
.. _sysctl document: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.rst
|
|
|
|
* TcpExtTCPACKSkippedSynRecv
|
|
|
|
The ACK is skipped in Syn-Recv status. The Syn-Recv status means the
|
|
TCP stack receives a SYN and replies SYN+ACK. Now the TCP stack is
|
|
waiting for an ACK. Generally, the TCP stack doesn't need to send ACK
|
|
in the Syn-Recv status. But in several scenarios, the TCP stack need
|
|
to send an ACK. E.g., the TCP stack receives the same SYN packet
|
|
repeately, the received packet does not pass the PAWS check, or the
|
|
received packet sequence number is out of window. In these scenarios,
|
|
the TCP stack needs to send ACK. If the ACk sending frequency is higher than
|
|
tcp_invalid_ratelimit allows, the TCP stack will skip sending ACK and
|
|
increase TcpExtTCPACKSkippedSynRecv.
|
|
|
|
|
|
* TcpExtTCPACKSkippedPAWS
|
|
|
|
The ACK is skipped due to PAWS (Protect Against Wrapped Sequence
|
|
numbers) check fails. If the PAWS check fails in Syn-Recv, Fin-Wait-2
|
|
or Time-Wait statuses, the skipped ACK would be counted to
|
|
TcpExtTCPACKSkippedSynRecv, TcpExtTCPACKSkippedFinWait2 or
|
|
TcpExtTCPACKSkippedTimeWait. In all other statuses, the skipped ACK
|
|
would be counted to TcpExtTCPACKSkippedPAWS.
|
|
|
|
* TcpExtTCPACKSkippedSeq
|
|
|
|
The sequence number is out of window and the timestamp passes the PAWS
|
|
check and the TCP status is not Syn-Recv, Fin-Wait-2, and Time-Wait.
|
|
|
|
* TcpExtTCPACKSkippedFinWait2
|
|
|
|
The ACK is skipped in Fin-Wait-2 status, the reason would be either
|
|
PAWS check fails or the received sequence number is out of window.
|
|
|
|
* TcpExtTCPACKSkippedTimeWait
|
|
|
|
The ACK is skipped in Time-Wait status, the reason would be either
|
|
PAWS check failed or the received sequence number is out of window.
|
|
|
|
* TcpExtTCPACKSkippedChallenge
|
|
|
|
The ACK is skipped if the ACK is a challenge ACK. The RFC 5961 defines
|
|
3 kind of challenge ACK, please refer `RFC 5961 section 3.2`_,
|
|
`RFC 5961 section 4.2`_ and `RFC 5961 section 5.2`_. Besides these
|
|
three scenarios, In some TCP status, the linux TCP stack would also
|
|
send challenge ACKs if the ACK number is before the first
|
|
unacknowledged number (more strict than `RFC 5961 section 5.2`_).
|
|
|
|
.. _RFC 5961 section 3.2: https://tools.ietf.org/html/rfc5961#page-7
|
|
.. _RFC 5961 section 4.2: https://tools.ietf.org/html/rfc5961#page-9
|
|
.. _RFC 5961 section 5.2: https://tools.ietf.org/html/rfc5961#page-11
|
|
|
|
TCP receive window
|
|
==================
|
|
* TcpExtTCPWantZeroWindowAdv
|
|
|
|
Depending on current memory usage, the TCP stack tries to set receive
|
|
window to zero. But the receive window might still be a no-zero
|
|
value. For example, if the previous window size is 10, and the TCP
|
|
stack receives 3 bytes, the current window size would be 7 even if the
|
|
window size calculated by the memory usage is zero.
|
|
|
|
* TcpExtTCPToZeroWindowAdv
|
|
|
|
The TCP receive window is set to zero from a no-zero value.
|
|
|
|
* TcpExtTCPFromZeroWindowAdv
|
|
|
|
The TCP receive window is set to no-zero value from zero.
|
|
|
|
|
|
Delayed ACK
|
|
===========
|
|
The TCP Delayed ACK is a technique which is used for reducing the
|
|
packet count in the network. For more details, please refer the
|
|
`Delayed ACK wiki`_
|
|
|
|
.. _Delayed ACK wiki: https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment
|
|
|
|
* TcpExtDelayedACKs
|
|
|
|
A delayed ACK timer expires. The TCP stack will send a pure ACK packet
|
|
and exit the delayed ACK mode.
|
|
|
|
* TcpExtDelayedACKLocked
|
|
|
|
A delayed ACK timer expires, but the TCP stack can't send an ACK
|
|
immediately due to the socket is locked by a userspace program. The
|
|
TCP stack will send a pure ACK later (after the userspace program
|
|
unlock the socket). When the TCP stack sends the pure ACK later, the
|
|
TCP stack will also update TcpExtDelayedACKs and exit the delayed ACK
|
|
mode.
|
|
|
|
* TcpExtDelayedACKLost
|
|
|
|
It will be updated when the TCP stack receives a packet which has been
|
|
ACKed. A Delayed ACK loss might cause this issue, but it would also be
|
|
triggered by other reasons, such as a packet is duplicated in the
|
|
network.
|
|
|
|
Tail Loss Probe (TLP)
|
|
=====================
|
|
TLP is an algorithm which is used to detect TCP packet loss. For more
|
|
details, please refer the `TLP paper`_.
|
|
|
|
.. _TLP paper: https://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01
|
|
|
|
* TcpExtTCPLossProbes
|
|
|
|
A TLP probe packet is sent.
|
|
|
|
* TcpExtTCPLossProbeRecovery
|
|
|
|
A packet loss is detected and recovered by TLP.
|
|
|
|
TCP Fast Open description
|
|
=========================
|
|
TCP Fast Open is a technology which allows data transfer before the
|
|
3-way handshake complete. Please refer the `TCP Fast Open wiki`_ for a
|
|
general description.
|
|
|
|
.. _TCP Fast Open wiki: https://en.wikipedia.org/wiki/TCP_Fast_Open
|
|
|
|
* TcpExtTCPFastOpenActive
|
|
|
|
When the TCP stack receives an ACK packet in the SYN-SENT status, and
|
|
the ACK packet acknowledges the data in the SYN packet, the TCP stack
|
|
understand the TFO cookie is accepted by the other side, then it
|
|
updates this counter.
|
|
|
|
* TcpExtTCPFastOpenActiveFail
|
|
|
|
This counter indicates that the TCP stack initiated a TCP Fast Open,
|
|
but it failed. This counter would be updated in three scenarios: (1)
|
|
the other side doesn't acknowledge the data in the SYN packet. (2) The
|
|
SYN packet which has the TFO cookie is timeout at least once. (3)
|
|
after the 3-way handshake, the retransmission timeout happens
|
|
net.ipv4.tcp_retries1 times, because some middle-boxes may black-hole
|
|
fast open after the handshake.
|
|
|
|
* TcpExtTCPFastOpenPassive
|
|
|
|
This counter indicates how many times the TCP stack accepts the fast
|
|
open request.
|
|
|
|
* TcpExtTCPFastOpenPassiveFail
|
|
|
|
This counter indicates how many times the TCP stack rejects the fast
|
|
open request. It is caused by either the TFO cookie is invalid or the
|
|
TCP stack finds an error during the socket creating process.
|
|
|
|
* TcpExtTCPFastOpenListenOverflow
|
|
|
|
When the pending fast open request number is larger than
|
|
fastopenq->max_qlen, the TCP stack will reject the fast open request
|
|
and update this counter. When this counter is updated, the TCP stack
|
|
won't update TcpExtTCPFastOpenPassive or
|
|
TcpExtTCPFastOpenPassiveFail. The fastopenq->max_qlen is set by the
|
|
TCP_FASTOPEN socket operation and it could not be larger than
|
|
net.core.somaxconn. For example:
|
|
|
|
setsockopt(sfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
|
|
|
|
* TcpExtTCPFastOpenCookieReqd
|
|
|
|
This counter indicates how many times a client wants to request a TFO
|
|
cookie.
|
|
|
|
SYN cookies
|
|
===========
|
|
SYN cookies are used to mitigate SYN flood, for details, please refer
|
|
the `SYN cookies wiki`_.
|
|
|
|
.. _SYN cookies wiki: https://en.wikipedia.org/wiki/SYN_cookies
|
|
|
|
* TcpExtSyncookiesSent
|
|
|
|
It indicates how many SYN cookies are sent.
|
|
|
|
* TcpExtSyncookiesRecv
|
|
|
|
How many reply packets of the SYN cookies the TCP stack receives.
|
|
|
|
* TcpExtSyncookiesFailed
|
|
|
|
The MSS decoded from the SYN cookie is invalid. When this counter is
|
|
updated, the received packet won't be treated as a SYN cookie and the
|
|
TcpExtSyncookiesRecv counter won't be updated.
|
|
|
|
Challenge ACK
|
|
=============
|
|
For details of challenge ACK, please refer the explanation of
|
|
TcpExtTCPACKSkippedChallenge.
|
|
|
|
* TcpExtTCPChallengeACK
|
|
|
|
The number of challenge acks sent.
|
|
|
|
* TcpExtTCPSYNChallenge
|
|
|
|
The number of challenge acks sent in response to SYN packets. After
|
|
updates this counter, the TCP stack might send a challenge ACK and
|
|
update the TcpExtTCPChallengeACK counter, or it might also skip to
|
|
send the challenge and update the TcpExtTCPACKSkippedChallenge.
|
|
|
|
prune
|
|
=====
|
|
When a socket is under memory pressure, the TCP stack will try to
|
|
reclaim memory from the receiving queue and out of order queue. One of
|
|
the reclaiming method is 'collapse', which means allocate a big skb,
|
|
copy the contiguous skbs to the single big skb, and free these
|
|
contiguous skbs.
|
|
|
|
* TcpExtPruneCalled
|
|
|
|
The TCP stack tries to reclaim memory for a socket. After updates this
|
|
counter, the TCP stack will try to collapse the out of order queue and
|
|
the receiving queue. If the memory is still not enough, the TCP stack
|
|
will try to discard packets from the out of order queue (and update the
|
|
TcpExtOfoPruned counter)
|
|
|
|
* TcpExtOfoPruned
|
|
|
|
The TCP stack tries to discard packet on the out of order queue.
|
|
|
|
* TcpExtRcvPruned
|
|
|
|
After 'collapse' and discard packets from the out of order queue, if
|
|
the actually used memory is still larger than the max allowed memory,
|
|
this counter will be updated. It means the 'prune' fails.
|
|
|
|
* TcpExtTCPRcvCollapsed
|
|
|
|
This counter indicates how many skbs are freed during 'collapse'.
|
|
|
|
examples
|
|
========
|
|
|
|
ping test
|
|
---------
|
|
Run the ping command against the public dns server 8.8.8.8::
|
|
|
|
nstatuser@nstat-a:~$ ping 8.8.8.8 -c 1
|
|
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
|
|
64 bytes from 8.8.8.8: icmp_seq=1 ttl=119 time=17.8 ms
|
|
|
|
--- 8.8.8.8 ping statistics ---
|
|
1 packets transmitted, 1 received, 0% packet loss, time 0ms
|
|
rtt min/avg/max/mdev = 17.875/17.875/17.875/0.000 ms
|
|
|
|
The nstayt result::
|
|
|
|
nstatuser@nstat-a:~$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpInDelivers 1 0.0
|
|
IpOutRequests 1 0.0
|
|
IcmpInMsgs 1 0.0
|
|
IcmpInEchoReps 1 0.0
|
|
IcmpOutMsgs 1 0.0
|
|
IcmpOutEchos 1 0.0
|
|
IcmpMsgInType0 1 0.0
|
|
IcmpMsgOutType8 1 0.0
|
|
IpExtInOctets 84 0.0
|
|
IpExtOutOctets 84 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
The Linux server sent an ICMP Echo packet, so IpOutRequests,
|
|
IcmpOutMsgs, IcmpOutEchos and IcmpMsgOutType8 were increased 1. The
|
|
server got ICMP Echo Reply from 8.8.8.8, so IpInReceives, IcmpInMsgs,
|
|
IcmpInEchoReps and IcmpMsgInType0 were increased 1. The ICMP Echo Reply
|
|
was passed to the ICMP layer via IP layer, so IpInDelivers was
|
|
increased 1. The default ping data size is 48, so an ICMP Echo packet
|
|
and its corresponding Echo Reply packet are constructed by:
|
|
|
|
* 14 bytes MAC header
|
|
* 20 bytes IP header
|
|
* 16 bytes ICMP header
|
|
* 48 bytes data (default value of the ping command)
|
|
|
|
So the IpExtInOctets and IpExtOutOctets are 20+16+48=84.
|
|
|
|
tcp 3-way handshake
|
|
-------------------
|
|
On server side, we run::
|
|
|
|
nstatuser@nstat-b:~$ nc -lknv 0.0.0.0 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
On client side, we run::
|
|
|
|
nstatuser@nstat-a:~$ nc -nv 192.168.122.251 9000
|
|
Connection to 192.168.122.251 9000 port [tcp/*] succeeded!
|
|
|
|
The server listened on tcp 9000 port, the client connected to it, they
|
|
completed the 3-way handshake.
|
|
|
|
On server side, we can find below nstat output::
|
|
|
|
nstatuser@nstat-b:~$ nstat | grep -i tcp
|
|
TcpPassiveOpens 1 0.0
|
|
TcpInSegs 2 0.0
|
|
TcpOutSegs 1 0.0
|
|
TcpExtTCPPureAcks 1 0.0
|
|
|
|
On client side, we can find below nstat output::
|
|
|
|
nstatuser@nstat-a:~$ nstat | grep -i tcp
|
|
TcpActiveOpens 1 0.0
|
|
TcpInSegs 1 0.0
|
|
TcpOutSegs 2 0.0
|
|
|
|
When the server received the first SYN, it replied a SYN+ACK, and came into
|
|
SYN-RCVD state, so TcpPassiveOpens increased 1. The server received
|
|
SYN, sent SYN+ACK, received ACK, so server sent 1 packet, received 2
|
|
packets, TcpInSegs increased 2, TcpOutSegs increased 1. The last ACK
|
|
of the 3-way handshake is a pure ACK without data, so
|
|
TcpExtTCPPureAcks increased 1.
|
|
|
|
When the client sent SYN, the client came into the SYN-SENT state, so
|
|
TcpActiveOpens increased 1, the client sent SYN, received SYN+ACK, sent
|
|
ACK, so client sent 2 packets, received 1 packet, TcpInSegs increased
|
|
1, TcpOutSegs increased 2.
|
|
|
|
TCP normal traffic
|
|
------------------
|
|
Run nc on server::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
Run nc on client::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
|
|
Input a string in the nc client ('hello' in our example)::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
hello
|
|
|
|
The client side nstat output::
|
|
|
|
nstatuser@nstat-a:~$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpInDelivers 1 0.0
|
|
IpOutRequests 1 0.0
|
|
TcpInSegs 1 0.0
|
|
TcpOutSegs 1 0.0
|
|
TcpExtTCPPureAcks 1 0.0
|
|
TcpExtTCPOrigDataSent 1 0.0
|
|
IpExtInOctets 52 0.0
|
|
IpExtOutOctets 58 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
The server side nstat output::
|
|
|
|
nstatuser@nstat-b:~$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpInDelivers 1 0.0
|
|
IpOutRequests 1 0.0
|
|
TcpInSegs 1 0.0
|
|
TcpOutSegs 1 0.0
|
|
IpExtInOctets 58 0.0
|
|
IpExtOutOctets 52 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
Input a string in nc client side again ('world' in our example)::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
hello
|
|
world
|
|
|
|
Client side nstat output::
|
|
|
|
nstatuser@nstat-a:~$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpInDelivers 1 0.0
|
|
IpOutRequests 1 0.0
|
|
TcpInSegs 1 0.0
|
|
TcpOutSegs 1 0.0
|
|
TcpExtTCPHPAcks 1 0.0
|
|
TcpExtTCPOrigDataSent 1 0.0
|
|
IpExtInOctets 52 0.0
|
|
IpExtOutOctets 58 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
|
|
Server side nstat output::
|
|
|
|
nstatuser@nstat-b:~$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpInDelivers 1 0.0
|
|
IpOutRequests 1 0.0
|
|
TcpInSegs 1 0.0
|
|
TcpOutSegs 1 0.0
|
|
TcpExtTCPHPHits 1 0.0
|
|
IpExtInOctets 58 0.0
|
|
IpExtOutOctets 52 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
Compare the first client-side nstat and the second client-side nstat,
|
|
we could find one difference: the first one had a 'TcpExtTCPPureAcks',
|
|
but the second one had a 'TcpExtTCPHPAcks'. The first server-side
|
|
nstat and the second server-side nstat had a difference too: the
|
|
second server-side nstat had a TcpExtTCPHPHits, but the first
|
|
server-side nstat didn't have it. The network traffic patterns were
|
|
exactly the same: the client sent a packet to the server, the server
|
|
replied an ACK. But kernel handled them in different ways. When the
|
|
TCP window scale option is not used, kernel will try to enable fast
|
|
path immediately when the connection comes into the established state,
|
|
but if the TCP window scale option is used, kernel will disable the
|
|
fast path at first, and try to enable it after kernel receives
|
|
packets. We could use the 'ss' command to verify whether the window
|
|
scale option is used. e.g. run below command on either server or
|
|
client::
|
|
|
|
nstatuser@nstat-a:~$ ss -o state established -i '( dport = :9000 or sport = :9000 )
|
|
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port
|
|
tcp 0 0 192.168.122.250:40654 192.168.122.251:9000
|
|
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
|
|
|
|
The 'wscale:7,7' means both server and client set the window scale
|
|
option to 7. Now we could explain the nstat output in our test:
|
|
|
|
In the first nstat output of client side, the client sent a packet, server
|
|
reply an ACK, when kernel handled this ACK, the fast path was not
|
|
enabled, so the ACK was counted into 'TcpExtTCPPureAcks'.
|
|
|
|
In the second nstat output of client side, the client sent a packet again,
|
|
and received another ACK from the server, in this time, the fast path is
|
|
enabled, and the ACK was qualified for fast path, so it was handled by
|
|
the fast path, so this ACK was counted into TcpExtTCPHPAcks.
|
|
|
|
In the first nstat output of server side, fast path was not enabled,
|
|
so there was no 'TcpExtTCPHPHits'.
|
|
|
|
In the second nstat output of server side, the fast path was enabled,
|
|
and the packet received from client qualified for fast path, so it
|
|
was counted into 'TcpExtTCPHPHits'.
|
|
|
|
TcpExtTCPAbortOnClose
|
|
---------------------
|
|
On the server side, we run below python script::
|
|
|
|
import socket
|
|
import time
|
|
|
|
port = 9000
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.bind(('0.0.0.0', port))
|
|
s.listen(1)
|
|
sock, addr = s.accept()
|
|
while True:
|
|
time.sleep(9999999)
|
|
|
|
This python script listen on 9000 port, but doesn't read anything from
|
|
the connection.
|
|
|
|
On the client side, we send the string "hello" by nc::
|
|
|
|
nstatuser@nstat-a:~$ echo "hello" | nc nstat-b 9000
|
|
|
|
Then, we come back to the server side, the server has received the "hello"
|
|
packet, and the TCP layer has acked this packet, but the application didn't
|
|
read it yet. We type Ctrl-C to terminate the server script. Then we
|
|
could find TcpExtTCPAbortOnClose increased 1 on the server side::
|
|
|
|
nstatuser@nstat-b:~$ nstat | grep -i abort
|
|
TcpExtTCPAbortOnClose 1 0.0
|
|
|
|
If we run tcpdump on the server side, we could find the server sent a
|
|
RST after we type Ctrl-C.
|
|
|
|
TcpExtTCPAbortOnMemory and TcpExtTCPAbortOnTimeout
|
|
---------------------------------------------------
|
|
Below is an example which let the orphan socket count be higher than
|
|
net.ipv4.tcp_max_orphans.
|
|
Change tcp_max_orphans to a smaller value on client::
|
|
|
|
sudo bash -c "echo 10 > /proc/sys/net/ipv4/tcp_max_orphans"
|
|
|
|
Client code (create 64 connection to server)::
|
|
|
|
nstatuser@nstat-a:~$ cat client_orphan.py
|
|
import socket
|
|
import time
|
|
|
|
server = 'nstat-b' # server address
|
|
port = 9000
|
|
|
|
count = 64
|
|
|
|
connection_list = []
|
|
|
|
for i in range(64):
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect((server, port))
|
|
connection_list.append(s)
|
|
print("connection_count: %d" % len(connection_list))
|
|
|
|
while True:
|
|
time.sleep(99999)
|
|
|
|
Server code (accept 64 connection from client)::
|
|
|
|
nstatuser@nstat-b:~$ cat server_orphan.py
|
|
import socket
|
|
import time
|
|
|
|
port = 9000
|
|
count = 64
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.bind(('0.0.0.0', port))
|
|
s.listen(count)
|
|
connection_list = []
|
|
while True:
|
|
sock, addr = s.accept()
|
|
connection_list.append((sock, addr))
|
|
print("connection_count: %d" % len(connection_list))
|
|
|
|
Run the python scripts on server and client.
|
|
|
|
On server::
|
|
|
|
python3 server_orphan.py
|
|
|
|
On client::
|
|
|
|
python3 client_orphan.py
|
|
|
|
Run iptables on server::
|
|
|
|
sudo iptables -A INPUT -i ens3 -p tcp --destination-port 9000 -j DROP
|
|
|
|
Type Ctrl-C on client, stop client_orphan.py.
|
|
|
|
Check TcpExtTCPAbortOnMemory on client::
|
|
|
|
nstatuser@nstat-a:~$ nstat | grep -i abort
|
|
TcpExtTCPAbortOnMemory 54 0.0
|
|
|
|
Check orphaned socket count on client::
|
|
|
|
nstatuser@nstat-a:~$ ss -s
|
|
Total: 131 (kernel 0)
|
|
TCP: 14 (estab 1, closed 0, orphaned 10, synrecv 0, timewait 0/0), ports 0
|
|
|
|
Transport Total IP IPv6
|
|
* 0 - -
|
|
RAW 1 0 1
|
|
UDP 1 1 0
|
|
TCP 14 13 1
|
|
INET 16 14 2
|
|
FRAG 0 0 0
|
|
|
|
The explanation of the test: after run server_orphan.py and
|
|
client_orphan.py, we set up 64 connections between server and
|
|
client. Run the iptables command, the server will drop all packets from
|
|
the client, type Ctrl-C on client_orphan.py, the system of the client
|
|
would try to close these connections, and before they are closed
|
|
gracefully, these connections became orphan sockets. As the iptables
|
|
of the server blocked packets from the client, the server won't receive fin
|
|
from the client, so all connection on clients would be stuck on FIN_WAIT_1
|
|
stage, so they will keep as orphan sockets until timeout. We have echo
|
|
10 to /proc/sys/net/ipv4/tcp_max_orphans, so the client system would
|
|
only keep 10 orphan sockets, for all other orphan sockets, the client
|
|
system sent RST for them and delete them. We have 64 connections, so
|
|
the 'ss -s' command shows the system has 10 orphan sockets, and the
|
|
value of TcpExtTCPAbortOnMemory was 54.
|
|
|
|
An additional explanation about orphan socket count: You could find the
|
|
exactly orphan socket count by the 'ss -s' command, but when kernel
|
|
decide whither increases TcpExtTCPAbortOnMemory and sends RST, kernel
|
|
doesn't always check the exactly orphan socket count. For increasing
|
|
performance, kernel checks an approximate count firstly, if the
|
|
approximate count is more than tcp_max_orphans, kernel checks the
|
|
exact count again. So if the approximate count is less than
|
|
tcp_max_orphans, but exactly count is more than tcp_max_orphans, you
|
|
would find TcpExtTCPAbortOnMemory is not increased at all. If
|
|
tcp_max_orphans is large enough, it won't occur, but if you decrease
|
|
tcp_max_orphans to a small value like our test, you might find this
|
|
issue. So in our test, the client set up 64 connections although the
|
|
tcp_max_orphans is 10. If the client only set up 11 connections, we
|
|
can't find the change of TcpExtTCPAbortOnMemory.
|
|
|
|
Continue the previous test, we wait for several minutes. Because of the
|
|
iptables on the server blocked the traffic, the server wouldn't receive
|
|
fin, and all the client's orphan sockets would timeout on the
|
|
FIN_WAIT_1 state finally. So we wait for a few minutes, we could find
|
|
10 timeout on the client::
|
|
|
|
nstatuser@nstat-a:~$ nstat | grep -i abort
|
|
TcpExtTCPAbortOnTimeout 10 0.0
|
|
|
|
TcpExtTCPAbortOnLinger
|
|
----------------------
|
|
The server side code::
|
|
|
|
nstatuser@nstat-b:~$ cat server_linger.py
|
|
import socket
|
|
import time
|
|
|
|
port = 9000
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.bind(('0.0.0.0', port))
|
|
s.listen(1)
|
|
sock, addr = s.accept()
|
|
while True:
|
|
time.sleep(9999999)
|
|
|
|
The client side code::
|
|
|
|
nstatuser@nstat-a:~$ cat client_linger.py
|
|
import socket
|
|
import struct
|
|
|
|
server = 'nstat-b' # server address
|
|
port = 9000
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 10))
|
|
s.setsockopt(socket.SOL_TCP, socket.TCP_LINGER2, struct.pack('i', -1))
|
|
s.connect((server, port))
|
|
s.close()
|
|
|
|
Run server_linger.py on server::
|
|
|
|
nstatuser@nstat-b:~$ python3 server_linger.py
|
|
|
|
Run client_linger.py on client::
|
|
|
|
nstatuser@nstat-a:~$ python3 client_linger.py
|
|
|
|
After run client_linger.py, check the output of nstat::
|
|
|
|
nstatuser@nstat-a:~$ nstat | grep -i abort
|
|
TcpExtTCPAbortOnLinger 1 0.0
|
|
|
|
TcpExtTCPRcvCoalesce
|
|
--------------------
|
|
On the server, we run a program which listen on TCP port 9000, but
|
|
doesn't read any data::
|
|
|
|
import socket
|
|
import time
|
|
port = 9000
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.bind(('0.0.0.0', port))
|
|
s.listen(1)
|
|
sock, addr = s.accept()
|
|
while True:
|
|
time.sleep(9999999)
|
|
|
|
Save the above code as server_coalesce.py, and run::
|
|
|
|
python3 server_coalesce.py
|
|
|
|
On the client, save below code as client_coalesce.py::
|
|
|
|
import socket
|
|
server = 'nstat-b'
|
|
port = 9000
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect((server, port))
|
|
|
|
Run::
|
|
|
|
nstatuser@nstat-a:~$ python3 -i client_coalesce.py
|
|
|
|
We use '-i' to come into the interactive mode, then a packet::
|
|
|
|
>>> s.send(b'foo')
|
|
3
|
|
|
|
Send a packet again::
|
|
|
|
>>> s.send(b'bar')
|
|
3
|
|
|
|
On the server, run nstat::
|
|
|
|
ubuntu@nstat-b:~$ nstat
|
|
#kernel
|
|
IpInReceives 2 0.0
|
|
IpInDelivers 2 0.0
|
|
IpOutRequests 2 0.0
|
|
TcpInSegs 2 0.0
|
|
TcpOutSegs 2 0.0
|
|
TcpExtTCPRcvCoalesce 1 0.0
|
|
IpExtInOctets 110 0.0
|
|
IpExtOutOctets 104 0.0
|
|
IpExtInNoECTPkts 2 0.0
|
|
|
|
The client sent two packets, server didn't read any data. When
|
|
the second packet arrived at server, the first packet was still in
|
|
the receiving queue. So the TCP layer merged the two packets, and we
|
|
could find the TcpExtTCPRcvCoalesce increased 1.
|
|
|
|
TcpExtListenOverflows and TcpExtListenDrops
|
|
-------------------------------------------
|
|
On server, run the nc command, listen on port 9000::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
On client, run 3 nc commands in different terminals::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
|
|
The nc command only accepts 1 connection, and the accept queue length
|
|
is 1. On current linux implementation, set queue length to n means the
|
|
actual queue length is n+1. Now we create 3 connections, 1 is accepted
|
|
by nc, 2 in accepted queue, so the accept queue is full.
|
|
|
|
Before running the 4th nc, we clean the nstat history on the server::
|
|
|
|
nstatuser@nstat-b:~$ nstat -n
|
|
|
|
Run the 4th nc on the client::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
|
|
If the nc server is running on kernel 4.10 or higher version, you
|
|
won't see the "Connection to ... succeeded!" string, because kernel
|
|
will drop the SYN if the accept queue is full. If the nc client is running
|
|
on an old kernel, you would see that the connection is succeeded,
|
|
because kernel would complete the 3 way handshake and keep the socket
|
|
on half open queue. I did the test on kernel 4.15. Below is the nstat
|
|
on the server::
|
|
|
|
nstatuser@nstat-b:~$ nstat
|
|
#kernel
|
|
IpInReceives 4 0.0
|
|
IpInDelivers 4 0.0
|
|
TcpInSegs 4 0.0
|
|
TcpExtListenOverflows 4 0.0
|
|
TcpExtListenDrops 4 0.0
|
|
IpExtInOctets 240 0.0
|
|
IpExtInNoECTPkts 4 0.0
|
|
|
|
Both TcpExtListenOverflows and TcpExtListenDrops were 4. If the time
|
|
between the 4th nc and the nstat was longer, the value of
|
|
TcpExtListenOverflows and TcpExtListenDrops would be larger, because
|
|
the SYN of the 4th nc was dropped, the client was retrying.
|
|
|
|
IpInAddrErrors, IpExtInNoRoutes and IpOutNoRoutes
|
|
-------------------------------------------------
|
|
server A IP address: 192.168.122.250
|
|
server B IP address: 192.168.122.251
|
|
Prepare on server A, add a route to server B::
|
|
|
|
$ sudo ip route add 8.8.8.8/32 via 192.168.122.251
|
|
|
|
Prepare on server B, disable send_redirects for all interfaces::
|
|
|
|
$ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
|
|
$ sudo sysctl -w net.ipv4.conf.ens3.send_redirects=0
|
|
$ sudo sysctl -w net.ipv4.conf.lo.send_redirects=0
|
|
$ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
|
|
|
|
We want to let sever A send a packet to 8.8.8.8, and route the packet
|
|
to server B. When server B receives such packet, it might send a ICMP
|
|
Redirect message to server A, set send_redirects to 0 will disable
|
|
this behavior.
|
|
|
|
First, generate InAddrErrors. On server B, we disable IP forwarding::
|
|
|
|
$ sudo sysctl -w net.ipv4.conf.all.forwarding=0
|
|
|
|
On server A, we send packets to 8.8.8.8::
|
|
|
|
$ nc -v 8.8.8.8 53
|
|
|
|
On server B, we check the output of nstat::
|
|
|
|
$ nstat
|
|
#kernel
|
|
IpInReceives 3 0.0
|
|
IpInAddrErrors 3 0.0
|
|
IpExtInOctets 180 0.0
|
|
IpExtInNoECTPkts 3 0.0
|
|
|
|
As we have let server A route 8.8.8.8 to server B, and we disabled IP
|
|
forwarding on server B, Server A sent packets to server B, then server B
|
|
dropped packets and increased IpInAddrErrors. As the nc command would
|
|
re-send the SYN packet if it didn't receive a SYN+ACK, we could find
|
|
multiple IpInAddrErrors.
|
|
|
|
Second, generate IpExtInNoRoutes. On server B, we enable IP
|
|
forwarding::
|
|
|
|
$ sudo sysctl -w net.ipv4.conf.all.forwarding=1
|
|
|
|
Check the route table of server B and remove the default route::
|
|
|
|
$ ip route show
|
|
default via 192.168.122.1 dev ens3 proto static
|
|
192.168.122.0/24 dev ens3 proto kernel scope link src 192.168.122.251
|
|
$ sudo ip route delete default via 192.168.122.1 dev ens3 proto static
|
|
|
|
On server A, we contact 8.8.8.8 again::
|
|
|
|
$ nc -v 8.8.8.8 53
|
|
nc: connect to 8.8.8.8 port 53 (tcp) failed: Network is unreachable
|
|
|
|
On server B, run nstat::
|
|
|
|
$ nstat
|
|
#kernel
|
|
IpInReceives 1 0.0
|
|
IpOutRequests 1 0.0
|
|
IcmpOutMsgs 1 0.0
|
|
IcmpOutDestUnreachs 1 0.0
|
|
IcmpMsgOutType3 1 0.0
|
|
IpExtInNoRoutes 1 0.0
|
|
IpExtInOctets 60 0.0
|
|
IpExtOutOctets 88 0.0
|
|
IpExtInNoECTPkts 1 0.0
|
|
|
|
We enabled IP forwarding on server B, when server B received a packet
|
|
which destination IP address is 8.8.8.8, server B will try to forward
|
|
this packet. We have deleted the default route, there was no route for
|
|
8.8.8.8, so server B increase IpExtInNoRoutes and sent the "ICMP
|
|
Destination Unreachable" message to server A.
|
|
|
|
Third, generate IpOutNoRoutes. Run ping command on server B::
|
|
|
|
$ ping -c 1 8.8.8.8
|
|
connect: Network is unreachable
|
|
|
|
Run nstat on server B::
|
|
|
|
$ nstat
|
|
#kernel
|
|
IpOutNoRoutes 1 0.0
|
|
|
|
We have deleted the default route on server B. Server B couldn't find
|
|
a route for the 8.8.8.8 IP address, so server B increased
|
|
IpOutNoRoutes.
|
|
|
|
TcpExtTCPACKSkippedSynRecv
|
|
--------------------------
|
|
In this test, we send 3 same SYN packets from client to server. The
|
|
first SYN will let server create a socket, set it to Syn-Recv status,
|
|
and reply a SYN/ACK. The second SYN will let server reply the SYN/ACK
|
|
again, and record the reply time (the duplicate ACK reply time). The
|
|
third SYN will let server check the previous duplicate ACK reply time,
|
|
and decide to skip the duplicate ACK, then increase the
|
|
TcpExtTCPACKSkippedSynRecv counter.
|
|
|
|
Run tcpdump to capture a SYN packet::
|
|
|
|
nstatuser@nstat-a:~$ sudo tcpdump -c 1 -w /tmp/syn.pcap port 9000
|
|
tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes
|
|
|
|
Open another terminal, run nc command::
|
|
|
|
nstatuser@nstat-a:~$ nc nstat-b 9000
|
|
|
|
As the nstat-b didn't listen on port 9000, it should reply a RST, and
|
|
the nc command exited immediately. It was enough for the tcpdump
|
|
command to capture a SYN packet. A linux server might use hardware
|
|
offload for the TCP checksum, so the checksum in the /tmp/syn.pcap
|
|
might be not correct. We call tcprewrite to fix it::
|
|
|
|
nstatuser@nstat-a:~$ tcprewrite --infile=/tmp/syn.pcap --outfile=/tmp/syn_fixcsum.pcap --fixcsum
|
|
|
|
On nstat-b, we run nc to listen on port 9000::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
On nstat-a, we blocked the packet from port 9000, or nstat-a would send
|
|
RST to nstat-b::
|
|
|
|
nstatuser@nstat-a:~$ sudo iptables -A INPUT -p tcp --sport 9000 -j DROP
|
|
|
|
Send 3 SYN repeatedly to nstat-b::
|
|
|
|
nstatuser@nstat-a:~$ for i in {1..3}; do sudo tcpreplay -i ens3 /tmp/syn_fixcsum.pcap; done
|
|
|
|
Check snmp counter on nstat-b::
|
|
|
|
nstatuser@nstat-b:~$ nstat | grep -i skip
|
|
TcpExtTCPACKSkippedSynRecv 1 0.0
|
|
|
|
As we expected, TcpExtTCPACKSkippedSynRecv is 1.
|
|
|
|
TcpExtTCPACKSkippedPAWS
|
|
-----------------------
|
|
To trigger PAWS, we could send an old SYN.
|
|
|
|
On nstat-b, let nc listen on port 9000::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
On nstat-a, run tcpdump to capture a SYN::
|
|
|
|
nstatuser@nstat-a:~$ sudo tcpdump -w /tmp/paws_pre.pcap -c 1 port 9000
|
|
tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes
|
|
|
|
On nstat-a, run nc as a client to connect nstat-b::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
|
|
Now the tcpdump has captured the SYN and exit. We should fix the
|
|
checksum::
|
|
|
|
nstatuser@nstat-a:~$ tcprewrite --infile /tmp/paws_pre.pcap --outfile /tmp/paws.pcap --fixcsum
|
|
|
|
Send the SYN packet twice::
|
|
|
|
nstatuser@nstat-a:~$ for i in {1..2}; do sudo tcpreplay -i ens3 /tmp/paws.pcap; done
|
|
|
|
On nstat-b, check the snmp counter::
|
|
|
|
nstatuser@nstat-b:~$ nstat | grep -i skip
|
|
TcpExtTCPACKSkippedPAWS 1 0.0
|
|
|
|
We sent two SYN via tcpreplay, both of them would let PAWS check
|
|
failed, the nstat-b replied an ACK for the first SYN, skipped the ACK
|
|
for the second SYN, and updated TcpExtTCPACKSkippedPAWS.
|
|
|
|
TcpExtTCPACKSkippedSeq
|
|
----------------------
|
|
To trigger TcpExtTCPACKSkippedSeq, we send packets which have valid
|
|
timestamp (to pass PAWS check) but the sequence number is out of
|
|
window. The linux TCP stack would avoid to skip if the packet has
|
|
data, so we need a pure ACK packet. To generate such a packet, we
|
|
could create two sockets: one on port 9000, another on port 9001. Then
|
|
we capture an ACK on port 9001, change the source/destination port
|
|
numbers to match the port 9000 socket. Then we could trigger
|
|
TcpExtTCPACKSkippedSeq via this packet.
|
|
|
|
On nstat-b, open two terminals, run two nc commands to listen on both
|
|
port 9000 and port 9001::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 9000
|
|
Listening on [0.0.0.0] (family 0, port 9000)
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 9001
|
|
Listening on [0.0.0.0] (family 0, port 9001)
|
|
|
|
On nstat-a, run two nc clients::
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
|
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
|
|
|
nstatuser@nstat-a:~$ nc -v nstat-b 9001
|
|
Connection to nstat-b 9001 port [tcp/*] succeeded!
|
|
|
|
On nstat-a, run tcpdump to capture an ACK::
|
|
|
|
nstatuser@nstat-a:~$ sudo tcpdump -w /tmp/seq_pre.pcap -c 1 dst port 9001
|
|
tcpdump: listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes
|
|
|
|
On nstat-b, send a packet via the port 9001 socket. E.g. we sent a
|
|
string 'foo' in our example::
|
|
|
|
nstatuser@nstat-b:~$ nc -lkv 9001
|
|
Listening on [0.0.0.0] (family 0, port 9001)
|
|
Connection from nstat-a 42132 received!
|
|
foo
|
|
|
|
On nstat-a, the tcpdump should have captured the ACK. We should check
|
|
the source port numbers of the two nc clients::
|
|
|
|
nstatuser@nstat-a:~$ ss -ta '( dport = :9000 || dport = :9001 )' | tee
|
|
State Recv-Q Send-Q Local Address:Port Peer Address:Port
|
|
ESTAB 0 0 192.168.122.250:50208 192.168.122.251:9000
|
|
ESTAB 0 0 192.168.122.250:42132 192.168.122.251:9001
|
|
|
|
Run tcprewrite, change port 9001 to port 9000, change port 42132 to
|
|
port 50208::
|
|
|
|
nstatuser@nstat-a:~$ tcprewrite --infile /tmp/seq_pre.pcap --outfile /tmp/seq.pcap -r 9001:9000 -r 42132:50208 --fixcsum
|
|
|
|
Now the /tmp/seq.pcap is the packet we need. Send it to nstat-b::
|
|
|
|
nstatuser@nstat-a:~$ for i in {1..2}; do sudo tcpreplay -i ens3 /tmp/seq.pcap; done
|
|
|
|
Check TcpExtTCPACKSkippedSeq on nstat-b::
|
|
|
|
nstatuser@nstat-b:~$ nstat | grep -i skip
|
|
TcpExtTCPACKSkippedSeq 1 0.0
|