Skip to main content

netstack3_core/
testutil.rs

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