iroh_quinn_proto/connection/
stats.rs

1//! Connection statistics
2
3use crate::FrameType;
4use crate::{Dir, Duration, frame::Frame};
5
6/// Statistics about UDP datagrams transmitted or received on a connection
7///
8/// All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.
9#[derive(Default, Debug, Copy, Clone)]
10#[non_exhaustive]
11pub struct UdpStats {
12    /// The amount of UDP datagrams observed
13    pub datagrams: u64,
14    /// The total amount of bytes which have been transferred inside UDP datagrams
15    pub bytes: u64,
16    /// The amount of I/O operations executed
17    ///
18    /// Can be less than `datagrams` when GSO, GRO, and/or batched system calls are in use.
19    pub ios: u64,
20}
21
22impl UdpStats {
23    pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
24        self.datagrams += datagrams;
25        self.bytes += bytes as u64;
26        self.ios += 1;
27    }
28}
29
30/// Number of frames transmitted or received of each frame type
31#[derive(Default, Copy, Clone)]
32#[non_exhaustive]
33#[allow(missing_docs)]
34pub struct FrameStats {
35    pub acks: u64,
36    pub path_acks: u64,
37    pub ack_frequency: u64,
38    pub crypto: u64,
39    pub connection_close: u64,
40    pub data_blocked: u64,
41    pub datagram: u64,
42    pub handshake_done: u8,
43    pub immediate_ack: u64,
44    pub max_data: u64,
45    pub max_stream_data: u64,
46    pub max_streams_bidi: u64,
47    pub max_streams_uni: u64,
48    pub new_connection_id: u64,
49    pub path_new_connection_id: u64,
50    pub new_token: u64,
51    pub path_challenge: u64,
52    pub path_response: u64,
53    pub ping: u64,
54    pub reset_stream: u64,
55    pub retire_connection_id: u64,
56    pub path_retire_connection_id: u64,
57    pub stream_data_blocked: u64,
58    pub streams_blocked_bidi: u64,
59    pub streams_blocked_uni: u64,
60    pub stop_sending: u64,
61    pub stream: u64,
62    pub observed_addr: u64,
63    pub path_abandon: u64,
64    pub path_status_available: u64,
65    pub path_status_backup: u64,
66    pub max_path_id: u64,
67    pub paths_blocked: u64,
68    pub path_cids_blocked: u64,
69    pub add_address: u64,
70    pub reach_out: u64,
71    pub remove_address: u64,
72}
73
74impl FrameStats {
75    pub(crate) fn record(&mut self, frame: &Frame) {
76        match frame {
77            Frame::Padding => {}
78            Frame::Ping => self.ping += 1,
79            Frame::Ack(_) => self.acks += 1,
80            Frame::PathAck(_) => self.path_acks += 1,
81            Frame::ResetStream(_) => self.reset_stream += 1,
82            Frame::StopSending(_) => self.stop_sending += 1,
83            Frame::Crypto(_) => self.crypto += 1,
84            Frame::Datagram(_) => self.datagram += 1,
85            Frame::NewToken(_) => self.new_token += 1,
86            Frame::MaxData(_) => self.max_data += 1,
87            Frame::MaxStreamData { .. } => self.max_stream_data += 1,
88            Frame::MaxStreams { dir, .. } => {
89                if *dir == Dir::Bi {
90                    self.max_streams_bidi += 1;
91                } else {
92                    self.max_streams_uni += 1;
93                }
94            }
95            Frame::DataBlocked { .. } => self.data_blocked += 1,
96            Frame::Stream(_) => self.stream += 1,
97            Frame::StreamDataBlocked { .. } => self.stream_data_blocked += 1,
98            Frame::StreamsBlocked { dir, .. } => {
99                if *dir == Dir::Bi {
100                    self.streams_blocked_bidi += 1;
101                } else {
102                    self.streams_blocked_uni += 1;
103                }
104            }
105            Frame::NewConnectionId(frame) => match frame.path_id {
106                Some(_) => self.path_new_connection_id += 1,
107                None => self.new_connection_id += 1,
108            },
109            Frame::RetireConnectionId(frame) => match frame.get_type() {
110                FrameType::RETIRE_CONNECTION_ID => self.retire_connection_id += 1,
111                FrameType::PATH_RETIRE_CONNECTION_ID => self.path_retire_connection_id += 1,
112                _ => unreachable!(),
113            },
114            Frame::PathChallenge(_) => self.path_challenge += 1,
115            Frame::PathResponse(_) => self.path_response += 1,
116            Frame::Close(_) => self.connection_close += 1,
117            Frame::AckFrequency(_) => self.ack_frequency += 1,
118            Frame::ImmediateAck => self.immediate_ack += 1,
119            Frame::HandshakeDone => self.handshake_done = self.handshake_done.saturating_add(1),
120            Frame::ObservedAddr(_) => self.observed_addr += 1,
121            Frame::PathAbandon(_) => self.path_abandon = self.path_abandon.saturating_add(1),
122            Frame::PathStatusAvailable(_) => {
123                self.path_status_available = self.path_status_available.saturating_add(1)
124            }
125            Frame::PathStatusBackup(_) => {
126                self.path_status_backup = self.path_status_backup.saturating_add(1)
127            }
128            Frame::MaxPathId(_) => self.max_path_id = self.max_path_id.saturating_add(1),
129            Frame::PathsBlocked(_) => self.paths_blocked = self.paths_blocked.saturating_add(1),
130            Frame::PathCidsBlocked(_) => {
131                self.path_cids_blocked = self.path_cids_blocked.saturating_add(1)
132            }
133            Frame::AddAddress(_) => self.add_address = self.add_address.saturating_add(1),
134            Frame::ReachOut(_) => self.reach_out = self.reach_out.saturating_add(1),
135            Frame::RemoveAddress(_) => self.remove_address = self.remove_address.saturating_add(1),
136        }
137    }
138}
139
140impl std::fmt::Debug for FrameStats {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        f.debug_struct("FrameStats")
143            .field("ACK", &self.acks)
144            .field("ACK_FREQUENCY", &self.ack_frequency)
145            .field("CONNECTION_CLOSE", &self.connection_close)
146            .field("CRYPTO", &self.crypto)
147            .field("DATA_BLOCKED", &self.data_blocked)
148            .field("DATAGRAM", &self.datagram)
149            .field("HANDSHAKE_DONE", &self.handshake_done)
150            .field("IMMEDIATE_ACK", &self.immediate_ack)
151            .field("MAX_DATA", &self.max_data)
152            .field("MAX_PATH_ID", &self.max_path_id)
153            .field("MAX_STREAM_DATA", &self.max_stream_data)
154            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
155            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
156            .field("NEW_CONNECTION_ID", &self.new_connection_id)
157            .field("NEW_TOKEN", &self.new_token)
158            .field("PATHS_BLOCKED", &self.paths_blocked)
159            .field("PATH_ABANDON", &self.path_abandon)
160            .field("PATH_ACK", &self.path_acks)
161            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
162            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
163            .field("PATH_CHALLENGE", &self.path_challenge)
164            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
165            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
166            .field("PATH_RESPONSE", &self.path_response)
167            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
168            .field("PING", &self.ping)
169            .field("RESET_STREAM", &self.reset_stream)
170            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
171            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
172            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
173            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
174            .field("STOP_SENDING", &self.stop_sending)
175            .field("STREAM", &self.stream)
176            .finish()
177    }
178}
179
180/// Statistics related to a transmission path
181#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
182#[non_exhaustive]
183pub struct PathStats {
184    /// Current best estimate of this connection's latency (round-trip-time)
185    pub rtt: Duration,
186    /// Current congestion window of the connection
187    pub cwnd: u64,
188    /// Congestion events on the connection
189    pub congestion_events: u64,
190    /// The amount of packets lost on this path
191    pub lost_packets: u64,
192    /// The amount of bytes lost on this path
193    pub lost_bytes: u64,
194    /// The amount of packets sent on this path
195    pub sent_packets: u64,
196    /// The amount of PLPMTUD probe packets sent on this path (also counted by `sent_packets`)
197    pub sent_plpmtud_probes: u64,
198    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
199    /// `lost_bytes`)
200    pub lost_plpmtud_probes: u64,
201    /// The number of times a black hole was detected in the path
202    pub black_holes_detected: u64,
203    /// Largest UDP payload size the path currently supports
204    pub current_mtu: u16,
205}
206
207/// Connection statistics
208#[derive(Debug, Default, Clone)]
209#[non_exhaustive]
210pub struct ConnectionStats {
211    /// Statistics about UDP datagrams transmitted on a connection
212    pub udp_tx: UdpStats,
213    /// Statistics about UDP datagrams received on a connection
214    pub udp_rx: UdpStats,
215    /// Statistics about frames transmitted on a connection
216    pub frame_tx: FrameStats,
217    /// Statistics about frames received on a connection
218    pub frame_rx: FrameStats,
219}