datagram_socket/
socket_stats.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use std::ops::Deref;
28use std::sync::atomic::AtomicI64;
29use std::sync::atomic::Ordering;
30use std::sync::Arc;
31use std::sync::RwLock;
32use std::time::Duration;
33use std::time::SystemTime;
34
35pub trait AsSocketStats {
36    fn as_socket_stats(&self) -> SocketStats;
37
38    fn as_quic_stats(&self) -> Option<&Arc<QuicAuditStats>> {
39        None
40    }
41}
42
43#[derive(Debug, Clone, Copy, Default)]
44pub struct SocketStats {
45    pub pmtu: u16,
46    pub rtt_us: i64,
47    pub cwnd: u64,
48    pub packets_sent: u64,
49    pub packets_recvd: u64,
50    pub packets_lost: u64,
51    pub packets_retrans: u64,
52    pub bytes_sent: u64,
53    pub bytes_recvd: u64,
54    pub bytes_lost: u64,
55    pub bytes_retrans: u64,
56}
57
58type BoxError = Box<dyn std::error::Error + Send + Sync>;
59
60#[derive(Debug)]
61pub struct QuicAuditStats {
62    /// A transport-level connection error code received from the client
63    recvd_conn_close_transport_error_code: AtomicI64,
64    /// A transport-level connection error code sent to the client
65    sent_conn_close_transport_error_code: AtomicI64,
66    /// An application-level connection error code received from the client
67    recvd_conn_close_application_error_code: AtomicI64,
68    /// An application-level connection error code sent to the client
69    sent_conn_close_application_error_code: AtomicI64,
70    /// Time taken for the QUIC handshake in microseconds
71    transport_handshake_duration_us: AtomicI64,
72    /// The start time of the handshake.
73    transport_handshake_start: Arc<RwLock<Option<SystemTime>>>,
74    /// The reason the QUIC connection was closed
75    connection_close_reason: RwLock<Option<BoxError>>,
76    /// The server's chosen QUIC connection ID
77    /// The QUIC connection ID is presently an array of 20 bytes (160 bits)
78    pub quic_connection_id: Vec<u8>,
79}
80
81impl QuicAuditStats {
82    #[inline]
83    pub fn new(quic_connection_id: Vec<u8>) -> Self {
84        Self {
85            recvd_conn_close_transport_error_code: AtomicI64::new(-1),
86            sent_conn_close_transport_error_code: AtomicI64::new(-1),
87            recvd_conn_close_application_error_code: AtomicI64::new(-1),
88            sent_conn_close_application_error_code: AtomicI64::new(-1),
89            transport_handshake_duration_us: AtomicI64::new(-1),
90            transport_handshake_start: Arc::new(RwLock::new(None)),
91            connection_close_reason: RwLock::new(None),
92            quic_connection_id,
93        }
94    }
95
96    #[inline]
97    pub fn recvd_conn_close_transport_error_code(&self) -> i64 {
98        self.recvd_conn_close_transport_error_code
99            .load(Ordering::SeqCst)
100    }
101
102    #[inline]
103    pub fn sent_conn_close_transport_error_code(&self) -> i64 {
104        self.sent_conn_close_transport_error_code
105            .load(Ordering::SeqCst)
106    }
107
108    #[inline]
109    pub fn recvd_conn_close_application_error_code(&self) -> i64 {
110        self.recvd_conn_close_application_error_code
111            .load(Ordering::SeqCst)
112    }
113
114    #[inline]
115    pub fn sent_conn_close_application_error_code(&self) -> i64 {
116        self.sent_conn_close_application_error_code
117            .load(Ordering::SeqCst)
118    }
119
120    #[inline]
121    pub fn set_recvd_conn_close_transport_error_code(
122        &self, recvd_conn_close_transport_error_code: i64,
123    ) {
124        self.recvd_conn_close_transport_error_code
125            .store(recvd_conn_close_transport_error_code, Ordering::SeqCst)
126    }
127
128    #[inline]
129    pub fn set_sent_conn_close_transport_error_code(
130        &self, sent_conn_close_transport_error_code: i64,
131    ) {
132        self.sent_conn_close_transport_error_code
133            .store(sent_conn_close_transport_error_code, Ordering::SeqCst)
134    }
135
136    #[inline]
137    pub fn set_recvd_conn_close_application_error_code(
138        &self, recvd_conn_close_application_error_code: i64,
139    ) {
140        self.recvd_conn_close_application_error_code
141            .store(recvd_conn_close_application_error_code, Ordering::SeqCst)
142    }
143
144    #[inline]
145    pub fn set_sent_conn_close_application_error_code(
146        &self, sent_conn_close_application_error_code: i64,
147    ) {
148        self.sent_conn_close_application_error_code
149            .store(sent_conn_close_application_error_code, Ordering::SeqCst)
150    }
151
152    #[inline]
153    pub fn transport_handshake_duration_us(&self) -> i64 {
154        self.transport_handshake_duration_us.load(Ordering::SeqCst)
155    }
156
157    #[inline]
158    pub fn set_transport_handshake_start(&self, start_time: SystemTime) {
159        *self.transport_handshake_start.write().unwrap() = Some(start_time);
160    }
161
162    #[inline]
163    pub fn set_transport_handshake_duration(&self, duration: Duration) {
164        let dur = i64::try_from(duration.as_micros()).unwrap_or(-1);
165        self.transport_handshake_duration_us
166            .store(dur, Ordering::SeqCst);
167    }
168
169    #[inline]
170    pub fn transport_handshake_start(&self) -> Arc<RwLock<Option<SystemTime>>> {
171        Arc::clone(&self.transport_handshake_start)
172    }
173
174    #[inline]
175    pub fn connection_close_reason(
176        &self,
177    ) -> impl Deref<Target = Option<BoxError>> + '_ {
178        self.connection_close_reason.read().unwrap()
179    }
180
181    #[inline]
182    pub fn set_connection_close_reason(&self, error: BoxError) {
183        *self.connection_close_reason.write().unwrap() = Some(error);
184    }
185}
186
187#[derive(Debug, Copy, Clone, PartialEq, Eq)]
188pub enum StreamClosureKind {
189    None,
190    Implicit,
191    Explicit,
192}