Skip to main content

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