Skip to main content

netstack3_core/
testutil.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Testing-related utilities.
6
7#![cfg(any(test, feature = "testutils"))]
8
9pub use netstack3_base::testutil::{FakeDeviceId, TestIpExt};
10pub use netstack3_filter::testutil::new_filter_egress_ip_packet;
11
12use alloc::borrow::ToOwned;
13use alloc::sync::Arc;
14use alloc::vec;
15use alloc::vec::Vec;
16
17use core::borrow::Borrow;
18use core::convert::Infallible as Never;
19use core::fmt::Debug;
20use core::hash::Hash;
21use core::ops::{Deref, DerefMut};
22use core::time::Duration;
23
24use derivative::Derivative;
25use net_types::ethernet::Mac;
26use net_types::ip::{
27    AddrSubnet, AddrSubnetEither, GenericOverIp, Ip, IpAddr, IpAddress, IpInvariant, IpVersion,
28    Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Mtu, Subnet, SubnetEither,
29};
30use net_types::{MulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
31use netstack3_base::sync::{DynDebugReferences, Mutex};
32use netstack3_base::testutil::{
33    AlwaysDefaultsSettingsContext, FakeAtomicInstant, FakeCryptoRng, FakeFrameCtx, FakeInstant,
34    FakeNetwork, FakeNetworkLinks, FakeNetworkSpec, FakeSocketWritableListener, FakeTimerCtx,
35    FakeTimerCtxExt, FakeTimerId, MonotonicIdentifier, TestAddrs, WithFakeFrameContext,
36    WithFakeTimerContext,
37};
38use netstack3_base::{
39    AddressResolutionFailed, CtxPair, DeferredResourceRemovalContext, EventContext,
40    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    /// Moves the fake clock forward by `duration`. Doesn't trigger any timers.
769    pub fn sleep(&self, duration: Duration) {
770        self.with_inner_mut(|ctx| ctx.timers.instant.sleep(duration));
771    }
772}
773
774impl MatcherBindingsTypes for FakeBindingsCtx {
775    type DeviceClass = ();
776    type BindingsPacketMatcher = Never;
777}
778
779impl SocketOpsFilterBindingContext<DeviceId<FakeBindingsCtx>> for FakeBindingsCtx {
780    fn socket_ops_filter(&self) -> impl SocketOpsFilter<DeviceId<FakeBindingsCtx>> {
781        NoOpSocketOpsFilter
782    }
783}
784
785impl WithFakeTimerContext<TimerId<FakeBindingsCtx>> for FakeBindingsCtx {
786    fn with_fake_timer_ctx<O, F: FnOnce(&FakeTimerCtx<TimerId<FakeBindingsCtx>>) -> O>(
787        &self,
788        f: F,
789    ) -> O {
790        self.with_inner(|ctx| f(&ctx.timers))
791    }
792
793    fn with_fake_timer_ctx_mut<O, F: FnOnce(&mut FakeTimerCtx<TimerId<FakeBindingsCtx>>) -> O>(
794        &mut self,
795        f: F,
796    ) -> O {
797        self.with_inner_mut(|ctx| f(&mut ctx.timers))
798    }
799}
800
801impl WithFakeFrameContext<DispatchedFrame> for FakeBindingsCtx {
802    fn with_fake_frame_ctx_mut<O, F: FnOnce(&mut FakeFrameCtx<DispatchedFrame>) -> O>(
803        &mut self,
804        f: F,
805    ) -> O {
806        self.with_inner_mut(|ctx| f(&mut ctx.frames))
807    }
808}
809
810impl InstantBindingsTypes for FakeBindingsCtx {
811    type Instant = FakeInstant;
812    type AtomicInstant = FakeAtomicInstant;
813}
814
815impl InstantContext for FakeBindingsCtx {
816    fn now(&self) -> FakeInstant {
817        self.with_inner(|ctx| ctx.now())
818    }
819}
820
821impl TimerBindingsTypes for FakeBindingsCtx {
822    type Timer = <FakeTimerCtx<TimerId<Self>> as TimerBindingsTypes>::Timer;
823    type DispatchId = TimerId<Self>;
824    type UniqueTimerId = <FakeTimerCtx<TimerId<Self>> as TimerBindingsTypes>::UniqueTimerId;
825}
826
827impl TimerContext for FakeBindingsCtx {
828    fn new_timer(&mut self, id: Self::DispatchId) -> Self::Timer {
829        self.with_inner_mut(|ctx| ctx.new_timer(id))
830    }
831
832    fn schedule_timer_instant(
833        &mut self,
834        time: Self::Instant,
835        timer: &mut Self::Timer,
836    ) -> Option<Self::Instant> {
837        // Filter out conntrack GC timers. We don't need conntrack GC in most
838        // tests, and this causes issues with tests that are expecting the
839        // netstack to quiesce.
840        match timer.dispatch_id.0 {
841            TimerIdInner::IpLayer(IpLayerTimerId::FilterTimerv4(FilterTimerId::ConntrackGc(_)))
842            | TimerIdInner::IpLayer(IpLayerTimerId::FilterTimerv6(FilterTimerId::ConntrackGc(_))) =>
843            {
844                return None;
845            }
846            _ => {}
847        }
848        self.with_inner_mut(|ctx| ctx.schedule_timer_instant(time, timer))
849    }
850
851    fn cancel_timer(&mut self, timer: &mut Self::Timer) -> Option<Self::Instant> {
852        self.with_inner_mut(|ctx| ctx.cancel_timer(timer))
853    }
854
855    fn scheduled_instant(&self, timer: &mut Self::Timer) -> Option<Self::Instant> {
856        self.with_inner_mut(|ctx| ctx.scheduled_instant(timer))
857    }
858
859    fn unique_timer_id(&self, timer: &Self::Timer) -> Self::UniqueTimerId {
860        self.with_inner_mut(|ctx| ctx.unique_timer_id(timer))
861    }
862}
863
864impl TxMetadataBindingsTypes for FakeBindingsCtx {
865    type TxMetadata = CoreTxMetadata<Self>;
866}
867
868impl RngContext for FakeBindingsCtx {
869    type Rng<'a> = FakeCryptoRng;
870
871    fn rng(&mut self) -> Self::Rng<'_> {
872        let Self(this) = self;
873        this.lock().rng()
874    }
875}
876
877impl<T: Into<DispatchedEvent>> EventContext<T> for FakeBindingsCtx {
878    fn on_event(&mut self, event: T) {
879        self.with_inner_mut(|ctx| ctx.events.on_event(event.into()))
880    }
881}
882
883impl TcpBindingsTypes for FakeBindingsCtx {
884    type ReceiveBuffer = Arc<Mutex<RingBuffer>>;
885
886    type SendBuffer = TestSendBuffer;
887
888    type ReturnedBuffers = ClientBuffers;
889
890    type ListenerNotifierOrProvidedBuffers = ProvidedBuffers;
891
892    fn new_passive_open_buffers(
893        buffer_sizes: BufferSizes,
894    ) -> (Self::ReceiveBuffer, Self::SendBuffer, Self::ReturnedBuffers) {
895        let client = ClientBuffers::new(buffer_sizes);
896        (
897            Arc::clone(&client.receive),
898            TestSendBuffer::new(Arc::clone(&client.send), RingBuffer::default()),
899            client,
900        )
901    }
902}
903
904impl IpRoutingBindingsTypes for FakeBindingsCtx {
905    type RoutingTableId = ();
906}
907
908impl MarksBindingsContext for FakeBindingsCtx {
909    fn marks_to_keep_on_egress() -> &'static [MarkDomain] {
910        const MARKS: [MarkDomain; 1] = [MarkDomain::Mark1];
911        &MARKS
912    }
913
914    fn marks_to_set_on_ingress() -> &'static [MarkDomain] {
915        const MARKS: [MarkDomain; 1] = [MarkDomain::Mark2];
916        &MARKS
917    }
918}
919
920#[cfg(not(loom))]
921mod fake_notifiers {
922    use core::convert::Infallible as Never;
923
924    use super::*;
925
926    impl ReferenceNotifiers for FakeBindingsCtx {
927        type ReferenceReceiver<T: 'static> = Never;
928
929        type ReferenceNotifier<T: Send + 'static> = Never;
930
931        fn new_reference_notifier<T: Send + 'static>(
932            debug_references: DynDebugReferences,
933        ) -> (Self::ReferenceNotifier<T>, Self::ReferenceReceiver<T>) {
934            // NB: We don't want deferred destruction in core tests. These are
935            // always single-threaded and single-task, and we want to encourage
936            // explicit cleanup.
937            panic!(
938                "FakeBindingsCtx can't create deferred reference notifiers for type {}: \
939                debug_references={debug_references:?}",
940                core::any::type_name::<T>()
941            );
942        }
943    }
944
945    impl DeferredResourceRemovalContext for FakeBindingsCtx {
946        fn defer_removal<T: Send + 'static>(&mut self, receiver: Self::ReferenceReceiver<T>) {
947            match receiver {}
948        }
949    }
950}
951
952/// Implements the notifier methods for loom tests, which use multiple threads
953/// and hence need to handle notifiers.
954#[cfg(loom)]
955mod loom_notifiers {
956    use super::*;
957
958    use core::sync::atomic::{self, AtomicBool};
959    use netstack3_sync::rc::Notifier;
960
961    #[derive(Debug)]
962    pub struct LoomNotifier(Arc<AtomicBool>);
963
964    #[derive(Debug)]
965    pub struct LoomReceiver {
966        pub debug_refs: DynDebugReferences,
967        pub signal: Arc<AtomicBool>,
968    }
969
970    impl LoomReceiver {
971        #[track_caller]
972        pub fn assert_signalled(&self) {
973            let Self { debug_refs, signal } = self;
974            assert!(signal.load(atomic::Ordering::SeqCst), "pending references: {debug_refs:?}")
975        }
976    }
977
978    impl<T> Notifier<T> for LoomNotifier {
979        fn notify(&mut self, _data: T) {
980            let Self(signal) = self;
981            signal.store(true, atomic::Ordering::SeqCst);
982        }
983    }
984
985    impl ReferenceNotifiers for FakeBindingsCtx {
986        type ReferenceReceiver<T: 'static> = LoomReceiver;
987        type ReferenceNotifier<T: Send + 'static> = LoomNotifier;
988
989        fn new_reference_notifier<T: Send + 'static>(
990            debug_refs: DynDebugReferences,
991        ) -> (Self::ReferenceNotifier<T>, Self::ReferenceReceiver<T>) {
992            let signal = Arc::new(AtomicBool::default());
993            (LoomNotifier(Arc::clone(&signal)), LoomReceiver { debug_refs, signal })
994        }
995    }
996
997    impl DeferredResourceRemovalContext for FakeBindingsCtx {
998        fn defer_removal<T: Send + 'static>(&mut self, receiver: Self::ReferenceReceiver<T>) {
999            self.state_mut().deferred_receivers.push(receiver);
1000        }
1001    }
1002}
1003
1004/// A link resolution notifier that ignores all notifications.
1005#[derive(Debug)]
1006pub struct NoOpLinkResolutionNotifier;
1007
1008impl<D: LinkDevice> LinkResolutionContext<D> for FakeBindingsCtx {
1009    type Notifier = NoOpLinkResolutionNotifier;
1010}
1011
1012impl<D: LinkDevice> LinkResolutionNotifier<D> for NoOpLinkResolutionNotifier {
1013    type Observer = ();
1014
1015    fn new() -> (Self, Self::Observer) {
1016        (NoOpLinkResolutionNotifier, ())
1017    }
1018
1019    fn notify(self, _result: Result<D::Address, AddressResolutionFailed>) {}
1020}
1021
1022#[derive(Clone)]
1023struct DeviceConfig {
1024    mac: UnicastAddr<Mac>,
1025    addr_subnet: Option<AddrSubnetEither>,
1026    ipv4_config: Option<Ipv4DeviceConfigurationUpdate>,
1027    ipv6_config: Option<Ipv6DeviceConfigurationUpdate>,
1028}
1029
1030/// A builder for `FakeCtx`s.
1031///
1032/// A `FakeCtxBuilder` is capable of storing the configuration of a network
1033/// stack including forwarding table entries, devices and their assigned
1034/// addresses and configurations, ARP table entries, etc. It can be built using
1035/// `build`, producing a `FakeCtx` with all of the appropriate state configured.
1036#[derive(Clone, Default)]
1037pub struct FakeCtxBuilder {
1038    devices: Vec<DeviceConfig>,
1039    // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1040    arp_table_entries: Vec<(usize, SpecifiedAddr<Ipv4Addr>, UnicastAddr<Mac>)>,
1041    ndp_table_entries: Vec<(usize, UnicastAddr<Ipv6Addr>, UnicastAddr<Mac>)>,
1042    // usize refers to index into devices Vec.
1043    device_routes: Vec<(SubnetEither, usize)>,
1044}
1045
1046impl FakeCtxBuilder {
1047    /// Construct a `FakeCtxBuilder` from a `TestAddrs`.
1048    pub fn with_addrs<A: IpAddress>(addrs: TestAddrs<A>) -> FakeCtxBuilder {
1049        assert!(addrs.subnet.contains(&addrs.local_ip));
1050        assert!(addrs.subnet.contains(&addrs.remote_ip));
1051
1052        let mut builder = FakeCtxBuilder::default();
1053        builder.devices.push(DeviceConfig {
1054            mac: addrs.local_mac,
1055            addr_subnet: Some(
1056                AddrSubnetEither::new(addrs.local_ip.get().into(), addrs.subnet.prefix()).unwrap(),
1057            ),
1058            ipv4_config: None,
1059            ipv6_config: None,
1060        });
1061
1062        match addrs.remote_ip.into() {
1063            IpAddr::V4(ip) => builder.arp_table_entries.push((0, ip, addrs.remote_mac)),
1064            IpAddr::V6(ip) => builder.ndp_table_entries.push((
1065                0,
1066                UnicastAddr::new(ip.get()).unwrap(),
1067                addrs.remote_mac,
1068            )),
1069        };
1070
1071        // Even with fixed ipv4 address we can have IPv6 link local addresses
1072        // pre-cached.
1073        builder.ndp_table_entries.push((
1074            0,
1075            addrs.remote_mac.to_ipv6_link_local().addr().get(),
1076            addrs.remote_mac,
1077        ));
1078
1079        builder.device_routes.push((addrs.subnet.into(), 0));
1080        builder
1081    }
1082
1083    /// Add a device.
1084    ///
1085    /// `add_device` returns a key which can be used to refer to the device in
1086    /// future calls to `add_arp_table_entry` and `add_device_route`.
1087    pub fn add_device(&mut self, mac: UnicastAddr<Mac>) -> usize {
1088        let idx = self.devices.len();
1089        self.devices.push(DeviceConfig {
1090            mac,
1091            addr_subnet: None,
1092            ipv4_config: None,
1093            ipv6_config: None,
1094        });
1095        idx
1096    }
1097
1098    /// Add a device with an IPv4 and IPv6 configuration.
1099    ///
1100    /// `add_device_with_config` is like `add_device`, except that it takes an
1101    /// IPv4 and IPv6 configuration to apply to the device when it is enabled.
1102    pub fn add_device_with_config(
1103        &mut self,
1104        mac: UnicastAddr<Mac>,
1105        ipv4_config: Ipv4DeviceConfigurationUpdate,
1106        ipv6_config: Ipv6DeviceConfigurationUpdate,
1107    ) -> usize {
1108        let idx = self.devices.len();
1109        self.devices.push(DeviceConfig {
1110            mac,
1111            addr_subnet: None,
1112            ipv4_config: Some(ipv4_config),
1113            ipv6_config: Some(ipv6_config),
1114        });
1115        idx
1116    }
1117
1118    /// Add a device with an associated IP address.
1119    ///
1120    /// `add_device_with_ip` is like `add_device`, except that it takes an
1121    /// associated IP address and subnet to assign to the device.
1122    pub fn add_device_with_ip<A: IpAddress>(
1123        &mut self,
1124        mac: UnicastAddr<Mac>,
1125        ip: A,
1126        subnet: Subnet<A>,
1127    ) -> usize {
1128        assert!(subnet.contains(&ip));
1129        let idx = self.devices.len();
1130        self.devices.push(DeviceConfig {
1131            mac,
1132            addr_subnet: Some(AddrSubnetEither::new(ip.into(), subnet.prefix()).unwrap()),
1133            ipv4_config: None,
1134            ipv6_config: None,
1135        });
1136        self.device_routes.push((subnet.into(), idx));
1137        idx
1138    }
1139
1140    /// Add a device with an associated IP address and a particular IPv4 and
1141    /// IPv6 configuration.
1142    ///
1143    /// `add_device_with_ip_and_config` is like `add_device`, except that it
1144    /// takes an associated IP address and subnet to assign to the device, as
1145    /// well as IPv4 and IPv6 configurations to apply to the device when it is
1146    /// enabled.
1147    pub fn add_device_with_ip_and_config<A: IpAddress>(
1148        &mut self,
1149        mac: UnicastAddr<Mac>,
1150        ip: A,
1151        subnet: Subnet<A>,
1152        ipv4_config: Ipv4DeviceConfigurationUpdate,
1153        ipv6_config: Ipv6DeviceConfigurationUpdate,
1154    ) -> usize {
1155        assert!(subnet.contains(&ip));
1156        let idx = self.devices.len();
1157        self.devices.push(DeviceConfig {
1158            mac,
1159            addr_subnet: Some(AddrSubnetEither::new(ip.into(), subnet.prefix()).unwrap()),
1160            ipv4_config: Some(ipv4_config),
1161            ipv6_config: Some(ipv6_config),
1162        });
1163        self.device_routes.push((subnet.into(), idx));
1164        idx
1165    }
1166
1167    /// Add an ARP table entry for a device's ARP table.
1168    pub fn add_arp_table_entry(
1169        &mut self,
1170        device: usize,
1171        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1172        ip: SpecifiedAddr<Ipv4Addr>,
1173        mac: UnicastAddr<Mac>,
1174    ) {
1175        self.arp_table_entries.push((device, ip, mac));
1176    }
1177
1178    /// Add an NDP table entry for a device's NDP table.
1179    pub fn add_ndp_table_entry(
1180        &mut self,
1181        device: usize,
1182        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1183        ip: UnicastAddr<Ipv6Addr>,
1184        mac: UnicastAddr<Mac>,
1185    ) {
1186        self.ndp_table_entries.push((device, ip, mac));
1187    }
1188
1189    /// Add either an NDP entry (if IPv6) or ARP entry (if IPv4) to a
1190    /// `FakeCtxBuilder`.
1191    pub fn add_arp_or_ndp_table_entry<A: IpAddress>(
1192        &mut self,
1193        device: usize,
1194        // TODO(https://fxbug.dev/42083952): Use NeighborAddr when available.
1195        ip: SpecifiedAddr<A>,
1196        mac: UnicastAddr<Mac>,
1197    ) {
1198        match ip.into() {
1199            IpAddr::V4(ip) => self.add_arp_table_entry(device, ip, mac),
1200            IpAddr::V6(ip) => {
1201                self.add_ndp_table_entry(device, UnicastAddr::new(ip.get()).unwrap(), mac)
1202            }
1203        }
1204    }
1205
1206    /// Builds a `Ctx` from the present configuration with a default dispatcher.
1207    pub fn build(self) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1208        self.build_with_modifications(|_| {})
1209    }
1210
1211    /// `build_with_modifications` is equivalent to `build`, except that after
1212    /// the `StackStateBuilder` is initialized, it is passed to `f` for further
1213    /// modification before the `Ctx` is constructed.
1214    pub fn build_with_modifications<F: FnOnce(&mut StackStateBuilder)>(
1215        self,
1216        f: F,
1217    ) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1218        let mut stack_builder = StackStateBuilder::default();
1219        f(&mut stack_builder);
1220        self.build_with(stack_builder)
1221    }
1222
1223    /// Build a `Ctx` from the present configuration with a caller-provided
1224    /// dispatcher and `StackStateBuilder`.
1225    pub fn build_with(
1226        self,
1227        state_builder: StackStateBuilder,
1228    ) -> (FakeCtx, Vec<EthernetDeviceId<FakeBindingsCtx>>) {
1229        let mut ctx = Ctx::new_with_builder(state_builder);
1230
1231        let FakeCtxBuilder { devices, arp_table_entries, ndp_table_entries, device_routes } = self;
1232        let idx_to_device_id: Vec<_> = devices
1233            .into_iter()
1234            .map(|DeviceConfig { mac, addr_subnet: ip_and_subnet, ipv4_config, ipv6_config }| {
1235                let eth_id =
1236                    ctx.core_api().device::<EthernetLinkDevice>().add_device_with_default_state(
1237                        EthernetCreationProperties {
1238                            mac: mac,
1239                            max_frame_size: IPV6_MIN_IMPLIED_MAX_FRAME_SIZE,
1240                        },
1241                        DEFAULT_INTERFACE_METRIC,
1242                    );
1243                let id = eth_id.clone().into();
1244                if let Some(ipv4_config) = ipv4_config {
1245                    let _previous = ctx
1246                        .core_api()
1247                        .device_ip::<Ipv4>()
1248                        .update_configuration(&id, ipv4_config)
1249                        .unwrap();
1250                }
1251                if let Some(ipv6_config) = ipv6_config {
1252                    let _previous = ctx
1253                        .core_api()
1254                        .device_ip::<Ipv6>()
1255                        .update_configuration(&id, ipv6_config)
1256                        .unwrap();
1257                }
1258                ctx.test_api().enable_device(&id);
1259                match ip_and_subnet {
1260                    Some(addr_sub) => {
1261                        ctx.core_api().device_ip_any().add_ip_addr_subnet(&id, addr_sub).unwrap();
1262                    }
1263                    None => {}
1264                }
1265                eth_id
1266            })
1267            .collect();
1268        for (idx, ip, mac) in arp_table_entries {
1269            let device = &idx_to_device_id[idx];
1270            ctx.core_api()
1271                .neighbor::<Ipv4, EthernetLinkDevice>()
1272                .insert_static_entry(&device, ip.get(), mac.get())
1273                .expect("error inserting static ARP entry");
1274        }
1275        for (idx, ip, mac) in ndp_table_entries {
1276            let device = &idx_to_device_id[idx];
1277            ctx.core_api()
1278                .neighbor::<Ipv6, EthernetLinkDevice>()
1279                .insert_static_entry(&device, ip.get(), mac.get())
1280                .expect("error inserting static NDP entry");
1281        }
1282
1283        for (subnet, idx) in device_routes {
1284            let device = &idx_to_device_id[idx];
1285            ctx.test_api()
1286                .add_route(AddableEntryEither::without_gateway(
1287                    subnet,
1288                    device.clone().into(),
1289                    AddableMetric::ExplicitMetric(RawMetric(0)),
1290                ))
1291                .expect("add device route");
1292        }
1293
1294        (ctx, idx_to_device_id)
1295    }
1296}
1297
1298/// The fake network spec to use in integration tests.
1299///
1300/// It creates an Ethernet network.
1301pub enum FakeCtxNetworkSpec {}
1302
1303impl FakeNetworkSpec for FakeCtxNetworkSpec {
1304    type Context = FakeCtx;
1305    type TimerId = TimerId<FakeBindingsCtx>;
1306    type SendMeta = DispatchedFrame;
1307    type RecvMeta = EthernetDeviceId<FakeBindingsCtx>;
1308    fn handle_frame(ctx: &mut FakeCtx, device_id: Self::RecvMeta, data: Buf<Vec<u8>>) {
1309        ctx.core_api()
1310            .device::<EthernetLinkDevice>()
1311            .receive_frame(RecvEthernetFrameMeta { device_id }, data)
1312    }
1313    fn handle_timer(ctx: &mut FakeCtx, dispatch: Self::TimerId, timer: FakeTimerId) {
1314        ctx.core_api().handle_timer(dispatch, timer)
1315    }
1316    fn process_queues(ctx: &mut FakeCtx) -> bool {
1317        ctx.test_api().handle_queued_rx_packets()
1318    }
1319    fn fake_frames(ctx: &mut FakeCtx) -> &mut impl WithFakeFrameContext<Self::SendMeta> {
1320        &mut ctx.bindings_ctx
1321    }
1322}
1323
1324impl<I: IpExt> UdpReceiveBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1325    fn receive_udp(
1326        &mut self,
1327        id: &UdpSocketId<I, WeakDeviceId<Self>, FakeBindingsCtx>,
1328        _device_id: &DeviceId<Self>,
1329        _meta: UdpPacketMeta<I>,
1330        body: &[u8],
1331    ) -> Result<(), ReceiveUdpError> {
1332        let mut state = self.state_mut();
1333        let received =
1334            (&mut *state).udp_state_mut::<I>().entry(id.clone()).or_insert_with(Vec::default);
1335        received.push(body.to_owned());
1336        Ok(())
1337    }
1338}
1339
1340impl UdpBindingsTypes for FakeBindingsCtx {
1341    type ExternalData<I: Ip> = ();
1342    type SocketWritableListener = FakeSocketWritableListener;
1343}
1344
1345impl<I: IpExt> IcmpEchoBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1346    fn receive_icmp_echo_reply<B: BufferMut>(
1347        &mut self,
1348        conn: &IcmpSocketId<I, WeakDeviceId<FakeBindingsCtx>, FakeBindingsCtx>,
1349        _device: &DeviceId<Self>,
1350        _src_ip: I::Addr,
1351        _dst_ip: I::Addr,
1352        _id: u16,
1353        data: B,
1354    ) -> Result<(), ReceiveIcmpEchoError> {
1355        I::map_ip_in(
1356            (IpInvariant(self.state_mut()), conn.clone()),
1357            |(IpInvariant(mut state), conn)| {
1358                let replies = state.icmpv4_replies.entry(conn).or_insert_with(Vec::default);
1359                replies.push(data.as_ref().to_owned());
1360            },
1361            |(IpInvariant(mut state), conn)| {
1362                let replies = state.icmpv6_replies.entry(conn).or_insert_with(Vec::default);
1363                replies.push(data.as_ref().to_owned());
1364            },
1365        );
1366        Ok(())
1367    }
1368}
1369
1370impl IcmpEchoBindingsTypes for FakeBindingsCtx {
1371    type ExternalData<I: Ip> = ();
1372    type SocketWritableListener = FakeSocketWritableListener;
1373}
1374
1375impl DeviceSocketTypes for FakeBindingsCtx {
1376    type SocketState<D: Send + Sync + Debug> = Mutex<Vec<(WeakDeviceId<FakeBindingsCtx>, Vec<u8>)>>;
1377}
1378
1379impl RawIpSocketsBindingsTypes for FakeBindingsCtx {
1380    type RawIpSocketState<I: Ip> = ();
1381}
1382
1383impl DeviceSocketBindingsContext<DeviceId<Self>> for FakeBindingsCtx {
1384    fn receive_frame(
1385        &self,
1386        socket_id: &SocketId<Self>,
1387        device: &DeviceId<Self>,
1388        _frame: device::socket::Frame<&[u8]>,
1389        raw_frame: &[u8],
1390    ) -> Result<(), ReceiveFrameError> {
1391        let state = socket_id.socket_state();
1392        state.lock().push((device.downgrade(), raw_frame.into()));
1393        Ok(())
1394    }
1395}
1396
1397impl<I: IpExt> RawIpSocketsBindingsContext<I, DeviceId<Self>> for FakeBindingsCtx {
1398    fn receive_packet<B: SplitByteSlice>(
1399        &self,
1400        _socket: &RawIpSocketId<I, WeakDeviceId<Self>, Self>,
1401        _packet: &I::Packet<B>,
1402        _device: &DeviceId<Self>,
1403    ) -> Result<(), ReceivePacketError> {
1404        unimplemented!()
1405    }
1406}
1407
1408impl DeviceLayerStateTypes for FakeBindingsCtx {
1409    type LoopbackDeviceState = ();
1410    type EthernetDeviceState = ();
1411    type BlackholeDeviceState = ();
1412    type PureIpDeviceState = ();
1413    type DeviceIdentifier = MonotonicIdentifier;
1414}
1415
1416impl ReceiveQueueBindingsContext<LoopbackDeviceId<Self>> for FakeBindingsCtx {
1417    fn wake_rx_task(&mut self, device: &LoopbackDeviceId<FakeBindingsCtx>) {
1418        self.state_mut().rx_available.push(device.clone());
1419    }
1420}
1421
1422impl<D: Clone + Into<DeviceId<Self>>> TransmitQueueBindingsContext<D> for FakeBindingsCtx {
1423    fn wake_tx_task(&mut self, device: &D) {
1424        self.state_mut().tx_available.push(device.clone().into());
1425    }
1426}
1427
1428impl DeviceLayerEventDispatcher for FakeBindingsCtx {
1429    type DequeueContext = ();
1430
1431    fn send_ethernet_frame(
1432        &mut self,
1433        device: &EthernetDeviceId<FakeBindingsCtx>,
1434        frame: Buf<Vec<u8>>,
1435        _dequeue_context: Option<&mut Self::DequeueContext>,
1436    ) -> Result<(), DeviceSendFrameError> {
1437        let frame_meta = DispatchedFrame::Ethernet(device.downgrade());
1438        self.with_inner_mut(|ctx| ctx.frames.push(frame_meta, frame.into_inner()));
1439        Ok(())
1440    }
1441
1442    fn send_ip_packet(
1443        &mut self,
1444        device: &PureIpDeviceId<FakeBindingsCtx>,
1445        packet: Buf<Vec<u8>>,
1446        ip_version: IpVersion,
1447        _dequeue_context: Option<&mut Self::DequeueContext>,
1448    ) -> Result<(), DeviceSendFrameError> {
1449        let frame_meta = DispatchedFrame::PureIp(PureIpDeviceAndIpVersion {
1450            device: device.downgrade(),
1451            version: ip_version,
1452        });
1453        self.with_inner_mut(|ctx| ctx.frames.push(frame_meta, packet.into_inner()));
1454        Ok(())
1455    }
1456}
1457
1458impl AlwaysDefaultsSettingsContext for FakeBindingsCtx {}
1459
1460/// Wraps all events emitted by Core into a single enum type.
1461#[derive(Debug, Eq, PartialEq, Hash, GenericOverIp)]
1462#[generic_over_ip()]
1463#[allow(missing_docs)]
1464pub enum DispatchedEvent {
1465    IpDeviceIpv4(IpDeviceEvent<WeakDeviceId<FakeBindingsCtx>, Ipv4, FakeInstant>),
1466    IpDeviceIpv6(IpDeviceEvent<WeakDeviceId<FakeBindingsCtx>, Ipv6, FakeInstant>),
1467    IpLayerIpv4(IpLayerEvent<WeakDeviceId<FakeBindingsCtx>, Ipv4>),
1468    IpLayerIpv6(IpLayerEvent<WeakDeviceId<FakeBindingsCtx>, Ipv6>),
1469    NeighborIpv4(nud::Event<Mac, EthernetWeakDeviceId<FakeBindingsCtx>, Ipv4, FakeInstant>),
1470    NeighborIpv6(nud::Event<Mac, EthernetWeakDeviceId<FakeBindingsCtx>, Ipv6, FakeInstant>),
1471    RouterAdvertisement(RouterAdvertisementEvent<WeakDeviceId<FakeBindingsCtx>>),
1472    EthernetDevice(EthernetDeviceEvent<EthernetWeakDeviceId<FakeBindingsCtx>>),
1473}
1474
1475/// A tuple of device ID and IP version.
1476#[derive(Derivative)]
1477#[derivative(Debug(bound = ""))]
1478#[allow(missing_docs)]
1479pub struct PureIpDeviceAndIpVersion<BT: DeviceLayerTypes> {
1480    pub device: PureIpWeakDeviceId<BT>,
1481    pub version: IpVersion,
1482}
1483
1484/// A frame that's been dispatched to Bindings to be sent out the device driver.
1485#[derive(Debug)]
1486pub enum DispatchedFrame {
1487    /// A frame that's been dispatched to an Ethernet device.
1488    Ethernet(EthernetWeakDeviceId<FakeBindingsCtx>),
1489    /// A frame that's been dispatched to a PureIp device.
1490    PureIp(PureIpDeviceAndIpVersion<FakeBindingsCtx>),
1491}
1492
1493impl<I: Ip> From<IpDeviceEvent<DeviceId<FakeBindingsCtx>, I, FakeInstant>> for DispatchedEvent {
1494    fn from(e: IpDeviceEvent<DeviceId<FakeBindingsCtx>, I, FakeInstant>) -> DispatchedEvent {
1495        let e = e.map_device(|d| d.downgrade());
1496        I::map_ip(e, |e| DispatchedEvent::IpDeviceIpv4(e), |e| DispatchedEvent::IpDeviceIpv6(e))
1497    }
1498}
1499
1500impl<I: IpExt> From<IpLayerEvent<DeviceId<FakeBindingsCtx>, I>> for DispatchedEvent {
1501    fn from(e: IpLayerEvent<DeviceId<FakeBindingsCtx>, I>) -> DispatchedEvent {
1502        let e = e.map_device(|d| d.downgrade());
1503        I::map_ip(e, |e| DispatchedEvent::IpLayerIpv4(e), |e| DispatchedEvent::IpLayerIpv6(e))
1504    }
1505}
1506
1507impl<I: Ip> From<nud::Event<Mac, EthernetDeviceId<FakeBindingsCtx>, I, FakeInstant>>
1508    for DispatchedEvent
1509{
1510    fn from(
1511        e: nud::Event<Mac, EthernetDeviceId<FakeBindingsCtx>, I, FakeInstant>,
1512    ) -> DispatchedEvent {
1513        let e = e.map_device(|d| d.downgrade());
1514        I::map_ip(e, |e| DispatchedEvent::NeighborIpv4(e), |e| DispatchedEvent::NeighborIpv6(e))
1515    }
1516}
1517
1518impl From<EthernetDeviceEvent<EthernetDeviceId<FakeBindingsCtx>>> for DispatchedEvent {
1519    fn from(e: EthernetDeviceEvent<EthernetDeviceId<FakeBindingsCtx>>) -> DispatchedEvent {
1520        let e = e.map_device(|d| d.downgrade());
1521        DispatchedEvent::EthernetDevice(e)
1522    }
1523}
1524
1525impl From<RouterAdvertisementEvent<DeviceId<FakeBindingsCtx>>> for DispatchedEvent {
1526    fn from(e: RouterAdvertisementEvent<DeviceId<FakeBindingsCtx>>) -> DispatchedEvent {
1527        let e = e.map_device(|d| d.downgrade());
1528        DispatchedEvent::RouterAdvertisement(e)
1529    }
1530}
1531
1532/// Creates a new [`FakeNetwork`] of [`Ctx`]s in a simple two-host
1533/// configuration.
1534///
1535/// Two hosts are created with the given names. Packets emitted by one
1536/// arrive at the other and vice-versa.
1537pub fn new_simple_fake_network<CtxId: Copy + Debug + Hash + Eq>(
1538    a_id: CtxId,
1539    a: FakeCtx,
1540    a_device_id: EthernetWeakDeviceId<FakeBindingsCtx>,
1541    b_id: CtxId,
1542    b: FakeCtx,
1543    b_device_id: EthernetWeakDeviceId<FakeBindingsCtx>,
1544) -> FakeNetwork<
1545    FakeCtxNetworkSpec,
1546    CtxId,
1547    impl FakeNetworkLinks<DispatchedFrame, EthernetDeviceId<FakeBindingsCtx>, CtxId>,
1548> {
1549    let contexts = vec![(a_id, a), (b_id, b)].into_iter();
1550    FakeNetwork::new(contexts, move |net, _frame: DispatchedFrame| {
1551        if net == a_id {
1552            b_device_id
1553                .upgrade()
1554                .map(|device_id| (b_id, device_id, None))
1555                .into_iter()
1556                .collect::<Vec<_>>()
1557        } else {
1558            a_device_id
1559                .upgrade()
1560                .map(|device_id| (a_id, device_id, None))
1561                .into_iter()
1562                .collect::<Vec<_>>()
1563        }
1564    })
1565}