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