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#[cfg(any(test, feature = "testutils"))]
182pub(crate) mod testutil {
183    use alloc::sync::Arc;
184    use core::sync::atomic::AtomicBool;
185
186    use super::*;
187
188    use crate::InterfaceProperties;
189    use crate::testutil::FakeCoreCtx;
190
191    #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
192    #[allow(missing_docs)]
193    pub enum FakeDeviceClass {
194        Ethernet,
195        Wlan,
196    }
197
198    /// A fake weak device id.
199    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
200    pub struct FakeWeakDeviceId<D>(pub D);
201
202    impl<D: PartialEq> PartialEq<D> for FakeWeakDeviceId<D> {
203        fn eq(&self, other: &D) -> bool {
204            let Self(this) = self;
205            this == other
206        }
207    }
208
209    impl<D: FakeStrongDeviceId> WeakDeviceIdentifier for FakeWeakDeviceId<D> {
210        type Strong = D;
211
212        fn upgrade(&self) -> Option<D> {
213            let Self(inner) = self;
214            inner.is_alive().then(|| inner.clone())
215        }
216    }
217
218    impl<D: DeviceIdentifier> DeviceIdentifier for FakeWeakDeviceId<D> {
219        fn is_loopback(&self) -> bool {
220            let Self(inner) = self;
221            inner.is_loopback()
222        }
223    }
224
225    /// A fake device ID for use in testing.
226    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
227    pub struct FakeDeviceId;
228
229    impl FakeDeviceId {
230        /// The name of the fake device.
231        pub const FAKE_NAME: &'static str = "FakeDeviceId";
232    }
233
234    impl StrongDeviceIdentifier for FakeDeviceId {
235        type Weak = FakeWeakDeviceId<Self>;
236
237        fn downgrade(&self) -> Self::Weak {
238            FakeWeakDeviceId(self.clone())
239        }
240    }
241
242    impl DeviceIdentifier for FakeDeviceId {
243        fn is_loopback(&self) -> bool {
244            false
245        }
246    }
247
248    impl FakeStrongDeviceId for FakeDeviceId {
249        fn is_alive(&self) -> bool {
250            true
251        }
252    }
253
254    impl PartialEq<FakeWeakDeviceId<FakeDeviceId>> for FakeDeviceId {
255        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<FakeDeviceId>) -> bool {
256            self == other
257        }
258    }
259
260    impl InterfaceProperties<()> for FakeDeviceId {
261        fn id_matches(&self, _: &core::num::NonZeroU64) -> bool {
262            unimplemented!()
263        }
264
265        fn name_matches(&self, name: &str) -> bool {
266            name == Self::FAKE_NAME
267        }
268
269        fn device_class_matches(&self, _: &()) -> bool {
270            unimplemented!()
271        }
272    }
273
274    /// A fake device ID for use in testing.
275    ///
276    /// [`FakeReferencyDeviceId`] behaves like a referency device ID, each
277    /// constructed instance represents a new device.
278    #[derive(Clone, Debug, Default)]
279    pub struct FakeReferencyDeviceId {
280        removed: Arc<AtomicBool>,
281    }
282
283    impl core::hash::Hash for FakeReferencyDeviceId {
284        fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
285            let Self { removed } = self;
286            core::ptr::hash(alloc::sync::Arc::as_ptr(removed), state)
287        }
288    }
289
290    impl core::cmp::Eq for FakeReferencyDeviceId {}
291
292    impl core::cmp::PartialEq for FakeReferencyDeviceId {
293        fn eq(&self, Self { removed: other }: &Self) -> bool {
294            let Self { removed } = self;
295            alloc::sync::Arc::ptr_eq(removed, other)
296        }
297    }
298
299    impl core::cmp::Ord for FakeReferencyDeviceId {
300        fn cmp(&self, Self { removed: other }: &Self) -> core::cmp::Ordering {
301            let Self { removed } = self;
302            alloc::sync::Arc::as_ptr(removed).cmp(&alloc::sync::Arc::as_ptr(other))
303        }
304    }
305
306    impl core::cmp::PartialOrd for FakeReferencyDeviceId {
307        fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
308            Some(self.cmp(other))
309        }
310    }
311
312    impl FakeReferencyDeviceId {
313        /// Marks this device as removed, all weak references will not be able
314        /// to upgrade anymore.
315        pub fn mark_removed(&self) {
316            self.removed.store(true, core::sync::atomic::Ordering::Relaxed);
317        }
318
319        const FAKE_NAME: &'static str = "FakeReferencyDeviceId";
320    }
321
322    impl StrongDeviceIdentifier for FakeReferencyDeviceId {
323        type Weak = FakeWeakDeviceId<Self>;
324
325        fn downgrade(&self) -> Self::Weak {
326            FakeWeakDeviceId(self.clone())
327        }
328    }
329
330    impl DeviceIdentifier for FakeReferencyDeviceId {
331        fn is_loopback(&self) -> bool {
332            false
333        }
334    }
335
336    impl FakeStrongDeviceId for FakeReferencyDeviceId {
337        fn is_alive(&self) -> bool {
338            !self.removed.load(core::sync::atomic::Ordering::Relaxed)
339        }
340    }
341
342    impl PartialEq<FakeWeakDeviceId<FakeReferencyDeviceId>> for FakeReferencyDeviceId {
343        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<FakeReferencyDeviceId>) -> bool {
344            self == other
345        }
346    }
347
348    impl InterfaceProperties<()> for FakeReferencyDeviceId {
349        fn id_matches(&self, _: &core::num::NonZeroU64) -> bool {
350            unimplemented!()
351        }
352
353        fn name_matches(&self, name: &str) -> bool {
354            name == Self::FAKE_NAME
355        }
356
357        fn device_class_matches(&self, _: &()) -> bool {
358            unimplemented!()
359        }
360    }
361
362    /// Marks a fake strong device id.
363    pub trait FakeStrongDeviceId:
364        StrongDeviceIdentifier<Weak = FakeWeakDeviceId<Self>> + 'static + Ord
365    {
366        /// Returns whether this ID is still alive.
367        ///
368        /// This is used by [`FakeWeakDeviceId`] to return `None` when trying to
369        /// upgrade back a `FakeStrongDeviceId`.
370        fn is_alive(&self) -> bool;
371    }
372
373    /// A device ID type that supports identifying more than one distinct
374    /// device.
375    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
376    #[allow(missing_docs)]
377    pub enum MultipleDevicesId {
378        A,
379        B,
380        C,
381    }
382
383    impl MultipleDevicesId {
384        /// Returns all variants.
385        pub fn all() -> [Self; 3] {
386            [Self::A, Self::B, Self::C]
387        }
388
389        fn fake_name(&self) -> &'static str {
390            match self {
391                MultipleDevicesId::A => "A",
392                MultipleDevicesId::B => "B",
393                MultipleDevicesId::C => "C",
394            }
395        }
396    }
397
398    impl DeviceIdentifier for MultipleDevicesId {
399        fn is_loopback(&self) -> bool {
400            false
401        }
402    }
403
404    impl StrongDeviceIdentifier for MultipleDevicesId {
405        type Weak = FakeWeakDeviceId<Self>;
406
407        fn downgrade(&self) -> Self::Weak {
408            FakeWeakDeviceId(self.clone())
409        }
410    }
411
412    impl FakeStrongDeviceId for MultipleDevicesId {
413        fn is_alive(&self) -> bool {
414            true
415        }
416    }
417
418    impl InterfaceProperties<()> for MultipleDevicesId {
419        fn id_matches(&self, _: &core::num::NonZeroU64) -> bool {
420            unimplemented!()
421        }
422
423        fn name_matches(&self, name: &str) -> bool {
424            self.fake_name() == name
425        }
426
427        fn device_class_matches(&self, _: &()) -> bool {
428            unimplemented!()
429        }
430    }
431
432    /// Holds per-device state for a [`MultipleDevicesId`].
433    #[derive(Default)]
434    pub struct MultipleDevicesIdState<T> {
435        a: T,
436        b: T,
437        c: T,
438    }
439
440    impl<T> MultipleDevicesIdState<T> {
441        /// Accesses the per-device state for the given device ID.
442        pub fn state(&self, device: &MultipleDevicesId) -> &T {
443            match device {
444                MultipleDevicesId::A => &self.a,
445                MultipleDevicesId::B => &self.b,
446                MultipleDevicesId::C => &self.c,
447            }
448        }
449    }
450
451    impl<S, Meta, D: StrongDeviceIdentifier> DeviceIdContext<AnyDevice> for FakeCoreCtx<S, Meta, D> {
452        type DeviceId = D;
453        type WeakDeviceId = D::Weak;
454    }
455
456    impl<S, Meta, D: StrongDeviceIdentifier> DeviceIdContext<AnyDevice>
457        for &mut FakeCoreCtx<S, Meta, D>
458    {
459        type DeviceId = D;
460        type WeakDeviceId = D::Weak;
461    }
462
463    impl PartialEq<FakeWeakDeviceId<MultipleDevicesId>> for MultipleDevicesId {
464        fn eq(&self, FakeWeakDeviceId(other): &FakeWeakDeviceId<MultipleDevicesId>) -> bool {
465            self == other
466        }
467    }
468}