netstack3_device/
id.rs

1// Copyright 2023 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 device identifier types.
6
7use alloc::sync::Arc;
8use core::fmt::{self, Debug};
9use core::hash::Hash;
10use core::num::NonZeroU64;
11
12use derivative::Derivative;
13use netstack3_base::sync::{DynDebugReferences, PrimaryRc, StrongRc};
14use netstack3_base::{
15    Device, DeviceIdentifier, InterfaceProperties, StrongDeviceIdentifier, WeakDeviceIdentifier,
16};
17
18use crate::blackhole::{BlackholeDevice, BlackholeDeviceId, BlackholeWeakDeviceId};
19use crate::internal::base::{
20    DeviceClassMatcher as _, DeviceIdAndNameMatcher as _, DeviceLayerTypes, OriginTracker,
21};
22use crate::internal::ethernet::EthernetLinkDevice;
23use crate::internal::loopback::{LoopbackDevice, LoopbackDeviceId, LoopbackWeakDeviceId};
24use crate::internal::pure_ip::{PureIpDevice, PureIpDeviceId, PureIpWeakDeviceId};
25use crate::internal::state::{BaseDeviceState, DeviceStateSpec, IpLinkDeviceState, WeakCookie};
26
27/// A weak ID identifying a device.
28///
29/// This device ID makes no claim about the live-ness of the underlying device.
30/// See [`DeviceId`] for a device ID that acts as a witness to the live-ness of
31/// a device.
32#[derive(Derivative)]
33#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
34#[allow(missing_docs)]
35pub enum WeakDeviceId<BT: DeviceLayerTypes> {
36    Ethernet(EthernetWeakDeviceId<BT>),
37    Loopback(LoopbackWeakDeviceId<BT>),
38    PureIp(PureIpWeakDeviceId<BT>),
39    Blackhole(BlackholeWeakDeviceId<BT>),
40}
41
42impl<BT: DeviceLayerTypes> PartialEq<DeviceId<BT>> for WeakDeviceId<BT> {
43    fn eq(&self, other: &DeviceId<BT>) -> bool {
44        <DeviceId<BT> as PartialEq<WeakDeviceId<BT>>>::eq(other, self)
45    }
46}
47
48impl<BT: DeviceLayerTypes> From<EthernetWeakDeviceId<BT>> for WeakDeviceId<BT> {
49    fn from(id: EthernetWeakDeviceId<BT>) -> WeakDeviceId<BT> {
50        WeakDeviceId::Ethernet(id)
51    }
52}
53
54impl<BT: DeviceLayerTypes> From<LoopbackWeakDeviceId<BT>> for WeakDeviceId<BT> {
55    fn from(id: LoopbackWeakDeviceId<BT>) -> WeakDeviceId<BT> {
56        WeakDeviceId::Loopback(id)
57    }
58}
59
60impl<BT: DeviceLayerTypes> From<PureIpWeakDeviceId<BT>> for WeakDeviceId<BT> {
61    fn from(id: PureIpWeakDeviceId<BT>) -> WeakDeviceId<BT> {
62        WeakDeviceId::PureIp(id)
63    }
64}
65
66impl<BT: DeviceLayerTypes> From<BlackholeWeakDeviceId<BT>> for WeakDeviceId<BT> {
67    fn from(id: BlackholeWeakDeviceId<BT>) -> WeakDeviceId<BT> {
68        WeakDeviceId::Blackhole(id)
69    }
70}
71
72impl<BT: DeviceLayerTypes> WeakDeviceId<BT> {
73    /// Attempts to upgrade the ID.
74    pub fn upgrade(&self) -> Option<DeviceId<BT>> {
75        for_any_device_id!(WeakDeviceId, self, id => id.upgrade().map(Into::into))
76    }
77
78    /// Creates a [`DebugReferences`] instance for this device.
79    pub fn debug_references(&self) -> DynDebugReferences {
80        for_any_device_id!(
81            WeakDeviceId,
82            self,
83            BaseWeakDeviceId { cookie } => cookie.weak_ref.debug_references().into_dyn()
84        )
85    }
86
87    /// Returns the bindings identifier associated with the device.
88    pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
89        for_any_device_id!(WeakDeviceId, self, id => id.bindings_id())
90    }
91}
92
93impl<BT: DeviceLayerTypes> DeviceIdentifier for WeakDeviceId<BT> {
94    fn is_loopback(&self) -> bool {
95        match self {
96            WeakDeviceId::Loopback(_) => true,
97            WeakDeviceId::Ethernet(_) | WeakDeviceId::PureIp(_) | WeakDeviceId::Blackhole(_) => {
98                false
99            }
100        }
101    }
102}
103
104impl<BT: DeviceLayerTypes> WeakDeviceIdentifier for WeakDeviceId<BT> {
105    type Strong = DeviceId<BT>;
106
107    fn upgrade(&self) -> Option<Self::Strong> {
108        self.upgrade()
109    }
110}
111
112impl<BT: DeviceLayerTypes> Debug for WeakDeviceId<BT> {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        for_any_device_id!(WeakDeviceId, self, id => Debug::fmt(id, f))
115    }
116}
117
118/// A strong ID identifying a device.
119///
120/// Holders may safely assume that the underlying device is "alive" in the sense
121/// that the device is still recognized by the stack. That is, operations that
122/// use this device ID will never fail as a result of "unrecognized device"-like
123/// errors.
124#[derive(Derivative)]
125#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
126#[allow(missing_docs)]
127pub enum DeviceId<BT: DeviceLayerTypes> {
128    Ethernet(EthernetDeviceId<BT>),
129    Loopback(LoopbackDeviceId<BT>),
130    PureIp(PureIpDeviceId<BT>),
131    Blackhole(BlackholeDeviceId<BT>),
132}
133
134/// Evaluates the expression for the given device_id, regardless of its variant.
135///
136/// This macro supports multiple forms.
137///
138/// Form #1: for_any_device_id!(device_id_enum, device_id,
139///          variable => expression)
140///   - `device_id_enum`: the type of device ID, either [`DeviceId`] or
141///     [`WeakDeviceId`].
142///   - `device_id`: The id to match on. I.e. a value of type `device_id_enum`.
143///   - `variable`: The local variable to bind the inner device id type to. This
144///     variable may be referenced from `expression`.
145///   - `expression`: The expression to evaluate against the given `device_id`.
146///
147/// Example: for_any_device_id!(DeviceId, my_device, id => println!({:?}, id))
148///
149/// Form #2: for_any_device_id!(device_id_enum, provider_trait, type_param,
150///          device_id, variable => expression)
151///   - `device_id_enum`, `device_id`, `variable`, and `expression`: Same as for
152///     Form #1.
153///   - `provider_trait`: The [`DeviceProvider`] trait.
154///   - `type_param`: The name of the type parameter to hold the values provided
155///     by `provider_trait`. This type parameter may be referenced from
156///     `expression`.
157///
158/// The second form is useful in situations where the compiler cannot infer a
159/// device type that differs for each variant of `device_id_enum`.
160///
161/// Example: for_any_device_id!(
162///     DeviceId,
163///     DeviceProvider,
164///     D,
165///     my_device, id => fn_with_id::<D>(id)
166/// )
167#[macro_export]
168macro_rules! for_any_device_id {
169    // Form #1
170    ($device_id_enum_type:ident, $device_id:expr, $variable:pat => $expression:expr) => {
171        match $device_id {
172            $device_id_enum_type::Loopback($variable) => $expression,
173            $device_id_enum_type::Ethernet($variable) => $expression,
174            $device_id_enum_type::PureIp($variable) => $expression,
175            $device_id_enum_type::Blackhole($variable) => $expression,
176        }
177    };
178    // Form #2
179    (
180        $device_id_enum_type:ident,
181        $provider_trait:ident,
182        $type_param:ident,
183        $device_id:expr, $variable:pat => $expression:expr) => {
184        match $device_id {
185            $device_id_enum_type::Loopback($variable) => {
186                type $type_param = <() as $provider_trait>::Loopback;
187                $expression
188            }
189            $device_id_enum_type::Ethernet($variable) => {
190                type $type_param = <() as $provider_trait>::Ethernet;
191                $expression
192            }
193            $device_id_enum_type::PureIp($variable) => {
194                type $type_param = <() as $provider_trait>::PureIp;
195                $expression
196            }
197            $device_id_enum_type::Blackhole($variable) => {
198                type $type_param = <() as $provider_trait>::Blackhole;
199                $expression
200            }
201        }
202    };
203}
204pub(crate) use crate::for_any_device_id;
205
206/// Provides the [`Device`] type for each device domain.
207pub trait DeviceProvider {
208    /// The [`Device`] type for Ethernet devices.
209    type Ethernet: Device;
210    /// The [`Device`] type for Loopback devices.
211    type Loopback: Device;
212    /// The [`Device`] type for pure IP devices.
213    type PureIp: Device;
214    /// The [`Device`] type for Blackhole devices.
215    type Blackhole: Device;
216}
217
218/// This implementation is used in the `for_any_device_id` macro.
219impl DeviceProvider for () {
220    type Ethernet = EthernetLinkDevice;
221    type Loopback = LoopbackDevice;
222    type PureIp = PureIpDevice;
223    type Blackhole = BlackholeDevice;
224}
225
226impl<BT: DeviceLayerTypes> Clone for DeviceId<BT> {
227    #[cfg_attr(feature = "instrumented", track_caller)]
228    fn clone(&self) -> Self {
229        for_any_device_id!(DeviceId, self, id => id.clone().into())
230    }
231}
232
233impl<BT: DeviceLayerTypes> PartialEq<WeakDeviceId<BT>> for DeviceId<BT> {
234    fn eq(&self, other: &WeakDeviceId<BT>) -> bool {
235        match (self, other) {
236            (DeviceId::Ethernet(strong), WeakDeviceId::Ethernet(weak)) => strong == weak,
237            (DeviceId::Loopback(strong), WeakDeviceId::Loopback(weak)) => strong == weak,
238            (DeviceId::PureIp(strong), WeakDeviceId::PureIp(weak)) => strong == weak,
239            (DeviceId::Blackhole(strong), WeakDeviceId::Blackhole(weak)) => strong == weak,
240            (DeviceId::Ethernet(_), _)
241            | (DeviceId::Loopback(_), _)
242            | (DeviceId::PureIp(_), _)
243            | (DeviceId::Blackhole(_), _) => false,
244        }
245    }
246}
247
248impl<BT: DeviceLayerTypes> PartialOrd for DeviceId<BT> {
249    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
250        Some(self.cmp(other))
251    }
252}
253
254impl<BT: DeviceLayerTypes> Ord for DeviceId<BT> {
255    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
256        // Assigns an arbitrary but orderable identifier, `u8`, to each variant
257        // of `DeviceId`.
258        fn discriminant<BT: DeviceLayerTypes>(d: &DeviceId<BT>) -> u8 {
259            match d {
260                // Each variant must provide a unique discriminant!
261                DeviceId::Ethernet(_) => 0,
262                DeviceId::Loopback(_) => 1,
263                DeviceId::PureIp(_) => 2,
264                DeviceId::Blackhole(_) => 3,
265            }
266        }
267        match (self, other) {
268            (DeviceId::Ethernet(me), DeviceId::Ethernet(other)) => me.cmp(other),
269            (DeviceId::Loopback(me), DeviceId::Loopback(other)) => me.cmp(other),
270            (DeviceId::PureIp(me), DeviceId::PureIp(other)) => me.cmp(other),
271            (DeviceId::Blackhole(me), DeviceId::Blackhole(other)) => me.cmp(other),
272            (me @ DeviceId::Ethernet(_), other)
273            | (me @ DeviceId::Loopback(_), other)
274            | (me @ DeviceId::PureIp(_), other)
275            | (me @ DeviceId::Blackhole(_), other) => discriminant(me).cmp(&discriminant(other)),
276        }
277    }
278}
279
280impl<BT: DeviceLayerTypes> From<EthernetDeviceId<BT>> for DeviceId<BT> {
281    fn from(id: EthernetDeviceId<BT>) -> DeviceId<BT> {
282        DeviceId::Ethernet(id)
283    }
284}
285
286impl<BT: DeviceLayerTypes> From<LoopbackDeviceId<BT>> for DeviceId<BT> {
287    fn from(id: LoopbackDeviceId<BT>) -> DeviceId<BT> {
288        DeviceId::Loopback(id)
289    }
290}
291
292impl<BT: DeviceLayerTypes> From<PureIpDeviceId<BT>> for DeviceId<BT> {
293    fn from(id: PureIpDeviceId<BT>) -> DeviceId<BT> {
294        DeviceId::PureIp(id)
295    }
296}
297
298impl<BT: DeviceLayerTypes> From<BlackholeDeviceId<BT>> for DeviceId<BT> {
299    fn from(id: BlackholeDeviceId<BT>) -> DeviceId<BT> {
300        DeviceId::Blackhole(id)
301    }
302}
303
304impl<BT: DeviceLayerTypes> DeviceId<BT> {
305    /// Downgrade to a [`WeakDeviceId`].
306    pub fn downgrade(&self) -> WeakDeviceId<BT> {
307        for_any_device_id!(DeviceId, self, id => id.downgrade().into())
308    }
309
310    /// Returns the bindings identifier associated with the device.
311    pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
312        for_any_device_id!(DeviceId, self, id => id.bindings_id())
313    }
314}
315
316impl<BT: DeviceLayerTypes> DeviceIdentifier for DeviceId<BT> {
317    fn is_loopback(&self) -> bool {
318        match self {
319            DeviceId::Loopback(_) => true,
320            DeviceId::Ethernet(_) | DeviceId::PureIp(_) | DeviceId::Blackhole(_) => false,
321        }
322    }
323}
324
325impl<BT: DeviceLayerTypes> StrongDeviceIdentifier for DeviceId<BT> {
326    type Weak = WeakDeviceId<BT>;
327
328    fn downgrade(&self) -> Self::Weak {
329        self.downgrade()
330    }
331}
332
333impl<BT: DeviceLayerTypes> Debug for DeviceId<BT> {
334    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335        for_any_device_id!(DeviceId, self, id => Debug::fmt(id, f))
336    }
337}
338
339impl<BT: DeviceLayerTypes> InterfaceProperties<BT::DeviceClass> for DeviceId<BT> {
340    fn id_matches(&self, id: &NonZeroU64) -> bool {
341        self.bindings_id().id_matches(id)
342    }
343
344    fn name_matches(&self, name: &str) -> bool {
345        self.bindings_id().name_matches(name)
346    }
347
348    fn device_class_matches(&self, device_class: &BT::DeviceClass) -> bool {
349        for_any_device_id!(
350            DeviceId,
351            self,
352            id => id.external_state().device_class_matches(device_class)
353        )
354    }
355}
356
357/// A base weak device identifier.
358///
359/// Allows multiple device implementations to share the same shape for
360/// maintaining reference identifiers.
361#[derive(Derivative)]
362#[derivative(Clone(bound = ""))]
363pub struct BaseWeakDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
364    // NB: This is not a tuple struct because regular structs play nicer with
365    // type aliases, which is how we use BaseDeviceId.
366    cookie: Arc<WeakCookie<T, BT>>,
367}
368
369impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq for BaseWeakDeviceId<T, BT> {
370    fn eq(&self, other: &Self) -> bool {
371        self.cookie.weak_ref.ptr_eq(&other.cookie.weak_ref)
372    }
373}
374
375impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Eq for BaseWeakDeviceId<T, BT> {}
376
377impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Hash for BaseWeakDeviceId<T, BT> {
378    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
379        self.cookie.weak_ref.hash(state)
380    }
381}
382
383impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BaseDeviceId<T, BT>>
384    for BaseWeakDeviceId<T, BT>
385{
386    fn eq(&self, other: &BaseDeviceId<T, BT>) -> bool {
387        <BaseDeviceId<T, BT> as PartialEq<BaseWeakDeviceId<T, BT>>>::eq(other, self)
388    }
389}
390
391impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BaseWeakDeviceId<T, BT> {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        let Self { cookie } = self;
394        write!(f, "Weak{}({:?})", T::DEBUG_TYPE, &cookie.bindings_id)
395    }
396}
397
398impl<T: DeviceStateSpec, BT: DeviceLayerTypes> DeviceIdentifier for BaseWeakDeviceId<T, BT> {
399    fn is_loopback(&self) -> bool {
400        T::IS_LOOPBACK
401    }
402}
403
404impl<T: DeviceStateSpec, BT: DeviceLayerTypes> WeakDeviceIdentifier for BaseWeakDeviceId<T, BT> {
405    type Strong = BaseDeviceId<T, BT>;
406
407    fn upgrade(&self) -> Option<Self::Strong> {
408        self.upgrade()
409    }
410}
411
412impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BaseWeakDeviceId<T, BT> {
413    /// Attempts to upgrade the ID to a strong ID, failing if the
414    /// device no longer exists.
415    pub fn upgrade(&self) -> Option<BaseDeviceId<T, BT>> {
416        let Self { cookie } = self;
417        cookie.weak_ref.upgrade().map(|rc| BaseDeviceId { rc })
418    }
419
420    /// Returns the bindings identifier associated with the device.
421    pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
422        &self.cookie.bindings_id
423    }
424}
425
426/// A base device identifier.
427///
428/// Allows multiple device implementations to share the same shape for
429/// maintaining reference identifiers.
430#[derive(Derivative)]
431#[derivative(Hash(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
432pub struct BaseDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
433    // NB: This is not a tuple struct because regular structs play nicer with
434    // type aliases, which is how we use BaseDeviceId.
435    rc: StrongRc<BaseDeviceState<T, BT>>,
436}
437
438impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Clone for BaseDeviceId<T, BT> {
439    #[cfg_attr(feature = "instrumented", track_caller)]
440    fn clone(&self) -> Self {
441        let Self { rc } = self;
442        Self { rc: StrongRc::clone(rc) }
443    }
444}
445
446impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BaseWeakDeviceId<T, BT>>
447    for BaseDeviceId<T, BT>
448{
449    fn eq(&self, BaseWeakDeviceId { cookie }: &BaseWeakDeviceId<T, BT>) -> bool {
450        let Self { rc: me_rc } = self;
451        StrongRc::weak_ptr_eq(me_rc, &cookie.weak_ref)
452    }
453}
454
455impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BasePrimaryDeviceId<T, BT>>
456    for BaseDeviceId<T, BT>
457{
458    fn eq(&self, BasePrimaryDeviceId { rc: other_rc }: &BasePrimaryDeviceId<T, BT>) -> bool {
459        let Self { rc: me_rc } = self;
460        PrimaryRc::ptr_eq(other_rc, me_rc)
461    }
462}
463
464impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialOrd for BaseDeviceId<T, BT> {
465    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
466        Some(self.cmp(other))
467    }
468}
469
470impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Ord for BaseDeviceId<T, BT> {
471    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
472        let Self { rc: me } = self;
473        let Self { rc: other } = other;
474
475        StrongRc::ptr_cmp(me, other)
476    }
477}
478
479impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BaseDeviceId<T, BT> {
480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481        let Self { rc } = self;
482        write!(f, "{}({:?})", T::DEBUG_TYPE, &rc.weak_cookie.bindings_id)
483    }
484}
485
486impl<T: DeviceStateSpec, BT: DeviceLayerTypes> DeviceIdentifier for BaseDeviceId<T, BT> {
487    fn is_loopback(&self) -> bool {
488        T::IS_LOOPBACK
489    }
490}
491
492impl<T: DeviceStateSpec, BT: DeviceLayerTypes> StrongDeviceIdentifier for BaseDeviceId<T, BT> {
493    type Weak = BaseWeakDeviceId<T, BT>;
494
495    fn downgrade(&self) -> Self::Weak {
496        self.downgrade()
497    }
498}
499
500impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BaseDeviceId<T, BT> {
501    /// Returns a reference to the device state.
502    ///
503    /// Requires an OriginTracker to ensure this is being access from the proper
504    /// context and disallow usage in bindings.
505    pub fn device_state(&self, tracker: &OriginTracker) -> &IpLinkDeviceState<T, BT> {
506        debug_assert_eq!(tracker, &self.rc.ip.origin);
507        &self.rc.ip
508    }
509
510    /// Returns a reference to the external state for the device.
511    pub fn external_state(&self) -> &T::External<BT> {
512        &self.rc.external_state
513    }
514
515    /// Returns the bindings identifier associated with the device.
516    pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
517        &self.rc.weak_cookie.bindings_id
518    }
519
520    /// Downgrades the ID to an [`EthernetWeakDeviceId`].
521    pub fn downgrade(&self) -> BaseWeakDeviceId<T, BT> {
522        let Self { rc } = self;
523        BaseWeakDeviceId { cookie: Arc::clone(&rc.weak_cookie) }
524    }
525}
526
527/// The primary reference to a device.
528pub struct BasePrimaryDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
529    // NB: This is not a tuple struct because regular structs play nicer with
530    // type aliases, which is how we use BaseDeviceId.
531    rc: PrimaryRc<BaseDeviceState<T, BT>>,
532}
533
534impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BasePrimaryDeviceId<T, BT> {
535    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536        let Self { rc } = self;
537        write!(f, "Primary{}({:?})", T::DEBUG_TYPE, &rc.weak_cookie.bindings_id)
538    }
539}
540
541impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BasePrimaryDeviceId<T, BT> {
542    /// Returns a strong clone of this primary ID.
543    #[cfg_attr(feature = "instrumented", track_caller)]
544    pub fn clone_strong(&self) -> BaseDeviceId<T, BT> {
545        let Self { rc } = self;
546        BaseDeviceId { rc: PrimaryRc::clone_strong(rc) }
547    }
548
549    pub(crate) fn new<F: FnOnce(BaseWeakDeviceId<T, BT>) -> IpLinkDeviceState<T, BT>>(
550        ip: F,
551        external_state: T::External<BT>,
552        bindings_id: BT::DeviceIdentifier,
553    ) -> Self {
554        Self {
555            rc: PrimaryRc::new_cyclic(move |weak_ref| {
556                let weak_cookie = Arc::new(WeakCookie { bindings_id, weak_ref });
557                let ip = ip(BaseWeakDeviceId { cookie: Arc::clone(&weak_cookie) });
558                BaseDeviceState { ip, external_state, weak_cookie }
559            }),
560        }
561    }
562
563    pub(crate) fn into_inner(self) -> PrimaryRc<BaseDeviceState<T, BT>> {
564        self.rc
565    }
566}
567
568/// A strong device ID identifying an ethernet device.
569///
570/// This device ID is like [`DeviceId`] but specifically for ethernet devices.
571pub type EthernetDeviceId<BT> = BaseDeviceId<EthernetLinkDevice, BT>;
572/// A weak device ID identifying an ethernet device.
573pub type EthernetWeakDeviceId<BT> = BaseWeakDeviceId<EthernetLinkDevice, BT>;
574/// The primary Ethernet device reference.
575pub type EthernetPrimaryDeviceId<BT> = BasePrimaryDeviceId<EthernetLinkDevice, BT>;
576
577#[cfg(any(test, feature = "testutils"))]
578mod testutil {
579    use super::*;
580
581    impl<BT: DeviceLayerTypes> TryFrom<DeviceId<BT>> for EthernetDeviceId<BT> {
582        type Error = DeviceId<BT>;
583        fn try_from(id: DeviceId<BT>) -> Result<EthernetDeviceId<BT>, DeviceId<BT>> {
584            match id {
585                DeviceId::Ethernet(id) => Ok(id),
586                DeviceId::Loopback(_) | DeviceId::PureIp(_) | DeviceId::Blackhole(_) => Err(id),
587            }
588        }
589    }
590
591    impl<BT: DeviceLayerTypes> DeviceId<BT> {
592        /// Extracts an ethernet device from self or panics.
593        pub fn unwrap_ethernet(self) -> EthernetDeviceId<BT> {
594            assert_matches::assert_matches!(self, DeviceId::Ethernet(e) => e)
595        }
596    }
597}