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