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