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}