status: a session's TCP connection as the kernel sees it; a route in full
When a session advertised a full table to a route collector at two thousand routes a second, nothing on the status page said whether the daemon or the peer set that pace. The answer is in the kernel: the peer's receive window, the queues, and how long the connection was held up by the window or by the send buffer, which `ss -ti` shows. This change asks the kernel the same way `ss` does, over the socket diagnostics netlink family (`NETLINK_SOCK_DIAG`, `SOCK_DIAG_BY_FAMILY` with the connection's addresses and ports), and reads `tcp_info` and the congestion control name from the answer. No external program is run and no unsafe code is needed: the request and the answer are byte layouts of the kernel's ABI, read field by field, with fields an older kernel does not send left at zero. A socket on a link-local address is found by its interface, which the request names. * `bgp_fib::sockdiag`: the request, the parser, `TcpConnection`, and `tcp_connection(local, peer)`; `Ok(None)` where there is no such connection or the system is not Linux. * `GET /api/tcp?peer=ADDRESS` in the HTTP API: the connection of the session with that peer, with times in milliseconds, the two windows named by what they allow, and the held-up times also as shares of the busy time. * The status page: a "tcp" link on each established session, which expands a row below it with the figures, re-asked on every refresh while open. The group rows' span is corrected for the "sent" column. Also on the status page: the attributes of a received route that have no column of their own were badges with their value in a tooltip, which is hard to read. Clicking the attrs cell now expands a row under the route with every attribute by its name (ORIGIN, AS_PATH, NEXT_HOP, MED, LOCAL_PREF, COMMUNITIES, LARGE_COMMUNITIES, AGGREGATOR and the rest), the path identifier, the selection rank and the RPKI and ASPA states; the badges stay. Both expansions use the terms of `ss` and the RFCs rather than sentences. Documentation: the endpoint in the HTTP API reference, and the link and the expansion in the monitoring guide. Testing: unit tests of the request and of the parser on a reply built field by field, and of a "no such socket" and a refused answer; a Linux-only test that asks the kernel about a loopback connection of the test's own and about one that does not exist; the lab's basics phase checks the endpoint for the FRR session; the page was checked by hand against the daemon in the Linux VM. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
efde277fd8
commit
99a47da2af
8 files changed
+807
-10
No files matched your search
@@ -39,7 +39,13 @@ Below the status line, the page displays the following information:
|
||||
- Each neighbor, with its session state, time established, next hop,
|
||||
session properties, number of accepted routes, number of routes
|
||||
advertised to it (**sent**), rejected announcements, and held routes,
|
||||
and its last error. Neighbors that need attention are
|
||||
and its last error. The **tcp** link on an established session expands
|
||||
a row with the connection as the kernel reports it: round-trip time,
|
||||
congestion window, the neighbor's receive window and Ferrix's, queues,
|
||||
retransmissions, throughput, and the share of time the connection was
|
||||
held up by the neighbor's window or by the local send buffer. This
|
||||
shows whether a slow advertisement of a table is paced by the neighbor.
|
||||
Linux only. Neighbors that need attention are
|
||||
listed first; the others are grouped by role. The IPv4 and IPv6 sessions
|
||||
of one neighbor, named `NAME-v4` and `NAME-v6`, are listed together,
|
||||
IPv6 first, and joined by a bar. The **problems only** option hides the neighbors that
|
||||
@@ -153,6 +159,12 @@ advertise the same routes can therefore have different counts. The number
|
||||
of routes currently held, because they are RPKI or ASPA invalid or their
|
||||
next hop is unreachable, is displayed in the neighbor table as **held**.
|
||||
|
||||
In the received routes table, the **attrs** column shows the attributes
|
||||
that have no column of their own as badges. Clicking the cell expands a
|
||||
row below the route that lists every attribute of the route by its name,
|
||||
with the path identifier, the selection rank, and the RPKI and ASPA
|
||||
states.
|
||||
|
||||
```json
|
||||
[{"peer":"transit-a","total":3,"by_reason":{"RPKI invalid":2,"bogon prefix":1},
|
||||
"recent":[{"prefix":"203.0.113.0/24","reason":"RPKI invalid","as_path":[64496,64511]}]}]
|
||||
|
||||
@@ -37,6 +37,7 @@ server returns the following error statuses:
|
||||
| `/api/routes/rejected` | Rejected routes, by neighbor. |
|
||||
| `/api/rpki` | RPKI cache, data, and data age. |
|
||||
| `/api/kernel` | Kernel route installation state and interfaces. |
|
||||
| `/api/tcp` | The TCP connection of one session, as the kernel reports it. |
|
||||
| `/api/events` | Session and route changes, as server-sent events. |
|
||||
| `/metricz` | Prometheus metrics. See [Metrics Reference](@/docs/reference/metrics.md). |
|
||||
|
||||
@@ -230,6 +231,74 @@ routes. To return the records for one neighbor, add `?peer=NAME`.
|
||||
| `refused` | Number of prefixes whose active route the kernel rejected. |
|
||||
| `errors` | Number of requests that the kernel rejected. |
|
||||
|
||||
## TCP Connection
|
||||
|
||||
`/api/tcp?peer=ADDRESS` returns what the kernel reports about the TCP
|
||||
connection of the session with the neighbor at `ADDRESS`, given as
|
||||
`peer_addr` in `/api/peers` shows it: the round-trip time, the congestion
|
||||
and receive windows, the queues, the retransmissions, and how long the
|
||||
connection has been held up by the neighbor's receive window or by the
|
||||
local send buffer. Linux only; on other systems, and for a session the
|
||||
kernel has no connection for, `available` is `false` and `reason` says
|
||||
why.
|
||||
|
||||
```json
|
||||
{
|
||||
"available": true,
|
||||
"reason": null,
|
||||
"connection": {
|
||||
"state": "established",
|
||||
"congestion": "cubic",
|
||||
"rtt_ms": 0.62,
|
||||
"rtt_variance_ms": 0.11,
|
||||
"min_rtt_ms": 0.4,
|
||||
"rto_ms": 201,
|
||||
"mss": 1428,
|
||||
"pmtu": 1500,
|
||||
"cwnd_segments": 10,
|
||||
"ssthresh_segments": null,
|
||||
"send_window": 1048576,
|
||||
"receive_window": 3145728,
|
||||
"receive_space": 65536,
|
||||
"send_window_scale": 7,
|
||||
"receive_window_scale": 10,
|
||||
"send_queue": 0,
|
||||
"not_sent": 0,
|
||||
"recv_queue": 0,
|
||||
"unacked_segments": 0,
|
||||
"retrans_segments": 0,
|
||||
"lost_segments": 0,
|
||||
"total_retrans": 0,
|
||||
"bytes_retrans": 0,
|
||||
"pacing_rate": 2309920000,
|
||||
"delivery_rate": 184860000,
|
||||
"bytes_sent": 312993,
|
||||
"bytes_acked": 312994,
|
||||
"bytes_received": 190,
|
||||
"segments_out": 230,
|
||||
"segments_in": 118,
|
||||
"busy_ms": 41,
|
||||
"limited_by_receive_window_ms": 0,
|
||||
"limited_by_send_buffer_ms": 0,
|
||||
"limited_by_receive_window_share": 0,
|
||||
"limited_by_send_buffer_share": 0,
|
||||
"last_data_sent_ms": 1234,
|
||||
"last_data_received_ms": 1300,
|
||||
"last_ack_received_ms": 1234
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Times are in milliseconds, sizes in octets, rates in octets per second.
|
||||
`send_window` is the neighbor's receive window, which bounds what Ferrix
|
||||
can send; `receive_window` is what Ferrix lets the neighbor send. The two
|
||||
`_share` values are the fractions of the busy time, 0 to 1, in which the
|
||||
connection was held up by the neighbor's window and by the local send
|
||||
buffer. A session that advertises a table to a neighbor that reads it
|
||||
slowly shows a high `limited_by_receive_window_share`. The status page
|
||||
shows the same information under the **tcp** link of each established
|
||||
session.
|
||||
|
||||
## Events
|
||||
|
||||
`/api/events` is a stream of
|
||||
|
||||
Reference in new issue
Block a user