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