Skip to main content

netstack3_core/
testutil.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
5//! Testing-related utilities.
6
7#![cfg(any(test, feature = "testutils"))]
8
9pub use netstack3_base::testutil::{FakeDeviceId, TestIpExt};
10pub use netstack3_filter::testutil::new_filter_egress_ip_packet;
11
12use alloc::borrow::ToOwned;
13use alloc::sync::Arc;
14use alloc::vec;
15use alloc::vec::Vec;
16
17use core::borrow::Borrow;
18use core::convert::Infallible as Never;
19use core::fmt::Debug;
20use core::hash::Hash;
21use core::ops::{Deref, DerefMut};
22use core::time::Duration;
23
24use derivative::Derivative;
25use net_types::ethernet::Mac;
26use net_types::ip::{
27    AddrSubnet, AddrSubnetEither, GenericOverIp, Ip, IpAddr, IpAddress, IpInvariant, IpVersion,
28    Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Mtu, Subnet, SubnetEither,
29};
30use net_types::{MulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
31use netstack3_base::sync::{DynDebugReferences, Mutex};
32use netstack3_base::testutil::{
33    AlwaysDefaultsSettingsContext, FakeAtomicInstant, FakeCryptoRng, FakeFrameCtx, FakeInstant,
34    FakeNetwork, FakeNetworkLinks, FakeNetworkSpec, FakeSocketWritableListener, FakeTimerCtx,
35    FakeTimerCtxExt, FakeTimerId, MonotonicIdentifier, TestAddrs, WithFakeFrameContext,
36    WithFakeTimerContext,
37};
38use netstack3_base::{
39    AddressResolutionFailed, CtxPair, DeferredResourceRemovalContext, EventContext,
40    InstantBindingsTypes, InstantContext, IpDeviceAddr, LinkDevice, LocalFrameDestination,
41    MarkDomain, Marks, MatcherBindingsTypes, NetworkParsingContext, NotFoundError,
42    ReferenceNotifiers, RemoveResourceResult, RemoveResourceResultWithContext, RngContext,
43    SocketDiagnosticsSeed, TimerBindingsTypes, TimerContext, TimerHandler, TxMetadataBindingsTypes,
44    WorkQueueReport,
45};
46use netstack3_datagram::PendingDatagramSocketError;
47use netstack3_device::ethernet::{
48    EthernetCreationProperties, EthernetDeviceEvent, EthernetDeviceId, EthernetLinkDevice,
49    EthernetWeakDeviceId, RecvEthernetFrameMeta,
50};
51use netstack3_device::loopback::{LoopbackCreationProperties, LoopbackDevice, LoopbackDeviceId};
52use netstack3_device::pure_ip::{PureIpDeviceId, PureIpWeakDeviceId};
53use netstack3_device::queue::{ReceiveQueueBindingsContext, TransmitQueueBindingsContext};
54use netstack3_device::socket::{
55    DeviceSocketBindingsContext, DeviceSocketTypes, ReceiveFrameError, SocketId,
56};
57use netstack3_device::testutil::IPV6_MIN_IMPLIED_MAX_FRAME_SIZE;
58use netstack3_device::{
59    self as device, DeviceBufferBindingsTypes, DeviceId, DeviceLayerEventDispatcher,
60    DeviceLayerStateTypes, DeviceLayerTypes, DeviceProvider, DeviceSendFrameError, WeakDeviceId,
61    for_any_device_id,
62};
63use netstack3_filter::testutil::NoOpSocketOpsFilter;
64use netstack3_filter::{FilterTimerId, SocketOpsFilter, SocketOpsFilterBindingContext};
65use netstack3_hashmap::HashMap;
66use netstack3_icmp_echo::{
67    IcmpEchoBindingsContext, IcmpEchoBindingsTypes, IcmpSocketId, ReceiveIcmpEchoError,
68};
69use netstack3_ip::device::{
70    IpDeviceConfiguration, IpDeviceConfigurationUpdate, IpDeviceEvent,
71    Ipv4DeviceConfigurationUpdate, Ipv6DeviceConfigurationUpdate,
72};
73use netstack3_ip::nud::{self, LinkResolutionContext, LinkResolutionNotifier};
74use netstack3_ip::raw::{
75    RawIpSocketId, RawIpSocketsBindingsContext, RawIpSocketsBindingsTypes, ReceivePacketError,
76};
77use netstack3_ip::{
78    self as ip, AddRouteError, AddableEntryEither, AddableMetric, DeviceIpLayerMetadata,
79    IpLayerEvent, IpLayerTimerId, IpRoutingBindingsTypes, MarksBindingsContext, RawMetric,
80    ResolveRouteError, ResolvedRoute, RoutableIpAddr, RouterAdvertisementEvent,
81};
82use netstack3_tcp::testutil::{ClientBuffers, ProvidedBuffers, RingBuffer, TestSendBuffer};
83use netstack3_tcp::{
84    BufferSizes, TcpBindingsTypes, TcpSocketDestructionContext, TcpSocketDiagnostics,
85};
86use netstack3_udp::{
87    ReceiveUdpError, UdpBindingsTypes, UdpPacketMeta, UdpReceiveBindingsContext, UdpSocketId,
88};
89use packet::{Buf, BufferMut};
90use zerocopy::SplitByteSlice;
91
92use crate::api::CoreApi;
93use crate::context::UnlockedCoreCtx;
94use crate::context::prelude::*;
95use crate::state::{StackState, StackStateBuilder};
96use crate::time::{TimerId, TimerIdInner};
97use crate::{BindingsContext, BindingsTypes, CoreTxMetadata, IpExt};
98
99/// The default interface routing metric for test interfaces.
100pub const DEFAULT_INTERFACE_METRIC: RawMetric = RawMetric(100);
101
102/// Context available during the execution of the netstack.
103pub type Ctx<BT> = CtxPair<StackState<BT>, BT>;
104
105/// Extensions to [`CtxPair`] when it holds a full stack state.
106pub trait CtxPairExt<BC: BindingsContext> {
107    /// Retrieves the core and bindings context, respectively.
108    ///
109    /// This function can be used to call into non-api core functions that want
110    /// a core context.
111    fn contexts(&mut self) -> (UnlockedCoreCtx<'_, BC>, &mut BC);
112
113    /// Retrieves a [`CoreApi`] from this context pair.
114    fn core_api(&mut self) -> CoreApi<'_, &mut BC> {
115        let (core_ctx, bindings_ctx) = self.contexts();
116        CoreApi::new(CtxPair { core_ctx, bindings_ctx })
117    }
118
119    /// Like [`CtxPairExt::contexts`], but retrieves only the core context.
120    fn core_ctx(&self) -> UnlockedCoreCtx<'_, BC>;
121
122    /// Retrieves a [`TestApi`] from this context pair.
123    fn test_api(&mut self) -> TestApi<'_, BC> {
124        let (core_ctx, bindings_ctx) = self.contexts();
125        TestApi(core_ctx, bindings_ctx)
126    }
127
128    /// Shortcut for [`FakeTimerCtxExt::trigger_next_timer`].
129    fn trigger_next_timer<Id>(&mut self) -> Option<Id>
130    where
131        BC: FakeTimerCtxExt<Id>,
132        for<'a> UnlockedCoreCtx<'a, BC>: TimerHandler<BC, Id>,
133    {
134        let (mut core_ctx, bindings_ctx) = self.contexts();
135        bindings_ctx.trigger_next_timer(&mut core_ctx)
136    }
137
138    /// Shortcut for [`FakeTimerCtxExt::trigger_timers_for`].
139    fn trigger_timers_for<Id>(&mut self, duration: Duration) -> Vec<Id>
140    where
141        BC: FakeTimerCtxExt<Id>,
142        for<'a> UnlockedCoreCtx<'a, BC>: TimerHandler<BC, Id>,
143    {
144        let (mut core_ctx, bindings_ctx) = self.contexts();
145        bindings_ctx.trigger_timers_for(duration, &mut core_ctx)
146    }
147
148    /// Shortcut for [`FaketimerCtx::trigger_timers_until_instant`].
149    fn trigger_timers_until_instant<Id>(&mut self, instant: FakeInstant) -> Vec<Id>
150    where
151        BC: FakeTimerCtxExt<Id>,
152        for<'a> UnlockedCoreCtx<'a, BC>: TimerHandler<BC, Id>,
153    {
154        let (mut core_ctx, bindings_ctx) = self.contexts();
155        bindings_ctx.trigger_timers_until_instant(instant, &mut core_ctx)
156    }
157
158    /// Shortcut for [`FakeTimerCtxExt::trigger_timers_until_and_expect_unordered`].
159    fn trigger_timers_until_and_expect_unordered<Id, I: IntoIterator<Item = Id>>(
160        &mut self,
161        instant: FakeInstant,
162        timers: I,
163    ) where
164        Id: Debug + Hash + Eq,
165        BC: FakeTimerCtxExt<Id>,
166        for<'a> UnlockedCoreCtx<'a, BC>: TimerHandler<BC, Id>,
167    {
168        let (mut core_ctx, bindings_ctx) = self.contexts();
169        bindings_ctx.trigger_timers_until_and_expect_unordered(instant, timers, &mut core_ctx)
170    }
171}
172
173impl<CC, BC> CtxPairExt<BC> for CtxPair<CC, BC>
174where
175    CC: Borrow<StackState<BC>>,
176    BC: BindingsContext,
177{
178    fn contexts(&mut self) -> (UnlockedCoreCtx<'_, BC>, &mut BC) {
179        let Self { core_ctx, bindings_ctx } = self;
180        (UnlockedCoreCtx::new(CC::borrow(core_ctx)), bindings_ctx)
181    }
182
183    fn core_ctx(&self) -> UnlockedCoreCtx<'_, BC> {
184        UnlockedCoreCtx::new(CC::borrow(&self.core_ctx))
185    }
186}
187
188/// An API struct for test utilities.
189pub struct TestApi<'a, BT: BindingsTypes>(UnlockedCoreCtx<'a, BT>, &'a mut BT);
190
191impl<'l, BC> TestApi<'l, BC>
192where
193    BC: BindingsContext,
194{
195    fn contexts(&mut self) -> (&mut UnlockedCoreCtx<'l, BC>, &mut BC) {
196        let Self(core_ctx, bindings_ctx) = self;
197        (core_ctx, bindings_ctx)
198    }
199
200    fn core_api(&mut self) -> CoreApi<'_, &mut BC> {
201        let (core_ctx, bindings_ctx) = self.contexts();
202        let core_ctx = core_ctx.as_owned();
203        CoreApi::new(CtxPair { core_ctx, bindings_ctx })
204    }
205
206    /// Joins the multicast group `multicast_addr` for `device`.
207    #[netstack3_macros::context_ip_bounds(A::Version, BC, crate)]
208    pub fn join_ip_multicast<A: IpAddress>(
209        &mut self,
210        device: &DeviceId<BC>,
211        multicast_addr: MulticastAddr<A>,
212    ) where
213        A::Version: IpExt,
214    {
215        let (core_ctx, bindings_ctx) = self.contexts();
216        ip::device::join_ip_multicast::<A::Version, _, _>(
217            core_ctx,
218            bindings_ctx,
219            device,
220            multicast_addr,
221        );
222    }
223
224    /// Leaves the multicast group `multicast_addr` for `device`.
225    #[netstack3_macros::context_ip_bounds(A::Version, BC, crate)]
226    pub fn leave_ip_multicast<A: IpAddress>(
227        &mut self,
228        device: &DeviceId<BC>,
229        multicast_addr: MulticastAddr<A>,
230    ) where
231        A::Version: IpExt,
232    {
233        let (core_ctx, bindings_ctx) = self.contexts();
234        ip::device::leave_ip_multicast::<A::Version, _, _>(
235            core_ctx,
236            bindings_ctx,
237            device,
238            multicast_addr,
239        );
240    }
241
242    /// Returns whether `device` is in the multicast group `addr`.
243    #[netstack3_macros::context_ip_bounds(A::Version, BC, crate)]
244    pub fn is_in_ip_multicast<A: IpAddress>(
245        &mut self,
246        device: &DeviceId<BC>,
247        addr: MulticastAddr<A>,
248    ) -> bool
249    where
250        A::Version: IpExt,
251    {
252        use ip::{
253            AddressStatus, IpDeviceIngressStateContext, IpLayerIpExt, Ipv4PresentAddressStatus,
254            Ipv6PresentAddressStatus,
255        };
256
257        let (core_ctx, _) = self.contexts();
258        let addr_status = IpDeviceIngressStateContext::<A::Version>::address_status_for_device(
259            core_ctx,
260            addr.into_specified(),
261            device,
262        );
263        let status = match addr_status {
264            AddressStatus::Present(p) => p,
265            AddressStatus::Unassigned => return false,
266        };
267        #[derive(GenericOverIp)]
268        #[generic_over_ip(I, Ip)]
269        struct Wrap<I: IpLayerIpExt>(I::AddressStatus);
270        A::Version::map_ip(
271            Wrap(status),
272            |Wrap(v4)| match v4 {
273                Ipv4PresentAddressStatus::Multicast => true,
274                Ipv4PresentAddressStatus::LimitedBroadcast
275                | Ipv4PresentAddressStatus::SubnetBroadcast
276                | Ipv4PresentAddressStatus::LoopbackSubnet
277                | Ipv4PresentAddressStatus::UnicastAssigned
278                | Ipv4PresentAddressStatus::UnicastTentative => false,
279            },
280            |Wrap(v6)| match v6 {
281                Ipv6PresentAddressStatus::Multicast => true,
282                Ipv6PresentAddressStatus::UnicastAssigned
283                | Ipv6PresentAddressStatus::UnicastTentative => false,
284            },
285        )
286    }
287
288    /// Receive an IP packet from a device.
289    ///
290    /// `receive_ip_packet` injects a packet directly at the IP layer for this
291    /// context.
292    pub fn receive_ip_packet<I: Ip, B: BufferMut>(
293        &mut self,
294        device: &DeviceId<BC>,
295        frame_dst: Option<LocalFrameDestination>,
296        buffer: B,
297    ) {
298        self.receive_ip_packet_with_marks::<I, B>(device, frame_dst, buffer, Default::default())
299    }
300
301    /// Receive an IP packet from a device with given marks.
302    ///
303    /// `receive_ip_packet_with_marks` injects a packet directly at the IP layer
304    /// for this context with the given marks.
305    pub fn receive_ip_packet_with_marks<I: Ip, B: BufferMut>(
306        &mut self,
307        device: &DeviceId<BC>,
308        frame_dst: Option<LocalFrameDestination>,
309        buffer: B,
310        marks: Marks,
311    ) {
312        self.receive_ip_packet_with_marks_and_context::<I, B>(
313            device,
314            frame_dst,
315            buffer,
316            marks,
317            NetworkParsingContext::default(),
318        )
319    }
320
321    /// Receive an IP packet from a device with given marks and parsing context.
322    ///
323    /// `receive_ip_packet_with_marks_and_context` injects a packet directly at
324    /// the IP layer for this context with the given marks and parsing context.
325    pub fn receive_ip_packet_with_marks_and_context<I: Ip, B: BufferMut>(
326        &mut self,
327        device: &DeviceId<BC>,
328        frame_dst: Option<LocalFrameDestination>,
329        buffer: B,
330        marks: Marks,
331        parsing_context: NetworkParsingContext,
332    ) {
333        let (core_ctx, bindings_ctx) = self.contexts();
334        match I::VERSION {
335            IpVersion::V4 => ip::receive_ipv4_packet(
336                core_ctx,
337                bindings_ctx,
338                device,
339                frame_dst,
340                DeviceIpLayerMetadata::with_marks(marks),
341                parsing_context,
342                buffer,
343            ),
344            IpVersion::V6 => ip::receive_ipv6_packet(
345                core_ctx,
346                bindings_ctx,
347                device,
348                frame_dst,
349                DeviceIpLayerMetadata::with_marks(marks),
350                parsing_context,
351                buffer,
352            ),
353        }
354    }
355
356    /// Receive an Ethernet frame from a device.
357    pub fn receive_ethernet_frame<B: BufferMut + Debug>(
358        &mut self,
359        device: &EthernetDeviceId<BC>,
360        buffer: B,
361    ) {
362        self.core_api().device::<EthernetLinkDevice>().receive_frame(
363            RecvEthernetFrameMeta {
364                device_id: device.clone(),
365                parsing_context: NetworkParsingContext::default(),
366            },
367            buffer,
368        );
369    }
370
371    /// Add a route directly to the forwarding table.
372    pub fn add_route(
373        &mut self,
374        entry: AddableEntryEither<DeviceId<BC>>,
375    ) -> Result<(), AddRouteError> {
376        let (core_ctx, _bindings_ctx) = self.contexts();
377        match entry {
378            AddableEntryEither::V4(entry) => ip::testutil::add_route::<Ipv4, _, _>(core_ctx, entry),
379            AddableEntryEither::V6(entry) => ip::testutil::add_route::<Ipv6, _, _>(core_ctx, entry),
380        }
381    }
382
383    /// Install rules, these rules will replace the rules currently installed.
384    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
385    pub fn set_rules<I: IpExt>(&mut self, rules: Vec<netstack3_ip::Rule<I, DeviceId<BC>, BC>>) {
386        let (core_ctx, _bindings_ctx) = self.contexts();
387        ip::testutil::set_rules(core_ctx, rules)
388    }
389
390    /// Resolves a route with a given source address.
391    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
392    pub fn resolve_route_with_src_addr<I: IpExt>(
393        &mut self,
394        src_ip: IpDeviceAddr<I::Addr>,
395        dst_ip: Option<RoutableIpAddr<I::Addr>>,
396    ) -> Result<ResolvedRoute<I, DeviceId<BC>>, ResolveRouteError> {
397        let (core_ctx, _bindings_ctx) = self.contexts();
398        ip::resolve_output_route_to_destination(
399            core_ctx,
400            None,
401            Some((src_ip, ip::NonLocalSrcAddrPolicy::Deny)),
402            dst_ip,
403            &Default::default(),
404        )
405    }
406
407    /// Resolves a route with a given mark.
408    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
409    pub fn resolve_route_with_marks<I: IpExt>(
410        &mut self,
411        dst_ip: Option<RoutableIpAddr<I::Addr>>,
412        marks: &Marks,
413    ) -> Result<ResolvedRoute<I, DeviceId<BC>>, ResolveRouteError> {
414        let (core_ctx, _bindings_ctx) = self.contexts();
415        ip::resolve_output_route_to_destination(core_ctx, None, None, dst_ip, marks)
416    }
417
418    /// Delete a route from the forwarding table, returning `Err` if no route
419    /// was found to be deleted.
420    pub fn del_routes_to_subnet(
421        &mut self,
422        subnet: net_types::ip::SubnetEither,
423    ) -> Result<(), NotFoundError> {
424        let (core_ctx, _bindings_ctx) = self.contexts();
425        match subnet {
426            SubnetEither::V4(subnet) => {
427                ip::testutil::del_routes_to_subnet::<Ipv4, _, _>(core_ctx, subnet)
428            }
429            SubnetEither::V6(subnet) => {
430                ip::testutil::del_routes_to_subnet::<Ipv6, _, _>(core_ctx, subnet)
431            }
432        }
433    }
434
435    /// Deletes all routes targeting `device`.
436    pub fn del_device_routes(&mut self, device: &DeviceId<BC>) {
437        let (core_ctx, _bindings_ctx) = self.contexts();
438        ip::testutil::del_device_routes::<Ipv4, _, _>(core_ctx, device);
439        ip::testutil::del_device_routes::<Ipv6, _, _>(core_ctx, device);
440    }
441
442    /// Removes all of the routes through the device, then removes the device.
443    pub fn clear_routes_and_remove_device<D: Into<DeviceId<BC>>>(&mut self, device: D) {
444        let device = device.into();
445        self.del_device_routes(&device);
446
447        for_any_device_id!(DeviceId, DeviceProvider, D, device,
448            device => match self.core_api().device::<D>().remove_device(device) {
449                RemoveResourceResult::Removed(_external_state) => {}
450                RemoveResourceResult::Deferred(_reference_receiver) => {
451                panic!("failed to remove device")
452            }
453        });
454    }
455
456    /// Enables or disables the device for IP version `I` and returns whether it
457    /// was enabled before.
458    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
459    pub fn set_ip_device_enabled<I: IpExt>(
460        &mut self,
461        device: &DeviceId<BC>,
462        enabled: bool,
463    ) -> bool {
464        let update =
465            IpDeviceConfigurationUpdate { ip_enabled: Some(enabled), ..Default::default() };
466        let prev =
467            self.core_api().device_ip::<I>().update_configuration(device, update.into()).unwrap();
468        prev.as_ref().ip_enabled.unwrap()
469    }
470
471    /// Enables `device`.
472    pub fn enable_device(&mut self, device: &DeviceId<BC>) {
473        let _was_enabled: bool = self.set_ip_device_enabled::<Ipv4>(device, true);
474        let _was_enabled: bool = self.set_ip_device_enabled::<Ipv6>(device, true);
475    }
476
477    /// Enables or disables IP packet unicast forwarding on `device`.
478    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
479    pub fn set_unicast_forwarding_enabled<I: IpExt>(
480        &mut self,
481        device: &DeviceId<BC>,
482        enabled: bool,
483    ) {
484        let _config = self
485            .core_api()
486            .device_ip::<I>()
487            .update_configuration(
488                device,
489                IpDeviceConfigurationUpdate {
490                    unicast_forwarding_enabled: Some(enabled),
491                    ..Default::default()
492                }
493                .into(),
494            )
495            .unwrap();
496    }
497
498    /// Enables or disables IP packet multicast forwarding on `device`.
499    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
500    pub fn set_multicast_forwarding_enabled<I: IpExt>(
501        &mut self,
502        device: &DeviceId<BC>,
503        enabled: bool,
504    ) {
505        let _config = self
506            .core_api()
507            .device_ip::<I>()
508            .update_configuration(
509                device,
510                IpDeviceConfigurationUpdate {
511                    multicast_forwarding_enabled: Some(enabled),
512                    ..Default::default()
513                }
514                .into(),
515            )
516            .unwrap();
517    }
518
519    /// Returns whether IP packet unicast forwarding is enabled on `device`.
520    #[netstack3_macros::context_ip_bounds(I, BC, crate)]
521    pub fn is_unicast_forwarding_enabled<I: IpExt>(&mut self, device: &DeviceId<BC>) -> bool {
522        let configuration = self.core_api().device_ip::<I>().get_configuration(device);
523        let IpDeviceConfiguration { unicast_forwarding_enabled, .. } = configuration.as_ref();
524        *unicast_forwarding_enabled
525    }
526
527    /// Adds a loopback device with the IPv4/IPv6 loopback addresses assigned.
528    pub fn add_loopback(&mut self) -> LoopbackDeviceId<BC>
529    where
530        <BC as DeviceLayerStateTypes>::DeviceIdentifier: Default,
531        <BC as DeviceLayerStateTypes>::LoopbackDeviceState: Default,
532    {
533        let loopback_id = self.core_api().device::<LoopbackDevice>().add_device_with_default_state(
534            LoopbackCreationProperties { mtu: Mtu::new(u32::MAX) },
535            DEFAULT_INTERFACE_METRIC,
536        );
537        let device_id: DeviceId<_> = loopback_id.clone().into();
538        self.enable_device(&device_id);
539
540        self.core_api()
541            .device_ip::<Ipv4>()
542            .add_ip_addr_subnet(
543                &device_id,
544                AddrSubnet::from_witness(Ipv4::LOOPBACK_ADDRESS, Ipv4::LOOPBACK_SUBNET.prefix())
545                    .unwrap(),
546            )
547            .unwrap();
548
549        self.core_api()
550            .device_ip::<Ipv6>()
551            .add_ip_addr_subnet(
552                &device_id,
553                AddrSubnet::from_witness(Ipv6::LOOPBACK_ADDRESS, Ipv6::LOOPBACK_SUBNET.prefix())
554                    .unwrap(),
555            )
556            .unwrap();
557        loopback_id
558    }
559}
560
561impl<'a> TestApi<'a, FakeBindingsCtx> {
562    /// Handles any pending frames and returns true if any frames that were in
563    /// the RX queue were processed.
564    pub fn handle_queued_rx_packets(&mut self) -> bool {
565        let mut handled = false;
566        loop {
567            let (_, bindings_ctx) = self.contexts();
568            let rx_available = core::mem::take(&mut bindings_ctx.state_mut().rx_available);
569            if rx_available.len() == 0 {
570                break handled;
571            }
572            handled = true;
573            for id in rx_available.into_iter() {
574                loop {
575                    match self.core_api().receive_queue().handle_queued_frames(&id) {
576                        WorkQueueReport::AllDone => break,
577                        WorkQueueReport::Pending => (),
578                    }
579                }
580            }
581        }
582    }
583}
584
585#[derive(Default)]
586/// Bindings context state held by [`FakeBindingsCtx`].
587pub struct FakeBindingsCtxState {
588    icmpv4_replies:
589        HashMap<IcmpSocketId<Ipv4, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>, Vec<Vec<u8>>>,
590    icmpv6_replies:
591        HashMap<IcmpSocketId<Ipv6, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>, Vec<Vec<u8>>>,
592    udpv4_received:
593        HashMap<UdpSocketId<Ipv4, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>, Vec<Vec<u8>>>,
594    udpv6_received:
595        HashMap<UdpSocketId<Ipv6, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>, Vec<Vec<u8>>>,
596    /// IDs with rx queue signaled available.
597    pub rx_available: Vec<LoopbackDeviceId<FakeBindingsCtx>>,
598    /// IDs with tx queue signaled available.
599    pub tx_available: Vec<DeviceId<FakeBindingsCtx>>,
600    /// Deferred resource removals.
601    #[cfg(loom)]
602    pub deferred_receivers: Vec<loom_notifiers::LoomReceiver>,
603}
604
605impl FakeBindingsCtxState {
606    pub(crate) fn udp_state_mut<I: IpExt>(
607        &mut self,
608    ) -> &mut HashMap<UdpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>, Vec<Vec<u8>>>
609    {
610        #[derive(GenericOverIp)]
611        #[generic_over_ip(I, Ip)]
612        struct Wrapper<'a, I: IpExt>(
613            &'a mut HashMap<
614                UdpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>,
615                Vec<Vec<u8>>,
616            >,
617        );
618        let Wrapper(map) = I::map_ip_out::<_, Wrapper<'_, I>>(
619            self,
620            |this| Wrapper(&mut this.udpv4_received),
621            |this| Wrapper(&mut this.udpv6_received),
622        );
623        map
624    }
625}
626
627/// Shorthand for [`Ctx`] with a [`FakeBindingsCtx`].
628pub type FakeCtx = Ctx<FakeBindingsCtx>;
629/// Shorthand for [`StackState`] that uses a [`FakeBindingsCtx`].
630pub type FakeCoreCtx = StackState<FakeBindingsCtx>;
631
632type InnerFakeBindingsCtx = netstack3_base::testutil::FakeBindingsCtx<
633    TimerId<FakeBindingsCtx>,
634    DispatchedEvent,
635    FakeBindingsCtxState,
636    DispatchedFrame,
637>;
638
639/// Test-only implementation of [`BindingsContext`].
640#[derive(Default, Clone)]
641pub struct FakeBindingsCtx(Arc<Mutex<InnerFakeBindingsCtx>>);
642
643/// A wrapper type that makes it easier to implement `Deref` (and optionally
644/// `DerefMut`) for a value that is protected by a lock.
645///
646/// The first field is the type that provides access to the inner value,
647/// probably a lock guard. The second and third fields are functions that, given
648/// the first field, provide shared and mutable access (respectively) to the
649/// inner value.
650// TODO(https://github.com/rust-lang/rust/issues/117108): Replace this with
651// mapped mutex guards once stable.
652struct Wrapper<S, Callback, CallbackMut>(S, Callback, CallbackMut);
653
654impl<T: ?Sized, S: Deref, Callback: for<'a> Fn(&'a <S as Deref>::Target) -> &'a T, CallbackMut>
655    Deref for Wrapper<S, Callback, CallbackMut>
656{
657    type Target = T;
658
659    fn deref(&self) -> &T {
660        let Self(guard, f, _) = self;
661        let target = guard.deref();
662        f(target)
663    }
664}
665
666impl<
667    T: ?Sized,
668    S: DerefMut,
669    Callback: for<'a> Fn(&'a <S as Deref>::Target) -> &'a T,
670    CallbackMut: for<'a> Fn(&'a mut <S as Deref>::Target) -> &'a mut T,
671> DerefMut for Wrapper<S, Callback, CallbackMut>
672{
673    fn deref_mut(&mut self) -> &mut T {
674        let Self(guard, _, f) = self;
675        let target = guard.deref_mut();
676        f(target)
677    }
678}
679
680impl FakeBindingsCtx {
681    fn with_inner<F: FnOnce(&InnerFakeBindingsCtx) -> O, O>(&self, f: F) -> O {
682        let Self(this) = self;
683        let locked = this.lock();
684        f(&*locked)
685    }
686
687    fn with_inner_mut<F: FnOnce(&mut InnerFakeBindingsCtx) -> O, O>(&self, f: F) -> O {
688        let Self(this) = self;
689        let mut locked = this.lock();
690        f(&mut *locked)
691    }
692
693    /// Gets the fake timer context.
694    pub fn timer_ctx(&self) -> impl Deref<Target = FakeTimerCtx<TimerId<Self>>> + '_ {
695        // NB: Helper function is required to satisfy lifetime requirements of
696        // borrow.
697        fn get_timers<'a>(
698            i: &'a InnerFakeBindingsCtx,
699        ) -> &'a FakeTimerCtx<TimerId<FakeBindingsCtx>> {
700            &i.timers
701        }
702        Wrapper(self.0.lock(), get_timers, ())
703    }
704
705    /// Returns a mutable reference guard to [`FakeBindingsCtxState`].
706    pub fn state_mut(&mut self) -> impl DerefMut<Target = FakeBindingsCtxState> + '_ {
707        // NB: Helper functions are required to satisfy lifetime requirements of
708        // borrow.
709        fn get_state<'a>(i: &'a InnerFakeBindingsCtx) -> &'a FakeBindingsCtxState {
710            &i.state
711        }
712        fn get_state_mut<'a>(i: &'a mut InnerFakeBindingsCtx) -> &'a mut FakeBindingsCtxState {
713            &mut i.state
714        }
715        Wrapper(self.0.lock(), get_state, get_state_mut)
716    }
717
718    /// Copy all ethernet frames sent so far.
719    ///
720    /// # Panics
721    ///
722    /// Panics if the there are non-Ethernet frames stored.
723    pub fn copy_ethernet_frames(
724        &mut self,
725    ) -> Vec<(EthernetWeakDeviceId<FakeBindingsCtx>, Vec<u8>)> {
726        self.with_inner_mut(|ctx| {
727            ctx.frames
728                .frames()
729                .iter()
730                .map(|(meta, frame)| match meta {
731                    DispatchedFrame::Ethernet(eth) => (eth.clone(), frame.clone()),
732                    DispatchedFrame::PureIp(ip) => panic!("unexpected IP packet {ip:?}: {frame:?}"),
733                })
734                .collect()
735        })
736    }
737
738    /// Take all ethernet frames sent so far.
739    ///
740    /// # Panics
741    ///
742    /// Panics if the there are non-Ethernet frames stored.
743    pub fn take_ethernet_frames(
744        &mut self,
745    ) -> Vec<(EthernetWeakDeviceId<FakeBindingsCtx>, Vec<u8>)> {
746        self.with_inner_mut(|ctx| {
747            ctx.frames
748                .take_frames()
749                .into_iter()
750                .map(|(meta, frame)| match meta {
751                    DispatchedFrame::Ethernet(eth) => (eth, frame),
752                    DispatchedFrame::PureIp(ip) => panic!("unexpected IP packet {ip:?}: {frame:?}"),
753                })
754                .collect()
755        })
756    }
757
758    /// Take all IP frames sent so far.
759    ///
760    /// # Panics
761    ///
762    /// Panics if the there are non-IP frames stored.
763    pub fn take_ip_frames(&mut self) -> Vec<(PureIpDeviceAndIpVersion<FakeBindingsCtx>, Vec<u8>)> {
764        self.with_inner_mut(|ctx| {
765            ctx.frames
766                .take_frames()
767                .into_iter()
768                .map(|(meta, frame)| match meta {
769                    DispatchedFrame::Ethernet(eth) => {
770                        panic!("unexpected Ethernet frame {eth:?}: {frame:?}")
771                    }
772                    DispatchedFrame::PureIp(ip) => (ip, frame),
773                })
774                .collect()
775        })
776    }
777
778    /// Takes all the events stored in the fake context.
779    pub fn take_events(&mut self) -> Vec<DispatchedEvent> {
780        self.with_inner_mut(|ctx| ctx.events.take())
781    }
782
783    /// Takes all the received ICMP replies for a given `conn`.
784    pub fn take_icmp_replies<I: IpExt>(
785        &mut self,
786        conn: &IcmpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>,
787    ) -> Vec<Vec<u8>> {
788        I::map_ip_in(
789            (IpInvariant(self), conn),
790            |(IpInvariant(this), conn)| this.state_mut().icmpv4_replies.remove(conn),
791            |(IpInvariant(this), conn)| this.state_mut().icmpv6_replies.remove(conn),
792        )
793        .unwrap_or_else(Vec::default)
794    }
795
796    /// Takes all received UDP frames from the fake bindings context.
797    pub fn take_udp_received<I: IpExt>(
798        &mut self,
799        conn: &UdpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>,
800    ) -> Vec<Vec<u8>> {
801        self.state_mut().udp_state_mut::<I>().remove(conn).unwrap_or_else(Vec::default)
802    }
803
804    /// Seed the RNG.
805    pub fn seed_rng(&self, seed: u128) {
806        self.with_inner_mut(|ctx| {
807            ctx.rng = FakeCryptoRng::new_xorshift(seed);
808        })
809    }
810
811    /// Moves the fake clock forward by `duration`. Doesn't trigger any timers.
812    pub fn sleep(&self, duration: Duration) {
813        self.with_inner_mut(|ctx| ctx.timers.instant.sleep(duration));
814    }
815}
816
817impl MatcherBindingsTypes for FakeBindingsCtx {
818    type DeviceClass = ();
819    type BindingsPacketMatcher = Never;
820}
821
822impl DeviceBufferBindingsTypes for FakeBindingsCtx {
823    type TxBuffer = packet::Buf<Vec<u8>>;
824    type TxAllocator = netstack3_device::queue::BufVecU8Allocator;
825}
826
827impl SocketOpsFilterBindingContext<DeviceId<FakeBindingsCtx>> for FakeBindingsCtx {
828    fn socket_ops_filter(&self) -> impl SocketOpsFilter<DeviceId<FakeBindingsCtx>> {
829        NoOpSocketOpsFilter
830    }
831}
832
833impl WithFakeTimerContext<TimerId<FakeBindingsCtx>> for FakeBindingsCtx {
834    fn with_fake_timer_ctx<O, F: FnOnce(&FakeTimerCtx<TimerId<FakeBindingsCtx>>) -> O>(
835        &self,
836        f: F,
837    ) -> O {
838        self.with_inner(|ctx| f(&ctx.timers))
839    }
840
841    fn with_fake_timer_ctx_mut<O, F: FnOnce(&mut FakeTimerCtx<TimerId<FakeBindingsCtx>>) -> O>(
842        &mut self,
843        f: F,
844    ) -> O {
845        self.with_inner_mut(|ctx| f(&mut ctx.timers))
846    }
847}
848
849impl WithFakeFrameContext<DispatchedFrame> for FakeBindingsCtx {
850    fn with_fake_frame_ctx_mut<O, F: FnOnce(&mut FakeFrameCtx<DispatchedFrame>) -> O>(
851        &mut self,
852        f: F,
853    ) -> O {
854        self.with_inner_mut(|ctx| f(&mut ctx.frames))
855    }
856}
857
858impl InstantBindingsTypes for FakeBindingsCtx {
859    type Instant = FakeInstant;
860    type AtomicInstant = FakeAtomicInstant;
861}
862
863impl InstantContext for FakeBindingsCtx {
864    fn now(&self) -> FakeInstant {
865        self.with_inner(|ctx| ctx.now())
866    }
867}
868
869impl TimerBindingsTypes for FakeBindingsCtx {
870    type Timer = <FakeTimerCtx<TimerId<Self>> as TimerBindingsTypes>::Timer;
871    type DispatchId = TimerId<Self>;
872    type UniqueTimerId = <FakeTimerCtx<TimerId<Self>> as TimerBindingsTypes>::UniqueTimerId;
873}
874
875impl TimerContext for FakeBindingsCtx {
876    fn new_timer(&mut self, id: Self::DispatchId) -> Self::Timer {
877        self.with_inner_mut(|ctx| ctx.new_timer(id))
878    }
879
880    fn schedule_timer_instant(
881        &mut self,
882        time: Self::Instant,
883        timer: &mut Self::Timer,
884    ) -> Option<Self::Instant> {
885        // Filter out conntrack GC timers. We don't need conntrack GC in most
886        // tests, and this causes issues with tests that are expecting the
887        // netstack to quiesce.
888        match timer.dispatch_id.0 {
889            TimerIdInner::IpLayer(IpLayerTimerId::FilterTimerv4(FilterTimerId::ConntrackGc(_)))
890            | TimerIdInner::IpLayer(IpLayerTimerId::FilterTimerv6(FilterTimerId::ConntrackGc(_))) =>
891            {
892                return None;
893            }
894            _ => {}
895        }
896        self.with_inner_mut(|ctx| ctx.schedule_timer_instant(time, timer))
897    }
898
899    fn cancel_timer(&mut self, timer: &mut Self::Timer) -> Option<Self::Instant> {
900        self.with_inner_mut(|ctx| ctx.cancel_timer(timer))
901    }
902
903    fn scheduled_instant(&self, timer: &mut Self::Timer) -> Option<Self::Instant> {
904        self.with_inner_mut(|ctx| ctx.scheduled_instant(timer))
905    }
906
907    fn unique_timer_id(&self, timer: &Self::Timer) -> Self::UniqueTimerId {
908        self.with_inner_mut(|ctx| ctx.unique_timer_id(timer))
909    }
910}
911
912impl TxMetadataBindingsTypes for FakeBindingsCtx {
913    type TxMetadata = CoreTxMetadata<Self>;
914}
915
916impl RngContext for FakeBindingsCtx {
917    type Rng<'a> = FakeCryptoRng;
918
919    fn rng(&mut self) -> Self::Rng<'_> {
920        let Self(this) = self;
921        this.lock().rng()
922    }
923}
924
925impl<T: Into<DispatchedEvent>> EventContext<T> for FakeBindingsCtx {
926    fn on_event(&mut self, event: T) {
927        self.with_inner_mut(|ctx| ctx.events.on_event(event.into()))
928    }
929}
930
931impl TcpBindingsTypes for FakeBindingsCtx {
932    type ReceiveBuffer = Arc<Mutex<RingBuffer>>;
933
934    type SendBuffer = TestSendBuffer;
935
936    type ReturnedBuffers = ClientBuffers;
937
938    type ListenerNotifierOrProvidedBuffers = ProvidedBuffers;
939
940    fn new_passive_open_buffers(
941        buffer_sizes: BufferSizes,
942    ) -> (Self::ReceiveBuffer, Self::SendBuffer, Self::ReturnedBuffers) {
943        let client = ClientBuffers::new(buffer_sizes);
944        (
945            Arc::clone(&client.receive),
946            TestSendBuffer::new(Arc::clone(&client.send), RingBuffer::default()),
947            client,
948        )
949    }
950}
951
952impl IpRoutingBindingsTypes for FakeBindingsCtx {
953    type RoutingTableId = ();
954}
955
956impl MarksBindingsContext for FakeBindingsCtx {
957    fn marks_to_keep_on_egress() -> &'static [MarkDomain] {
958        const MARKS: [MarkDomain; 1] = [MarkDomain::Mark1];
959        &MARKS
960    }
961
962    fn marks_to_set_on_ingress() -> &'static [MarkDomain] {
963        const MARKS: [MarkDomain; 1] = [MarkDomain::Mark2];
964        &MARKS
965    }
966}
967
968#[cfg(not(loom))]
969mod fake_notifiers {
970    use core::convert::Infallible as Never;
971
972    use super::*;
973
974    impl ReferenceNotifiers for FakeBindingsCtx {
975        type ReferenceReceiver<T: 'static> = Never;
976
977        type ReferenceNotifier<T: Send + 'static> = Never;
978
979        fn new_reference_notifier<T: Send + 'static>(
980            debug_references: DynDebugReferences,
981        ) -> (Self::ReferenceNotifier<T>, Self::ReferenceReceiver<T>) {
982            // NB: We don't want deferred destruction in core tests. These are
983            // always single-threaded and single-task, and we want to encourage
984            // explicit cleanup.
985            panic!(
986                "FakeBindingsCtx can't create deferred reference notifiers for type {}: \
987                debug_references={debug_references:?}",
988                core::any::type_name::<T>()
989            );
990        }
991    }
992
993    impl DeferredResourceRemovalContext for FakeBindingsCtx {
994        fn defer_removal<T: Send + 'static>(&mut self, receiver: Self::ReferenceReceiver<T>) {
995            match receiver {}
996        }
997    }
998}
999
1000/// Implements the notifier methods for loom tests, which use multiple threads
1001/// and hence need to handle notifiers.
1002#[cfg(loom)]
1003mod loom_notifiers {
1004    use super::*;
1005
1006    use core::sync::atomic::{self, AtomicBool};
1007    use netstack3_sync::rc::Notifier;
1008
1009    #[derive(Debug)]
1010    pub struct LoomNotifier(Arc<AtomicBool>);
1011
1012    #[derive(Debug)]
1013    pub struct LoomReceiver {
1014        pub debug_refs: DynDebugReferences,
1015        pub signal: Arc<AtomicBool>,
1016    }
1017
1018    impl LoomReceiver {
1019        #[track_caller]
1020        pub fn assert_signalled(&self) {
1021            let Self { debug_refs, signal } = self;
1022            assert!(signal.load(atomic::Ordering::SeqCst), "pending references: {debug_refs:?}")
1023        }
1024    }
1025
1026    impl<T> Notifier<T> for LoomNotifier {
1027        fn notify(&mut self, _data: T) {
1028            let Self(signal) = self;
1029            signal.store(true, atomic::Ordering::SeqCst);
1030        }
1031    }
1032
1033    impl ReferenceNotifiers for FakeBindingsCtx {
1034        type ReferenceReceiver<T: 'static> = LoomReceiver;
1035        type ReferenceNotifier<T: Send + 'static> = LoomNotifier;
1036
1037        fn new_reference_notifier<T: Send + 'static>(
1038            debug_refs: DynDebugReferences,
1039        ) -> (Self::ReferenceNotifier<T>, Self::ReferenceReceiver<T>) {
1040            let signal = Arc::new(AtomicBool::default());
1041            (LoomNotifier(Arc::clone(&signal)), LoomReceiver { debug_refs, signal })
1042        }
1043    }
1044
1045    impl DeferredResourceRemovalContext for FakeBindingsCtx {
1046        fn defer_removal<T: Send + 'static>(&mut self, receiver: Self::ReferenceReceiver<T>) {
1047            self.state_mut().deferred_receivers.push(receiver);
1048        }
1049    }
1050}
1051
1052/// A link resolution notifier that ignores all notifications.
1053#[derive(Debug)]
1054pub struct NoOpLinkResolutionNotifier;
1055
1056impl<D: LinkDevice> LinkResolutionContext<D> for FakeBindingsCtx {
1057    type Notifier = NoOpLinkResolutionNotifier;
1058}
1059
1060impl<D: LinkDevice> LinkResolutionNotifier<D> for NoOpLinkResolutionNotifier {
1061    type Observer = ();
1062
1063    fn new() -> (Self, Self::Observer) {
1064        (NoOpLinkResolutionNotifier, ())
1065    }
1066
1067    fn notify(self, _result: Result<UnicastAddr<D::Address>, AddressResolutionFailed>) {}
1068}
1069
1070#[derive(Clone)]
1071struct DeviceConfig {
1072    mac: UnicastAddr<Mac>,
1073    addr_subnet: Option<AddrSubnetEither>,
1074    ipv4_config: Option<Ipv4DeviceConfigurationUpdate>,
1075    ipv6_config: Option<Ipv6DeviceConfigurationUpdate>,
1076}
1077
1078/// A builder for `FakeCtx`s.
1079///
1080/// A `FakeCtxBuilder` is capable of storing the configuration of a network
1081/// stack including forwarding table entries, devices and their assigned
1082/// addresses and configurations, ARP table entries, etc. It can be built using
1083/// `build`, producing a `FakeCtx` with all of the appropriate state configured.
1084#[derive(Clone, Default)]
1085pub struct FakeCtxBuilder {
1086    devices: Vec<DeviceConfig>,
1087    // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1088    arp_table_entries: Vec<(usize, SpecifiedAddr<Ipv4Addr>, UnicastAddr<Mac>)>,
1089    ndp_table_entries: Vec<(usize, UnicastAddr<Ipv6Addr>, UnicastAddr<Mac>)>,
1090    // usize refers to index into devices Vec.
1091    device_routes: Vec<(SubnetEither, usize)>,
1092}
1093
1094impl FakeCtxBuilder {
1095    /// Construct a `FakeCtxBuilder` from a `TestAddrs`.
1096    pub fn with_addrs<A: IpAddress>(addrs: TestAddrs<A>) -> FakeCtxBuilder {
1097        assert!(addrs.subnet.contains(&addrs.local_ip));
1098        assert!(addrs.subnet.contains(&addrs.remote_ip));
1099
1100        let mut builder = FakeCtxBuilder::default();
1101        builder.devices.push(DeviceConfig {
1102            mac: addrs.local_mac,
1103            addr_subnet: Some(
1104                AddrSubnetEither::new(addrs.local_ip.get().into(), addrs.subnet.prefix()).unwrap(),
1105            ),
1106            ipv4_config: None,
1107            ipv6_config: None,
1108        });
1109
1110        match addrs.remote_ip.into() {
1111            IpAddr::V4(ip) => builder.arp_table_entries.push((0, ip, addrs.remote_mac)),
1112            IpAddr::V6(ip) => builder.ndp_table_entries.push((
1113                0,
1114                UnicastAddr::new(ip.get()).unwrap(),
1115                addrs.remote_mac,
1116            )),
1117        };
1118
1119        // Even with fixed ipv4 address we can have IPv6 link local addresses
1120        // pre-cached.
1121        builder.ndp_table_entries.push((
1122            0,
1123            addrs.remote_mac.to_ipv6_link_local().addr().get(),
1124            addrs.remote_mac,
1125        ));
1126
1127        builder.device_routes.push((addrs.subnet.into(), 0));
1128        builder
1129    }
1130
1131    /// Add a device.
1132    ///
1133    /// `add_device` returns a key which can be used to refer to the device in
1134    /// future calls to `add_arp_table_entry` and `add_device_route`.
1135    pub fn add_device(&mut self, mac: UnicastAddr<Mac>) -> usize {
1136        let idx = self.devices.len();
1137        self.devices.push(DeviceConfig {
1138            mac,
1139            addr_subnet: None,
1140            ipv4_config: None,
1141            ipv6_config: None,
1142        });
1143        idx
1144    }
1145
1146    /// Add a device with an IPv4 and IPv6 configuration.
1147    ///
1148    /// `add_device_with_config` is like `add_device`, except that it takes an
1149    /// IPv4 and IPv6 configuration to apply to the device when it is enabled.
1150    pub fn add_device_with_config(
1151        &mut self,
1152        mac: UnicastAddr<Mac>,
1153        ipv4_config: Ipv4DeviceConfigurationUpdate,
1154        ipv6_config: Ipv6DeviceConfigurationUpdate,
1155    ) -> usize {
1156        let idx = self.devices.len();
1157        self.devices.push(DeviceConfig {
1158            mac,
1159            addr_subnet: None,
1160            ipv4_config: Some(ipv4_config),
1161            ipv6_config: Some(ipv6_config),
1162        });
1163        idx
1164    }
1165
1166    /// Add a device with an associated IP address.
1167    ///
1168    /// `add_device_with_ip` is like `add_device`, except that it takes an
1169    /// associated IP address and subnet to assign to the device.
1170    pub fn add_device_with_ip<A: IpAddress>(
1171        &mut self,
1172        mac: UnicastAddr<Mac>,
1173        ip: A,
1174        subnet: Subnet<A>,
1175    ) -> usize {
1176        assert!(subnet.contains(&ip));
1177        let idx = self.devices.len();
1178        self.devices.push(DeviceConfig {
1179            mac,
1180            addr_subnet: Some(AddrSubnetEither::new(ip.into(), subnet.prefix()).unwrap()),
1181            ipv4_config: None,
1182            ipv6_config: None,
1183        });
1184        self.device_routes.push((subnet.into(), idx));
1185        idx
1186    }
1187
1188    /// Add a device with an associated IP address and a particular IPv4 and
1189    /// IPv6 configuration.
1190    ///
1191    /// `add_device_with_ip_and_config` is like `add_device`, except that it
1192    /// takes an associated IP address and subnet to assign to the device, as
1193    /// well as IPv4 and IPv6 configurations to apply to the device when it is
1194    /// enabled.
1195    pub fn add_device_with_ip_and_config<A: IpAddress>(
1196        &mut self,
1197        mac: UnicastAddr<Mac>,
1198        ip: A,
1199        subnet: Subnet<A>,
1200        ipv4_config: Ipv4DeviceConfigurationUpdate,
1201        ipv6_config: Ipv6DeviceConfigurationUpdate,
1202    ) -> usize {
1203        assert!(subnet.contains(&ip));
1204        let idx = self.devices.len();
1205        self.devices.push(DeviceConfig {
1206            mac,
1207            addr_subnet: Some(AddrSubnetEither::new(ip.into(), subnet.prefix()).unwrap()),
1208            ipv4_config: Some(ipv4_config),
1209            ipv6_config: Some(ipv6_config),
1210        });
1211        self.device_routes.push((subnet.into(), idx));
1212        idx
1213    }
1214
1215    /// Add an ARP table entry for a device's ARP table.
1216    pub fn add_arp_table_entry(
1217        &mut self,
1218        device: usize,
1219        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1220        ip: SpecifiedAddr<Ipv4Addr>,
1221        mac: UnicastAddr<Mac>,
1222    ) {
1223        self.arp_table_entries.push((device, ip, mac));
1224    }
1225
1226    /// Add an NDP table entry for a device's NDP table.
1227    pub fn add_ndp_table_entry(
1228        &mut self,
1229        device: usize,
1230        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1231        ip: UnicastAddr<Ipv6Addr>,
1232        mac: UnicastAddr<Mac>,
1233    ) {
1234        self.ndp_table_entries.push((device, ip, mac));
1235    }
1236
1237    /// Add either an NDP entry (if IPv6) or ARP entry (if IPv4) to a
1238    /// `FakeCtxBuilder`.
1239    pub fn add_arp_or_ndp_table_entry<A: IpAddress>(
1240        &mut self,
1241        device: usize,
1242        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1243        ip: SpecifiedAddr<A>,
1244        mac: UnicastAddr<Mac>,
1245    ) {
1246        match ip.into() {
1247            IpAddr::V4(ip) => self.add_arp_table_entry(device, ip, mac),
1248            IpAddr::V6(ip) => {
1249                self.add_ndp_table_entry(device, UnicastAddr::new(ip.get()).unwrap(), mac)
1250            }
1251        }
1252    }
1253
1254    /// Builds a `Ctx` from the present configuration with a default dispatcher.
1255    pub fn build(self) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1256        self.build_with_modifications(|_| {})
1257    }
1258
1259    /// `build_with_modifications` is equivalent to `build`, except that after
1260    /// the `StackStateBuilder` is initialized, it is passed to `f` for further
1261    /// modification before the `Ctx` is constructed.
1262    pub fn build_with_modifications<F: FnOnce(&mut StackStateBuilder)>(
1263        self,
1264        f: F,
1265    ) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1266        let mut stack_builder = StackStateBuilder::default();
1267        f(&mut stack_builder);
1268        self.build_with(stack_builder)
1269    }
1270
1271    /// Build a `Ctx` from the present configuration with a caller-provided
1272    /// dispatcher and `StackStateBuilder`.
1273    pub fn build_with(
1274        self,
1275        state_builder: StackStateBuilder,
1276    ) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1277        let mut ctx = Ctx::new_with_builder(state_builder);
1278
1279        let FakeCtxBuilder { devices, arp_table_entries, ndp_table_entries, device_routes } = self;
1280        let idx_to_device_id: Vec<_> = devices
1281            .into_iter()
1282            .map(|DeviceConfig { mac, addr_subnet: ip_and_subnet, ipv4_config, ipv6_config }| {
1283                let eth_id =
1284                    ctx.core_api().device::<EthernetLinkDevice>().add_device_with_default_state(
1285                        EthernetCreationProperties {
1286                            mac: mac,
1287                            max_frame_size: IPV6_MIN_IMPLIED_MAX_FRAME_SIZE,
1288                            tx_offload_spec: Default::default(),
1289                        },
1290                        DEFAULT_INTERFACE_METRIC,
1291                    );
1292                let id = eth_id.clone().into();
1293                if let Some(ipv4_config) = ipv4_config {
1294                    let _previous = ctx
1295                        .core_api()
1296                        .device_ip::<Ipv4>()
1297                        .update_configuration(&id, ipv4_config)
1298                        .unwrap();
1299                }
1300                if let Some(ipv6_config) = ipv6_config {
1301                    let _previous = ctx
1302                        .core_api()
1303                        .device_ip::<Ipv6>()
1304                        .update_configuration(&id, ipv6_config)
1305                        .unwrap();
1306                }
1307                ctx.test_api().enable_device(&id);
1308                match ip_and_subnet {
1309                    Some(addr_sub) => {
1310                        ctx.core_api().device_ip_any().add_ip_addr_subnet(&id, addr_sub).unwrap();
1311                    }
1312                    None => {}
1313                }
1314                eth_id
1315            })
1316            .collect();
1317        for (idx, ip, mac) in arp_table_entries {
1318            let device = &idx_to_device_id[idx];
1319            ctx.core_api()
1320                .neighbor::<Ipv4, EthernetLinkDevice>()
1321                .insert_static_entry(&device, ip.get(), mac)
1322                .expect("error inserting static ARP entry");
1323        }
1324        for (idx, ip, mac) in ndp_table_entries {
1325            let device = &idx_to_device_id[idx];
1326            ctx.core_api()
1327                .neighbor::<Ipv6, EthernetLinkDevice>()
1328                .insert_static_entry(&device, ip.get(), mac)
1329                .expect("error inserting static NDP entry");
1330        }
1331
1332        for (subnet, idx) in device_routes {
1333            let device = &idx_to_device_id[idx];
1334            ctx.test_api()
1335                .add_route(AddableEntryEither::without_gateway(
1336                    subnet,
1337                    device.clone().into(),
1338                    AddableMetric::ExplicitMetric(RawMetric(0)),
1339                ))
1340                .expect("add device route");
1341        }
1342
1343        (ctx, idx_to_device_id)
1344    }
1345}
1346
1347/// The fake network spec to use in integration tests.
1348///
1349/// It creates an Ethernet network.
1350pub enum FakeCtxNetworkSpec {}
1351
1352impl FakeNetworkSpec for FakeCtxNetworkSpec {
1353    type Context = FakeCtx;
1354    type TimerId = TimerId<FakeBindingsCtx>;
1355    type SendMeta = DispatchedFrame;
1356    type RecvMeta = EthernetDeviceId<FakeBindingsCtx>;
1357    fn handle_frame(ctx: &mut FakeCtx, device_id: Self::RecvMeta, data: Buf<Vec<u8>>) {
1358        ctx.core_api().device::<EthernetLinkDevice>().receive_frame(
1359            RecvEthernetFrameMeta { device_id, parsing_context: NetworkParsingContext::default() },
1360            data,
1361        )
1362    }
1363    fn handle_timer(ctx: &mut FakeCtx, dispatch: Self::TimerId, timer: FakeTimerId) {
1364        ctx.core_api().handle_timer(dispatch, timer)
1365    }
1366    fn process_queues(ctx: &mut FakeCtx) -> bool {
1367        ctx.test_api().handle_queued_rx_packets()
1368    }
1369    fn fake_frames(ctx: &mut FakeCtx) -> &mut impl WithFakeFrameContext<Self::SendMeta> {
1370        &mut ctx.bindings_ctx
1371    }
1372}
1373
1374impl<I: IpExt> UdpReceiveBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1375    fn receive_udp(
1376        &mut self,
1377        id: &UdpSocketId<I, WeakDeviceId<Self>, FakeBindingsCtx>,
1378        _device_id: &DeviceId<Self>,
1379        _meta: UdpPacketMeta<I>,
1380        body: &[u8],
1381    ) -> Result<(), ReceiveUdpError> {
1382        let mut state = self.state_mut();
1383        let received =
1384            (&mut *state).udp_state_mut::<I>().entry(id.clone()).or_insert_with(Vec::default);
1385        received.push(body.to_owned());
1386        Ok(())
1387    }
1388
1389    fn on_socket_error(
1390        &mut self,
1391        _id: &UdpSocketId<I, WeakDeviceId<Self>, FakeBindingsCtx>,
1392        _err: PendingDatagramSocketError,
1393    ) {
1394    }
1395}
1396
1397impl UdpBindingsTypes for FakeBindingsCtx {
1398    type ExternalData<I: Ip> = ();
1399    type SocketWritableListener = FakeSocketWritableListener;
1400}
1401
1402impl<I: IpExt> IcmpEchoBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1403    fn receive_icmp_echo_reply<B: BufferMut>(
1404        &mut self,
1405        conn: &IcmpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>,
1406        _device: &DeviceId<Self>,
1407        _src_ip: I::Addr,
1408        _dst_ip: I::Addr,
1409        _id: u16,
1410        data: B,
1411    ) -> Result<(), ReceiveIcmpEchoError> {
1412        I::map_ip_in(
1413            (IpInvariant(self.state_mut()), conn.clone()),
1414            |(IpInvariant(mut state), conn)| {
1415                let replies = state.icmpv4_replies.entry(conn).or_insert_with(Vec::default);
1416                replies.push(data.as_ref().to_owned());
1417            },
1418            |(IpInvariant(mut state), conn)| {
1419                let replies = state.icmpv6_replies.entry(conn).or_insert_with(Vec::default);
1420                replies.push(data.as_ref().to_owned());
1421            },
1422        );
1423        Ok(())
1424    }
1425}
1426
1427impl IcmpEchoBindingsTypes for FakeBindingsCtx {
1428    type ExternalData<I: Ip> = ();
1429    type SocketWritableListener = FakeSocketWritableListener;
1430}
1431
1432impl DeviceSocketTypes for FakeBindingsCtx {
1433    type SocketState<D: Send + Sync + Debug> = Mutex<Vec<(WeakDeviceId<FakeBindingsCtx>, Vec<u8>)>>;
1434}
1435
1436impl RawIpSocketsBindingsTypes for FakeBindingsCtx {
1437    type RawIpSocketState<I: Ip> = ();
1438}
1439
1440impl DeviceSocketBindingsContext<DeviceId<Self>> for FakeBindingsCtx {
1441    fn receive_frame(
1442        &self,
1443        socket_id: &SocketId<Self>,
1444        device: &DeviceId<Self>,
1445        _frame: device::socket::Frame<&[u8]>,
1446        raw_frame: &[u8],
1447    ) -> Result<(), ReceiveFrameError> {
1448        let state = socket_id.socket_state();
1449        state.lock().push((device.downgrade(), raw_frame.into()));
1450        Ok(())
1451    }
1452}
1453
1454impl<I: IpExt> RawIpSocketsBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1455    fn receive_packet<B: SplitByteSlice>(
1456        &self,
1457        _socket: &RawIpSocketId<I, WeakDeviceId<Self>, Self>,
1458        _packet: &I::Packet<B>,
1459        _device: &DeviceId<Self>,
1460    ) -> Result<(), ReceivePacketError> {
1461        unimplemented!()
1462    }
1463}
1464
1465impl DeviceLayerStateTypes for FakeBindingsCtx {
1466    type LoopbackDeviceState = ();
1467    type EthernetDeviceState = ();
1468    type BlackholeDeviceState = ();
1469    type PureIpDeviceState = ();
1470    type DeviceIdentifier = MonotonicIdentifier;
1471}
1472
1473impl ReceiveQueueBindingsContext<LoopbackDeviceId<Self>> for FakeBindingsCtx {
1474    fn wake_rx_task(&mut self, device: &LoopbackDeviceId<FakeBindingsCtx>) {
1475        self.state_mut().rx_available.push(device.clone());
1476    }
1477}
1478
1479impl<D: Clone + Into<DeviceId<Self>>> TransmitQueueBindingsContext<D> for FakeBindingsCtx {
1480    fn wake_tx_task(&mut self, device: &D) {
1481        self.state_mut().tx_available.push(device.clone().into());
1482    }
1483}
1484
1485impl DeviceLayerEventDispatcher for FakeBindingsCtx {
1486    type DequeueContext = ();
1487
1488    fn send_ethernet_frame(
1489        &mut self,
1490        device: &EthernetDeviceId<FakeBindingsCtx>,
1491        frame: Buf<Vec<u8>>,
1492        _dequeue_context: Option<&mut Self::DequeueContext>,
1493        _csum_offload: Option<netstack3_base::ChecksumOffloadResult>,
1494    ) -> Result<(), DeviceSendFrameError> {
1495        let frame_meta = DispatchedFrame::Ethernet(device.downgrade());
1496        self.with_inner_mut(|ctx| ctx.frames.push(frame_meta, frame.into_inner()));
1497        Ok(())
1498    }
1499
1500    fn send_ip_packet(
1501        &mut self,
1502        device: &PureIpDeviceId<FakeBindingsCtx>,
1503        packet: Buf<Vec<u8>>,
1504        ip_version: IpVersion,
1505        _dequeue_context: Option<&mut Self::DequeueContext>,
1506        _csum_offload: Option<netstack3_base::ChecksumOffloadResult>,
1507    ) -> Result<(), DeviceSendFrameError> {
1508        let frame_meta = DispatchedFrame::PureIp(PureIpDeviceAndIpVersion {
1509            device: device.downgrade(),
1510            version: ip_version,
1511        });
1512        self.with_inner_mut(|ctx| ctx.frames.push(frame_meta, packet.into_inner()));
1513        Ok(())
1514    }
1515}
1516
1517impl AlwaysDefaultsSettingsContext for FakeBindingsCtx {}
1518
1519/// Wraps all events emitted by Core into a single enum type.
1520#[derive(Debug, Eq, PartialEq, Hash, GenericOverIp)]
1521#[generic_over_ip()]
1522#[allow(missing_docs)]
1523pub enum DispatchedEvent {
1524    IpDeviceIpv4(IpDeviceEvent<WeakDeviceId<FakeBindingsCtx>, Ipv4, FakeInstant>),
1525    IpDeviceIpv6(IpDeviceEvent<WeakDeviceId<FakeBindingsCtx>, Ipv6, FakeInstant>),
1526    IpLayerIpv4(IpLayerEvent<WeakDeviceId<FakeBindingsCtx>, Ipv4>),
1527    IpLayerIpv6(IpLayerEvent<WeakDeviceId<FakeBindingsCtx>, Ipv6>),
1528    NeighborIpv4(nud::Event<Mac, EthernetWeakDeviceId<FakeBindingsCtx>, Ipv4, FakeInstant>),
1529    NeighborIpv6(nud::Event<Mac, EthernetWeakDeviceId<FakeBindingsCtx>, Ipv6, FakeInstant>),
1530    RouterAdvertisement(RouterAdvertisementEvent<WeakDeviceId<FakeBindingsCtx>>),
1531    EthernetDevice(EthernetDeviceEvent<EthernetWeakDeviceId<FakeBindingsCtx>>),
1532}
1533
1534/// A tuple of device ID and IP version.
1535#[derive(Derivative)]
1536#[derivative(Debug(bound = ""))]
1537#[allow(missing_docs)]
1538pub struct PureIpDeviceAndIpVersion<BT: DeviceLayerTypes> {
1539    pub device: PureIpWeakDeviceId<BT>,
1540    pub version: IpVersion,
1541}
1542
1543/// A frame that's been dispatched to Bindings to be sent out the device driver.
1544#[derive(Debug)]
1545pub enum DispatchedFrame {
1546    /// A frame that's been dispatched to an Ethernet device.
1547    Ethernet(EthernetWeakDeviceId<FakeBindingsCtx>),
1548    /// A frame that's been dispatched to a PureIp device.
1549    PureIp(PureIpDeviceAndIpVersion<FakeBindingsCtx>),
1550}
1551
1552impl<I: Ip> From<IpDeviceEvent<DeviceId<FakeBindingsCtx>, I, FakeInstant>> for DispatchedEvent {
1553    fn from(e: IpDeviceEvent<DeviceId<FakeBindingsCtx>, I, FakeInstant>) -> DispatchedEvent {
1554        let e = e.map_device(|d| d.downgrade());
1555        I::map_ip(e, |e| DispatchedEvent::IpDeviceIpv4(e), |e| DispatchedEvent::IpDeviceIpv6(e))
1556    }
1557}
1558
1559impl<I: IpExt> From<IpLayerEvent<DeviceId<FakeBindingsCtx>, I>> for DispatchedEvent {
1560    fn from(e: IpLayerEvent<DeviceId<FakeBindingsCtx>, I>) -> DispatchedEvent {
1561        let e = e.map_device(|d| d.downgrade());
1562        I::map_ip(e, |e| DispatchedEvent::IpLayerIpv4(e), |e| DispatchedEvent::IpLayerIpv6(e))
1563    }
1564}
1565
1566impl<I: Ip> From<nud::Event<Mac, EthernetDeviceId<FakeBindingsCtx>, I, FakeInstant>>
1567    for DispatchedEvent
1568{
1569    fn from(
1570        e: nud::Event<Mac, EthernetDeviceId<FakeBindingsCtx>, I, FakeInstant>,
1571    ) -> DispatchedEvent {
1572        let e = e.map_device(|d| d.downgrade());
1573        I::map_ip(e, |e| DispatchedEvent::NeighborIpv4(e), |e| DispatchedEvent::NeighborIpv6(e))
1574    }
1575}
1576
1577impl From<EthernetDeviceEvent<EthernetDeviceId<FakeBindingsCtx>>> for DispatchedEvent {
1578    fn from(e: EthernetDeviceEvent<EthernetDeviceId<FakeBindingsCtx>>) -> DispatchedEvent {
1579        let e = e.map_device(|d| d.downgrade());
1580        DispatchedEvent::EthernetDevice(e)
1581    }
1582}
1583
1584impl From<RouterAdvertisementEvent<DeviceId<FakeBindingsCtx>>> for DispatchedEvent {
1585    fn from(e: RouterAdvertisementEvent<DeviceId<FakeBindingsCtx>>) -> DispatchedEvent {
1586        let e = e.map_device(|d| d.downgrade());
1587        DispatchedEvent::RouterAdvertisement(e)
1588    }
1589}
1590
1591impl TcpSocketDestructionContext for FakeBindingsCtx {
1592    fn defer_tcp_socket_destruction<I, S>(&self, _result: RemoveResourceResultWithContext<S, Self>)
1593    where
1594        I: Ip,
1595        S: SocketDiagnosticsSeed<Output = TcpSocketDiagnostics<I, Self::Instant>> + Send + 'static,
1596    {
1597        // Do nothing since we don't care about these notifications in unit tests.
1598    }
1599}
1600
1601/// Creates a new [`FakeNetwork`] of [`Ctx`]s in a simple two-host
1602/// configuration.
1603///
1604/// Two hosts are created with the given names. Packets emitted by one
1605/// arrive at the other and vice-versa.
1606pub fn new_simple_fake_network<CtxId: Copy + Debug + Hash + Eq>(
1607    a_id: CtxId,
1608    a: FakeCtx,
1609    a_device_id: EthernetWeakDeviceId<FakeBindingsCtx>,
1610    b_id: CtxId,
1611    b: FakeCtx,
1612    b_device_id: EthernetWeakDeviceId<FakeBindingsCtx>,
1613) -> FakeNetwork<
1614    FakeCtxNetworkSpec,
1615    CtxId,
1616    impl FakeNetworkLinks<DispatchedFrame, EthernetDeviceId<FakeBindingsCtx>, CtxId>,
1617> {
1618    let contexts = vec![(a_id, a), (b_id, b)].into_iter();
1619    FakeNetwork::new(contexts, move |net, _frame: DispatchedFrame| {
1620        if net == a_id {
1621            b_device_id
1622                .upgrade()
1623                .map(|device_id| (b_id, device_id, None))
1624                .into_iter()
1625                .collect::<Vec<_>>()
1626        } else {
1627            a_device_id
1628                .upgrade()
1629                .map(|device_id| (a_id, device_id, None))
1630                .into_iter()
1631                .collect::<Vec<_>>()
1632        }
1633    })
1634}