Skip to main content

netstack3_device/
pure_ip.rs

1// Copyright 2024 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//! A pure IP device, capable of directly sending/receiving IPv4 & IPv6 packets.
6
7use core::convert::Infallible as Never;
8use core::fmt::Debug;
9
10use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
11use log::debug;
12use net_types::ip::{Ip, IpVersion, Ipv4, Ipv6, Mtu};
13use netstack3_base::sync::{Mutex, RwLock};
14use netstack3_base::{
15    BroadcastIpExt, ChecksumOffloadSpec, CoreTimerContext, Device, DeviceIdContext,
16    NetworkParsingContext, NetworkSerializer, ReceivableFrameMeta, RecvFrameContext,
17    RecvIpFrameMeta, ResourceCounterContext, SendFrameError, SendFrameErrorReason,
18    SendableFrameMeta, TimerContext, TxMetadataBindingsTypes, WeakDeviceIdentifier,
19};
20use netstack3_ip::{DeviceIpLayerMetadata, IpPacketDestination};
21use packet::BufferMut;
22
23use crate::internal::base::{
24    DeviceBufferBindingsTypes, DeviceCounters, DeviceLayerTypes, DeviceReceiveFrameSpec,
25    PureIpDeviceCounters,
26};
27use crate::internal::id::{BaseDeviceId, BasePrimaryDeviceId, BaseWeakDeviceId, DeviceId};
28use crate::internal::queue::tx::{
29    TransmitQueue, TransmitQueueHandler, TransmitQueueState, TxQueuePacketMetadataCommon,
30};
31use crate::internal::queue::{DequeueState, DeviceBufferSpec, TransmitQueueFrameError};
32use crate::internal::socket::{
33    DeviceSocketHandler, DeviceSocketMetadata, DeviceSocketSendTypes, Frame, IpFrame, ReceivedFrame,
34};
35use crate::internal::state::{DeviceStateSpec, IpLinkDeviceState};
36
37/// A weak device ID identifying a pure IP device.
38///
39/// This device ID is like [`WeakDeviceId`] but specifically for pure IP
40/// devices.
41///
42/// [`WeakDeviceId`]: crate::device::WeakDeviceId
43pub type PureIpWeakDeviceId<BT> = BaseWeakDeviceId<PureIpDevice, BT>;
44
45/// A strong device ID identifying a pure IP device.
46///
47/// This device ID is like [`DeviceId`] but specifically for pure IP devices.
48///
49/// [`DeviceId`]: crate::device::DeviceId
50pub type PureIpDeviceId<BT> = BaseDeviceId<PureIpDevice, BT>;
51
52/// The primary reference for a pure IP device.
53pub type PureIpPrimaryDeviceId<BT> = BasePrimaryDeviceId<PureIpDevice, BT>;
54
55/// A marker type identifying a pure IP device.
56#[derive(Copy, Clone)]
57pub enum PureIpDevice {}
58
59/// The parameters required to create a pure IP device.
60#[derive(Debug)]
61pub struct PureIpDeviceCreationProperties {
62    /// The MTU of the device.
63    pub mtu: Mtu,
64    /// The checksum offload capabilities of the device.
65    pub tx_offload_spec: netstack3_base::ChecksumOffloadSpec,
66}
67
68/// Metadata for IP packets held in the TX queue.
69pub struct PureIpDeviceTxQueueFrameMetadata<BT: TxMetadataBindingsTypes> {
70    /// The IP version of the sent packet.
71    pub ip_version: IpVersion,
72    /// Tx metadata associated with the frame.
73    pub tx_metadata: BT::TxMetadata,
74}
75
76impl<BT: TxMetadataBindingsTypes> TxQueuePacketMetadataCommon
77    for PureIpDeviceTxQueueFrameMetadata<BT>
78where
79    BT::TxMetadata: TxQueuePacketMetadataCommon,
80{
81    fn set_checksum_offload_result(
82        &mut self,
83        result: Option<netstack3_base::ChecksumOffloadResult>,
84    ) {
85        self.tx_metadata.set_checksum_offload_result(result);
86    }
87}
88
89/// Metadata for sending IP packets from a device socket.
90#[derive(Debug, PartialEq)]
91pub struct PureIpHeaderParams {
92    /// The IP version of the packet to send.
93    pub ip_version: IpVersion,
94}
95
96/// State for a pure IP device.
97pub struct PureIpDeviceState<BT: DeviceLayerTypes> {
98    /// The device's dynamic state.
99    dynamic_state: RwLock<DynamicPureIpDeviceState>,
100    /// The device's transmit queue.
101    pub tx_queue: TransmitQueue<
102        PureIpDeviceTxQueueFrameMetadata<BT>,
103        <PureIpDevice as DeviceBufferSpec<BT>>::TxBuffer,
104        <PureIpDevice as DeviceBufferSpec<BT>>::TxAllocator,
105    >,
106    /// Counters specific to pure IP devices.
107    pub counters: PureIpDeviceCounters,
108}
109
110/// Dynamic state for a pure IP device.
111pub struct DynamicPureIpDeviceState {
112    /// The MTU of the device.
113    pub(crate) mtu: Mtu,
114}
115
116impl Device for PureIpDevice {}
117
118impl<BT: DeviceBufferBindingsTypes> DeviceBufferSpec<BT> for PureIpDevice {
119    type TxBuffer = BT::TxBuffer;
120    type TxAllocator = BT::TxAllocator;
121}
122
123impl DeviceStateSpec for PureIpDevice {
124    type State<BT: DeviceLayerTypes> = PureIpDeviceState<BT>;
125    type External<BT: DeviceLayerTypes> = BT::PureIpDeviceState;
126    type CreationProperties = PureIpDeviceCreationProperties;
127    type Counters = PureIpDeviceCounters;
128    const IS_LOOPBACK: bool = false;
129    const DEBUG_TYPE: &'static str = "PureIP";
130    type TimerId<D: WeakDeviceIdentifier> = Never;
131
132    fn new_device_state<
133        CC: CoreTimerContext<Self::TimerId<CC::WeakDeviceId>, BC> + DeviceIdContext<Self>,
134        BC: DeviceLayerTypes + TimerContext,
135    >(
136        _bindings_ctx: &mut BC,
137        _self_id: CC::WeakDeviceId,
138        PureIpDeviceCreationProperties { mtu, tx_offload_spec }: Self::CreationProperties,
139        tx_allocator: <Self as DeviceBufferSpec<BC>>::TxAllocator,
140    ) -> Self::State<BC>
141    where
142        Self: DeviceBufferSpec<BC>,
143    {
144        PureIpDeviceState {
145            dynamic_state: RwLock::new(DynamicPureIpDeviceState { mtu }),
146            tx_queue: TransmitQueue::new(tx_allocator, tx_offload_spec),
147            counters: PureIpDeviceCounters::default(),
148        }
149    }
150
151    fn tx_offload_spec<BT: DeviceLayerTypes>(
152        state: &Self::State<BT>,
153    ) -> Option<ChecksumOffloadSpec> {
154        Some(state.tx_queue.tx_offload_spec())
155    }
156}
157
158/// Metadata for IP packets received on a pure IP device.
159pub struct PureIpDeviceReceiveFrameMetadata<D> {
160    /// The device a packet was received on.
161    pub device_id: D,
162    /// The IP version of the received packet.
163    pub ip_version: IpVersion,
164    /// The parsing context for the received packet.
165    pub parsing_context: NetworkParsingContext,
166}
167
168impl DeviceReceiveFrameSpec for PureIpDevice {
169    type FrameMetadata<D> = PureIpDeviceReceiveFrameMetadata<D>;
170}
171
172/// Provides access to a pure IP device's state.
173pub trait PureIpDeviceStateContext: DeviceIdContext<PureIpDevice> {
174    /// Calls the function with an immutable reference to the pure IP device's
175    /// dynamic state.
176    fn with_pure_ip_state<O, F: FnOnce(&DynamicPureIpDeviceState) -> O>(
177        &mut self,
178        device_id: &Self::DeviceId,
179        cb: F,
180    ) -> O;
181
182    /// Calls the function with a mutable reference to the pure IP device's
183    /// dynamic state.
184    fn with_pure_ip_state_mut<O, F: FnOnce(&mut DynamicPureIpDeviceState) -> O>(
185        &mut self,
186        device_id: &Self::DeviceId,
187        cb: F,
188    ) -> O;
189}
190
191impl DeviceSocketSendTypes for PureIpDevice {
192    type Metadata = PureIpHeaderParams;
193}
194
195impl<CC, BC> ReceivableFrameMeta<CC, BC> for PureIpDeviceReceiveFrameMetadata<CC::DeviceId>
196where
197    CC: DeviceIdContext<PureIpDevice>
198        + RecvFrameContext<RecvIpFrameMeta<CC::DeviceId, DeviceIpLayerMetadata<BC>, Ipv4>, BC>
199        + RecvFrameContext<RecvIpFrameMeta<CC::DeviceId, DeviceIpLayerMetadata<BC>, Ipv6>, BC>
200        + ResourceCounterContext<CC::DeviceId, DeviceCounters>
201        + DeviceSocketHandler<PureIpDevice, BC>,
202    BC: TxMetadataBindingsTypes,
203{
204    fn receive_meta<B: BufferMut + Debug>(
205        self,
206        core_ctx: &mut CC,
207        bindings_ctx: &mut BC,
208        buffer: B,
209    ) {
210        let Self { device_id, ip_version, parsing_context } = self;
211
212        core_ctx.add_both_usize(&device_id, buffer.len(), |counters: &DeviceCounters| {
213            &counters.recv_bytes
214        });
215        core_ctx.increment_both(&device_id, |counters: &DeviceCounters| &counters.recv_frame);
216
217        // NB: For conformance with Linux, don't verify that the contents of
218        // of the buffer are a valid IPv4/IPv6 packet. Device sockets are
219        // allowed to receive malformed packets.
220        core_ctx.handle_frame(
221            bindings_ctx,
222            &device_id,
223            Frame::Received(ReceivedFrame::Ip(IpFrame { ip_version, body: buffer.as_ref() })),
224            buffer.as_ref(),
225        );
226
227        match ip_version {
228            IpVersion::V4 => {
229                core_ctx.increment_both(&device_id, |counters: &DeviceCounters| {
230                    &counters.recv_ipv4_delivered
231                });
232                core_ctx.receive_frame(
233                    bindings_ctx,
234                    RecvIpFrameMeta::<_, _, Ipv4>::new(
235                        device_id,
236                        None,
237                        DeviceIpLayerMetadata::default(),
238                        parsing_context,
239                    ),
240                    buffer,
241                )
242            }
243            IpVersion::V6 => {
244                core_ctx.increment_both(&device_id, |counters: &DeviceCounters| {
245                    &counters.recv_ipv6_delivered
246                });
247                core_ctx.receive_frame(
248                    bindings_ctx,
249                    RecvIpFrameMeta::<_, _, Ipv6>::new(
250                        device_id,
251                        None,
252                        DeviceIpLayerMetadata::default(),
253                        parsing_context,
254                    ),
255                    buffer,
256                )
257            }
258        }
259    }
260}
261
262impl<CC, BC> SendableFrameMeta<CC, BC> for DeviceSocketMetadata<PureIpDevice, CC::DeviceId>
263where
264    CC: TransmitQueueHandler<PureIpDevice, BC, Meta = PureIpDeviceTxQueueFrameMetadata<BC>>
265        + ResourceCounterContext<CC::DeviceId, DeviceCounters>,
266    BC: TxMetadataBindingsTypes,
267{
268    fn send_meta<S>(
269        self,
270        core_ctx: &mut CC,
271        bindings_ctx: &mut BC,
272        body: S,
273    ) -> Result<(), SendFrameError<S>>
274    where
275        S: NetworkSerializer,
276        S::Buffer: BufferMut,
277    {
278        let Self { device_id, metadata: PureIpHeaderParams { ip_version } } = self;
279        // TODO(https://fxbug.dev/391946195): Apply send buffer enforcement from
280        // device sockets instead of using default.
281        let tx_meta: BC::TxMetadata = Default::default();
282        net_types::for_any_ip_version!(
283            ip_version,
284            I,
285            queue_ip_frame::<_, _, I, _>(core_ctx, bindings_ctx, &device_id, body, tx_meta)
286        )
287    }
288}
289
290/// Enqueues the given IP packet on the TX queue for the given [`PureIpDevice`].
291pub fn send_ip_frame<BC, CC, I, S>(
292    core_ctx: &mut CC,
293    bindings_ctx: &mut BC,
294    device_id: &CC::DeviceId,
295    destination: IpPacketDestination<I, &DeviceId<BC>>,
296    packet: S,
297    tx_meta: BC::TxMetadata,
298) -> Result<(), SendFrameError<S>>
299where
300    BC: DeviceLayerTypes,
301    CC: TransmitQueueHandler<PureIpDevice, BC, Meta = PureIpDeviceTxQueueFrameMetadata<BC>>
302        + ResourceCounterContext<CC::DeviceId, DeviceCounters>,
303    I: Ip + BroadcastIpExt,
304    S: NetworkSerializer,
305    S::Buffer: BufferMut,
306{
307    core_ctx.increment_both(device_id, |counters| &counters.send_total_frames);
308    core_ctx.increment_both(device_id, DeviceCounters::send_frame::<I>);
309
310    match destination {
311        IpPacketDestination::Broadcast(_)
312        | IpPacketDestination::Multicast(_)
313        | IpPacketDestination::Neighbor(_) => (),
314        IpPacketDestination::Loopback(_) => {
315            unreachable!("Loopback packets must be delivered through the loopback device");
316        }
317    };
318
319    queue_ip_frame::<_, _, I, _>(core_ctx, bindings_ctx, device_id, packet, tx_meta)
320}
321
322fn queue_ip_frame<BC, CC, I, S>(
323    core_ctx: &mut CC,
324    bindings_ctx: &mut BC,
325    device_id: &CC::DeviceId,
326    packet: S,
327    tx_metadata: BC::TxMetadata,
328) -> Result<(), SendFrameError<S>>
329where
330    CC: TransmitQueueHandler<PureIpDevice, BC, Meta = PureIpDeviceTxQueueFrameMetadata<BC>>
331        + ResourceCounterContext<CC::DeviceId, DeviceCounters>,
332    BC: TxMetadataBindingsTypes,
333    I: Ip,
334    S: NetworkSerializer,
335    S::Buffer: BufferMut,
336{
337    let result = TransmitQueueHandler::<PureIpDevice, _>::queue_tx_frame(
338        core_ctx,
339        bindings_ctx,
340        device_id,
341        PureIpDeviceTxQueueFrameMetadata { ip_version: I::VERSION, tx_metadata },
342        packet,
343    );
344    match result {
345        Ok(len) => {
346            core_ctx.add_both_usize(device_id, len, |counters| &counters.send_bytes);
347            core_ctx.increment_both(device_id, |counters| &counters.send_frame);
348            Ok(())
349        }
350        Err(TransmitQueueFrameError::NoQueue(err)) => {
351            core_ctx.increment_both(device_id, |counters| &counters.send_dropped_no_queue);
352            debug!("device {device_id:?} failed to send frame: {err:?}.");
353            Ok(())
354        }
355        Err(TransmitQueueFrameError::QueueFull(serializer)) => {
356            core_ctx.increment_both(device_id, |counters| &counters.send_queue_full);
357            Err(SendFrameError { serializer, error: SendFrameErrorReason::QueueFull })
358        }
359        Err(TransmitQueueFrameError::SerializeError(err)) => {
360            core_ctx.increment_both(device_id, |counters| &counters.send_serialize_error);
361            Err(err.err_into())
362        }
363    }
364}
365
366/// Gets the MTU of the given [`PureIpDevice`].
367pub fn get_mtu<CC: PureIpDeviceStateContext>(core_ctx: &mut CC, device_id: &CC::DeviceId) -> Mtu {
368    core_ctx.with_pure_ip_state(device_id, |DynamicPureIpDeviceState { mtu }| *mtu)
369}
370
371/// Updates the MTU of the given [`PureIpDevice`].
372pub fn set_mtu<CC: PureIpDeviceStateContext>(
373    core_ctx: &mut CC,
374    device_id: &CC::DeviceId,
375    new_mtu: Mtu,
376) {
377    core_ctx.with_pure_ip_state_mut(device_id, |DynamicPureIpDeviceState { mtu }| *mtu = new_mtu)
378}
379
380impl<BT: DeviceLayerTypes> OrderedLockAccess<DynamicPureIpDeviceState>
381    for IpLinkDeviceState<PureIpDevice, BT>
382{
383    type Lock = RwLock<DynamicPureIpDeviceState>;
384    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
385        OrderedLockRef::new(&self.link.dynamic_state)
386    }
387}
388
389impl<BT: DeviceLayerTypes>
390    OrderedLockAccess<
391        TransmitQueueState<PureIpDeviceTxQueueFrameMetadata<BT>, BT::TxBuffer, BT::TxAllocator>,
392    > for IpLinkDeviceState<PureIpDevice, BT>
393{
394    type Lock = Mutex<
395        TransmitQueueState<PureIpDeviceTxQueueFrameMetadata<BT>, BT::TxBuffer, BT::TxAllocator>,
396    >;
397    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
398        OrderedLockRef::new(&self.link.tx_queue.queue)
399    }
400}
401
402impl<BT: DeviceLayerTypes>
403    OrderedLockAccess<DequeueState<PureIpDeviceTxQueueFrameMetadata<BT>, BT::TxBuffer>>
404    for IpLinkDeviceState<PureIpDevice, BT>
405{
406    type Lock = Mutex<DequeueState<PureIpDeviceTxQueueFrameMetadata<BT>, BT::TxBuffer>>;
407    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
408        OrderedLockRef::new(&self.link.tx_queue.deque)
409    }
410}