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