Skip to main content

s2n_quic_dc/msg/
send.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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            // the caller can only push until the last undersized segment
229            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        // sendmsg has a limitation on the total length of the payload, even with GSO
239        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            // SAFETY: IoSlice is guaranteed to have the same layout as iovec
251            &*(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            // SAFETY: IoSlice is guaranteed to have the same layout as iovec
269            &*(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    /// The maximum number of segments that can be sent in a single GSO payload
344    #[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                // notify the GSO impl that we got an error
355                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 we haven't actually sent anything then immediately free it
509        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        // reset the current payload
568        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}