netstack3_datagram/
sndbuf.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Datagram socket sendbuffer definitions.
6
7use core::borrow::Borrow;
8use core::mem::ManuallyDrop;
9
10use derivative::Derivative;
11use net_types::ip::{GenericOverIp, Ip, IpVersion};
12use netstack3_base::socket::{SendBufferFullError, SendBufferSpace};
13use netstack3_base::{PositiveIsize, WeakDeviceIdentifier};
14use packet::FragmentedBuffer;
15
16use crate::internal::datagram::{DatagramSocketSpec, IpExt};
17
18/// Maximum send buffer size. Value taken from Linux defaults.
19pub(crate) const MAX_SEND_BUFFER_SIZE: PositiveIsize = PositiveIsize::new(4 * 1024 * 1024).unwrap();
20/// Default send buffer size. Value taken from Linux defaults.
21pub(crate) const DEFAULT_SEND_BUFFER_SIZE: PositiveIsize = PositiveIsize::new(208 * 1024).unwrap();
22/// Minimum send buffer size. Value taken from Linux defaults.
23pub(crate) const MIN_SEND_BUFFER_SIZE: usize = 4 * 1024;
24
25#[derive(Derivative)]
26#[derivative(Debug(bound = ""))]
27pub(crate) struct SendBufferTracking<S: DatagramSocketSpec>(
28    netstack3_base::socket::SendBufferTracking<S::SocketWritableListener>,
29);
30
31pub(crate) enum SendBufferError {
32    SendBufferFull,
33    InvalidLength,
34}
35
36impl From<SendBufferFullError> for SendBufferError {
37    fn from(SendBufferFullError: SendBufferFullError) -> Self {
38        Self::SendBufferFull
39    }
40}
41
42impl<S: DatagramSocketSpec> SendBufferTracking<S> {
43    pub(crate) fn new(listener: S::SocketWritableListener) -> Self {
44        Self(netstack3_base::socket::SendBufferTracking::new(DEFAULT_SEND_BUFFER_SIZE, listener))
45    }
46
47    pub(crate) fn set_capacity(&self, capacity: usize) {
48        let Self(tracking) = self;
49        let capacity = PositiveIsize::new_unsigned(capacity.max(MIN_SEND_BUFFER_SIZE))
50            .unwrap_or(MAX_SEND_BUFFER_SIZE)
51            .min(MAX_SEND_BUFFER_SIZE);
52        tracking.set_capacity(capacity);
53    }
54
55    pub(crate) fn capacity(&self) -> usize {
56        let Self(tracking) = self;
57        tracking.capacity().into()
58    }
59
60    #[cfg(any(test, feature = "testutils"))]
61    pub(crate) fn available(&self) -> usize {
62        let Self(tracking) = self;
63        tracking.available().map(Into::into).unwrap_or(0)
64    }
65
66    pub(crate) fn prepare_for_send<
67        WireI: Ip,
68        SocketI: IpExt,
69        D: WeakDeviceIdentifier,
70        B: FragmentedBuffer,
71    >(
72        &self,
73        id: &S::SocketId<SocketI, D>,
74        buffer: &B,
75    ) -> Result<TxMetadata<SocketI, D, S>, SendBufferError> {
76        // Always penalize the send buffer by the cost of a fixed header.
77        let header_len = match WireI::VERSION {
78            IpVersion::V4 => packet_formats::ipv4::HDR_PREFIX_LEN,
79            IpVersion::V6 => packet_formats::ipv6::IPV6_FIXED_HDR_LEN,
80        } + S::FIXED_HEADER_SIZE;
81        self.prepare_for_send_inner(buffer.len() + header_len, id)
82    }
83
84    fn prepare_for_send_inner<I: IpExt, D: WeakDeviceIdentifier>(
85        &self,
86        size: usize,
87        id: &S::SocketId<I, D>,
88    ) -> Result<TxMetadata<I, D, S>, SendBufferError> {
89        let Self(tracking) = self;
90        // System imposes a limit of isize::max length for a single datagram.
91        let size = PositiveIsize::new_unsigned(size).ok_or(SendBufferError::InvalidLength)?;
92        let space = tracking.acquire(size)?;
93        Ok(TxMetadata { socket: S::downgrade_socket_id(id), space: ManuallyDrop::new(space) })
94    }
95}
96
97/// The tx metadata associated with a datagram socket.
98#[derive(Derivative, GenericOverIp)]
99#[generic_over_ip(I, Ip)]
100#[derivative(Debug(bound = ""))]
101pub struct TxMetadata<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
102    socket: S::WeakSocketId<I, D>,
103    space: ManuallyDrop<SendBufferSpace>,
104}
105
106impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Drop for TxMetadata<I, D, S> {
107    fn drop(&mut self) {
108        let Self { socket, space } = self;
109        // Take space out and leave the slot in uninitialized state so drop is
110        // not called.
111        //
112        // SAFETY: space is not used again (shadowed here).
113        let space = unsafe { ManuallyDrop::take(space) };
114        match S::upgrade_socket_id(socket) {
115            Some(socket) => {
116                let SendBufferTracking(tracking) = &socket.borrow().send_buffer;
117                tracking.release(space)
118            }
119            None => {
120                // Failed to upgrade the socket, acknowledge the space being
121                // dropped.
122                space.acknowledge_drop();
123            }
124        }
125    }
126}
127
128#[cfg(any(test, feature = "testutils"))]
129impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> PartialEq for TxMetadata<I, D, S> {
130    fn eq(&self, other: &Self) -> bool {
131        // Tx metadata is always a unique instance accompanying a frame and it's
132        // not copiable. So it may only be equal to another instance if they're
133        // the exact same object.
134        core::ptr::eq(self, other)
135    }
136}