1use super::{addr::Addr, cmsg};
5use crate::allocator::{self, Allocator};
6use core::{fmt, num::NonZeroU16, task::Poll};
7use libc::{iovec, msghdr, sendmsg};
8use s2n_quic_core::{
9 assume, ensure,
10 inet::{ExplicitCongestionNotification, SocketAddress, Unspecified},
11 ready,
12};
13use s2n_quic_platform::features;
14use std::{io, os::fd::AsRawFd};
15use tracing::trace;
16
17type Idx = u16;
18type RetransmissionIdx = NonZeroU16;
19
20#[cfg(debug_assertions)]
21type Instance = u64;
22#[cfg(not(debug_assertions))]
23type Instance = ();
24
25#[inline(always)]
26fn instance_id() -> Instance {
27 #[cfg(debug_assertions)]
28 {
29 use core::sync::atomic::{AtomicU64, Ordering};
30 static INSTANCES: AtomicU64 = AtomicU64::new(0);
31 INSTANCES.fetch_add(1, Ordering::Relaxed)
32 }
33}
34
35#[derive(Debug)]
36pub struct Segment {
37 idx: Idx,
38 instance_id: Instance,
39}
40
41impl Segment {
42 #[inline(always)]
43 fn get<'a>(&'a self, buffers: &'a [Vec<u8>]) -> &'a Vec<u8> {
44 unsafe {
45 assume!(buffers.len() > self.idx as usize);
46 }
47 &buffers[self.idx as usize]
48 }
49
50 #[inline(always)]
51 fn get_mut<'a>(&self, buffers: &'a mut [Vec<u8>]) -> &'a mut Vec<u8> {
52 unsafe {
53 assume!(buffers.len() > self.idx as usize);
54 }
55 &mut buffers[self.idx as usize]
56 }
57}
58
59impl allocator::Segment for Segment {
60 #[inline]
61 fn leak(&mut self) {
62 self.idx = Idx::MAX;
63 }
64}
65
66#[cfg(debug_assertions)]
67impl Drop for Segment {
68 #[expect(
69 clippy::panic,
70 reason = "debug-only leak detector that fires only when a segment was dropped without being freed"
71 )]
72 fn drop(&mut self) {
73 if self.idx != Idx::MAX && !std::thread::panicking() {
74 panic!("message segment {} leaked", self.idx);
75 }
76 }
77}
78
79#[derive(Debug)]
80pub struct Retransmission {
81 idx: RetransmissionIdx,
82 instance_id: Instance,
83}
84
85impl allocator::Segment for Retransmission {
86 #[inline]
87 fn leak(&mut self) {
88 self.idx = unsafe { RetransmissionIdx::new_unchecked(Idx::MAX) };
89 }
90}
91
92impl Retransmission {
93 #[inline(always)]
94 fn idx(&self) -> Idx {
95 self.idx.get() - 1
96 }
97
98 #[inline(always)]
99 fn get<'a>(&'a self, buffers: &'a [Vec<u8>]) -> &'a Vec<u8> {
100 let idx = self.idx() as usize;
101 unsafe {
102 assume!(buffers.len() > idx);
103 }
104 &buffers[idx]
105 }
106
107 #[inline]
108 fn into_segment(mut self) -> Segment {
109 let idx = core::mem::replace(&mut self.idx, unsafe {
110 RetransmissionIdx::new_unchecked(Idx::MAX)
111 });
112 let idx = idx.get() - 1;
113 let instance_id = self.instance_id;
114 Segment { idx, instance_id }
115 }
116
117 #[inline]
118 fn from_segment(mut handle: Segment) -> Self {
119 let idx = core::mem::replace(&mut handle.idx, Idx::MAX);
120 let idx = idx.saturating_add(1);
121 let idx = unsafe { RetransmissionIdx::new_unchecked(idx) };
122 let instance_id = handle.instance_id;
123 Retransmission { idx, instance_id }
124 }
125}
126
127#[cfg(debug_assertions)]
128impl Drop for Retransmission {
129 #[expect(
130 clippy::panic,
131 reason = "debug-only leak detector that fires only when a segment was dropped without being freed"
132 )]
133 fn drop(&mut self) {
134 if self.idx.get() != Idx::MAX && !std::thread::panicking() {
135 panic!("message segment {} leaked", self.idx.get());
136 }
137 }
138}
139
140pub struct Message {
141 addr: Addr,
142 gso: features::Gso,
143 segment_len: u16,
144 total_len: u16,
145 can_push: bool,
146 buffers: Vec<Vec<u8>>,
147 free: Vec<Segment>,
148 pending_free: Vec<Segment>,
149 payload: Vec<libc::iovec>,
150 ecn: ExplicitCongestionNotification,
151 instance_id: Instance,
152 #[cfg(debug_assertions)]
153 allocated: std::collections::BTreeSet<Idx>,
154}
155
156impl fmt::Debug for Message {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 let mut d = f.debug_struct("Message");
159
160 d.field("addr", &self.addr)
161 .field("segment_len", &self.segment_len)
162 .field("total_len", &self.total_len)
163 .field("can_push", &self.can_push)
164 .field("buffers", &self.buffers.len())
165 .field("free", &self.free.len())
166 .field("pending_free", &self.pending_free.len())
167 .field("segments", &self.payload.len())
168 .field("ecn", &self.ecn);
169
170 #[cfg(debug_assertions)]
171 {
172 d.field("instance_id", &self.instance_id)
173 .field("allocated", &self.allocated.len());
174 }
175
176 d.finish()
177 }
178}
179
180unsafe impl Send for Message {}
181unsafe impl Sync for Message {}
182
183impl Message {
184 #[inline]
185 pub fn new(remote_address: SocketAddress, gso: features::Gso) -> Self {
186 let burst_size = 16;
187 Self {
188 addr: Addr::new(remote_address),
189 gso,
190 segment_len: 0,
191 total_len: 0,
192 can_push: true,
193 buffers: Vec::with_capacity(burst_size),
194 free: Vec::with_capacity(burst_size),
195 pending_free: Vec::with_capacity(burst_size),
196 payload: Vec::with_capacity(burst_size),
197 ecn: ExplicitCongestionNotification::NotEct,
198 instance_id: instance_id(),
199 #[cfg(debug_assertions)]
200 allocated: Default::default(),
201 }
202 }
203
204 #[inline]
205 fn push_payload(&mut self, segment: &Segment) {
206 debug_assert!(self.can_push());
207 debug_assert_eq!(segment.instance_id, self.instance_id);
208
209 let mut iovec = unsafe { core::mem::zeroed::<iovec>() };
210 let buffer = segment.get_mut(&mut self.buffers);
211
212 debug_assert!(!buffer.is_empty());
213 debug_assert!(
214 buffer.len() <= u16::MAX as usize,
215 "cannot transmit more than 2^16 bytes in a single packet"
216 );
217
218 let iov_base: *mut u8 = buffer.as_mut_ptr();
219 iovec.iov_base = iov_base as *mut _;
220 iovec.iov_len = buffer.len() as _;
221
222 self.total_len += buffer.len() as u16;
223
224 if self.payload.is_empty() {
225 self.segment_len = buffer.len() as _;
226 } else {
227 debug_assert!(buffer.len() <= self.segment_len as usize);
228 self.can_push &= buffer.len() == self.segment_len as usize;
230 }
231
232 self.payload.push(iovec);
233
234 let max_segments = self.gso.max_segments();
235
236 self.can_push &= self.payload.len() < max_segments;
237
238 let next_size = self.total_len as usize + self.segment_len as usize;
240 let max_size = u16::MAX as usize;
241 self.can_push &= next_size <= max_size;
242 }
243
244 #[inline]
245 pub fn send_with<Snd>(&mut self, send: Snd) -> io::Result<usize>
246 where
247 Snd: FnOnce(&Addr, ExplicitCongestionNotification, &[io::IoSlice]) -> io::Result<usize>,
248 {
249 let iov = unsafe {
250 &*(self.payload.as_slice() as *const [libc::iovec] as *const [io::IoSlice])
252 };
253 let res = send(&self.addr, self.ecn, iov);
254 self.on_transmit(&res);
255 res
256 }
257
258 #[inline]
259 pub fn poll_send_with<Snd>(&mut self, send: Snd) -> Poll<io::Result<usize>>
260 where
261 Snd: FnOnce(
262 &Addr,
263 ExplicitCongestionNotification,
264 &[io::IoSlice],
265 ) -> Poll<io::Result<usize>>,
266 {
267 let iov = unsafe {
268 &*(self.payload.as_slice() as *const [libc::iovec] as *const [io::IoSlice])
270 };
271 let res = ready!(send(&self.addr, self.ecn, iov));
272 self.on_transmit(&res);
273 res.into()
274 }
275
276 #[inline]
277 pub fn send<S: AsRawFd>(&mut self, s: &S) -> io::Result<()> {
278 let segment_len = self.segment_len;
279
280 self.send_with(|addr, ecn, iov| {
281 use cmsg::Encoder as _;
282
283 let mut msg = unsafe { core::mem::zeroed::<msghdr>() };
284
285 msg.msg_iov = iov.as_ptr() as *mut _;
286 msg.msg_iovlen = iov.len() as _;
287
288 debug_assert!(
289 !addr.get().ip().is_unspecified(),
290 "cannot send packet to unspecified address"
291 );
292 debug_assert!(
293 addr.get().port() != 0,
294 "cannot send packet to unspecified port"
295 );
296 addr.send_with_msg(&mut msg);
297
298 let mut cmsg_storage = cmsg::Storage::<{ cmsg::ENCODER_LEN }>::default();
299 let mut cmsg = cmsg_storage.encoder();
300 if ecn != ExplicitCongestionNotification::NotEct {
301 let _ = cmsg.encode_ecn(ecn, &addr.get());
302 }
303
304 if iov.len() > 1 {
305 let _ = cmsg.encode_gso(segment_len);
306 }
307
308 if !cmsg.is_empty() {
309 msg.msg_control = cmsg.as_mut_ptr() as *mut _;
310 msg.msg_controllen = cmsg.len() as _;
311 }
312
313 let flags = Default::default();
314
315 let result = unsafe { sendmsg(s.as_raw_fd(), &msg, flags) };
316
317 trace!(
318 dest = %addr,
319 segments = iov.len(),
320 segment_len,
321 cmsg_len = msg.msg_controllen,
322 result,
323 );
324
325 if result >= 0 {
326 Ok(result as usize)
327 } else {
328 Err(io::Error::last_os_error())
329 }
330 })?;
331
332 Ok(())
333 }
334
335 #[inline]
336 pub fn drain(&mut self) -> Drain<'_> {
337 Drain {
338 message: self,
339 index: 0,
340 }
341 }
342
343 #[inline]
345 pub fn max_segments(&self) -> usize {
346 self.gso.max_segments()
347 }
348
349 #[inline]
350 fn on_transmit(&mut self, result: &io::Result<usize>) {
351 let len = match result {
352 Ok(len) => *len,
353 Err(err) => {
354 self.gso.handle_socket_error(err);
356 return;
357 }
358 };
359
360 if self.total_len as usize > len {
361 todo!();
362 }
363
364 self.force_clear()
365 }
366}
367
368impl Allocator for Message {
369 type Segment = Segment;
370
371 type Retransmission = Retransmission;
372
373 #[inline]
374 fn alloc(&mut self) -> Option<Self::Segment> {
375 ensure!(self.can_push(), None);
376
377 if let Some(segment) = self.free.pop() {
378 #[cfg(debug_assertions)]
379 assert!(self.allocated.insert(segment.idx));
380 trace!(operation = "alloc", ?segment);
381 return Some(segment);
382 }
383
384 let idx = self.buffers.len().try_into().ok()?;
385 let instance_id = self.instance_id;
386 let segment = Segment { idx, instance_id };
387 self.buffers.push(vec![]);
388
389 #[cfg(debug_assertions)]
390 assert!(self.allocated.insert(segment.idx));
391 trace!(operation = "alloc", ?segment);
392
393 Some(segment)
394 }
395
396 #[inline]
397 fn get<'a>(&'a self, segment: &'a Segment) -> &'a Vec<u8> {
398 debug_assert_eq!(segment.instance_id, self.instance_id);
399
400 #[cfg(debug_assertions)]
401 assert!(self.allocated.contains(&segment.idx));
402
403 segment.get(&self.buffers)
404 }
405
406 #[inline]
407 fn get_mut(&mut self, segment: &Segment) -> &mut Vec<u8> {
408 debug_assert_eq!(segment.instance_id, self.instance_id);
409
410 #[cfg(debug_assertions)]
411 assert!(self.allocated.contains(&segment.idx));
412
413 segment.get_mut(&mut self.buffers)
414 }
415
416 #[inline]
417 fn push(&mut self, segment: Segment) {
418 trace!(operation = "push", ?segment);
419 self.push_payload(&segment);
420
421 #[cfg(debug_assertions)]
422 assert!(self.allocated.contains(&segment.idx));
423
424 self.pending_free.push(segment);
425 }
426
427 #[inline]
428 fn push_with_retransmission(&mut self, segment: Segment) -> Retransmission {
429 trace!(operation = "push_with_retransmission", ?segment);
430 self.push_payload(&segment);
431
432 #[cfg(debug_assertions)]
433 assert!(self.allocated.contains(&segment.idx));
434
435 Retransmission::from_segment(segment)
436 }
437
438 #[inline]
439 fn retransmit(&mut self, segment: Retransmission) -> Segment {
440 debug_assert_eq!(segment.instance_id, self.instance_id);
441 debug_assert!(
442 self.payload.is_empty(),
443 "cannot retransmit with pending payload"
444 );
445
446 let segment = segment.into_segment();
447
448 #[cfg(debug_assertions)]
449 assert!(self.allocated.contains(&segment.idx));
450
451 segment
452 }
453
454 #[inline]
455 fn retransmit_copy(&mut self, retransmission: &Retransmission) -> Option<Segment> {
456 debug_assert_eq!(retransmission.instance_id, self.instance_id);
457 #[cfg(debug_assertions)]
458 assert!(
459 self.allocated.contains(&retransmission.idx()),
460 "{retransmission:?} {self:?}"
461 );
462
463 let segment = self.alloc()?;
464
465 let mut target = core::mem::take(self.get_mut(&segment));
466 debug_assert!(target.is_empty());
467
468 let source = retransmission.get(&self.buffers);
469 debug_assert!(
470 !source.is_empty(),
471 "cannot retransmit empty payload; source: {retransmission:?}, target: {segment:?}"
472 );
473 target.extend_from_slice(source);
474
475 *self.get_mut(&segment) = target;
476
477 Some(segment)
478 }
479
480 #[inline]
481 fn can_push(&self) -> bool {
482 self.can_push
483 }
484
485 #[inline]
486 fn is_empty(&self) -> bool {
487 self.payload.is_empty()
488 }
489
490 #[inline]
491 fn segment_len(&self) -> Option<u16> {
492 debug_assert_eq!(self.segment_len == 0, self.is_empty());
493 if self.segment_len == 0 {
494 None
495 } else {
496 Some(self.segment_len)
497 }
498 }
499
500 #[inline]
501 fn free(&mut self, segment: Segment) {
502 debug_assert_eq!(segment.instance_id, self.instance_id);
503 trace!(operation = "free", ?segment);
504
505 #[cfg(debug_assertions)]
506 assert!(self.allocated.contains(&segment.idx));
507
508 if self.is_empty() {
510 #[cfg(debug_assertions)]
511 assert!(self.allocated.remove(&segment.idx));
512
513 self.free.push(segment);
514 } else {
515 self.pending_free.push(segment);
516 }
517 }
518
519 #[inline]
520 fn free_retransmission(&mut self, segment: Retransmission) {
521 debug_assert_eq!(segment.instance_id, self.instance_id);
522 debug_assert!(
523 self.payload.is_empty(),
524 "cannot free a retransmission with pending payload"
525 );
526
527 trace!(operation = "free_retransmission", ?segment);
528
529 let segment = segment.into_segment();
530
531 let buffer = self.get_mut(&segment);
532 buffer.clear();
533
534 #[cfg(debug_assertions)]
535 assert!(self.allocated.remove(&segment.idx));
536
537 self.free.push(segment);
538 }
539
540 #[inline]
541 fn ecn(&self) -> ExplicitCongestionNotification {
542 self.ecn
543 }
544
545 #[inline]
546 fn set_ecn(&mut self, ecn: ExplicitCongestionNotification) {
547 self.ecn = ecn;
548 }
549
550 #[inline]
551 fn remote_address(&self) -> SocketAddress {
552 self.addr.get()
553 }
554
555 #[inline]
556 fn set_remote_address(&mut self, remote_address: SocketAddress) {
557 self.addr.set(remote_address);
558 }
559
560 #[inline]
561 fn set_remote_port(&mut self, port: u16) {
562 self.addr.set_port(port);
563 }
564
565 #[inline]
566 fn force_clear(&mut self) {
567 self.payload.clear();
569 self.ecn = ExplicitCongestionNotification::NotEct;
570 self.segment_len = 0;
571 self.total_len = 0;
572 self.can_push = true;
573
574 for segment in &self.pending_free {
575 segment.get_mut(&mut self.buffers).clear();
576 #[cfg(debug_assertions)]
577 assert!(self.allocated.remove(&segment.idx));
578 }
579
580 if self.free.is_empty() {
581 core::mem::swap(&mut self.free, &mut self.pending_free);
582 } else {
583 self.free.append(&mut self.pending_free);
584 }
585 }
586}
587
588#[cfg(debug_assertions)]
589impl Drop for Message {
590 fn drop(&mut self) {
591 use allocator::Segment;
592 for segment in &mut self.free {
593 segment.leak();
594 }
595 for segment in &mut self.pending_free {
596 segment.leak();
597 }
598 }
599}
600
601pub struct Drain<'a> {
602 message: &'a mut Message,
603 index: usize,
604}
605
606impl<'a> Iterator for Drain<'a> {
607 type Item = &'a [u8];
608
609 #[inline]
610 fn next(&mut self) -> Option<Self::Item> {
611 let v = self.message.payload.get(self.index)?;
612 self.index += 1;
613 let v = unsafe { core::slice::from_raw_parts(v.iov_base as *const u8, v.iov_len) };
614 Some(v)
615 }
616}
617
618impl Drop for Drain<'_> {
619 #[inline]
620 fn drop(&mut self) {
621 self.message.force_clear();
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628
629 #[test]
630 fn happy_path() {
631 let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
632
633 let addr: std::net::SocketAddr = "127.0.0.1:4433".parse().unwrap();
634 let mut message = Message::new(addr.into(), Default::default());
635
636 let handle = message.alloc().unwrap();
637 let payload = message.get_mut(&handle);
638 payload.extend_from_slice(b"hello\n");
639 let hello = message.push_with_retransmission(handle);
640
641 let world = if message.gso.max_segments() > 1 {
642 let handle = message.alloc().unwrap();
643 let payload = message.get_mut(&handle);
644 payload.extend_from_slice(b"world\n");
645 let world = message.push_with_retransmission(handle);
646 Some(world)
647 } else {
648 None
649 };
650
651 message.send(&socket).unwrap();
652
653 let world = world.map(|world| message.retransmit(world));
654 let hello = message.retransmit(hello);
655
656 if let Some(world) = world {
657 assert_eq!(message.get(&world), b"world\n");
658 message.push(world);
659 }
660
661 assert_eq!(message.get(&hello), b"hello\n");
662 message.push(hello);
663
664 message.send(&socket).unwrap();
665 }
666}