netstack3_base/
device.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//! Common traits abstracting the device layer.
6//!
7//! Devices are abstracted away throughout the netstack3 crates. This module
8//! provides the base abstraction definitions.
9//!
10//! Abstracting devices provides:
11//!
12//! * A useful way to remove a lot of state and complexity from tests.
13//! * Opaqueness to steer state access towards context traits.
14//! * Type signature reduction since real device identifiers are parameterized
15//!   by bindings types.
16//! * Modularity.
17
18use alloc::borrow::Cow;
19use core::borrow::Borrow;
20use core::fmt::Debug;
21use core::hash::Hash;
22
23pub(crate) mod address;
24pub(crate) mod link;
25
26/// An identifier for a device.
27pub trait DeviceIdentifier: Clone + Debug + Eq + Hash + PartialEq + Send + Sync + 'static {
28    /// Returns true if the device is a loopback device.
29    fn is_loopback(&self) -> bool;
30}
31
32/// A strong device reference.
33///
34/// [`StrongDeviceIdentifier`] indicates that the referenced device is alive
35/// while the instance exists.
36pub trait StrongDeviceIdentifier: DeviceIdentifier + PartialEq<Self::Weak> {
37    /// The weak version of this identifier.
38    type Weak: WeakDeviceIdentifier<Strong = Self>;
39
40    /// Returns a weak ID for this strong ID.
41    fn downgrade(&self) -> Self::Weak;
42}
43
44/// A weak device reference.
45///
46/// This is the weak reference equivalent of [`StrongDeviceIdentifier`].
47pub trait WeakDeviceIdentifier: DeviceIdentifier + PartialEq<Self::Strong> {
48    /// The strong version of this identifier.
49    type Strong: StrongDeviceIdentifier<Weak = Self>;
50
51    /// Attempts to upgrade this weak ID to a strong ID.
52    ///
53    /// Returns `None` if the resource has been destroyed.
54    fn upgrade(&self) -> Option<Self::Strong>;
55}
56
57/// A device.
58///
59/// `Device` is used to identify a particular device implementation. It
60/// is only intended to exist at the type level, never instantiated at runtime.
61pub trait Device: 'static {}
62
63/// Marker type for a generic device.
64///
65/// This generally represents a device at the IP layer. Other implementations
66/// may exist for type safe link devices.
67pub enum AnyDevice {}
68
69impl Device for AnyDevice {}
70
71/// An execution context which provides device ID types type for various
72/// netstack internals to share.
73pub trait DeviceIdContext<D: Device> {
74    /// The type of device IDs.
75    type DeviceId: StrongDeviceIdentifier<Weak = Self::WeakDeviceId> + 'static;
76
77    /// The type of weakly referenced device IDs.
78    type WeakDeviceId: WeakDeviceIdentifier<Strong = Self::DeviceId> + 'static;
79}
80
81/// A marker trait tying [`DeviceIdContext`] implementations.
82///
83/// To call into the IP layer, we need to be able to represent device
84/// identifiers in the [`AnyDevice`] domain. This trait is a statement that a
85/// [`DeviceIdContext`] in some domain `D` has its identifiers convertible into
86/// the [`AnyDevice`] domain with `From` bounds.
87///
88/// It is provided as a blanket implementation for [`DeviceIdContext`]s that
89/// fulfill the conversion.
90pub trait DeviceIdAnyCompatContext<D: Device>:
91    DeviceIdContext<D>
92    + DeviceIdContext<
93        AnyDevice,
94        DeviceId: From<<Self as DeviceIdContext<D>>::DeviceId>,
95        WeakDeviceId: From<<Self as DeviceIdContext<D>>::WeakDeviceId>,
96    >
97{
98}
99
100impl<CC, D> DeviceIdAnyCompatContext<D> for CC
101where
102    D: Device,
103    CC: DeviceIdContext<D>
104        + DeviceIdContext<
105            AnyDevice,
106            DeviceId: From<<CC as DeviceIdContext<D>>::DeviceId>,
107            WeakDeviceId: From<<CC as DeviceIdContext<D>>::WeakDeviceId>,
108        >,
109{
110}
111
112/// A device id that might be either in its strong or weak form.
113#[derive(Copy, Clone)]
114#[allow(missing_docs)]
115pub enum EitherDeviceId<S, W> {
116    Strong(S),
117    Weak(W),
118}
119
120impl<S: PartialEq, W: PartialEq + PartialEq<S>> PartialEq for EitherDeviceId<S, W> {
121    fn eq(&self, other: &EitherDeviceId<S, W>) -> bool {
122        match (self, other) {
123            (EitherDeviceId::Strong(this), EitherDeviceId::Strong(other)) => this == other,
124            (EitherDeviceId::Strong(this), EitherDeviceId::Weak(other)) => other == this,
125            (EitherDeviceId::Weak(this), EitherDeviceId::Strong(other)) => this == other,
126            (EitherDeviceId::Weak(this), EitherDeviceId::Weak(other)) => this == other,
127        }
128    }
129}
130
131impl<S: StrongDeviceIdentifier, W: WeakDeviceIdentifier<Strong = S>> EitherDeviceId<&'_ S, &'_ W> {
132    /// Returns a [`Cow`] reference for the strong variant.
133    ///
134    /// Attempts to upgrade if this is a `Weak` variant.
135    pub fn as_strong_ref<'a>(&'a self) -> Option<Cow<'a, S>> {
136        match self {
137            EitherDeviceId::Strong(s) => Some(Cow::Borrowed(s)),
138            EitherDeviceId::Weak(w) => w.upgrade().map(Cow::Owned),
139        }
140    }
141}
142
143impl<S, W> EitherDeviceId<S, W> {
144    /// Returns a borrowed version of this `EitherDeviceId`.
145    pub fn as_ref<'a, S2, W2>(&'a self) -> EitherDeviceId<&'a S2, &'a W2>
146    where
147        S: Borrow<S2>,
148        W: Borrow<W2>,
149    {
150        match self {
151            EitherDeviceId::Strong(s) => EitherDeviceId::Strong(s.borrow()),
152            EitherDeviceId::Weak(w) => EitherDeviceId::Weak(w.borrow()),
153        }
154    }
155}
156
157impl<S: StrongDeviceIdentifier<Weak = W>, W: WeakDeviceIdentifier<Strong = S>>
158    EitherDeviceId<S, W>
159{
160    /// Returns a [`Cow`] reference for the `Strong` variant.
161    ///
162    /// Attempts to upgrade if this is a `Weak` variant.
163    pub fn as_strong<'a>(&'a self) -> Option<Cow<'a, S>> {
164        match self {
165            EitherDeviceId::Strong(s) => Some(Cow::Borrowed(s)),
166            EitherDeviceId::Weak(w) => w.upgrade().map(Cow::Owned),
167        }
168    }
169
170    /// Returns a [`Cow`] reference for the `Weak` variant.
171    ///
172    /// Downgrades if this is a `Strong` variant.
173    pub fn as_weak<'a>(&'a self) -> Cow<'a, W> {
174        match self {
175            EitherDeviceId::Strong(s) => Cow::Owned(s.downgrade()),
176            EitherDeviceId::Weak(w) => Cow::Borrowed(w),
177        }
178    }
179}
180
181/// Allows the user to match a device with a name.
182pub trait DeviceWithName {
183    /// Returns whether the provided name matches the interface.
184    fn name_matches(&self, name: &str) -> bool;
185}
186
187#[cfg(any(test, feature = "testutils"))]
188pub(crate) mod testutil {
189    use alloc::sync::Arc;
190    use core::sync::atomic::AtomicBool;
191
192    use super::*;
193
194    use crate::testutil::FakeCoreCtx;
195
196    /// A fake weak device id.
197    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
198    pub struct FakeWeakDeviceId<D>(pub D);
199
200    impl<D: PartialEq> PartialEq<D> for FakeWeakDeviceId<D> {
201        fn eq(&self, other: &D) -> bool {
202            let Self(this) = self;
203            this == other
204        }
205    }
206
207    impl<D: FakeStrongDeviceId> WeakDeviceIdentifier for FakeWeakDeviceId<D> {
208        type Strong = D;
209
210        fn upgrade(&self) -> Option<D> {
211            let Self(inner) = self;
212            inner.is_alive().then(|| inner.clone())
213        }
214    }
215
216    impl<D: DeviceIdentifier> DeviceIdentifier for FakeWeakDeviceId<D> {
217        fn is_loopback(&self) -> bool {
218            let Self(inner) = self;
219            inner.is_loopback()
220        }
221    }
222
223    /// A fake device ID for use in testing.
224    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
225    pub struct FakeDeviceId;
226
227    impl FakeDeviceId {
228        /// The name of the fake device.
229        pub const FAKE_NAME: &'static str = "FakeDeviceId";
230    }
231
232    impl StrongDeviceIdentifier for FakeDeviceId {
233        type Weak = FakeWeakDeviceId<Self>;
234
235        fn downgrade(&self) -> Self::Weak {
236            FakeWeakDeviceId(self.clone())
237        }
238    }
239
240    impl DeviceIdentifier for FakeDeviceId {
241        fn is_loopback(&self) -> bool {
242            false
243        }
244    }
245
246    impl DeviceWithName for FakeDeviceId {
247        fn name_matches(&self, name: &str) -> bool {
248            name == Self::FAKE_NAME
249        }
250    }
251
252    impl FakeStrongDeviceId for FakeDeviceId {
253        fn is_alive(&self) -> bool {
254            true
255        }
256    }
257
258    impl PartialEq<FakeWeakDeviceId<FakeDeviceId>> for FakeDeviceId {
259        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<FakeDeviceId>) -> bool {
260            self == other
261        }
262    }
263
264    /// A fake device ID for use in testing.
265    ///
266    /// [`FakeReferencyDeviceId`] behaves like a referency device ID, each
267    /// constructed instance represents a new device.
268    #[derive(Clone, Debug, Default)]
269    pub struct FakeReferencyDeviceId {
270        removed: Arc<AtomicBool>,
271    }
272
273    impl core::hash::Hash for FakeReferencyDeviceId {
274        fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
275            let Self { removed } = self;
276            core::ptr::hash(alloc::sync::Arc::as_ptr(removed), state)
277        }
278    }
279
280    impl core::cmp::Eq for FakeReferencyDeviceId {}
281
282    impl core::cmp::PartialEq for FakeReferencyDeviceId {
283        fn eq(&self, Self { removed: other }: &Self) -> bool {
284            let Self { removed } = self;
285            alloc::sync::Arc::ptr_eq(removed, other)
286        }
287    }
288
289    impl core::cmp::Ord for FakeReferencyDeviceId {
290        fn cmp(&self, Self { removed: other }: &Self) -> core::cmp::Ordering {
291            let Self { removed } = self;
292            alloc::sync::Arc::as_ptr(removed).cmp(&alloc::sync::Arc::as_ptr(other))
293        }
294    }
295
296    impl core::cmp::PartialOrd for FakeReferencyDeviceId {
297        fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
298            Some(self.cmp(other))
299        }
300    }
301
302    impl FakeReferencyDeviceId {
303        /// Marks this device as removed, all weak references will not be able
304        /// to upgrade anymore.
305        pub fn mark_removed(&self) {
306            self.removed.store(true, core::sync::atomic::Ordering::Relaxed);
307        }
308
309        const FAKE_NAME: &'static str = "FakeReferencyDeviceId";
310    }
311
312    impl StrongDeviceIdentifier for FakeReferencyDeviceId {
313        type Weak = FakeWeakDeviceId<Self>;
314
315        fn downgrade(&self) -> Self::Weak {
316            FakeWeakDeviceId(self.clone())
317        }
318    }
319
320    impl DeviceIdentifier for FakeReferencyDeviceId {
321        fn is_loopback(&self) -> bool {
322            false
323        }
324    }
325
326    impl DeviceWithName for FakeReferencyDeviceId {
327        fn name_matches(&self, name: &str) -> bool {
328            name == Self::FAKE_NAME
329        }
330    }
331
332    impl FakeStrongDeviceId for FakeReferencyDeviceId {
333        fn is_alive(&self) -> bool {
334            !self.removed.load(core::sync::atomic::Ordering::Relaxed)
335        }
336    }
337
338    impl PartialEq<FakeWeakDeviceId<FakeReferencyDeviceId>> for FakeReferencyDeviceId {
339        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<FakeReferencyDeviceId>) -> bool {
340            self == other
341        }
342    }
343
344    /// Marks a fake strong device id.
345    pub trait FakeStrongDeviceId:
346        StrongDeviceIdentifier<Weak = FakeWeakDeviceId<Self>> + 'static + Ord
347    {
348        /// Returns whether this ID is still alive.
349        ///
350        /// This is used by [`FakeWeakDeviceId`] to return `None` when trying to
351        /// upgrade back a `FakeStrongDeviceId`.
352        fn is_alive(&self) -> bool;
353    }
354
355    /// A device ID type that supports identifying more than one distinct
356    /// device.
357    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
358    #[allow(missing_docs)]
359    pub enum MultipleDevicesId {
360        A,
361        B,
362        C,
363    }
364
365    impl MultipleDevicesId {
366        /// Returns all variants.
367        pub fn all() -> [Self; 3] {
368            [Self::A, Self::B, Self::C]
369        }
370
371        fn fake_name(&self) -> &'static str {
372            match self {
373                MultipleDevicesId::A => "A",
374                MultipleDevicesId::B => "B",
375                MultipleDevicesId::C => "C",
376            }
377        }
378    }
379
380    impl DeviceIdentifier for MultipleDevicesId {
381        fn is_loopback(&self) -> bool {
382            false
383        }
384    }
385
386    impl DeviceWithName for MultipleDevicesId {
387        fn name_matches(&self, name: &str) -> bool {
388            self.fake_name() == name
389        }
390    }
391
392    impl StrongDeviceIdentifier for MultipleDevicesId {
393        type Weak = FakeWeakDeviceId<Self>;
394
395        fn downgrade(&self) -> Self::Weak {
396            FakeWeakDeviceId(self.clone())
397        }
398    }
399
400    impl FakeStrongDeviceId for MultipleDevicesId {
401        fn is_alive(&self) -> bool {
402            true
403        }
404    }
405
406    impl<S, Meta, D: StrongDeviceIdentifier> DeviceIdContext<AnyDevice> for FakeCoreCtx<S, Meta, D> {
407        type DeviceId = D;
408        type WeakDeviceId = D::Weak;
409    }
410
411    impl<S, Meta, D: StrongDeviceIdentifier> DeviceIdContext<AnyDevice>
412        for &mut FakeCoreCtx<S, Meta, D>
413    {
414        type DeviceId = D;
415        type WeakDeviceId = D::Weak;
416    }
417
418    impl PartialEq<FakeWeakDeviceId<MultipleDevicesId>> for MultipleDevicesId {
419        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<MultipleDevicesId>) -> bool {
420            self == other
421        }
422    }
423}