Skip to main content

netget/server/openvpn/
reliable.rs

1//! OpenVPN's control-channel reliability layer.
2//!
3//! OpenVPN runs its TLS control channel over UDP, so the transport has to
4//! supply the guarantees TLS assumes: every control packet carries a monotonic
5//! packet id, the peer acknowledges ids in the ACK array of the packets it
6//! sends back (or in a standalone `P_ACK_V1`), and anything unacknowledged is
7//! retransmitted. Without this, a TLS handshake spread over several datagrams
8//! cannot complete: the first lost or reordered fragment desynchronises the
9//! record stream permanently.
10//!
11//! Two halves, both deliberately transport-free — they hold no socket and no
12//! session ids, so they can be unit-tested and so the caller decides when bytes
13//! actually go out:
14//!
15//! * [`ReliableSender`] numbers outgoing packets, holds them until the peer
16//!   acknowledges them, and reports which are due for a first transmission or a
17//!   retransmission.
18//! * [`ReliableReceiver`] delivers inbound payloads **in packet-id order**,
19//!   buffering anything that arrives early, dropping duplicates and anything
20//!   outside the window, and accumulating the ids that still need acknowledging.
21//!
22//! # Window sizes
23//!
24//! OpenVPN's own reliability buffer holds `RELIABLE_CAPACITY` (12) packets and
25//! its ACK array reader refuses more than `RELIABLE_ACK_SIZE` (8) ids in one
26//! frame — a longer array is a parse error at the peer, not merely wasteful, so
27//! [`MAX_ACK_ARRAY`] must stay at or below 8. The send window is kept well
28//! under the peer's capacity because exceeding it means silent drops.
29
30use super::packet::Opcode;
31use crate::utils::clock::Instant;
32use std::collections::{BTreeMap, VecDeque};
33use std::time::Duration;
34
35/// Most packet ids to name in one ACK array.
36///
37/// OpenVPN's `reliable_ack_parse` rejects a frame whose ACK count exceeds
38/// `RELIABLE_ACK_SIZE` (8). Four leaves headroom and still clears a burst.
39pub const MAX_ACK_ARRAY: usize = 4;
40
41/// Most unacknowledged packets in flight at once. The peer's reliability buffer
42/// holds 12; staying well below it means a burst is never dropped for capacity.
43pub const SEND_WINDOW: usize = 4;
44
45/// How far ahead of `next_expected` an inbound packet id may be before it is
46/// dropped rather than buffered.
47pub const RECV_WINDOW: u32 = 8;
48
49/// Largest payload carried by a single `P_CONTROL_V1`.
50///
51/// The peer sizes its control-channel receive buffer from the link MTU, so a
52/// larger fragment is silently dropped by a default-configured client. A TLS
53/// server flight (ServerHello, certificate, CertificateVerify, Finished) is a
54/// few kilobytes and is fragmented across as many packets as this needs.
55pub const MAX_CONTROL_PAYLOAD: usize = 1100;
56
57/// First retransmission delay.
58const INITIAL_RETRANSMIT: Duration = Duration::from_millis(1000);
59
60/// Ceiling for the exponential backoff.
61const MAX_RETRANSMIT: Duration = Duration::from_secs(8);
62
63/// Transmissions of one packet before the session is declared dead. At the
64/// backoff above this is roughly a minute of trying.
65pub const MAX_ATTEMPTS: u32 = 8;
66
67/// Most ids held awaiting acknowledgement. A peer that floods control packets
68/// cannot make this grow without bound.
69const MAX_PENDING_ACKS: usize = 64;
70
71/// Most out-of-order payloads buffered at once.
72const MAX_BUFFERED: usize = RECV_WINDOW as usize;
73
74/// One outgoing control packet, held until the peer acknowledges it.
75#[derive(Debug, Clone)]
76pub struct Outgoing {
77    pub packet_id: u32,
78    pub opcode: Opcode,
79    /// Ids acknowledged by this packet. Fixed when the packet is queued so a
80    /// retransmission is byte-identical to the original — a peer that sees two
81    /// different frames with one packet id has to guess which it already
82    /// processed.
83    pub acks: Vec<u32>,
84    pub payload: Vec<u8>,
85    sent_at: Option<Instant>,
86    attempts: u32,
87}
88
89impl Outgoing {
90    /// When this packet next needs to go out, or `None` if it never has.
91    fn due_at(&self) -> Option<Instant> {
92        let sent = self.sent_at?;
93        let mut backoff = INITIAL_RETRANSMIT;
94        for _ in 1..self.attempts {
95            backoff = (backoff * 2).min(MAX_RETRANSMIT);
96        }
97        Some(sent + backoff)
98    }
99}
100
101/// Numbers, holds and retransmits outgoing control packets.
102pub struct ReliableSender {
103    next_packet_id: u32,
104    /// In packet-id order. The first [`SEND_WINDOW`] entries are the ones
105    /// allowed on the wire.
106    outgoing: VecDeque<Outgoing>,
107}
108
109impl ReliableSender {
110    pub fn new() -> Self {
111        ReliableSender {
112            next_packet_id: 0,
113            outgoing: VecDeque::new(),
114        }
115    }
116
117    /// Queue a packet and return the id it was given.
118    pub fn queue(&mut self, opcode: Opcode, acks: Vec<u32>, payload: Vec<u8>) -> u32 {
119        let packet_id = self.next_packet_id;
120        self.next_packet_id = self.next_packet_id.wrapping_add(1);
121        self.outgoing.push_back(Outgoing {
122            packet_id,
123            opcode,
124            acks,
125            payload,
126            sent_at: None,
127            attempts: 0,
128        });
129        packet_id
130    }
131
132    /// Drop everything the peer has acknowledged.
133    pub fn on_ack(&mut self, ids: &[u32]) {
134        if ids.is_empty() {
135            return;
136        }
137        self.outgoing.retain(|o| !ids.contains(&o.packet_id));
138    }
139
140    /// Packets that should go on the wire now, marked as transmitted.
141    ///
142    /// Only the first [`SEND_WINDOW`] queued packets are considered, so a large
143    /// TLS flight is released a window at a time as the peer acknowledges it.
144    pub fn take_due(&mut self, now: Instant) -> Vec<Outgoing> {
145        let mut due = Vec::new();
146        for entry in self.outgoing.iter_mut().take(SEND_WINDOW) {
147            let ready = match entry.due_at() {
148                None => true,
149                Some(at) => at <= now,
150            };
151            if ready {
152                entry.sent_at = Some(now);
153                entry.attempts = entry.attempts.saturating_add(1);
154                due.push(entry.clone());
155            }
156        }
157        due
158    }
159
160    /// Put every in-flight packet back on the wire at the next opportunity.
161    ///
162    /// Used when the peer retransmits its session reset, which says it never saw
163    /// our reply.
164    pub fn mark_all_due(&mut self) {
165        for entry in self.outgoing.iter_mut() {
166            entry.sent_at = None;
167        }
168    }
169
170    /// True once some packet has been transmitted [`MAX_ATTEMPTS`] times without
171    /// being acknowledged: the peer is gone.
172    pub fn is_exhausted(&self) -> bool {
173        self.outgoing.iter().any(|o| o.attempts >= MAX_ATTEMPTS)
174    }
175
176    pub fn in_flight(&self) -> usize {
177        self.outgoing.len()
178    }
179}
180
181impl Default for ReliableSender {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187/// Orders inbound control payloads and tracks what still needs acknowledging.
188pub struct ReliableReceiver {
189    next_expected: u32,
190    buffered: BTreeMap<u32, Vec<u8>>,
191    pending_acks: VecDeque<u32>,
192}
193
194impl ReliableReceiver {
195    /// Start expecting `next_expected`, having already accepted everything
196    /// below it.
197    pub fn new(next_expected: u32) -> Self {
198        ReliableReceiver {
199            next_expected,
200            buffered: BTreeMap::new(),
201            pending_acks: VecDeque::new(),
202        }
203    }
204
205    /// Take a received control packet and return the payloads that are now
206    /// deliverable, oldest first.
207    ///
208    /// A duplicate is acknowledged again — the peer only retransmits because it
209    /// missed the first acknowledgement — but delivers nothing. A packet outside
210    /// the window is neither acknowledged nor buffered, so the peer keeps it and
211    /// retransmits it once the window has moved.
212    pub fn accept(&mut self, packet_id: u32, payload: Vec<u8>) -> Vec<Vec<u8>> {
213        if packet_id < self.next_expected {
214            self.note_ack(packet_id);
215            return Vec::new();
216        }
217        if packet_id.saturating_sub(self.next_expected) >= RECV_WINDOW {
218            return Vec::new();
219        }
220
221        self.note_ack(packet_id);
222
223        if packet_id > self.next_expected {
224            if self.buffered.len() < MAX_BUFFERED {
225                self.buffered.entry(packet_id).or_insert(payload);
226            }
227            return Vec::new();
228        }
229
230        let mut delivered = vec![payload];
231        self.next_expected = self.next_expected.wrapping_add(1);
232        while let Some(next) = self.buffered.remove(&self.next_expected) {
233            delivered.push(next);
234            self.next_expected = self.next_expected.wrapping_add(1);
235        }
236        delivered
237    }
238
239    /// Note an id that the peer must be told we have.
240    fn note_ack(&mut self, packet_id: u32) {
241        if self.pending_acks.contains(&packet_id) {
242            return;
243        }
244        if self.pending_acks.len() >= MAX_PENDING_ACKS {
245            self.pending_acks.pop_front();
246        }
247        self.pending_acks.push_back(packet_id);
248    }
249
250    /// Remove up to [`MAX_ACK_ARRAY`] ids to put in an outgoing ACK array.
251    pub fn take_acks(&mut self) -> Vec<u32> {
252        let n = self.pending_acks.len().min(MAX_ACK_ARRAY);
253        self.pending_acks.drain(..n).collect()
254    }
255
256    pub fn has_pending_acks(&self) -> bool {
257        !self.pending_acks.is_empty()
258    }
259
260    pub fn next_expected(&self) -> u32 {
261        self.next_expected
262    }
263}
264
265/// Split a TLS record stream into `P_CONTROL_V1`-sized fragments.
266pub fn fragment(data: &[u8]) -> Vec<Vec<u8>> {
267    data.chunks(MAX_CONTROL_PAYLOAD)
268        .map(|c| c.to_vec())
269        .collect()
270}