Skip to main content

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::{ChecksumOffloadResult, PositiveIsize, WeakDeviceIdentifier};
14use packet::FragmentedBuffer;
15
16use crate::internal::datagram::{DatagramSocketSpec, IpExt};
17use crate::internal::settings::DatagramSettings;
18
19#[derive(Derivative)]
20#[derivative(Debug(bound = ""))]
21pub(crate) struct SendBufferTracking<S: DatagramSocketSpec>(
22    netstack3_base::socket::SendBufferTracking<S::SocketWritableListener>,
23);
24
25pub(crate) enum SendBufferError {
26    SendBufferFull,
27    InvalidLength,
28}
29
30impl From<SendBufferFullError> for SendBufferError {
31    fn from(SendBufferFullError: SendBufferFullError) -> Self {
32        Self::SendBufferFull
33    }
34}
35
36impl<S: DatagramSocketSpec> SendBufferTracking<S> {
37    pub(crate) fn new(listener: S::SocketWritableListener, settings: &DatagramSettings) -> Self {
38        Self(netstack3_base::socket::SendBufferTracking::new(
39            settings.send_buffer.default(),
40            listener,
41        ))
42    }
43
44    pub(crate) fn set_capacity(&self, capacity: usize, settings: &DatagramSettings) {
45        let Self(tracking) = self;
46        let capacity = PositiveIsize::new_unsigned(capacity.max(settings.send_buffer.min().into()))
47            .unwrap_or_else(|| settings.send_buffer.max())
48            .min(settings.send_buffer.max());
49        tracking.set_capacity(capacity);
50    }
51
52    pub(crate) fn capacity(&self) -> usize {
53        let Self(tracking) = self;
54        tracking.capacity().into()
55    }
56
57    #[cfg(any(test, feature = "testutils"))]
58    pub(crate) fn available(&self) -> usize {
59        let Self(tracking) = self;
60        tracking.available().map(Into::into).unwrap_or(0)
61    }
62
63    pub(crate) fn prepare_for_send<
64        WireI: Ip,
65        SocketI: IpExt,
66        D: WeakDeviceIdentifier,
67        B: FragmentedBuffer,
68    >(
69        &self,
70        id: &S::SocketId<SocketI, D>,
71        buffer: &B,
72    ) -> Result<TxMetadata<SocketI, D, S>, SendBufferError> {
73        // Always penalize the send buffer by the cost of a fixed header.
74        let header_len = match WireI::VERSION {
75            IpVersion::V4 => packet_formats::ipv4::HDR_PREFIX_LEN,
76            IpVersion::V6 => packet_formats::ipv6::IPV6_FIXED_HDR_LEN,
77        } + S::FIXED_HEADER_SIZE;
78        self.prepare_for_send_inner(buffer.len() + header_len, id)
79    }
80
81    fn prepare_for_send_inner<I: IpExt, D: WeakDeviceIdentifier>(
82        &self,
83        size: usize,
84        id: &S::SocketId<I, D>,
85    ) -> Result<TxMetadata<I, D, S>, SendBufferError> {
86        let Self(tracking) = self;
87        // System imposes a limit of isize::max length for a single datagram.
88        let size = PositiveIsize::new_unsigned(size).ok_or(SendBufferError::InvalidLength)?;
89        let space = tracking.acquire(size)?;
90        Ok(TxMetadata {
91            socket: S::downgrade_socket_id(id),
92            space: ManuallyDrop::new(space),
93            checksum_offload_result: None,
94        })
95    }
96}
97
98/// The tx metadata associated with a datagram socket.
99#[derive(Derivative, GenericOverIp)]
100#[generic_over_ip(I, Ip)]
101#[derivative(Debug(bound = ""))]
102pub struct TxMetadata<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
103    socket: S::WeakSocketId<I, D>,
104    space: ManuallyDrop<SendBufferSpace>,
105    checksum_offload_result: Option<ChecksumOffloadResult>,
106}
107
108impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> TxMetadata<I, D, S> {
109    /// Gets the socket from which the packet originates.
110    pub fn socket(&self) -> &S::WeakSocketId<I, D> {
111        &self.socket
112    }
113
114    /// Returns the checksum offload result.
115    pub fn checksum_offload_result(&self) -> Option<ChecksumOffloadResult> {
116        self.checksum_offload_result.clone()
117    }
118
119    /// Sets the checksum offload result.
120    pub fn set_checksum_offload_result(&mut self, result: Option<ChecksumOffloadResult>) {
121        self.checksum_offload_result = result;
122    }
123}
124
125impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Drop for TxMetadata<I, D, S> {
126    fn drop(&mut self) {
127        let Self { socket, space, checksum_offload_result: _ } = self;
128        // Take space out and leave the slot in uninitialized state so drop is
129        // not called.
130        //
131        // SAFETY: space is not used again (shadowed here).
132        let space = unsafe { ManuallyDrop::take(space) };
133        match S::upgrade_socket_id(socket) {
134            Some(socket) => {
135                let SendBufferTracking(tracking) = &socket.borrow().send_buffer;
136                tracking.release(space)
137            }
138            None => {
139                // Failed to upgrade the socket, acknowledge the space being
140                // dropped.
141                space.acknowledge_drop();
142            }
143        }
144    }
145}
146
147#[cfg(any(test, feature = "testutils"))]
148impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> PartialEq for TxMetadata<I, D, S> {
149    fn eq(&self, other: &Self) -> bool {
150        // Tx metadata is always a unique instance accompanying a frame and it's
151        // not copiable. So it may only be equal to another instance if they're
152        // the exact same object.
153        core::ptr::eq(self, other)
154    }
155}