netstack3_device/
base.rs

1// Copyright 2018 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
5use alloc::vec::Vec;
6use core::fmt::{Debug, Display};
7use core::num::NonZeroU64;
8
9use derivative::Derivative;
10use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
11use net_types::ethernet::Mac;
12use net_types::ip::{Ip, IpVersion, Ipv4, Ipv6};
13use netstack3_base::sync::RwLock;
14use netstack3_base::{
15    Counter, CounterRepr, Device, DeviceIdContext, HandleableTimer, Inspectable, Inspector,
16    InspectorExt as _, InstantContext, ReferenceNotifiers, TimerBindingsTypes, TimerHandler,
17    TxMetadataBindingsTypes,
18};
19use netstack3_filter::FilterBindingsTypes;
20use netstack3_hashmap::HashMap;
21use netstack3_ip::device::Ipv6LinkLayerAddr;
22use netstack3_ip::nud::{LinkResolutionContext, NudCounters};
23use packet::Buf;
24
25use crate::blackhole::{BlackholeDeviceId, BlackholePrimaryDeviceId};
26use crate::internal::arp::ArpCounters;
27use crate::internal::ethernet::{EthernetLinkDevice, EthernetTimerId};
28use crate::internal::id::{
29    BaseDeviceId, BasePrimaryDeviceId, DeviceId, EthernetDeviceId, EthernetPrimaryDeviceId,
30    EthernetWeakDeviceId,
31};
32use crate::internal::loopback::{LoopbackDeviceId, LoopbackPrimaryDeviceId};
33use crate::internal::pure_ip::{PureIpDeviceId, PureIpPrimaryDeviceId};
34use crate::internal::queue::rx::ReceiveQueueBindingsContext;
35use crate::internal::queue::tx::TransmitQueueBindingsContext;
36use crate::internal::socket::{self, DeviceSocketCounters, HeldSockets};
37use crate::internal::state::DeviceStateSpec;
38
39/// Iterator over devices.
40///
41/// Implements `Iterator<Item=DeviceId<C>>` by pulling from provided loopback
42/// and ethernet device ID iterators. This struct only exists as a named type
43/// so it can be an associated type on impls of the [`IpDeviceContext`] trait.
44pub struct DevicesIter<'s, BT: DeviceLayerTypes> {
45    pub(super) ethernet:
46        netstack3_hashmap::hash_map::Values<'s, EthernetDeviceId<BT>, EthernetPrimaryDeviceId<BT>>,
47    pub(super) pure_ip:
48        netstack3_hashmap::hash_map::Values<'s, PureIpDeviceId<BT>, PureIpPrimaryDeviceId<BT>>,
49    pub(super) blackhole: netstack3_hashmap::hash_map::Values<
50        's,
51        BlackholeDeviceId<BT>,
52        BlackholePrimaryDeviceId<BT>,
53    >,
54    pub(super) loopback: core::option::Iter<'s, LoopbackPrimaryDeviceId<BT>>,
55}
56
57impl<'s, BT: DeviceLayerTypes> Iterator for DevicesIter<'s, BT> {
58    type Item = DeviceId<BT>;
59
60    fn next(&mut self) -> Option<Self::Item> {
61        let Self { ethernet, pure_ip, blackhole, loopback } = self;
62        ethernet
63            .map(|primary| primary.clone_strong().into())
64            .chain(pure_ip.map(|primary| primary.clone_strong().into()))
65            .chain(blackhole.map(|primary| primary.clone_strong().into()))
66            .chain(loopback.map(|primary| primary.clone_strong().into()))
67            .next()
68    }
69}
70
71/// Supported link layer address types for IPv6.
72#[allow(missing_docs)]
73pub enum Ipv6DeviceLinkLayerAddr {
74    Mac(Mac),
75    // Add other link-layer address types as needed.
76}
77
78impl Ipv6LinkLayerAddr for Ipv6DeviceLinkLayerAddr {
79    fn as_bytes(&self) -> &[u8] {
80        match self {
81            Ipv6DeviceLinkLayerAddr::Mac(a) => a.as_ref(),
82        }
83    }
84
85    fn eui64_iid(&self) -> [u8; 8] {
86        match self {
87            Ipv6DeviceLinkLayerAddr::Mac(a) => a.to_eui64(),
88        }
89    }
90}
91
92/// The identifier for timer events in the device layer.
93#[derive(Derivative)]
94#[derivative(
95    Clone(bound = ""),
96    Eq(bound = ""),
97    PartialEq(bound = ""),
98    Hash(bound = ""),
99    Debug(bound = "")
100)]
101pub struct DeviceLayerTimerId<BT: DeviceLayerTypes>(DeviceLayerTimerIdInner<BT>);
102
103#[derive(Derivative)]
104#[derivative(
105    Clone(bound = ""),
106    Eq(bound = ""),
107    PartialEq(bound = ""),
108    Hash(bound = ""),
109    Debug(bound = "")
110)]
111#[allow(missing_docs)]
112enum DeviceLayerTimerIdInner<BT: DeviceLayerTypes> {
113    Ethernet(EthernetTimerId<EthernetWeakDeviceId<BT>>),
114}
115
116impl<BT: DeviceLayerTypes> From<EthernetTimerId<EthernetWeakDeviceId<BT>>>
117    for DeviceLayerTimerId<BT>
118{
119    fn from(id: EthernetTimerId<EthernetWeakDeviceId<BT>>) -> DeviceLayerTimerId<BT> {
120        DeviceLayerTimerId(DeviceLayerTimerIdInner::Ethernet(id))
121    }
122}
123
124impl<CC, BT> HandleableTimer<CC, BT> for DeviceLayerTimerId<BT>
125where
126    BT: DeviceLayerTypes,
127    CC: TimerHandler<BT, EthernetTimerId<EthernetWeakDeviceId<BT>>>,
128{
129    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BT, timer: BT::UniqueTimerId) {
130        let Self(id) = self;
131        match id {
132            DeviceLayerTimerIdInner::Ethernet(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
133        }
134    }
135}
136
137/// The collection of devices within [`DeviceLayerState`].
138#[derive(Derivative)]
139#[derivative(Default(bound = ""))]
140pub struct Devices<BT: DeviceLayerTypes> {
141    /// Collection of Ethernet devices.
142    pub ethernet: HashMap<EthernetDeviceId<BT>, EthernetPrimaryDeviceId<BT>>,
143    /// Collection of PureIP devices.
144    pub pure_ip: HashMap<PureIpDeviceId<BT>, PureIpPrimaryDeviceId<BT>>,
145    /// Collection of blackhole devices.
146    pub blackhole: HashMap<BlackholeDeviceId<BT>, BlackholePrimaryDeviceId<BT>>,
147    /// The loopback device, if installed.
148    pub loopback: Option<LoopbackPrimaryDeviceId<BT>>,
149}
150
151impl<BT: DeviceLayerTypes> Devices<BT> {
152    /// Gets an iterator over available devices.
153    pub fn iter(&self) -> DevicesIter<'_, BT> {
154        let Self { ethernet, pure_ip, blackhole, loopback } = self;
155        DevicesIter {
156            ethernet: ethernet.values(),
157            pure_ip: pure_ip.values(),
158            blackhole: blackhole.values(),
159            loopback: loopback.iter(),
160        }
161    }
162}
163
164/// The state associated with the device layer.
165#[derive(Derivative)]
166#[derivative(Default(bound = ""))]
167pub struct DeviceLayerState<BT: DeviceLayerTypes> {
168    devices: RwLock<Devices<BT>>,
169    /// Device layer origin tracker.
170    pub origin: OriginTracker,
171    /// Collection of all device sockets.
172    pub shared_sockets: HeldSockets<BT>,
173    /// Device socket counters.
174    pub device_socket_counters: DeviceSocketCounters,
175    /// Common device counters.
176    pub counters: DeviceCounters,
177    /// Ethernet counters.
178    pub ethernet_counters: EthernetDeviceCounters,
179    /// PureIp counters.
180    pub pure_ip_counters: PureIpDeviceCounters,
181    /// IPv4 NUD counters.
182    pub nud_v4_counters: NudCounters<Ipv4>,
183    /// IPv6 NUD counters.
184    pub nud_v6_counters: NudCounters<Ipv6>,
185    /// ARP counters.
186    pub arp_counters: ArpCounters,
187}
188
189impl<BT: DeviceLayerTypes> DeviceLayerState<BT> {
190    /// Helper to access NUD counters for an IP version.
191    pub fn nud_counters<I: Ip>(&self) -> &NudCounters<I> {
192        I::map_ip((), |()| &self.nud_v4_counters, |()| &self.nud_v6_counters)
193    }
194}
195
196impl<BT: DeviceLayerTypes> OrderedLockAccess<Devices<BT>> for DeviceLayerState<BT> {
197    type Lock = RwLock<Devices<BT>>;
198    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
199        OrderedLockRef::new(&self.devices)
200    }
201}
202
203/// Counters for ethernet devices.
204#[derive(Default)]
205pub struct EthernetDeviceCounters {
206    /// Count of incoming frames dropped due to an unsupported ethertype.
207    pub recv_unsupported_ethertype: Counter,
208    /// Count of incoming frames dropped due to an empty ethertype.
209    pub recv_no_ethertype: Counter,
210}
211
212impl Inspectable for EthernetDeviceCounters {
213    fn record<I: Inspector>(&self, inspector: &mut I) {
214        inspector.record_child("Ethernet", |inspector| {
215            let Self { recv_no_ethertype, recv_unsupported_ethertype } = self;
216            inspector.record_child("Rx", |inspector| {
217                inspector.record_counter("NoEthertype", recv_no_ethertype);
218                inspector.record_counter("UnsupportedEthertype", recv_unsupported_ethertype);
219            });
220        })
221    }
222}
223
224/// Counters for pure IP devices.
225#[derive(Default)]
226pub struct PureIpDeviceCounters {}
227
228impl Inspectable for PureIpDeviceCounters {
229    fn record<I: Inspector>(&self, _inspector: &mut I) {}
230}
231
232/// Counters for blackhole devices.
233pub struct BlackholeDeviceCounters;
234
235impl Inspectable for BlackholeDeviceCounters {
236    fn record<I: Inspector>(&self, _inspector: &mut I) {}
237}
238
239/// Device layer counters.
240#[derive(Default, Debug)]
241#[cfg_attr(
242    any(test, feature = "testutils"),
243    derive(PartialEq, netstack3_macros::CounterCollection)
244)]
245pub struct DeviceCounters<C: CounterRepr = Counter> {
246    /// Count of outgoing frames which enter the device layer (but may or may
247    /// not have been dropped prior to reaching the wire).
248    pub send_total_frames: C,
249    /// Count of frames sent.
250    pub send_frame: C,
251    /// Count of bytes sent.
252    pub send_bytes: C,
253    /// Count of frames that failed to send because of a full Tx queue.
254    pub send_queue_full: C,
255    /// Count of frames that failed to send because of a serialization error.
256    pub send_serialize_error: C,
257    /// Count of frames received.
258    pub recv_frame: C,
259    /// Count of bytes received.
260    pub recv_bytes: C,
261    /// Count of incoming frames dropped due to a parsing error.
262    pub recv_parse_error: C,
263    /// Count of incoming frames containing an IPv4 packet delivered.
264    pub recv_ipv4_delivered: C,
265    /// Count of incoming frames containing an IPv6 packet delivered.
266    pub recv_ipv6_delivered: C,
267    /// Count of sent frames containing an IPv4 packet.
268    pub send_ipv4_frame: C,
269    /// Count of sent frames containing an IPv6 packet.
270    pub send_ipv6_frame: C,
271    /// Count of frames that failed to send because there was no Tx queue.
272    pub send_dropped_no_queue: C,
273    /// Count of frames that were dropped during Tx queue dequeuing.
274    pub send_dropped_dequeue: C,
275}
276
277impl DeviceCounters {
278    /// Either `send_ipv4_frame` or `send_ipv6_frame` depending on `I`.
279    pub fn send_frame<I: Ip>(&self) -> &Counter {
280        match I::VERSION {
281            IpVersion::V4 => &self.send_ipv4_frame,
282            IpVersion::V6 => &self.send_ipv6_frame,
283        }
284    }
285}
286
287impl Inspectable for DeviceCounters {
288    fn record<I: Inspector>(&self, inspector: &mut I) {
289        let Self {
290            recv_frame,
291            recv_bytes,
292            recv_ipv4_delivered,
293            recv_ipv6_delivered,
294            recv_parse_error,
295            send_dropped_no_queue,
296            send_frame,
297            send_bytes,
298            send_ipv4_frame,
299            send_ipv6_frame,
300            send_queue_full,
301            send_serialize_error,
302            send_total_frames,
303            send_dropped_dequeue,
304        } = self;
305        inspector.record_child("Rx", |inspector| {
306            inspector.record_counter("TotalFrames", recv_frame);
307            inspector.record_counter("TotalBytes", recv_bytes);
308            inspector.record_counter("Malformed", recv_parse_error);
309            inspector.record_counter("Ipv4Delivered", recv_ipv4_delivered);
310            inspector.record_counter("Ipv6Delivered", recv_ipv6_delivered);
311        });
312        inspector.record_child("Tx", |inspector| {
313            inspector.record_counter("TotalFrames", send_total_frames);
314            inspector.record_counter("Sent", send_frame);
315            inspector.record_counter("SentBytes", send_bytes);
316            inspector.record_counter("SendIpv4Frame", send_ipv4_frame);
317            inspector.record_counter("SendIpv6Frame", send_ipv6_frame);
318            inspector.record_counter("NoQueue", send_dropped_no_queue);
319            inspector.record_counter("QueueFull", send_queue_full);
320            inspector.record_counter("SerializeError", send_serialize_error);
321            inspector.record_counter("DequeueDrop", send_dropped_dequeue);
322        });
323    }
324}
325/// Light-weight tracker for recording the source of some instance.
326///
327/// This should be held as a field in a parent type that is cloned into each
328/// child instance. Then, the origin of a child instance can be verified by
329/// asserting equality against the parent's field.
330///
331/// This is only enabled in debug builds; in non-debug builds, all
332/// `OriginTracker` instances are identical so all operations are no-ops.
333// TODO(https://fxbug.dev/320078167): Move this and OriginTrackerContext out of
334// the device module and apply to more places.
335#[derive(Clone, Debug, PartialEq)]
336pub struct OriginTracker(#[cfg(debug_assertions)] u64);
337
338impl Default for OriginTracker {
339    fn default() -> Self {
340        Self::new()
341    }
342}
343
344impl OriginTracker {
345    /// Creates a new `OriginTracker` that isn't derived from any other
346    /// instance.
347    ///
348    /// In debug builds, this creates a unique `OriginTracker` that won't be
349    /// equal to any instances except those cloned from it. In non-debug builds
350    /// all `OriginTracker` instances are identical.
351    #[cfg_attr(not(debug_assertions), inline)]
352    fn new() -> Self {
353        Self(
354            #[cfg(debug_assertions)]
355            {
356                static COUNTER: core::sync::atomic::AtomicU64 =
357                    core::sync::atomic::AtomicU64::new(0);
358                COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
359            },
360        )
361    }
362}
363
364/// A trait abstracting a context containing an [`OriginTracker`].
365///
366/// This allows API structs to extract origin from contexts when creating
367/// resources.
368pub trait OriginTrackerContext {
369    /// Gets the origin tracker for this context.
370    fn origin_tracker(&mut self) -> OriginTracker;
371}
372
373/// A context providing facilities to store and remove primary device IDs.
374///
375/// This allows the device layer APIs to be written generically on `D`.
376pub trait DeviceCollectionContext<D: Device + DeviceStateSpec, BT: DeviceLayerTypes>:
377    DeviceIdContext<D>
378{
379    /// Adds `device` to the device collection.
380    fn insert(&mut self, device: BasePrimaryDeviceId<D, BT>);
381
382    /// Removes `device` from the collection, if it exists.
383    fn remove(&mut self, device: &BaseDeviceId<D, BT>) -> Option<BasePrimaryDeviceId<D, BT>>;
384}
385
386/// Provides abstractions over the frame metadata received from bindings for
387/// implementers of [`Device`].
388///
389/// This trait allows [`api::DeviceApi`] to provide a single entrypoint for
390/// frames from bindings.
391pub trait DeviceReceiveFrameSpec {
392    /// The frame metadata for ingress frames, where `D` is a device identifier.
393    type FrameMetadata<D>;
394}
395
396/// Provides associated types used in the device layer.
397pub trait DeviceLayerStateTypes: InstantContext + FilterBindingsTypes {
398    /// The state associated with loopback devices.
399    type LoopbackDeviceState: Send + Sync + DeviceClassMatcher<Self::DeviceClass>;
400
401    /// The state associated with ethernet devices.
402    type EthernetDeviceState: Send + Sync + DeviceClassMatcher<Self::DeviceClass>;
403
404    /// The state associated with pure IP devices.
405    type PureIpDeviceState: Send + Sync + DeviceClassMatcher<Self::DeviceClass>;
406
407    /// The state associated with blackhole devices.
408    type BlackholeDeviceState: Send + Sync + DeviceClassMatcher<Self::DeviceClass>;
409
410    /// An opaque identifier that is available from both strong and weak device
411    /// references.
412    type DeviceIdentifier: Send + Sync + Debug + Display + DeviceIdAndNameMatcher;
413}
414
415/// Provides matching functionality for the device class of a device installed
416/// in the netstack.
417pub trait DeviceClassMatcher<DeviceClass> {
418    /// Returns whether the provided device class matches the class of the
419    /// device.
420    fn device_class_matches(&self, device_class: &DeviceClass) -> bool;
421}
422
423/// Provides matching functionality for the ID and name of a device installed in
424/// the netstack.
425pub trait DeviceIdAndNameMatcher {
426    /// Returns whether the provided ID matches the ID of the device.
427    fn id_matches(&self, id: &NonZeroU64) -> bool;
428
429    /// Returns whether the provided name matches the name of the device.
430    fn name_matches(&self, name: &str) -> bool;
431}
432
433/// Provides associated types used in the device layer.
434///
435/// This trait groups together state types used throughout the device layer. It
436/// is blanket-implemented for all types that implement
437/// [`socket::DeviceSocketTypes`] and [`DeviceLayerStateTypes`].
438pub trait DeviceLayerTypes:
439    DeviceLayerStateTypes
440    + socket::DeviceSocketTypes
441    + LinkResolutionContext<EthernetLinkDevice>
442    + TimerBindingsTypes
443    + ReferenceNotifiers
444    + TxMetadataBindingsTypes
445    + 'static
446{
447}
448impl<
449    BC: DeviceLayerStateTypes
450        + socket::DeviceSocketTypes
451        + LinkResolutionContext<EthernetLinkDevice>
452        + TimerBindingsTypes
453        + ReferenceNotifiers
454        + TxMetadataBindingsTypes
455        + 'static,
456> DeviceLayerTypes for BC
457{
458}
459
460/// An event dispatcher for the device layer.
461pub trait DeviceLayerEventDispatcher:
462    DeviceLayerTypes
463    + ReceiveQueueBindingsContext<LoopbackDeviceId<Self>>
464    + TransmitQueueBindingsContext<EthernetDeviceId<Self>>
465    + TransmitQueueBindingsContext<LoopbackDeviceId<Self>>
466    + TransmitQueueBindingsContext<PureIpDeviceId<Self>>
467    + Sized
468{
469    /// The transmit queue dequeueing context used by bindings.
470    ///
471    /// `DequeueContext` is a passthrough type from bindings (i.e. entirely
472    /// opaque to core) when using `TransmitQueueApi` to trigger the transmit
473    /// queue to send frames to the underlying devices.
474    type DequeueContext;
475
476    /// Send a frame to an Ethernet device driver.
477    ///
478    /// See [`DeviceSendFrameError`] for the ways this call may fail; all other
479    /// errors are silently ignored and reported as success. Implementations are
480    /// expected to gracefully handle non-conformant but correctable input, e.g.
481    /// by padding too-small frames.
482    ///
483    /// `dequeue_context` is `Some` iff this is called from the context of
484    /// operating the transmit queue via `TransmitQueueApi`.
485    fn send_ethernet_frame(
486        &mut self,
487        device: &EthernetDeviceId<Self>,
488        frame: Buf<Vec<u8>>,
489        dequeue_context: Option<&mut Self::DequeueContext>,
490    ) -> Result<(), DeviceSendFrameError>;
491
492    /// Send an IP packet to an IP device driver.
493    ///
494    /// See [`DeviceSendFrameError`] for the ways this call may fail; all other
495    /// errors are silently ignored and reported as success. Implementations are
496    /// expected to gracefully handle non-conformant but correctable input, e.g.
497    /// by padding too-small frames.
498    ///
499    /// `dequeue_context` is `Some` iff this is called from the context of
500    /// operating the transmit queue via `TransmitQueueApi`.
501    fn send_ip_packet(
502        &mut self,
503        device: &PureIpDeviceId<Self>,
504        packet: Buf<Vec<u8>>,
505        ip_version: IpVersion,
506        dequeue_context: Option<&mut Self::DequeueContext>,
507    ) -> Result<(), DeviceSendFrameError>;
508}
509
510/// An error encountered when sending a frame.
511#[derive(Debug, PartialEq, Eq)]
512pub enum DeviceSendFrameError {
513    /// The device doesn't have available buffers to send frames.
514    NoBuffers,
515}
516
517#[cfg(any(test, feature = "testutils"))]
518pub mod testutil {
519    use super::*;
520
521    use netstack3_base::{CounterCollection, ResourceCounterContext};
522
523    /// Expected values of [`DeviceCounters`].
524    pub type DeviceCounterExpectations = DeviceCounters<u64>;
525
526    impl DeviceCounterExpectations {
527        /// Assert that the counters tracked by `core_ctx` match expectations.
528        #[track_caller]
529        pub fn assert_counters<D, CC: ResourceCounterContext<D, DeviceCounters>>(
530            &self,
531            core_ctx: &CC,
532            device: &D,
533        ) {
534            assert_eq!(&core_ctx.counters().cast::<u64>(), self, "stack-wide counters");
535            assert_eq!(
536                &core_ctx.per_resource_counters(device).cast::<u64>(),
537                self,
538                "per-device counters"
539            );
540        }
541    }
542}
543
544#[cfg(test)]
545mod tests {
546    use super::*;
547
548    #[test]
549    fn origin_tracker() {
550        let tracker = OriginTracker::new();
551        if cfg!(debug_assertions) {
552            assert_ne!(tracker, OriginTracker::new());
553        } else {
554            assert_eq!(tracker, OriginTracker::new());
555        }
556        assert_eq!(tracker.clone(), tracker);
557    }
558}