Skip to main content

netstack3_datagram/
datagram.rs

1// Copyright 2022 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//! Shared code for implementing datagram sockets.
6
7use alloc::vec::Vec;
8use core::borrow::Borrow;
9use core::convert::Infallible as Never;
10use core::error::Error;
11use core::fmt::Debug;
12use core::hash::Hash;
13use core::marker::PhantomData;
14use core::num::{NonZeroU8, NonZeroU16};
15use core::ops::{Deref, DerefMut};
16use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
17use netstack3_ip::marker::OptionDelegationMarker;
18
19use derivative::Derivative;
20use either::Either;
21use net_types::ip::{GenericOverIp, Ip, IpAddress, Ipv4, Ipv6, Mtu};
22use net_types::{MulticastAddr, MulticastAddress as _, SpecifiedAddr, Witness, ZonedAddr};
23use netstack3_base::socket::{
24    self, AddrVec, BoundSocketMap, ConnAddr, ConnInfoAddr, ConnIpAddr, DualStackConnIpAddr,
25    DualStackListenerIpAddr, DualStackLocalIp, DualStackRemoteIp, EitherStack, InsertError,
26    ListenerAddr, ListenerIpAddr, MaybeDualStack, NotDualStackCapableError, Shutdown, ShutdownType,
27    SocketCookie, SocketDeviceUpdate, SocketDeviceUpdateNotAllowedError, SocketIpAddr, SocketIpExt,
28    SocketMapAddrSpec, SocketMapConflictPolicy, SocketMapStateSpec, SocketStateEntry,
29    SocketWritableListener, SocketZonedAddrExt as _, StrictlyZonedAddr,
30};
31use netstack3_base::sync::{self, RwLock};
32use netstack3_base::{
33    AnyDevice, BidirectionalConverter, ContextPair, CoreTxMetadataContext, DeviceIdContext,
34    DeviceIdentifier, EitherDeviceId, Inspector, InspectorDeviceExt, InspectorExt as _,
35    IpDeviceAddr, LocalAddressError, Mark, MarkDomain, Marks, NotFoundError,
36    OwnedOrRefsBidirectionalConverter, ReferenceNotifiers, ReferenceNotifiersExt,
37    RemoteAddressError, RemoveResourceResultWithContext, RngContext, SettingsContext, SocketError,
38    StrongDeviceIdentifier, TxMetadataBindingsTypes, WeakDeviceIdentifier, ZonedAddressError,
39};
40use netstack3_filter::{FilterIpExt, TransportPacketSerializer};
41use netstack3_hashmap::{HashMap, HashSet};
42use netstack3_ip::socket::{
43    DelegatedRouteResolutionOptions, DelegatedSendOptions, IpSock, IpSockCreateAndSendError,
44    IpSockCreationError, IpSockSendError, IpSocketArgs, IpSocketHandler, RouteResolutionOptions,
45    SendOneShotIpPacketError, SendOptions, SocketHopLimits,
46};
47use netstack3_ip::{
48    BaseTransportIpContext, HopLimits, IpLayerIpExt, MulticastMembershipHandler, ResolveRouteError,
49    SocketMetadata, TransportIpContext,
50};
51use packet::BufferMut;
52use packet_formats::ip::{DscpAndEcn, IpProtoExt};
53use ref_cast::RefCast;
54use thiserror::Error;
55
56use crate::internal::settings::DatagramSettings;
57use crate::internal::sndbuf::{SendBufferError, SendBufferTracking, TxMetadata};
58
59/// Top-level struct kept in datagram socket references.
60#[derive(Derivative)]
61#[derivative(Debug(bound = ""))]
62pub struct ReferenceState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
63    pub(crate) state: RwLock<SocketState<I, D, S>>,
64    pub(crate) external_data: S::ExternalData<I>,
65    pub(crate) send_buffer: SendBufferTracking<S>,
66    pub(crate) counters: S::Counters<I>,
67}
68
69// Local aliases for brevity.
70type PrimaryRc<I, D, S> = sync::PrimaryRc<ReferenceState<I, D, S>>;
71/// A convenient alias for a strong reference to a datagram socket.
72pub type StrongRc<I, D, S> = sync::StrongRc<ReferenceState<I, D, S>>;
73/// A convenient alias for a weak reference to a datagram socket.
74pub type WeakRc<I, D, S> = sync::WeakRc<ReferenceState<I, D, S>>;
75
76impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
77    OrderedLockAccess<SocketState<I, D, S>> for ReferenceState<I, D, S>
78{
79    type Lock = RwLock<SocketState<I, D, S>>;
80    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
81        OrderedLockRef::new(&self.state)
82    }
83}
84
85impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> ReferenceState<I, D, S> {
86    /// Returns the external data associated with the socket.
87    pub fn external_data(&self) -> &S::ExternalData<I> {
88        &self.external_data
89    }
90
91    /// Provides access to the socket state sidestepping lock ordering.
92    #[cfg(any(test, feature = "testutils"))]
93    pub fn state(&self) -> &RwLock<SocketState<I, D, S>> {
94        &self.state
95    }
96
97    /// Provides access to the socket's counters.
98    pub fn counters(&self) -> &S::Counters<I> {
99        &self.counters
100    }
101}
102
103/// A set containing all datagram sockets for a given implementation.
104#[derive(Derivative, GenericOverIp)]
105#[derivative(Default(bound = ""))]
106#[generic_over_ip(I, Ip)]
107pub struct DatagramSocketSet<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
108    HashMap<StrongRc<I, D, S>, PrimaryRc<I, D, S>>,
109);
110
111impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Debug
112    for DatagramSocketSet<I, D, S>
113{
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        let Self(rc) = self;
116        f.debug_list().entries(rc.keys().map(StrongRc::debug_id)).finish()
117    }
118}
119
120impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Deref
121    for DatagramSocketSet<I, D, S>
122{
123    type Target = HashMap<StrongRc<I, D, S>, PrimaryRc<I, D, S>>;
124    fn deref(&self) -> &Self::Target {
125        &self.0
126    }
127}
128
129impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> DerefMut
130    for DatagramSocketSet<I, D, S>
131{
132    fn deref_mut(&mut self) -> &mut Self::Target {
133        &mut self.0
134    }
135}
136
137/// Marker trait for datagram IP extensions.
138pub trait IpExt: netstack3_ip::IpLayerIpExt + DualStackIpExt {}
139impl<I: netstack3_ip::IpLayerIpExt + DualStackIpExt> IpExt for I {}
140
141/// A datagram socket's state.
142#[derive(Derivative, GenericOverIp)]
143#[generic_over_ip(I, Ip)]
144#[derivative(Debug(bound = ""))]
145pub struct SocketState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
146    /// Bind/connect state of the socket.
147    pub inner: SocketStateInner<I, D, S>,
148
149    /// Socket options that do not depend on the bind/connect state.
150    pub(crate) ip_options: IpOptions<I, D, S>,
151
152    /// Sharing state of the socket.
153    pub(crate) sharing: S::SharingState,
154}
155
156#[derive(Derivative, GenericOverIp)]
157#[generic_over_ip(I, Ip)]
158#[derivative(Debug(bound = ""))]
159#[allow(missing_docs)]
160pub enum SocketStateInner<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
161    Unbound(UnboundSocketState<D>),
162    Bound(BoundSocketState<I, D, S>),
163}
164
165impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> SocketState<I, D, S> {
166    /// Returns [`SocketInfo`] for this datagram socket.
167    pub fn to_socket_info(&self) -> SocketInfo<I::Addr, D> {
168        match &self.inner {
169            SocketStateInner::Unbound(_) => SocketInfo::Unbound,
170            SocketStateInner::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
171                match socket_type {
172                    BoundSocketStateType::Listener(state) => {
173                        let ListenerState { addr } = state;
174                        SocketInfo::Listener(addr.clone().into())
175                    }
176                    BoundSocketStateType::Connected(state) => {
177                        SocketInfo::Connected(S::conn_info_from_state(&state))
178                    }
179                }
180            }
181        }
182    }
183
184    /// Returns the local IP address, if the socket is bound to one.
185    pub fn local_ip(&self) -> Option<StrictlyZonedAddr<I::Addr, SpecifiedAddr<I::Addr>, D>> {
186        match self.to_socket_info() {
187            SocketInfo::Unbound => None,
188            SocketInfo::Listener(ListenerInfo { local_ip, .. }) => local_ip,
189            SocketInfo::Connected(ConnInfo { local_ip, .. }) => Some(local_ip),
190        }
191    }
192
193    /// Returns the local socket identifier (e.g. port), if the socket is bound to one.
194    pub fn local_identifier(&self) -> Option<NonZeroU16> {
195        match self.to_socket_info() {
196            SocketInfo::Unbound => None,
197            SocketInfo::Listener(ListenerInfo { local_identifier, .. }) => Some(local_identifier),
198            SocketInfo::Connected(ConnInfo { local_identifier, .. }) => Some(local_identifier),
199        }
200    }
201
202    /// Returns the remote IP address, if the datagram socket is connected.
203    pub fn remote_ip(&self) -> Option<StrictlyZonedAddr<I::Addr, SpecifiedAddr<I::Addr>, D>> {
204        match self.to_socket_info() {
205            SocketInfo::Unbound => None,
206            SocketInfo::Listener(_) => None,
207            SocketInfo::Connected(ConnInfo { remote_ip, .. }) => Some(remote_ip),
208        }
209    }
210
211    /// Returns the remote identifier (e.g. port), if the datagram socket is connected.
212    pub fn remote_identifier(&self) -> Option<u16> {
213        match self.to_socket_info() {
214            SocketInfo::Unbound => None,
215            SocketInfo::Listener(_) => None,
216            SocketInfo::Connected(ConnInfo { remote_identifier, .. }) => Some(remote_identifier),
217        }
218    }
219
220    /// Record inspect information generic to each datagram protocol.
221    pub fn record_common_info<N>(&self, inspector: &mut N)
222    where
223        N: Inspector + InspectorDeviceExt<D>,
224    {
225        inspector.record_str("TransportProtocol", S::NAME);
226        inspector.record_str("NetworkProtocol", I::NAME);
227
228        let socket_info = self.to_socket_info();
229        let (local, remote) = match socket_info {
230            SocketInfo::Unbound => (None, None),
231            SocketInfo::Listener(ListenerInfo { local_ip, local_identifier }) => (
232                Some((
233                    local_ip.map_or_else(
234                        || ZonedAddr::Unzoned(I::UNSPECIFIED_ADDRESS),
235                        |addr| addr.into_inner_without_witness(),
236                    ),
237                    local_identifier,
238                )),
239                None,
240            ),
241            SocketInfo::Connected(ConnInfo {
242                local_ip,
243                local_identifier,
244                remote_ip,
245                remote_identifier,
246            }) => (
247                Some((local_ip.into_inner_without_witness(), local_identifier)),
248                Some((remote_ip.into_inner_without_witness(), remote_identifier)),
249            ),
250        };
251        inspector.record_local_socket_addr::<N, _, _, _>(local);
252        inspector.record_remote_socket_addr::<N, _, _, _>(remote);
253
254        let IpOptions {
255            multicast_memberships: MulticastMemberships(multicast_memberships),
256            socket_options: _,
257            other_stack: _,
258            common,
259        } = self.options();
260        inspector.record_child("MulticastGroupMemberships", |node| {
261            for (index, (multicast_addr, device)) in multicast_memberships.iter().enumerate() {
262                node.record_debug_child(index, |node| {
263                    node.record_ip_addr("MulticastGroup", multicast_addr.get());
264                    N::record_device(node, "Device", device);
265                })
266            }
267        });
268        inspector.delegate_inspectable(&common.marks);
269    }
270
271    /// Returns the device to which the socket is bound.
272    pub fn get_device<
273        'a,
274        BC: DatagramBindingsTypes,
275        CC: DatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
276    >(
277        &'a self,
278        core_ctx: &CC,
279    ) -> &'a Option<CC::WeakDeviceId> {
280        match &self.inner {
281            SocketStateInner::Unbound(UnboundSocketState { device }) => device,
282            SocketStateInner::Bound(state) => state.get_device(core_ctx),
283        }
284    }
285
286    /// Returns `IpOptions`.
287    pub fn options(&self) -> &IpOptions<I, D, S> {
288        &self.ip_options
289    }
290
291    /// Returns mutable `IpOptions`.
292    pub fn options_mut(&mut self) -> &mut IpOptions<I, D, S> {
293        &mut self.ip_options
294    }
295}
296
297/// State associated with a Bound Socket.
298#[derive(Derivative)]
299#[derivative(Debug(bound = "D: Debug"))]
300pub struct BoundSocketState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
301    /// The type of bound socket (e.g. Listener vs. Connected), and any
302    /// type-specific state.
303    pub socket_type: BoundSocketStateType<I, D, S>,
304    /// The original bound address of the socket, as requested by the caller.
305    /// `None` if:
306    ///   * the socket was connected from unbound, or
307    ///   * listen was called without providing a local port.
308    pub original_bound_addr: Option<S::ListenerIpAddr<I>>,
309}
310
311impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundSocketState<I, D, S> {
312    fn get_device<
313        BC: DatagramBindingsTypes,
314        CC: DatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
315    >(
316        &self,
317        core_ctx: &CC,
318    ) -> &Option<D> {
319        match &self.socket_type {
320            BoundSocketStateType::Listener(ListenerState { addr: ListenerAddr { device, .. } }) => {
321                device
322            }
323            BoundSocketStateType::Connected(state) => match core_ctx.dual_stack_context() {
324                MaybeDualStack::DualStack(dual_stack) => {
325                    match dual_stack.ds_converter().convert(state) {
326                        DualStackConnState::ThisStack(state) => state.get_device(),
327                        DualStackConnState::OtherStack(state) => state.get_device(),
328                    }
329                }
330                MaybeDualStack::NotDualStack(not_dual_stack) => {
331                    not_dual_stack.nds_converter().convert(state).get_device()
332                }
333            },
334        }
335    }
336}
337
338/// State for the sub-types of bound socket (e.g. Listener or Connected).
339#[derive(Derivative)]
340#[derivative(Debug(bound = "D: Debug"))]
341#[allow(missing_docs)]
342pub enum BoundSocketStateType<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
343    Listener(ListenerState<I, D, S>),
344    Connected(S::ConnState<I, D>),
345}
346
347#[derive(Derivative)]
348#[derivative(Debug(bound = ""), Default(bound = ""))]
349pub struct UnboundSocketState<D: WeakDeviceIdentifier> {
350    device: Option<D>,
351}
352
353/// State associated with a listening socket.
354#[derive(Derivative)]
355#[derivative(Debug(bound = ""))]
356pub struct ListenerState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec + ?Sized> {
357    pub(crate) addr: ListenerAddr<S::ListenerIpAddr<I>, D>,
358}
359
360/// State associated with a connected socket.
361#[derive(Derivative)]
362#[derivative(Debug(bound = "D: Debug"))]
363pub struct ConnState<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec + ?Sized> {
364    pub(crate) socket: IpSock<WireI, D>,
365    pub(crate) shutdown: Shutdown,
366    pub(crate) addr: ConnAddr<
367        ConnIpAddr<
368            WireI::Addr,
369            <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
370            <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
371        >,
372        D,
373    >,
374    /// Determines whether a call to disconnect this socket should also clear
375    /// the device on the socket address.
376    ///
377    /// This will only be `true` if
378    ///   1) the corresponding address has a bound device
379    ///   2) the local address does not require a zone
380    ///   3) the remote address does require a zone
381    ///   4) the device was not set via [`set_unbound_device`]
382    ///
383    /// In that case, when the socket is disconnected, the device should be
384    /// cleared since it was set as part of a `connect` call, not explicitly.
385    pub(crate) clear_device_on_disconnect: bool,
386
387    /// The extra state for the connection.
388    ///
389    /// For UDP it should be [`()`], for ICMP it should be [`NonZeroU16`] to
390    /// remember the remote ID set by connect.
391    pub(crate) extra: S::ConnStateExtra,
392}
393
394impl<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<Shutdown>
395    for ConnState<WireI, D, S>
396{
397    fn as_ref(&self) -> &Shutdown {
398        &self.shutdown
399    }
400}
401
402impl<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsMut<Shutdown>
403    for ConnState<WireI, D, S>
404{
405    fn as_mut(&mut self) -> &mut Shutdown {
406        &mut self.shutdown
407    }
408}
409
410impl<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> ConnState<WireI, D, S> {
411    /// Returns true if the connection can receive traffic.
412    pub fn should_receive(&self) -> bool {
413        let Self { shutdown, socket: _, clear_device_on_disconnect: _, addr: _, extra: _ } = self;
414        let Shutdown { receive, send: _ } = shutdown;
415        !*receive
416    }
417
418    /// Returns the bound addresses for the connection.
419    pub fn addr(
420        &self,
421    ) -> &ConnAddr<
422        ConnIpAddr<
423            WireI::Addr,
424            <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
425            <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
426        >,
427        D,
428    > {
429        &self.addr
430    }
431
432    /// Returns the extra opaque information kept in connected state.
433    pub fn extra(&self) -> &S::ConnStateExtra {
434        &self.extra
435    }
436
437    fn get_device(&self) -> &Option<D> {
438        let Self { addr: ConnAddr { device, .. }, .. } = self;
439        device
440    }
441}
442
443/// Connection state belong to either this-stack or the other-stack.
444#[derive(Derivative)]
445#[derivative(Debug(bound = ""))]
446pub enum DualStackConnState<
447    I: IpExt + DualStackIpExt,
448    D: WeakDeviceIdentifier,
449    S: DatagramSocketSpec + ?Sized,
450> {
451    /// The [`ConnState`] for a socked connected with [`I::Version`].
452    ThisStack(ConnState<I, D, S>),
453    /// The [`ConnState`] for a socked connected with [`I::OtherVersion`].
454    OtherStack(ConnState<I::OtherVersion, D, S>),
455}
456
457impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<Shutdown>
458    for DualStackConnState<I, D, S>
459{
460    fn as_ref(&self) -> &Shutdown {
461        match self {
462            DualStackConnState::ThisStack(state) => state.as_ref(),
463            DualStackConnState::OtherStack(state) => state.as_ref(),
464        }
465    }
466}
467
468impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsMut<Shutdown>
469    for DualStackConnState<I, D, S>
470{
471    fn as_mut(&mut self) -> &mut Shutdown {
472        match self {
473            DualStackConnState::ThisStack(state) => state.as_mut(),
474            DualStackConnState::OtherStack(state) => state.as_mut(),
475        }
476    }
477}
478
479/// A datagram socket's options.
480///
481/// These options are held twice by dual stack sockets, since they hold
482/// different values per IP version.
483#[derive(Derivative, GenericOverIp)]
484#[generic_over_ip(I, Ip)]
485#[derivative(Clone(bound = ""), Debug, Default(bound = ""))]
486pub struct DatagramIpSpecificSocketOptions<I: IpExt, D: WeakDeviceIdentifier> {
487    /// The configured hop limits.
488    pub hop_limits: SocketHopLimits<I>,
489    /// The selected multicast interface.
490    pub multicast_interface: Option<D>,
491
492    /// Whether multicast packet loopback is enabled or not (see
493    /// IP_MULTICAST_LOOP flag). Enabled by default.
494    #[derivative(Default(value = "true"))]
495    pub multicast_loop: bool,
496
497    /// Set to `Some` when the socket can be used to send broadcast packets.
498    pub allow_broadcast: Option<I::BroadcastMarker>,
499
500    /// IPV6_TCLASS or IP_TOS option.
501    pub dscp_and_ecn: DscpAndEcn,
502}
503
504impl<I: IpExt, D: WeakDeviceIdentifier> SendOptions<I> for DatagramIpSpecificSocketOptions<I, D> {
505    fn hop_limit(&self, destination: &SpecifiedAddr<I::Addr>) -> Option<NonZeroU8> {
506        self.hop_limits.hop_limit_for_dst(destination)
507    }
508
509    fn multicast_loop(&self) -> bool {
510        self.multicast_loop
511    }
512
513    fn allow_broadcast(&self) -> Option<I::BroadcastMarker> {
514        self.allow_broadcast
515    }
516
517    fn dscp_and_ecn(&self) -> DscpAndEcn {
518        self.dscp_and_ecn
519    }
520
521    fn mtu(&self) -> Mtu {
522        Mtu::no_limit()
523    }
524}
525
526#[derive(Clone, Debug, Default)]
527struct DatagramIpAgnosticOptions {
528    transparent: bool,
529    marks: Marks,
530}
531
532impl<I: Ip> RouteResolutionOptions<I> for DatagramIpAgnosticOptions {
533    fn transparent(&self) -> bool {
534        self.transparent
535    }
536
537    fn marks(&self) -> &Marks {
538        &self.marks
539    }
540}
541
542/// Holds references to provide implementations of [`SendOptions`] and
543/// [`RouteResolutionOptions`] with appropriate access to underlying data.
544struct IpOptionsRef<'a, I: IpExt, D: WeakDeviceIdentifier> {
545    ip_specific: &'a DatagramIpSpecificSocketOptions<I, D>,
546    agnostic: &'a DatagramIpAgnosticOptions,
547}
548
549impl<'a, I: IpExt, D: WeakDeviceIdentifier> OptionDelegationMarker for IpOptionsRef<'a, I, D> {}
550
551impl<'a, I: IpExt, D: WeakDeviceIdentifier> DelegatedSendOptions<I> for IpOptionsRef<'a, I, D> {
552    fn delegate(&self) -> &impl SendOptions<I> {
553        self.ip_specific
554    }
555}
556
557impl<'a, I: IpExt, D: WeakDeviceIdentifier> DelegatedRouteResolutionOptions<I>
558    for IpOptionsRef<'a, I, D>
559{
560    fn delegate(&self) -> &impl RouteResolutionOptions<I> {
561        self.agnostic
562    }
563}
564
565/// A datagram socket's IP options.
566#[derive(Derivative, GenericOverIp)]
567#[generic_over_ip(I, Ip)]
568#[derivative(Clone(bound = ""), Debug, Default(bound = ""))]
569pub struct IpOptions<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec + ?Sized> {
570    multicast_memberships: MulticastMemberships<I::Addr, D>,
571    socket_options: DatagramIpSpecificSocketOptions<I, D>,
572    other_stack: S::OtherStackIpOptions<I, D>,
573    common: DatagramIpAgnosticOptions,
574}
575
576impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> IpOptions<I, D, S> {
577    /// Returns the IP options for the other stack.
578    pub fn other_stack(&self) -> &S::OtherStackIpOptions<I, D> {
579        &self.other_stack
580    }
581
582    /// Returns the transparent option.
583    pub fn transparent(&self) -> bool {
584        self.common.transparent
585    }
586
587    /// Returns `Marks`.
588    pub fn marks(&self) -> &Marks {
589        &self.common.marks
590    }
591
592    fn this_stack_options_ref(&self) -> IpOptionsRef<'_, I, D> {
593        IpOptionsRef { ip_specific: &self.socket_options, agnostic: &self.common }
594    }
595
596    fn other_stack_options_ref<
597        'a,
598        BC: DatagramBindingsTypes,
599        CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
600    >(
601        &'a self,
602        ctx: &CC,
603    ) -> IpOptionsRef<'_, I::OtherVersion, D> {
604        IpOptionsRef { ip_specific: ctx.to_other_socket_options(self), agnostic: &self.common }
605    }
606}
607
608#[derive(Clone, Debug, Derivative)]
609#[derivative(Default(bound = ""))]
610pub(crate) struct MulticastMemberships<A, D>(HashSet<(MulticastAddr<A>, D)>);
611
612#[cfg_attr(test, derive(Debug, PartialEq))]
613pub(crate) enum MulticastMembershipChange {
614    Join,
615    Leave,
616}
617
618impl<A: Eq + Hash, D: WeakDeviceIdentifier> MulticastMemberships<A, D> {
619    pub(crate) fn apply_membership_change(
620        &mut self,
621        address: MulticastAddr<A>,
622        device: &D,
623        want_membership: bool,
624    ) -> Option<MulticastMembershipChange> {
625        let device = device.clone();
626
627        let Self(map) = self;
628        if want_membership {
629            map.insert((address, device)).then_some(MulticastMembershipChange::Join)
630        } else {
631            map.remove(&(address, device)).then_some(MulticastMembershipChange::Leave)
632        }
633    }
634}
635
636impl<A: Eq + Hash, D: Eq + Hash> IntoIterator for MulticastMemberships<A, D> {
637    type Item = (MulticastAddr<A>, D);
638    type IntoIter = <HashSet<(MulticastAddr<A>, D)> as IntoIterator>::IntoIter;
639
640    fn into_iter(self) -> Self::IntoIter {
641        let Self(memberships) = self;
642        memberships.into_iter()
643    }
644}
645
646fn leave_all_joined_groups<A: IpAddress, BC, CC: MulticastMembershipHandler<A::Version, BC>>(
647    core_ctx: &mut CC,
648    bindings_ctx: &mut BC,
649    memberships: &MulticastMemberships<A, CC::WeakDeviceId>,
650) {
651    let MulticastMemberships(map) = memberships;
652    for (addr, device) in map.iter() {
653        let Some(device) = device.upgrade() else {
654            continue;
655        };
656        core_ctx.leave_multicast_group(bindings_ctx, &device, addr.clone())
657    }
658}
659
660/// Identifies a flow for a datagram socket.
661#[derive(Hash)]
662pub struct DatagramFlowId<A: IpAddress, RI> {
663    /// Socket's local address.
664    pub local_ip: SocketIpAddr<A>,
665    /// Socket's remote address.
666    pub remote_ip: SocketIpAddr<A>,
667    /// Socket's remote identifier (port).
668    pub remote_id: RI,
669}
670
671/// The core context providing access to datagram socket state.
672pub trait DatagramStateContext<I: IpExt, BC: DatagramBindingsTypes, S: DatagramSocketSpec>:
673    DeviceIdContext<AnyDevice>
674{
675    /// The core context passed to the callback provided to methods.
676    type SocketsStateCtx<'a>: DatagramBoundStateContext<I, BC, S>
677        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
678
679    /// Calls the function with mutable access to the set with all datagram
680    /// sockets.
681    fn with_all_sockets_mut<O, F: FnOnce(&mut DatagramSocketSet<I, Self::WeakDeviceId, S>) -> O>(
682        &mut self,
683        cb: F,
684    ) -> O;
685
686    /// Calls the function with immutable access to the set with all datagram
687    /// sockets.
688    fn with_all_sockets<O, F: FnOnce(&DatagramSocketSet<I, Self::WeakDeviceId, S>) -> O>(
689        &mut self,
690        cb: F,
691    ) -> O;
692
693    /// Calls the function with an immutable reference to the given socket's
694    /// state.
695    fn with_socket_state<
696        O,
697        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &SocketState<I, Self::WeakDeviceId, S>) -> O,
698    >(
699        &mut self,
700        id: &S::SocketId<I, Self::WeakDeviceId>,
701        cb: F,
702    ) -> O;
703
704    /// Calls the function with a mutable reference to the given socket's state.
705    fn with_socket_state_mut<
706        O,
707        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut SocketState<I, Self::WeakDeviceId, S>) -> O,
708    >(
709        &mut self,
710        id: &S::SocketId<I, Self::WeakDeviceId>,
711        cb: F,
712    ) -> O;
713
714    /// Call `f` with each socket's state.
715    fn for_each_socket<
716        F: FnMut(
717            &mut Self::SocketsStateCtx<'_>,
718            &S::SocketId<I, Self::WeakDeviceId>,
719            &SocketState<I, Self::WeakDeviceId, S>,
720        ),
721    >(
722        &mut self,
723        cb: F,
724    );
725}
726
727/// A convenient alias for the BoundSocketMap type to shorten type signatures.
728pub(crate) type BoundSocketsFromSpec<I, CC, S> =
729    BoundDatagramSocketMap<I, <CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, S>;
730
731/// A marker trait for bindings types traits used by datagram.
732pub trait DatagramBindingsTypes: TxMetadataBindingsTypes {}
733impl<BT> DatagramBindingsTypes for BT where BT: TxMetadataBindingsTypes {}
734
735/// The core context providing access to bound datagram sockets.
736pub trait DatagramBoundStateContext<
737    I: IpExt + DualStackIpExt,
738    BC: DatagramBindingsTypes,
739    S: DatagramSocketSpec,
740>: DeviceIdContext<AnyDevice>
741{
742    /// The core context passed to the callback provided to methods.
743    type IpSocketsCtx<'a>: TransportIpContext<I, BC>
744        + CoreTxMetadataContext<TxMetadata<I, Self::WeakDeviceId, S>, BC>
745        + MulticastMembershipHandler<I, BC>
746        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
747
748    /// Context for dual-stack socket state access.
749    ///
750    /// This type type provides access, via an implementation of the
751    /// [`DualStackDatagramBoundStateContext`] trait, to state necessary for
752    /// implementing dual-stack socket operations. While a type must always be
753    /// provided, implementations of [`DatagramBoundStateContext`] for socket
754    /// types that don't support dual-stack operation (like ICMP and raw IP
755    /// sockets, and UDPv4) can use the [`UninstantiableDualStackContext`] type,
756    /// which is uninstantiable.
757    type DualStackContext: DualStackDatagramBoundStateContext<
758            I,
759            BC,
760            S,
761            DeviceId = Self::DeviceId,
762            WeakDeviceId = Self::WeakDeviceId,
763        >;
764
765    /// Context for single-stack socket access.
766    ///
767    /// This type provides access, via an implementation of the
768    /// [`NonDualStackDatagramBoundStateContext`] trait, to functionality
769    /// necessary to implement sockets that do not support dual-stack operation.
770    type NonDualStackContext: NonDualStackDatagramBoundStateContext<
771            I,
772            BC,
773            S,
774            DeviceId = Self::DeviceId,
775            WeakDeviceId = Self::WeakDeviceId,
776        >;
777
778    /// Calls the function with an immutable reference to the datagram sockets.
779    fn with_bound_sockets<
780        O,
781        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &BoundSocketsFromSpec<I, Self, S>) -> O,
782    >(
783        &mut self,
784        cb: F,
785    ) -> O;
786
787    /// Calls the function with a mutable reference to the datagram sockets.
788    fn with_bound_sockets_mut<
789        O,
790        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut BoundSocketsFromSpec<I, Self, S>) -> O,
791    >(
792        &mut self,
793        cb: F,
794    ) -> O;
795
796    /// Provides access to either the dual-stack or non-dual-stack context.
797    ///
798    /// For socket types that don't support dual-stack operation (like ICMP,
799    /// raw IP sockets, and UDPv4), this method should always return a reference
800    /// to the non-dual-stack context to allow the caller to access
801    /// non-dual-stack state. Otherwise it should provide an instance of the
802    /// `DualStackContext`, which can be used by the caller to access dual-stack
803    /// state.
804    fn dual_stack_context(
805        &self,
806    ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext>;
807
808    /// The same as [`dual_stack_context`], but provides mutable references.
809    fn dual_stack_context_mut(
810        &mut self,
811    ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
812
813    /// Calls the function with only the inner context.
814    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
815        &mut self,
816        cb: F,
817    ) -> O;
818}
819
820/// A marker trait for the requirements of
821/// [`DualStackDatagramBoundStateContext::ds_converter`].
822pub trait DualStackConverter<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>:
823    'static
824    + OwnedOrRefsBidirectionalConverter<
825        S::ListenerIpAddr<I>,
826        DualStackListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
827    >
828    + OwnedOrRefsBidirectionalConverter<
829        S::ConnIpAddr<I>,
830        DualStackConnIpAddr<
831            I::Addr,
832            <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
833            <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
834        >,
835    >
836    + OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, DualStackConnState<I, D, S>>
837{
838}
839
840impl<I, D, S, O> DualStackConverter<I, D, S> for O
841where
842    I: IpExt,
843    D: WeakDeviceIdentifier,
844    S: DatagramSocketSpec,
845    O: 'static
846        + OwnedOrRefsBidirectionalConverter<
847            S::ListenerIpAddr<I>,
848            DualStackListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
849        >
850        + OwnedOrRefsBidirectionalConverter<
851            S::ConnIpAddr<I>,
852            DualStackConnIpAddr<
853                I::Addr,
854                <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
855                <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
856            >,
857        >
858        + OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, DualStackConnState<I, D, S>>,
859{
860}
861
862/// Provides access to dual-stack socket state.
863pub trait DualStackDatagramBoundStateContext<
864    I: IpExt,
865    BC: DatagramBindingsTypes,
866    S: DatagramSocketSpec,
867>: DeviceIdContext<AnyDevice>
868{
869    /// The core context passed to the callbacks to methods.
870    type IpSocketsCtx<'a>: TransportIpContext<I, BC>
871        + CoreTxMetadataContext<TxMetadata<I, Self::WeakDeviceId, S>, BC>
872        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
873        // Allow creating IP sockets for the other IP version.
874        + TransportIpContext<I::OtherVersion, BC>
875        + CoreTxMetadataContext<TxMetadata<I::OtherVersion, Self::WeakDeviceId, S>, BC>;
876
877    /// Returns if the socket state indicates dual-stack operation is enabled.
878    fn dual_stack_enabled(&self, ip_options: &IpOptions<I, Self::WeakDeviceId, S>) -> bool;
879
880    /// Returns the [`DatagramIpSpecificSocketOptions`] to use for packets in the other stack.
881    fn to_other_socket_options<'a>(
882        &self,
883        state: &'a IpOptions<I, Self::WeakDeviceId, S>,
884    ) -> &'a DatagramIpSpecificSocketOptions<I::OtherVersion, Self::WeakDeviceId>;
885
886    /// Asserts that the socket options indicates dual-stack operation is enabled.
887    ///
888    /// Provided trait function.
889    fn assert_dual_stack_enabled(&self, ip_options: &IpOptions<I, Self::WeakDeviceId, S>) {
890        debug_assert!(self.dual_stack_enabled(ip_options), "socket must be dual-stack enabled")
891    }
892
893    /// Returns an instance of a type that implements [`DualStackConverter`]
894    /// for addresses.
895    fn ds_converter(&self) -> impl DualStackConverter<I, Self::WeakDeviceId, S>;
896
897    /// Converts a socket ID to a bound socket ID.
898    ///
899    /// Converts a socket ID for IP version `I` into a bound socket ID that can
900    /// be inserted into the demultiplexing map for IP version `I::OtherVersion`.
901    fn to_other_bound_socket_id(
902        &self,
903        id: &S::SocketId<I, Self::WeakDeviceId>,
904    ) -> <S::SocketMapSpec<I::OtherVersion, Self::WeakDeviceId> as DatagramSocketMapSpec<
905        I::OtherVersion,
906        Self::WeakDeviceId,
907        S::AddrSpec,
908    >>::BoundSocketId;
909
910    /// Calls the provided callback with mutable access to both the
911    /// demultiplexing maps.
912    fn with_both_bound_sockets_mut<
913        O,
914        F: FnOnce(
915            &mut Self::IpSocketsCtx<'_>,
916            &mut BoundSocketsFromSpec<I, Self, S>,
917            &mut BoundSocketsFromSpec<I::OtherVersion, Self, S>,
918        ) -> O,
919    >(
920        &mut self,
921        cb: F,
922    ) -> O;
923
924    /// Calls the provided callback with mutable access to the demultiplexing
925    /// map for the other IP version.
926    fn with_other_bound_sockets_mut<
927        O,
928        F: FnOnce(
929            &mut Self::IpSocketsCtx<'_>,
930            &mut BoundSocketsFromSpec<I::OtherVersion, Self, S>,
931        ) -> O,
932    >(
933        &mut self,
934        cb: F,
935    ) -> O;
936
937    /// Calls the provided callback with access to the `IpSocketsCtx`.
938    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
939        &mut self,
940        cb: F,
941    ) -> O;
942}
943
944/// A marker trait for the requirements of
945/// [`NonDualStackDatagramBoundStateContext::nds_converter`].
946pub trait NonDualStackConverter<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>:
947    'static
948    + OwnedOrRefsBidirectionalConverter<
949        S::ListenerIpAddr<I>,
950        ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
951    >
952    + OwnedOrRefsBidirectionalConverter<
953        S::ConnIpAddr<I>,
954        ConnIpAddr<
955            I::Addr,
956            <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
957            <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
958        >,
959    >
960    + OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, ConnState<I, D, S>>
961{
962}
963
964impl<I, D, S, O> NonDualStackConverter<I, D, S> for O
965where
966    I: IpExt,
967    D: WeakDeviceIdentifier,
968    S: DatagramSocketSpec,
969    O: 'static
970        + OwnedOrRefsBidirectionalConverter<
971            S::ListenerIpAddr<I>,
972            ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
973        >
974        + OwnedOrRefsBidirectionalConverter<
975            S::ConnIpAddr<I>,
976            ConnIpAddr<
977                I::Addr,
978                <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
979                <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
980            >,
981        >
982        + OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, ConnState<I, D, S>>,
983{
984}
985
986/// Provides access to socket state for a single IP version.
987pub trait NonDualStackDatagramBoundStateContext<I: IpExt, BC, S: DatagramSocketSpec>:
988    DeviceIdContext<AnyDevice>
989{
990    /// Returns an instance of a type that implements [`NonDualStackConverter`]
991    /// for addresses.
992    fn nds_converter(&self) -> impl NonDualStackConverter<I, Self::WeakDeviceId, S>;
993}
994
995/// Blanket trait for bindings context requirements for datagram sockets.
996pub trait DatagramBindingsContext: RngContext + ReferenceNotifiers + DatagramBindingsTypes {}
997impl<BC> DatagramBindingsContext for BC where
998    BC: RngContext + ReferenceNotifiers + DatagramBindingsTypes
999{
1000}
1001
1002/// Types and behavior for datagram socket demultiplexing map.
1003///
1004/// `I: Ip` describes the type of packets that can be received by sockets in
1005/// the map.
1006pub trait DatagramSocketMapSpec<I: Ip, D: DeviceIdentifier, A: SocketMapAddrSpec>:
1007    SocketMapStateSpec<ListenerId = Self::BoundSocketId, ConnId = Self::BoundSocketId>
1008    + SocketMapConflictPolicy<
1009        ListenerAddr<ListenerIpAddr<I::Addr, A::LocalIdentifier>, D>,
1010        <Self as SocketMapStateSpec>::ListenerSharingState,
1011        I,
1012        D,
1013        A,
1014    > + SocketMapConflictPolicy<
1015        ConnAddr<ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>, D>,
1016        <Self as SocketMapStateSpec>::ConnSharingState,
1017        I,
1018        D,
1019        A,
1020    >
1021{
1022    /// The type of IDs stored in a [`BoundSocketMap`] for which this is the
1023    /// specification.
1024    ///
1025    /// This can be the same as [`DatagramSocketSpec::SocketId`] but doesn't
1026    /// have to be. In the case of
1027    /// dual-stack sockets, for example, an IPv4 socket will have type
1028    /// `DatagramSocketSpec::SocketId<Ipv4>` but the IPv4 demultiplexing map
1029    /// might have `BoundSocketId=Either<DatagramSocketSpec::SocketId<Ipv4>,
1030    /// DatagramSocketSpec::SocketId<Ipv6>>` to allow looking up IPv6 sockets
1031    /// when receiving IPv4 packets.
1032    type BoundSocketId: Clone + Debug;
1033}
1034
1035/// A marker trait for dual-stack socket features.
1036///
1037/// This trait acts as a marker for [`DualStackBaseIpExt`] for both `Self` and
1038/// `Self::OtherVersion`.
1039pub trait DualStackIpExt:
1040    DualStackBaseIpExt
1041    + socket::DualStackIpExt<OtherVersion: DualStackBaseIpExt + FilterIpExt + IpLayerIpExt>
1042{
1043}
1044
1045impl<I> DualStackIpExt for I where
1046    I: DualStackBaseIpExt
1047        + socket::DualStackIpExt<OtherVersion: DualStackBaseIpExt + FilterIpExt + IpLayerIpExt>
1048{
1049}
1050
1051/// Common features of dual-stack sockets that vary by IP version.
1052///
1053/// This trait exists to provide per-IP-version associated types that are
1054/// useful for implementing dual-stack sockets. The types are intentionally
1055/// asymmetric - `DualStackIpExt::Xxx` has a different shape for the [`Ipv4`]
1056/// and [`Ipv6`] impls.
1057pub trait DualStackBaseIpExt:
1058    socket::DualStackIpExt + SocketIpExt + netstack3_base::IpExt + FilterIpExt + IpLayerIpExt
1059{
1060    /// The type of socket that can receive an IP packet.
1061    ///
1062    /// For `Ipv4`, this is [`EitherIpSocket<S>`], and for `Ipv6` it is just
1063    /// `S::SocketId<Ipv6>`.
1064    ///
1065    /// [`EitherIpSocket<S>]`: [EitherIpSocket]
1066    type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec>: Clone + Debug + Eq;
1067
1068    /// The IP options type for the other stack that will be held for a socket.
1069    ///
1070    /// For [`Ipv4`], this is `()`, and for [`Ipv6`] it is `State`. For a
1071    /// protocol like UDP or TCP where the IPv6 socket is dual-stack capable,
1072    /// the generic state struct can have a field with type
1073    /// `I::OtherStackIpOptions<Ipv4InIpv6Options>`.
1074    type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync>: Clone
1075        + Debug
1076        + Default
1077        + Send
1078        + Sync;
1079
1080    /// A listener address for dual-stack operation.
1081    type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>>: Clone
1082        + Debug
1083        + Send
1084        + Sync
1085        + Into<(Option<SpecifiedAddr<Self::Addr>>, NonZeroU16)>;
1086
1087    /// A connected address for dual-stack operation.
1088    type DualStackConnIpAddr<S: DatagramSocketSpec>: Clone
1089        + Debug
1090        + Into<ConnInfoAddr<Self::Addr, <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>>;
1091
1092    /// Connection state for a dual-stack socket.
1093    type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec>: Debug + Send + Sync
1094    where
1095        Self::OtherVersion: DualStackBaseIpExt;
1096
1097    /// Convert a socket ID into a `Self::DualStackBoundSocketId`.
1098    ///
1099    /// For coherency reasons this can't be a `From` bound on
1100    /// `DualStackBoundSocketId`. If more methods are added, consider moving
1101    /// this to its own dedicated trait that bounds `DualStackBoundSocketId`.
1102    fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1103        id: S::SocketId<Self, D>,
1104    ) -> Self::DualStackBoundSocketId<D, S>
1105    where
1106        Self: IpExt;
1107
1108    /// Retrieves the associated connection address from the connection state.
1109    fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1110        state: &Self::DualStackConnState<D, S>,
1111    ) -> ConnAddr<Self::DualStackConnIpAddr<S>, D>
1112    where
1113        Self::OtherVersion: DualStackBaseIpExt;
1114}
1115
1116/// An IP Socket ID that is either `Ipv4` or `Ipv6`.
1117#[derive(Derivative)]
1118#[derivative(
1119    Clone(bound = ""),
1120    Debug(bound = ""),
1121    Eq(bound = "S::SocketId<Ipv4, D>: Eq, S::SocketId<Ipv6, D>: Eq"),
1122    PartialEq(bound = "S::SocketId<Ipv4, D>: PartialEq, S::SocketId<Ipv6, D>: PartialEq")
1123)]
1124#[allow(missing_docs)]
1125pub enum EitherIpSocket<D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
1126    V4(S::SocketId<Ipv4, D>),
1127    V6(S::SocketId<Ipv6, D>),
1128}
1129
1130impl<CC, D, S> SocketMetadata<CC> for EitherIpSocket<D, S>
1131where
1132    D: WeakDeviceIdentifier,
1133    S: DatagramSocketSpec,
1134    S::SocketId<Ipv4, D>: SocketMetadata<CC>,
1135    S::SocketId<Ipv6, D>: SocketMetadata<CC>,
1136{
1137    fn socket_cookie(&self, core_ctx: &mut CC) -> SocketCookie {
1138        match self {
1139            EitherIpSocket::V4(id) => id.socket_cookie(core_ctx),
1140            EitherIpSocket::V6(id) => id.socket_cookie(core_ctx),
1141        }
1142    }
1143    fn marks(&self, core_ctx: &mut CC) -> Marks {
1144        match self {
1145            EitherIpSocket::V4(id) => id.marks(core_ctx),
1146            EitherIpSocket::V6(id) => id.marks(core_ctx),
1147        }
1148    }
1149}
1150
1151impl DualStackBaseIpExt for Ipv4 {
1152    /// Incoming IPv4 packets may be received by either IPv4 or IPv6 sockets.
1153    type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
1154        EitherIpSocket<D, S>;
1155    type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync> = ();
1156    /// IPv4 sockets can't listen on dual-stack addresses.
1157    type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>> =
1158        ListenerIpAddr<Self::Addr, LocalIdentifier>;
1159    /// IPv4 sockets cannot connect on dual-stack addresses.
1160    type DualStackConnIpAddr<S: DatagramSocketSpec> = ConnIpAddr<
1161        Self::Addr,
1162        <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1163        <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
1164    >;
1165    /// IPv4 sockets cannot connect on dual-stack addresses.
1166    type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec> = ConnState<Self, D, S>;
1167
1168    fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1169        id: S::SocketId<Self, D>,
1170    ) -> Self::DualStackBoundSocketId<D, S> {
1171        EitherIpSocket::V4(id)
1172    }
1173
1174    fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1175        state: &Self::DualStackConnState<D, S>,
1176    ) -> ConnAddr<Self::DualStackConnIpAddr<S>, D> {
1177        let ConnState { socket: _, shutdown: _, addr, clear_device_on_disconnect: _, extra: _ } =
1178            state;
1179        addr.clone()
1180    }
1181}
1182
1183impl DualStackBaseIpExt for Ipv6 {
1184    /// Incoming IPv6 packets may only be received by IPv6 sockets.
1185    type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
1186        S::SocketId<Self, D>;
1187    type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync> = State;
1188    /// IPv6 listeners can listen on dual-stack addresses (if the protocol
1189    /// and socket are dual-stack-enabled).
1190    type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>> =
1191        DualStackListenerIpAddr<Self::Addr, LocalIdentifier>;
1192    /// IPv6 sockets can connect on dual-stack addresses (if the protocol and
1193    /// socket are dual-stack-enabled).
1194    type DualStackConnIpAddr<S: DatagramSocketSpec> = DualStackConnIpAddr<
1195        Self::Addr,
1196        <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1197        <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
1198    >;
1199    /// IPv6 sockets can connect on dual-stack addresses (if the protocol and
1200    /// socket are dual-stack-enabled).
1201    type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
1202        DualStackConnState<Self, D, S>;
1203
1204    fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1205        id: S::SocketId<Self, D>,
1206    ) -> Self::DualStackBoundSocketId<D, S> {
1207        id
1208    }
1209
1210    fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1211        state: &Self::DualStackConnState<D, S>,
1212    ) -> ConnAddr<Self::DualStackConnIpAddr<S>, D> {
1213        match state {
1214            DualStackConnState::ThisStack(state) => {
1215                let ConnState { addr, .. } = state;
1216                let ConnAddr { ip, device } = addr.clone();
1217                ConnAddr { ip: DualStackConnIpAddr::ThisStack(ip), device }
1218            }
1219            DualStackConnState::OtherStack(state) => {
1220                let ConnState {
1221                    socket: _,
1222                    shutdown: _,
1223                    addr,
1224                    clear_device_on_disconnect: _,
1225                    extra: _,
1226                } = state;
1227                let ConnAddr { ip, device } = addr.clone();
1228                ConnAddr { ip: DualStackConnIpAddr::OtherStack(ip), device }
1229            }
1230        }
1231    }
1232}
1233
1234#[derive(GenericOverIp)]
1235#[generic_over_ip(I, Ip)]
1236/// A wrapper to make [`DualStackIpExt::OtherStackIpOptions`] [`GenericOverIp`].
1237pub struct WrapOtherStackIpOptions<
1238    'a,
1239    I: DualStackIpExt,
1240    S: 'a + Clone + Debug + Default + Send + Sync,
1241>(pub &'a I::OtherStackIpOptions<S>);
1242
1243#[derive(GenericOverIp)]
1244#[generic_over_ip(I, Ip)]
1245/// A wrapper to make [`DualStackIpExt::OtherStackIpOptions`] [`GenericOverIp`].
1246pub struct WrapOtherStackIpOptionsMut<
1247    'a,
1248    I: DualStackIpExt,
1249    S: 'a + Clone + Debug + Default + Send + Sync,
1250>(pub &'a mut I::OtherStackIpOptions<S>);
1251
1252/// Types and behavior for datagram sockets.
1253///
1254/// These sockets may or may not support dual-stack operation.
1255pub trait DatagramSocketSpec: Sized + 'static {
1256    /// Name of this datagram protocol.
1257    const NAME: &'static str;
1258
1259    /// The socket address spec for the datagram socket type.
1260    ///
1261    /// This describes the types of identifiers the socket uses, e.g.
1262    /// local/remote port for UDP.
1263    type AddrSpec: SocketMapAddrSpec;
1264
1265    /// Identifier for an individual socket for a given IP version.
1266    ///
1267    /// Corresponds uniquely to a socket resource. This is the type that will
1268    /// be returned by [`create`] and used to identify which socket is being
1269    /// acted on by calls like [`listen`], [`connect`], [`remove`], etc.
1270    type SocketId<I: IpExt, D: WeakDeviceIdentifier>: Clone
1271        + Debug
1272        + Eq
1273        + Send
1274        + Borrow<StrongRc<I, D, Self>>
1275        + From<StrongRc<I, D, Self>>;
1276
1277    /// The weak version of `SocketId`.
1278    type WeakSocketId<I: IpExt, D: WeakDeviceIdentifier>: Clone + Debug + Eq + Send;
1279
1280    /// IP-level options for sending `I::OtherVersion` IP packets.
1281    type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier>: Clone
1282        + Debug
1283        + Default
1284        + Send
1285        + Sync;
1286
1287    /// The type of a listener IP address.
1288    ///
1289    /// For dual-stack-capable datagram protocols like UDP, this should use
1290    /// [`DualStackIpExt::ListenerIpAddr`], which will be one of
1291    /// [`ListenerIpAddr`] or [`DualStackListenerIpAddr`].
1292    /// Non-dual-stack-capable protocols (like ICMP and raw IP sockets) should
1293    /// just use [`ListenerIpAddr`].
1294    type ListenerIpAddr<I: IpExt>: Clone
1295        + Debug
1296        + Into<(Option<SpecifiedAddr<I::Addr>>, NonZeroU16)>
1297        + Send
1298        + Sync
1299        + 'static;
1300
1301    /// The sharing state for a socket.
1302    ///
1303    /// NB: The underlying [`BoundSocketMap`]` uses separate types for the
1304    /// sharing state of connected vs listening sockets. At the moment, datagram
1305    /// sockets have no need for differentiated sharing states, so consolidate
1306    /// them under one type.
1307    type SharingState: Clone + Debug + Default + Send + Sync + 'static;
1308
1309    /// The type of an IP address for a connected socket.
1310    ///
1311    /// For dual-stack-capable datagram protocols like UDP, this should use
1312    /// [`DualStackIpExt::ConnIpAddr`], which will be one of
1313    /// [`ConnIpAddr`] or [`DualStackConnIpAddr`].
1314    /// Non-dual-stack-capable protocols (like ICMP and raw IP sockets) should
1315    /// just use [`ConnIpAddr`].
1316    type ConnIpAddr<I: IpExt>: Clone
1317        + Debug
1318        + Into<ConnInfoAddr<I::Addr, <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>>;
1319
1320    /// The type of a state held by a connected socket.
1321    ///
1322    /// For dual-stack-capable datagram protocols like UDP, this should use
1323    /// [`DualStackIpExt::ConnState`], which will be one of [`ConnState`] or
1324    /// [`DualStackConnState`]. Non-dual-stack-capable protocols (like ICMP and
1325    /// raw IP sockets) should just use [`ConnState`].
1326    type ConnState<I: IpExt, D: WeakDeviceIdentifier>: Debug + Send + Sync;
1327
1328    /// The extra state that a connection state want to remember.
1329    ///
1330    /// For example: UDP sockets does not have any extra state to remember, so
1331    /// it should just be `()`; ICMP sockets need to remember the remote ID the
1332    /// socket is 'connected' to, the remote ID is not used when sending nor
1333    /// participating in the demuxing decisions. So it will be stored in the
1334    /// extra state so that it can be retrieved later, i.e, it should be
1335    /// `NonZeroU16` for ICMP sockets.
1336    type ConnStateExtra: Debug + Send + Sync;
1337
1338    /// The specification for the [`BoundSocketMap`] for a given IP version.
1339    ///
1340    /// Describes the per-address and per-socket values held in the
1341    /// demultiplexing map for a given IP version.
1342    type SocketMapSpec<I: IpExt + DualStackIpExt, D: WeakDeviceIdentifier>: DatagramSocketMapSpec<
1343            I,
1344            D,
1345            Self::AddrSpec,
1346            ListenerSharingState = Self::SharingState,
1347            ConnSharingState = Self::SharingState,
1348        >;
1349
1350    /// External data kept by datagram sockets.
1351    ///
1352    /// This is used to store opaque bindings data alongside the core data
1353    /// inside the socket references.
1354    type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
1355
1356    /// Settings type offered by bindings for this datagram socket.
1357    type Settings: AsRef<DatagramSettings> + Default;
1358
1359    /// Per-socket counters tracked by datagram sockets.
1360    type Counters<I: Ip>: Debug + Default + Send + Sync + 'static;
1361
1362    /// The listener type that is notified about the socket writable state.
1363    type SocketWritableListener: SocketWritableListener + Debug + Send + Sync + 'static;
1364
1365    /// The size in bytes of the fixed header for the datagram transport.
1366    ///
1367    /// This is used to calculate the per-packet send buffer cost of an egress
1368    /// datagram.
1369    ///
1370    /// This value must be the _additional_ bytes wrapped in a body when
1371    /// [`DatagramSocketSpec::make_packet`] is called.
1372    const FIXED_HEADER_SIZE: usize;
1373
1374    /// Returns the IP protocol of this datagram specification.
1375    fn ip_proto<I: IpProtoExt>() -> I::Proto;
1376
1377    /// Converts [`Self::SocketId`] to [`DatagramSocketMapSpec::BoundSocketId`].
1378    ///
1379    /// Constructs a socket identifier to its in-demultiplexing map form. For
1380    /// protocols with dual-stack sockets, like UDP, implementations should
1381    /// perform a transformation. Otherwise it should be the identity function.
1382    fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
1383        s: &Self::SocketId<I, D>,
1384    ) -> <Self::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, Self::AddrSpec>>::BoundSocketId;
1385
1386    /// The type of serializer returned by [`DatagramSocketSpec::make_packet`]
1387    /// for a given IP version and buffer type.
1388    type Serializer<I: IpExt, B: BufferMut>: TransportPacketSerializer<I, Buffer = B>;
1389    /// The potential error for serializing a packet. For example, in UDP, this
1390    /// should be infallible but for ICMP, there will be an error if the input
1391    /// is not an echo request.
1392    type SerializeError: Error;
1393
1394    /// Constructs a packet serializer with `addr` and `body`.
1395    fn make_packet<I: IpExt, B: BufferMut>(
1396        body: B,
1397        addr: &ConnIpAddr<
1398            I::Addr,
1399            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1400            <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
1401        >,
1402    ) -> Result<Self::Serializer<I, B>, Self::SerializeError>;
1403
1404    /// Attempts to allocate a local identifier for a listening socket.
1405    ///
1406    /// Returns the identifier on success, or `None` on failure.
1407    fn try_alloc_listen_identifier<I: IpExt, D: WeakDeviceIdentifier>(
1408        rng: &mut impl RngContext,
1409        is_available: impl Fn(
1410            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1411        ) -> Result<(), InUseError>,
1412    ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
1413
1414    /// Retrieves the associated connection info from the connection state.
1415    fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
1416        state: &Self::ConnState<I, D>,
1417    ) -> ConnInfo<I::Addr, D>;
1418
1419    /// Tries to allocate a local identifier.
1420    fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
1421        bound: &BoundDatagramSocketMap<I, D, Self>,
1422        bindings_ctx: &mut BC,
1423        flow: DatagramFlowId<I::Addr, <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
1424    ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
1425
1426    /// Downgrades a `SocketId` into a `WeakSocketId`.
1427    // TODO(https://fxbug.dev/392672414): Replace this with a base trait.
1428    fn downgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
1429        id: &Self::SocketId<I, D>,
1430    ) -> Self::WeakSocketId<I, D>;
1431
1432    /// Attempts to upgrade a `WeakSocketId` into a `SocketId`.
1433    // TODO(https://fxbug.dev/392672414): Replace this with a base trait.
1434    fn upgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
1435        id: &Self::WeakSocketId<I, D>,
1436    ) -> Option<Self::SocketId<I, D>>;
1437}
1438
1439/// The error returned when an identifier (i.e.) port is already in use.
1440pub struct InUseError;
1441
1442/// Creates a primary ID without inserting it into the all socket map.
1443pub fn create_primary_id<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1444    external_data: S::ExternalData<I>,
1445    writable_listener: S::SocketWritableListener,
1446    settings: &DatagramSettings,
1447) -> PrimaryRc<I, D, S> {
1448    PrimaryRc::new(ReferenceState {
1449        state: RwLock::new(SocketState {
1450            inner: SocketStateInner::Unbound(UnboundSocketState::default()),
1451            ip_options: Default::default(),
1452            sharing: Default::default(),
1453        }),
1454        external_data,
1455        send_buffer: SendBufferTracking::new(writable_listener, settings),
1456        counters: Default::default(),
1457    })
1458}
1459
1460/// Information associated with a datagram listener.
1461#[derive(GenericOverIp, Debug, Eq, PartialEq)]
1462#[generic_over_ip(A, IpAddress)]
1463pub struct ListenerInfo<A: IpAddress, D> {
1464    /// The local address associated with a datagram listener, or `None` for any
1465    /// address.
1466    pub local_ip: Option<StrictlyZonedAddr<A, SpecifiedAddr<A>, D>>,
1467    /// The local port associated with a datagram listener.
1468    pub local_identifier: NonZeroU16,
1469}
1470
1471impl<A: IpAddress, LA: Into<(Option<SpecifiedAddr<A>>, NonZeroU16)>, D> From<ListenerAddr<LA, D>>
1472    for ListenerInfo<A, D>
1473{
1474    fn from(ListenerAddr { ip, device }: ListenerAddr<LA, D>) -> Self {
1475        let (addr, local_identifier) = ip.into();
1476        Self {
1477            local_ip: addr.map(|addr| {
1478                StrictlyZonedAddr::new_with_zone(addr, || {
1479                    // The invariant that a zone is present if needed is upheld by
1480                    // set_bindtodevice and bind.
1481                    device.expect("device must be bound for addresses that require zones")
1482                })
1483            }),
1484            local_identifier,
1485        }
1486    }
1487}
1488
1489impl<A: IpAddress, D> From<NonZeroU16> for ListenerInfo<A, D> {
1490    fn from(local_identifier: NonZeroU16) -> Self {
1491        Self { local_ip: None, local_identifier }
1492    }
1493}
1494
1495/// Information associated with a datagram connection.
1496#[derive(Debug, GenericOverIp, PartialEq)]
1497#[generic_over_ip(A, IpAddress)]
1498pub struct ConnInfo<A: IpAddress, D> {
1499    /// The local address associated with a datagram connection.
1500    pub local_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
1501    /// The local identifier associated with a datagram connection.
1502    pub local_identifier: NonZeroU16,
1503    /// The remote address associated with a datagram connection.
1504    pub remote_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
1505    /// The remote identifier associated with a datagram connection.
1506    pub remote_identifier: u16,
1507}
1508
1509impl<A: IpAddress, D> ConnInfo<A, D> {
1510    /// Construct a new `ConnInfo`.
1511    pub fn new(
1512        local_ip: SpecifiedAddr<A>,
1513        local_identifier: NonZeroU16,
1514        remote_ip: SpecifiedAddr<A>,
1515        remote_identifier: u16,
1516        mut get_zone: impl FnMut() -> D,
1517    ) -> Self {
1518        Self {
1519            local_ip: StrictlyZonedAddr::new_with_zone(local_ip, &mut get_zone),
1520            local_identifier,
1521            remote_ip: StrictlyZonedAddr::new_with_zone(remote_ip, &mut get_zone),
1522            remote_identifier,
1523        }
1524    }
1525}
1526
1527/// Information about the addresses for a socket.
1528#[derive(GenericOverIp, Debug, PartialEq)]
1529#[generic_over_ip(A, IpAddress)]
1530pub enum SocketInfo<A: IpAddress, D> {
1531    /// The socket is not bound.
1532    Unbound,
1533    /// The socket is listening.
1534    Listener(ListenerInfo<A, D>),
1535    /// The socket is connected.
1536    Connected(ConnInfo<A, D>),
1537}
1538
1539/// A type of an operation that can be performed on a socket entry.
1540trait EntryOperationType {
1541    type ReverseOp: EntryOperationType<ReverseOp = Self>;
1542    type Error: Debug;
1543
1544    /// Applies the operation to the bound socket map. Returns the reverse operation.
1545    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1546        op: SocketEntryOp<I, D, S, Self>,
1547        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1548    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error>;
1549}
1550
1551enum EntryInsertOp {}
1552impl EntryOperationType for EntryInsertOp {
1553    type ReverseOp = EntryRemoveOp;
1554    type Error = InsertError;
1555
1556    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1557        op: SocketEntryOp<I, D, S, Self>,
1558        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1559    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error> {
1560        let SocketEntryOp { socket_id, sharing, addr, _marker } = op;
1561        match &addr {
1562            AddrVec::Listen(addr) => {
1563                let SocketStateEntry { .. } = sockets.listeners_mut().try_insert(
1564                    addr.clone(),
1565                    sharing.clone(),
1566                    socket_id.clone(),
1567                )?;
1568            }
1569            AddrVec::Conn(addr) => {
1570                let SocketStateEntry { .. } = sockets.conns_mut().try_insert(
1571                    addr.clone(),
1572                    sharing.clone(),
1573                    socket_id.clone(),
1574                )?;
1575            }
1576        };
1577        Ok(SocketEntryOp { socket_id, sharing, addr, _marker: PhantomData })
1578    }
1579}
1580
1581enum EntryRemoveOp {}
1582impl EntryOperationType for EntryRemoveOp {
1583    type ReverseOp = EntryInsertOp;
1584    type Error = NotFoundError;
1585
1586    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1587        op: SocketEntryOp<I, D, S, Self>,
1588        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1589    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error> {
1590        let SocketEntryOp { socket_id, sharing, addr, _marker } = op;
1591        match &addr {
1592            AddrVec::Listen(addr) => {
1593                sockets.listeners_mut().remove(&socket_id, &addr)?;
1594            }
1595            AddrVec::Conn(addr) => {
1596                sockets.conns_mut().remove(&socket_id, &addr)?;
1597            }
1598        };
1599        Ok(SocketEntryOp { socket_id, sharing, addr, _marker: PhantomData })
1600    }
1601}
1602
1603/// State associated with insertion or removal operations on the bound socket map.
1604struct SocketEntryOp<
1605    I: IpExt,
1606    D: WeakDeviceIdentifier,
1607    S: DatagramSocketSpec,
1608    O: EntryOperationType + ?Sized,
1609> {
1610    socket_id: <S::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, S::AddrSpec>>::BoundSocketId,
1611    sharing: S::SharingState,
1612    addr: AddrVec<I, D, S::AddrSpec>,
1613    _marker: PhantomData<O>,
1614}
1615
1616type SingleStackRemoveOperation<I, D, S> = SocketEntryOp<I, D, S, EntryRemoveOp>;
1617type SingleStackInsertOperation<I, D, S> = SocketEntryOp<I, D, S, EntryInsertOp>;
1618
1619impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> SingleStackRemoveOperation<I, D, S> {
1620    /// Constructs the remove operation from existing socket state.
1621    fn new_from_state<BC, CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
1622        core_ctx: &mut CC,
1623        socket_id: &S::SocketId<I, D>,
1624        state: &BoundSocketState<I, D, S>,
1625        sharing: S::SharingState,
1626    ) -> Self {
1627        let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
1628        match state {
1629            BoundSocketStateType::Listener(ListenerState { addr: ListenerAddr { ip, device } }) => {
1630                Self {
1631                    addr: AddrVec::Listen(ListenerAddr {
1632                        ip: core_ctx.nds_converter().convert(ip.clone()),
1633                        device: device.clone(),
1634                    }),
1635                    sharing,
1636                    socket_id: S::make_bound_socket_map_id(socket_id),
1637                    _marker: PhantomData,
1638                }
1639            }
1640            BoundSocketStateType::Connected(state) => {
1641                let ConnState {
1642                    addr,
1643                    socket: _,
1644                    clear_device_on_disconnect: _,
1645                    shutdown: _,
1646                    extra: _,
1647                } = core_ctx.nds_converter().convert(state);
1648                Self {
1649                    addr: AddrVec::Conn(addr.clone()),
1650                    sharing,
1651                    socket_id: S::make_bound_socket_map_id(socket_id),
1652                    _marker: PhantomData,
1653                }
1654            }
1655        }
1656    }
1657}
1658
1659impl<I, D, S, O> SocketEntryOp<I, D, S, O>
1660where
1661    I: IpExt,
1662    D: WeakDeviceIdentifier,
1663    S: DatagramSocketSpec,
1664    O: EntryOperationType,
1665{
1666    /// Applies the operation and returns the reverse operation.
1667    fn apply(
1668        self,
1669        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1670    ) -> Result<SocketEntryOp<I, D, S, O::ReverseOp>, O::Error> {
1671        O::apply(self, sockets)
1672    }
1673}
1674
1675struct DualStackListenerOp<I, D, S, O>
1676where
1677    I: IpExt,
1678    D: WeakDeviceIdentifier,
1679    S: DatagramSocketSpec,
1680    O: EntryOperationType,
1681{
1682    identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1683    device: Option<D>,
1684    sharing: S::SharingState,
1685    socket_ids: PairedBoundSocketIds<I, D, S>,
1686    _marker: PhantomData<O>,
1687}
1688
1689type DualStackListenerRemoveOperation<I, D, S> = DualStackListenerOp<I, D, S, EntryRemoveOp>;
1690type DualStackListenerInsertOperation<I, D, S> = DualStackListenerOp<I, D, S, EntryInsertOp>;
1691
1692impl<I, D, S, O> DualStackListenerOp<I, D, S, O>
1693where
1694    I: IpExt,
1695    D: WeakDeviceIdentifier,
1696    S: DatagramSocketSpec,
1697    O: EntryOperationType,
1698{
1699    fn this_stack_op(&self) -> SocketEntryOp<I, D, S, O> {
1700        SocketEntryOp {
1701            addr: AddrVec::Listen(ListenerAddr {
1702                ip: ListenerIpAddr { addr: None, identifier: self.identifier },
1703                device: self.device.clone(),
1704            }),
1705            sharing: self.sharing.clone(),
1706            socket_id: self.socket_ids.this.clone(),
1707            _marker: PhantomData,
1708        }
1709    }
1710
1711    fn other_stack_op(&self) -> SocketEntryOp<I::OtherVersion, D, S, O> {
1712        SocketEntryOp {
1713            addr: AddrVec::Listen(ListenerAddr {
1714                ip: ListenerIpAddr { addr: None, identifier: self.identifier },
1715                device: self.device.clone(),
1716            }),
1717            sharing: self.sharing.clone(),
1718            socket_id: self.socket_ids.other.clone(),
1719            _marker: PhantomData,
1720        }
1721    }
1722
1723    /// Apply this operation to the given `BoundDatagramSocketMap`s. Returns the reverse
1724    /// operation.
1725    fn apply(
1726        self,
1727        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1728        other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
1729    ) -> Result<DualStackListenerOp<I, D, S, O::ReverseOp>, O::Error> {
1730        let this_stack_reverse_op = self.this_stack_op().apply(sockets)?;
1731        match self.other_stack_op().apply(other_sockets) {
1732            Ok(SocketEntryOp::<_, _, _, O::ReverseOp> { .. }) => (),
1733            Err(e) => {
1734                let _: SocketEntryOp<_, _, _, O> = this_stack_reverse_op
1735                    .apply(sockets)
1736                    .expect("Failed to revert socket map operation");
1737                return Err(e);
1738            }
1739        };
1740
1741        let Self { identifier, device, sharing, socket_ids, _marker: _ } = self;
1742        Ok(DualStackListenerOp { identifier, device, sharing, socket_ids, _marker: PhantomData })
1743    }
1744}
1745
1746/// State associated with a dual-stack socket entry operation.
1747enum DualStackSocketEntryOp<I, D, S, O>
1748where
1749    I: IpExt,
1750    D: WeakDeviceIdentifier,
1751    S: DatagramSocketSpec,
1752    O: EntryOperationType,
1753{
1754    CurrentStack(SocketEntryOp<I, D, S, O>),
1755    OtherStack(SocketEntryOp<I::OtherVersion, D, S, O>),
1756    ListenerBothStacks(DualStackListenerOp<I, D, S, O>),
1757}
1758
1759type DualStackRemoveOperation<I, D, S> = DualStackSocketEntryOp<I, D, S, EntryRemoveOp>;
1760type DualStackInsertOperation<I, D, S> = DualStackSocketEntryOp<I, D, S, EntryInsertOp>;
1761
1762impl<I, D, S> DualStackRemoveOperation<I, D, S>
1763where
1764    I: IpExt,
1765    D: WeakDeviceIdentifier,
1766    S: DatagramSocketSpec,
1767{
1768    /// Constructs the removal operation from existing socket state.
1769    fn new_from_state<BC, CC>(
1770        core_ctx: &mut CC,
1771        socket_id: &S::SocketId<I, D>,
1772        ip_options: &IpOptions<I, D, S>,
1773        state: &BoundSocketState<I, D, S>,
1774        sharing: S::SharingState,
1775    ) -> Self
1776    where
1777        BC: DatagramBindingsTypes,
1778        CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
1779    {
1780        let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
1781        match state {
1782            BoundSocketStateType::Listener(ListenerState { addr }) => {
1783                let ListenerAddr { ip, device } = addr.clone();
1784                match (core_ctx.ds_converter().convert(ip), core_ctx.dual_stack_enabled(ip_options))
1785                {
1786                    // Dual-stack enabled, bound in both stacks.
1787                    (DualStackListenerIpAddr::BothStacks(identifier), true) => {
1788                        DualStackSocketEntryOp::ListenerBothStacks(DualStackListenerOp {
1789                            identifier: identifier.clone(),
1790                            device,
1791                            sharing,
1792                            socket_ids: PairedBoundSocketIds {
1793                                this: S::make_bound_socket_map_id(socket_id),
1794                                other: core_ctx.to_other_bound_socket_id(socket_id),
1795                            },
1796                            _marker: PhantomData,
1797                        })
1798                    }
1799                    // Bound in this stack, with/without dual-stack enabled.
1800                    (DualStackListenerIpAddr::ThisStack(addr), true | false) => {
1801                        DualStackSocketEntryOp::CurrentStack(SocketEntryOp {
1802                            addr: AddrVec::Listen(ListenerAddr { ip: addr, device }),
1803                            sharing,
1804                            socket_id: S::make_bound_socket_map_id(socket_id),
1805                            _marker: PhantomData,
1806                        })
1807                    }
1808                    // Dual-stack enabled, bound only in the other stack.
1809                    (DualStackListenerIpAddr::OtherStack(addr), true) => {
1810                        DualStackSocketEntryOp::OtherStack(SocketEntryOp {
1811                            addr: AddrVec::Listen(ListenerAddr { ip: addr, device }),
1812                            sharing,
1813                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
1814                            _marker: PhantomData,
1815                        })
1816                    }
1817                    (DualStackListenerIpAddr::OtherStack(_), false)
1818                    | (DualStackListenerIpAddr::BothStacks(_), false) => {
1819                        unreachable!("dual-stack disabled socket cannot use the other stack")
1820                    }
1821                }
1822            }
1823            BoundSocketStateType::Connected(state) => {
1824                match core_ctx.ds_converter().convert(state) {
1825                    DualStackConnState::ThisStack(ConnState { addr, .. }) => {
1826                        DualStackSocketEntryOp::CurrentStack(SocketEntryOp {
1827                            addr: AddrVec::Conn(addr.clone()),
1828                            sharing,
1829                            socket_id: S::make_bound_socket_map_id(socket_id),
1830                            _marker: PhantomData,
1831                        })
1832                    }
1833                    DualStackConnState::OtherStack(ConnState { addr, .. }) => {
1834                        core_ctx.assert_dual_stack_enabled(&ip_options);
1835                        DualStackSocketEntryOp::OtherStack(SocketEntryOp {
1836                            addr: AddrVec::Conn(addr.clone()),
1837                            sharing,
1838                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
1839                            _marker: PhantomData,
1840                        })
1841                    }
1842                }
1843            }
1844        }
1845    }
1846}
1847
1848impl<I, D, S, O> DualStackSocketEntryOp<I, D, S, O>
1849where
1850    I: IpExt,
1851    D: WeakDeviceIdentifier,
1852    S: DatagramSocketSpec,
1853    O: EntryOperationType,
1854{
1855    /// Apply this operation to the given `BoundSocketMap`s. Returns the reverse
1856    /// operation.
1857    fn apply(
1858        self,
1859        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1860        other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
1861    ) -> Result<DualStackSocketEntryOp<I, D, S, O::ReverseOp>, O::Error> {
1862        let result = match self {
1863            DualStackSocketEntryOp::CurrentStack(remove) => {
1864                DualStackSocketEntryOp::CurrentStack(remove.apply(sockets)?)
1865            }
1866            DualStackSocketEntryOp::OtherStack(remove) => {
1867                DualStackSocketEntryOp::OtherStack(remove.apply(other_sockets)?)
1868            }
1869            DualStackSocketEntryOp::ListenerBothStacks(listener_op) => {
1870                DualStackSocketEntryOp::ListenerBothStacks(
1871                    listener_op.apply(sockets, other_sockets)?,
1872                )
1873            }
1874        };
1875        Ok(result)
1876    }
1877}
1878
1879/// Abstraction for operations over one or two demultiplexing maps.
1880trait BoundStateHandler<I: IpExt, S: DatagramSocketSpec, D: WeakDeviceIdentifier> {
1881    /// The type of address that can be inserted or removed for listeners.
1882    type ListenerAddr: Clone;
1883    /// The type of ID that can be inserted or removed.
1884    type BoundSocketId;
1885
1886    /// Checks whether an entry could be inserted for the specified address and
1887    /// identifier.
1888    ///
1889    /// Returns `true` if a value could be inserted at the specified address and
1890    /// local ID, with the provided sharing state; otherwise returns `false`.
1891    fn is_listener_entry_available(
1892        &self,
1893        addr: Self::ListenerAddr,
1894        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1895        sharing_state: &S::SharingState,
1896    ) -> bool;
1897
1898    /// Inserts `id` at a listener address or returns an error.
1899    ///
1900    /// Inserts the identifier `id` at the listener address for `addr` and
1901    /// local `identifier` with device `device` and the given sharing state. If
1902    /// the insertion conflicts with an existing socket, a `LocalAddressError`
1903    /// is returned.
1904    fn try_insert_listener(
1905        &mut self,
1906        addr: Self::ListenerAddr,
1907        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1908        device: Option<D>,
1909        sharing: S::SharingState,
1910        id: Self::BoundSocketId,
1911    ) -> Result<(), LocalAddressError>;
1912}
1913
1914/// An alias for `BoundSocketMap` specialized for datagram sockets.
1915pub type BoundDatagramSocketMap<I, D, S> = BoundSocketMap<
1916    I,
1917    D,
1918    <S as DatagramSocketSpec>::AddrSpec,
1919    <S as DatagramSocketSpec>::SocketMapSpec<I, D>,
1920>;
1921
1922type BoundDatagramSocketId<I, D, S> =
1923    <<S as DatagramSocketSpec>::SocketMapSpec<I, D> as DatagramSocketMapSpec<
1924        I,
1925        D,
1926        <S as DatagramSocketSpec>::AddrSpec,
1927    >>::BoundSocketId;
1928
1929/// A sentinel type for the unspecified address in a dual-stack context.
1930///
1931/// This is kind of like [`Ipv6::UNSPECIFIED_ADDRESS`], but makes it clear that
1932/// the value is being used in a dual-stack context.
1933#[derive(Copy, Clone, Debug)]
1934struct DualStackUnspecifiedAddr;
1935
1936/// Implementation of BoundStateHandler for a single demultiplexing map.
1937impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
1938    for BoundDatagramSocketMap<I, D, S>
1939{
1940    type ListenerAddr = Option<SocketIpAddr<I::Addr>>;
1941    type BoundSocketId = BoundDatagramSocketId<I, D, S>;
1942
1943    fn is_listener_entry_available(
1944        &self,
1945        addr: Self::ListenerAddr,
1946        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1947        sharing: &S::SharingState,
1948    ) -> bool {
1949        let check_addr = ListenerAddr { device: None, ip: ListenerIpAddr { identifier, addr } };
1950        match self.listeners().could_insert(&check_addr, sharing) {
1951            Ok(()) => true,
1952            Err(
1953                InsertError::Exists
1954                | InsertError::IndirectConflict
1955                | InsertError::ShadowAddrExists
1956                | InsertError::ShadowerExists,
1957            ) => false,
1958        }
1959    }
1960
1961    fn try_insert_listener(
1962        &mut self,
1963        addr: Self::ListenerAddr,
1964        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1965        device: Option<D>,
1966        sharing: S::SharingState,
1967        id: Self::BoundSocketId,
1968    ) -> Result<(), LocalAddressError> {
1969        let _: SocketStateEntry<'_, _, _, _, _, _> = self
1970            .listeners_mut()
1971            .try_insert(
1972                ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device },
1973                sharing,
1974                id,
1975            )
1976            .map_err(Into::<LocalAddressError>::into)?;
1977
1978        Ok(())
1979    }
1980}
1981
1982struct PairedSocketMapMut<'a, I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
1983    bound: &'a mut BoundDatagramSocketMap<I, D, S>,
1984    other_bound: &'a mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
1985}
1986
1987#[derive(Derivative)]
1988#[derivative(Clone(bound = ""))]
1989struct PairedBoundSocketIds<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
1990    this: BoundDatagramSocketId<I, D, S>,
1991    other: BoundDatagramSocketId<I::OtherVersion, D, S>,
1992}
1993
1994/// Implementation for a pair of demultiplexing maps for different IP versions.
1995impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
1996    for PairedSocketMapMut<'_, I, D, S>
1997{
1998    type ListenerAddr = DualStackUnspecifiedAddr;
1999    type BoundSocketId = PairedBoundSocketIds<I, D, S>;
2000
2001    fn is_listener_entry_available(
2002        &self,
2003        DualStackUnspecifiedAddr: Self::ListenerAddr,
2004        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2005        sharing: &S::SharingState,
2006    ) -> bool {
2007        let PairedSocketMapMut { bound, other_bound } = self;
2008        BoundStateHandler::<I, S, D>::is_listener_entry_available(*bound, None, identifier, sharing)
2009            && BoundStateHandler::<I::OtherVersion, S, D>::is_listener_entry_available(
2010                *other_bound,
2011                None,
2012                identifier,
2013                sharing,
2014            )
2015    }
2016
2017    fn try_insert_listener(
2018        &mut self,
2019        DualStackUnspecifiedAddr: Self::ListenerAddr,
2020        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2021        device: Option<D>,
2022        sharing: S::SharingState,
2023        id: Self::BoundSocketId,
2024    ) -> Result<(), LocalAddressError> {
2025        let PairedSocketMapMut { bound: this, other_bound: other } = self;
2026
2027        let op = DualStackListenerInsertOperation {
2028            identifier,
2029            device,
2030            sharing,
2031            socket_ids: id,
2032            _marker: PhantomData,
2033        };
2034
2035        let _: DualStackListenerRemoveOperation<I, D, S> =
2036            op.apply(this, other).map_err(Into::<LocalAddressError>::into)?;
2037
2038        Ok(())
2039    }
2040}
2041
2042fn try_pick_identifier<
2043    I: IpExt,
2044    S: DatagramSocketSpec,
2045    D: WeakDeviceIdentifier,
2046    BS: BoundStateHandler<I, S, D>,
2047    BC: RngContext,
2048>(
2049    addr: BS::ListenerAddr,
2050    bound: &BS,
2051    bindings_ctx: &mut BC,
2052    sharing: &S::SharingState,
2053) -> Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
2054    S::try_alloc_listen_identifier::<I, D>(bindings_ctx, move |identifier| {
2055        bound
2056            .is_listener_entry_available(addr.clone(), identifier, sharing)
2057            .then_some(())
2058            .ok_or(InUseError)
2059    })
2060}
2061
2062fn try_pick_bound_address<
2063    I: IpExt,
2064    CC: TransportIpContext<I, BC>,
2065    BC: DatagramBindingsTypes,
2066    LI,
2067>(
2068    addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
2069    device: &Option<CC::WeakDeviceId>,
2070    core_ctx: &mut CC,
2071    identifier: LI,
2072    transparent: bool,
2073) -> Result<
2074    (Option<SocketIpAddr<I::Addr>>, Option<EitherDeviceId<CC::DeviceId, CC::WeakDeviceId>>, LI),
2075    LocalAddressError,
2076> {
2077    let (addr, device, identifier) = match addr {
2078        Some(addr) => {
2079            // Extract the specified address and the device. The device
2080            // is either the one from the address or the one to which
2081            // the socket was previously bound.
2082            let (addr, device) = addr.resolve_addr_with_device(device.clone())?;
2083
2084            // Binding to multicast addresses is allowed regardless.
2085            // Other addresses can only be bound to if they are assigned
2086            // to the device, or if the socket is transparent.
2087            if !addr.addr().is_multicast() && !transparent {
2088                BaseTransportIpContext::<I, _>::with_devices_with_assigned_addr(
2089                    core_ctx,
2090                    addr.into(),
2091                    |mut assigned_to| {
2092                        if let Some(device) = &device {
2093                            if !assigned_to.any(|d| device == &EitherDeviceId::Strong(d)) {
2094                                return Err(LocalAddressError::AddressMismatch);
2095                            }
2096                        } else {
2097                            if !assigned_to.any(|_: CC::DeviceId| true) {
2098                                return Err(LocalAddressError::CannotBindToAddress);
2099                            }
2100                        }
2101                        Ok(())
2102                    },
2103                )?;
2104            }
2105            (Some(addr), device, identifier)
2106        }
2107        None => (None, device.clone().map(EitherDeviceId::Weak), identifier),
2108    };
2109    Ok((addr, device, identifier))
2110}
2111
2112fn listen_inner<
2113    I: IpExt,
2114    BC: DatagramBindingsContext,
2115    CC: DatagramBoundStateContext<I, BC, S>,
2116    S: DatagramSocketSpec,
2117>(
2118    core_ctx: &mut CC,
2119    bindings_ctx: &mut BC,
2120    state: &mut SocketState<I, CC::WeakDeviceId, S>,
2121    id: &S::SocketId<I, CC::WeakDeviceId>,
2122    addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, CC::DeviceId>>,
2123    local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2124) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
2125    /// Possible operations that might be performed, depending on whether the
2126    /// socket state spec supports dual-stack operation and what the address
2127    /// looks like.
2128    #[derive(Debug, GenericOverIp)]
2129    #[generic_over_ip(I, Ip)]
2130    enum BoundOperation<'a, I: IpExt, DS: DeviceIdContext<AnyDevice>, NDS> {
2131        /// Bind to the "any" address on both stacks.
2132        DualStackAnyAddr(&'a mut DS),
2133        /// Bind to a non-dual-stack address only on the current stack.
2134        OnlyCurrentStack(
2135            MaybeDualStack<&'a mut DS, &'a mut NDS>,
2136            Option<ZonedAddr<SocketIpAddr<I::Addr>, DS::DeviceId>>,
2137        ),
2138        /// Bind to an address only on the other stack.
2139        OnlyOtherStack(
2140            &'a mut DS,
2141            Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, DS::DeviceId>>,
2142        ),
2143    }
2144
2145    let SocketState { inner, ip_options, sharing } = state;
2146    let UnboundSocketState { device } = match inner {
2147        SocketStateInner::Unbound(state) => state,
2148        SocketStateInner::Bound(_) => return Err(Either::Left(ExpectedUnboundError)),
2149    };
2150
2151    let dual_stack = core_ctx.dual_stack_context_mut();
2152    let bound_operation: BoundOperation<'_, I, _, _> = match (dual_stack, addr) {
2153        // Dual-stack support and unspecified address.
2154        (MaybeDualStack::DualStack(dual_stack), None) => {
2155            match dual_stack.dual_stack_enabled(ip_options) {
2156                // Socket is dual-stack enabled, bind in both stacks.
2157                true => BoundOperation::DualStackAnyAddr(dual_stack),
2158                // Dual-stack support but not enabled, so bind unspecified in the
2159                // current stack.
2160                false => {
2161                    BoundOperation::OnlyCurrentStack(MaybeDualStack::DualStack(dual_stack), None)
2162                }
2163            }
2164        }
2165        // There is dual-stack support and the address is not unspecified so how
2166        // to proceed is going to depend on the value of `addr`.
2167        (MaybeDualStack::DualStack(dual_stack), Some(addr)) => {
2168            match DualStackLocalIp::<I, _>::new(addr) {
2169                // `addr` can't be represented in the other stack.
2170                DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
2171                    MaybeDualStack::DualStack(dual_stack),
2172                    Some(addr),
2173                ),
2174                // There's a representation in the other stack, so use that if possible.
2175                DualStackLocalIp::OtherStack(addr) => {
2176                    match dual_stack.dual_stack_enabled(ip_options) {
2177                        true => BoundOperation::OnlyOtherStack(dual_stack, addr),
2178                        false => return Err(Either::Right(LocalAddressError::CannotBindToAddress)),
2179                    }
2180                }
2181            }
2182        }
2183        // No dual-stack support, so only bind on the current stack.
2184        (MaybeDualStack::NotDualStack(single_stack), None) => {
2185            BoundOperation::OnlyCurrentStack(MaybeDualStack::NotDualStack(single_stack), None)
2186        }
2187        // No dual-stack support, so check the address is allowed in the current
2188        // stack.
2189        (MaybeDualStack::NotDualStack(single_stack), Some(addr)) => {
2190            match DualStackLocalIp::<I, _>::new(addr) {
2191                // The address is only representable in the current stack.
2192                DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
2193                    MaybeDualStack::NotDualStack(single_stack),
2194                    Some(addr),
2195                ),
2196                // The address has a representation in the other stack but there's
2197                // no dual-stack support!
2198                DualStackLocalIp::OtherStack(_addr) => {
2199                    let _: Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, _>> =
2200                        _addr;
2201                    return Err(Either::Right(LocalAddressError::CannotBindToAddress));
2202                }
2203            }
2204        }
2205    };
2206
2207    fn try_bind_single_stack<
2208        I: IpExt,
2209        S: DatagramSocketSpec,
2210        CC: TransportIpContext<I, BC>,
2211        BC: DatagramBindingsContext,
2212    >(
2213        core_ctx: &mut CC,
2214        bindings_ctx: &mut BC,
2215        bound: &mut BoundSocketMap<
2216            I,
2217            CC::WeakDeviceId,
2218            S::AddrSpec,
2219            S::SocketMapSpec<I, CC::WeakDeviceId>,
2220        >,
2221        addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
2222        device: &Option<CC::WeakDeviceId>,
2223        local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2224        id: <S::SocketMapSpec<I, CC::WeakDeviceId> as SocketMapStateSpec>::ListenerId,
2225        sharing: S::SharingState,
2226        transparent: bool,
2227    ) -> Result<
2228        ListenerAddr<
2229            ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2230            CC::WeakDeviceId,
2231        >,
2232        LocalAddressError,
2233    > {
2234        let identifier = match local_id {
2235            Some(id) => Some(id),
2236            None => try_pick_identifier::<I, S, _, _, _>(
2237                addr.as_ref().map(ZonedAddr::addr),
2238                bound,
2239                bindings_ctx,
2240                &sharing,
2241            ),
2242        }
2243        .ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
2244        let (addr, device, identifier) =
2245            try_pick_bound_address::<I, _, _, _>(addr, device, core_ctx, identifier, transparent)?;
2246        let weak_device = device.map(|d| d.as_weak().into_owned());
2247
2248        BoundStateHandler::<_, S, _>::try_insert_listener(
2249            bound,
2250            addr,
2251            identifier,
2252            weak_device.clone(),
2253            sharing,
2254            id,
2255        )
2256        .map(|()| ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device: weak_device })
2257    }
2258
2259    let bound_addr: ListenerAddr<S::ListenerIpAddr<I>, CC::WeakDeviceId> = match bound_operation {
2260        BoundOperation::OnlyCurrentStack(either_dual_stack, addr) => {
2261            let converter = match either_dual_stack {
2262                MaybeDualStack::DualStack(ds) => MaybeDualStack::DualStack(ds.ds_converter()),
2263                MaybeDualStack::NotDualStack(nds) => {
2264                    MaybeDualStack::NotDualStack(nds.nds_converter())
2265                }
2266            };
2267            core_ctx
2268                .with_bound_sockets_mut(|core_ctx, bound| {
2269                    let id = S::make_bound_socket_map_id(id);
2270
2271                    try_bind_single_stack::<I, S, _, _>(
2272                        core_ctx,
2273                        bindings_ctx,
2274                        bound,
2275                        addr,
2276                        &device,
2277                        local_id,
2278                        id,
2279                        sharing.clone(),
2280                        ip_options.common.transparent,
2281                    )
2282                })
2283                .map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
2284                    let ip = match converter {
2285                        MaybeDualStack::DualStack(converter) => converter.convert_back(
2286                            DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
2287                        ),
2288                        MaybeDualStack::NotDualStack(converter) => {
2289                            converter.convert_back(ListenerIpAddr { addr, identifier })
2290                        }
2291                    };
2292                    ListenerAddr { ip, device }
2293                })
2294        }
2295        BoundOperation::OnlyOtherStack(core_ctx, addr) => {
2296            let id = core_ctx.to_other_bound_socket_id(id);
2297            core_ctx
2298                .with_other_bound_sockets_mut(|core_ctx, other_bound| {
2299                    try_bind_single_stack::<_, S, _, _>(
2300                        core_ctx,
2301                        bindings_ctx,
2302                        other_bound,
2303                        addr,
2304                        &device,
2305                        local_id,
2306                        id,
2307                        sharing.clone(),
2308                        ip_options.common.transparent,
2309                    )
2310                })
2311                .map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
2312                    ListenerAddr {
2313                        ip: core_ctx.ds_converter().convert_back(
2314                            DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
2315                                addr,
2316                                identifier,
2317                            }),
2318                        ),
2319                        device,
2320                    }
2321                })
2322        }
2323        BoundOperation::DualStackAnyAddr(core_ctx) => {
2324            let ids = PairedBoundSocketIds {
2325                this: S::make_bound_socket_map_id(id),
2326                other: core_ctx.to_other_bound_socket_id(id),
2327            };
2328            core_ctx
2329                .with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
2330                    let mut bound_pair = PairedSocketMapMut { bound, other_bound };
2331                    let sharing = sharing.clone();
2332
2333                    let identifier = match local_id {
2334                        Some(id) => Some(id),
2335                        None => try_pick_identifier::<I, S, _, _, _>(
2336                            DualStackUnspecifiedAddr,
2337                            &bound_pair,
2338                            bindings_ctx,
2339                            &sharing,
2340                        ),
2341                    }
2342                    .ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
2343                    let (_addr, device, identifier) = try_pick_bound_address::<I, _, _, _>(
2344                        None,
2345                        &device,
2346                        core_ctx,
2347                        identifier,
2348                        ip_options.common.transparent,
2349                    )?;
2350                    let weak_device = device.map(|d| d.as_weak().into_owned());
2351
2352                    BoundStateHandler::<_, S, _>::try_insert_listener(
2353                        &mut bound_pair,
2354                        DualStackUnspecifiedAddr,
2355                        identifier,
2356                        weak_device.clone(),
2357                        sharing,
2358                        ids,
2359                    )
2360                    .map(|()| (identifier, weak_device))
2361                })
2362                .map(|(identifier, device)| ListenerAddr {
2363                    ip: core_ctx
2364                        .ds_converter()
2365                        .convert_back(DualStackListenerIpAddr::BothStacks(identifier)),
2366                    device,
2367                })
2368        }
2369    }
2370    .map_err(Either::Right)?;
2371    // Match Linux behavior by only storing the original bound addr when the
2372    // local_id was provided by the caller.
2373    let original_bound_addr = local_id.map(|_id| {
2374        let ListenerAddr { ip, device: _ } = &bound_addr;
2375        ip.clone()
2376    });
2377
2378    // Replace the unbound state only after we're sure the
2379    // insertion has succeeded.
2380    state.inner = SocketStateInner::Bound(BoundSocketState {
2381        socket_type: BoundSocketStateType::Listener(ListenerState { addr: bound_addr }),
2382        original_bound_addr,
2383    });
2384    Ok(())
2385}
2386
2387/// An error when attempting to create a datagram socket.
2388#[derive(Error, Copy, Clone, Debug, Eq, PartialEq)]
2389pub enum ConnectError {
2390    /// An error was encountered creating an IP socket.
2391    #[error(transparent)]
2392    Ip(#[from] IpSockCreationError),
2393    /// No local port was specified, and none could be automatically allocated.
2394    #[error("a local port could not be allocated")]
2395    CouldNotAllocateLocalPort,
2396    /// The specified socket addresses (IP addresses and ports) conflict with an
2397    /// existing socket.
2398    #[error("the socket's IP address and port conflict with an existing socket")]
2399    SockAddrConflict,
2400    /// There was a problem with the provided address relating to its zone.
2401    #[error(transparent)]
2402    Zone(#[from] ZonedAddressError),
2403    /// The remote address is mapped (i.e. an ipv4-mapped-ipv6 address), but the
2404    /// socket is not dual-stack enabled.
2405    #[error("IPv4-mapped-IPv6 addresses are not supported by this socket")]
2406    RemoteUnexpectedlyMapped,
2407    /// The remote address is non-mapped (i.e not an ipv4-mapped-ipv6 address),
2408    /// but the socket is dual stack enabled and bound to a mapped address.
2409    #[error("non IPv4-mapped-Ipv6 addresses are not supported by this socket")]
2410    RemoteUnexpectedlyNonMapped,
2411}
2412
2413/// Parameters required to connect a socket.
2414struct ConnectParameters<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2415    local_ip: Option<SocketIpAddr<WireI::Addr>>,
2416    local_port: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2417    remote_ip: ZonedAddr<SocketIpAddr<WireI::Addr>, D::Strong>,
2418    remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2419    device: Option<D>,
2420    sharing: S::SharingState,
2421    common_ip_options: DatagramIpAgnosticOptions,
2422    socket_options: DatagramIpSpecificSocketOptions<WireI, D>,
2423    socket_id:
2424        <S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>>::BoundSocketId,
2425    original_shutdown: Option<Shutdown>,
2426    extra: S::ConnStateExtra,
2427}
2428
2429/// Inserts a connected socket into the bound socket map.
2430///
2431/// It accepts two closures that capture the logic required to remove and
2432/// reinsert the original state from/into the bound_socket_map. The original
2433/// state will only be reinserted if an error is encountered during connect.
2434/// The output of `remove_original` is fed into `reinsert_original`.
2435fn connect_inner<
2436    WireI: IpExt,
2437    D: WeakDeviceIdentifier,
2438    S: DatagramSocketSpec,
2439    R,
2440    BC: DatagramBindingsContext,
2441    CC: IpSocketHandler<WireI, BC, WeakDeviceId = D, DeviceId = D::Strong>,
2442>(
2443    connect_params: ConnectParameters<WireI, D, S>,
2444    core_ctx: &mut CC,
2445    bindings_ctx: &mut BC,
2446    sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2447    remove_original: impl FnOnce(
2448        &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2449    ) -> R,
2450    reinsert_original: impl FnOnce(
2451        &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2452        R,
2453    ),
2454) -> Result<ConnState<WireI, D, S>, ConnectError> {
2455    let ConnectParameters {
2456        local_ip,
2457        local_port,
2458        remote_ip,
2459        remote_port,
2460        device,
2461        sharing,
2462        common_ip_options,
2463        socket_options,
2464        socket_id,
2465        original_shutdown,
2466        extra,
2467    } = connect_params;
2468
2469    // Select multicast device if we are connecting to a multicast address.
2470    let device = device.or_else(|| {
2471        remote_ip
2472            .addr()
2473            .addr()
2474            .is_multicast()
2475            .then(|| socket_options.multicast_interface.clone())
2476            .flatten()
2477    });
2478
2479    let (remote_ip, socket_device) = remote_ip.resolve_addr_with_device(device.clone())?;
2480
2481    let clear_device_on_disconnect = device.is_none() && socket_device.is_some();
2482
2483    let ip_sock = IpSocketHandler::<WireI, _>::new_ip_socket(
2484        core_ctx,
2485        bindings_ctx,
2486        IpSocketArgs {
2487            device: socket_device.as_ref().map(|d| d.as_ref()),
2488            local_ip: local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
2489            remote_ip,
2490            proto: S::ip_proto::<WireI>(),
2491            options: &common_ip_options,
2492        },
2493    )?;
2494
2495    let local_port = match local_port {
2496        Some(id) => id.clone(),
2497        None => S::try_alloc_local_id(
2498            sockets,
2499            bindings_ctx,
2500            DatagramFlowId {
2501                local_ip: SocketIpAddr::from(*ip_sock.local_ip()),
2502                remote_ip: *ip_sock.remote_ip(),
2503                remote_id: remote_port.clone(),
2504            },
2505        )
2506        .ok_or(ConnectError::CouldNotAllocateLocalPort)?,
2507    };
2508    let conn_addr = ConnAddr {
2509        ip: ConnIpAddr {
2510            local: (SocketIpAddr::from(*ip_sock.local_ip()), local_port),
2511            remote: (*ip_sock.remote_ip(), remote_port),
2512        },
2513        device: ip_sock.device().cloned(),
2514    };
2515    // Now that all the other checks have been done, actually remove the
2516    // original state from the socket map.
2517    let reinsert_op = remove_original(sockets);
2518    // Try to insert the new connection, restoring the original state on
2519    // failure.
2520    let bound_addr = match sockets.conns_mut().try_insert(conn_addr, sharing, socket_id) {
2521        Ok(bound_entry) => bound_entry.get_addr().clone(),
2522        Err(
2523            InsertError::Exists
2524            | InsertError::IndirectConflict
2525            | InsertError::ShadowerExists
2526            | InsertError::ShadowAddrExists,
2527        ) => {
2528            reinsert_original(sockets, reinsert_op);
2529            return Err(ConnectError::SockAddrConflict);
2530        }
2531    };
2532    Ok(ConnState {
2533        socket: ip_sock,
2534        clear_device_on_disconnect,
2535        shutdown: original_shutdown.unwrap_or_else(Shutdown::default),
2536        addr: bound_addr,
2537        extra,
2538    })
2539}
2540
2541/// State required to perform single-stack connection of a socket.
2542struct SingleStackConnectOperation<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2543    params: ConnectParameters<I, D, S>,
2544    remove_op: Option<SingleStackRemoveOperation<I, D, S>>,
2545}
2546
2547impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2548    SingleStackConnectOperation<I, D, S>
2549{
2550    /// Constructs the connect operation from existing socket state.
2551    fn new_from_state<
2552        BC,
2553        CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
2554    >(
2555        core_ctx: &mut CC,
2556        socket_id: &S::SocketId<I, D>,
2557        state: &SocketState<I, D, S>,
2558        remote_ip: ZonedAddr<SocketIpAddr<I::Addr>, D::Strong>,
2559        remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2560        extra: S::ConnStateExtra,
2561    ) -> Self {
2562        let SocketState { ip_options, inner, sharing } = state;
2563        match inner {
2564            SocketStateInner::Unbound(UnboundSocketState { device }) => {
2565                SingleStackConnectOperation {
2566                    params: ConnectParameters {
2567                        local_ip: None,
2568                        local_port: None,
2569                        remote_ip,
2570                        remote_port,
2571                        device: device.clone(),
2572                        sharing: sharing.clone(),
2573                        common_ip_options: ip_options.common.clone(),
2574                        socket_options: ip_options.socket_options.clone(),
2575                        socket_id: S::make_bound_socket_map_id(socket_id),
2576                        original_shutdown: None,
2577                        extra,
2578                    },
2579                    remove_op: None,
2580                }
2581            }
2582            SocketStateInner::Bound(state) => {
2583                let remove_op = SingleStackRemoveOperation::new_from_state(
2584                    core_ctx,
2585                    socket_id,
2586                    state,
2587                    sharing.clone(),
2588                );
2589                let BoundSocketState { socket_type, original_bound_addr: _ } = state;
2590                match socket_type {
2591                    BoundSocketStateType::Listener(ListenerState {
2592                        addr: ListenerAddr { ip, device },
2593                    }) => {
2594                        let ListenerIpAddr { addr, identifier } =
2595                            core_ctx.nds_converter().convert(ip);
2596                        SingleStackConnectOperation {
2597                            params: ConnectParameters {
2598                                local_ip: addr.clone(),
2599                                local_port: Some(*identifier),
2600                                remote_ip,
2601                                remote_port,
2602                                device: device.clone(),
2603                                sharing: sharing.clone(),
2604                                common_ip_options: ip_options.common.clone(),
2605                                socket_options: ip_options.socket_options.clone(),
2606                                socket_id: S::make_bound_socket_map_id(socket_id),
2607                                original_shutdown: None,
2608                                extra,
2609                            },
2610                            remove_op: Some(remove_op),
2611                        }
2612                    }
2613                    BoundSocketStateType::Connected(state) => {
2614                        let ConnState {
2615                            socket: _,
2616                            shutdown,
2617                            addr:
2618                                ConnAddr {
2619                                    ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
2620                                    device,
2621                                },
2622                            clear_device_on_disconnect: _,
2623                            extra: _,
2624                        } = core_ctx.nds_converter().convert(state);
2625                        SingleStackConnectOperation {
2626                            params: ConnectParameters {
2627                                local_ip: Some(local_ip.clone()),
2628                                local_port: Some(*local_id),
2629                                remote_ip,
2630                                remote_port,
2631                                device: device.clone(),
2632                                sharing: sharing.clone(),
2633                                common_ip_options: ip_options.common.clone(),
2634                                socket_options: ip_options.socket_options.clone(),
2635                                socket_id: S::make_bound_socket_map_id(socket_id),
2636                                original_shutdown: Some(shutdown.clone()),
2637                                extra,
2638                            },
2639                            remove_op: Some(remove_op),
2640                        }
2641                    }
2642                }
2643            }
2644        }
2645    }
2646
2647    /// Performs this operation and connects the socket.
2648    ///
2649    /// This is primarily a wrapper around `connect_inner` that establishes the
2650    /// remove/reinsert closures for single stack removal.
2651    ///
2652    /// Returns the state for the new connection.
2653    fn apply<
2654        BC: DatagramBindingsContext,
2655        CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>,
2656    >(
2657        self,
2658        core_ctx: &mut CC,
2659        bindings_ctx: &mut BC,
2660        socket_map: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
2661    ) -> Result<ConnState<I, D, S>, ConnectError> {
2662        let SingleStackConnectOperation { params, remove_op } = self;
2663        let remove_fn =
2664            |sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>| {
2665                remove_op.map(|remove_op| {
2666                    remove_op.apply(sockets).expect("Failed to remove listener socket")
2667                })
2668            };
2669        let reinsert_fn =
2670            |sockets: &mut BoundDatagramSocketMap<I, D, S>,
2671             insert_op: Option<SingleStackInsertOperation<I, D, S>>| {
2672                if let Some(insert_op) = insert_op {
2673                    let _: SingleStackRemoveOperation<I, D, S> =
2674                        insert_op.apply(sockets).expect("Failed to revert listener socket removal");
2675                }
2676            };
2677        connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)
2678    }
2679}
2680
2681/// State required to perform dual-stack connection of a socket.
2682struct DualStackConnectOperation<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2683{
2684    params: EitherStack<ConnectParameters<I, D, S>, ConnectParameters<I::OtherVersion, D, S>>,
2685    remove_op: Option<DualStackRemoveOperation<I, D, S>>,
2686}
2687
2688impl<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2689    DualStackConnectOperation<I, D, S>
2690{
2691    /// Constructs the connect operation from existing socket state.
2692    fn new_from_state<
2693        BC: DatagramBindingsContext,
2694        CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
2695    >(
2696        core_ctx: &mut CC,
2697        socket_id: &S::SocketId<I, D>,
2698        state: &SocketState<I, D, S>,
2699        remote_ip: DualStackRemoteIp<I, D::Strong>,
2700        remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2701        extra: S::ConnStateExtra,
2702    ) -> Result<Self, ConnectError> {
2703        let SocketState { ip_options, inner, sharing } = state;
2704        match inner {
2705            SocketStateInner::Unbound(UnboundSocketState { device }) => {
2706                // Unbound sockets don't have a predisposition of which stack to
2707                // connect in. Instead, it's dictated entirely by the remote.
2708                let params = match remote_ip {
2709                    DualStackRemoteIp::ThisStack(remote_ip) => {
2710                        EitherStack::ThisStack(ConnectParameters {
2711                            local_ip: None,
2712                            local_port: None,
2713                            remote_ip,
2714                            remote_port,
2715                            device: device.clone(),
2716                            sharing: sharing.clone(),
2717                            common_ip_options: ip_options.common.clone(),
2718                            socket_options: ip_options.socket_options.clone(),
2719                            socket_id: S::make_bound_socket_map_id(socket_id),
2720                            original_shutdown: None,
2721                            extra,
2722                        })
2723                    }
2724                    DualStackRemoteIp::OtherStack(remote_ip) => {
2725                        if !core_ctx.dual_stack_enabled(ip_options) {
2726                            return Err(ConnectError::RemoteUnexpectedlyMapped);
2727                        }
2728                        EitherStack::OtherStack(ConnectParameters {
2729                            local_ip: None,
2730                            local_port: None,
2731                            remote_ip,
2732                            remote_port,
2733                            device: device.clone(),
2734                            sharing: sharing.clone(),
2735                            common_ip_options: ip_options.common.clone(),
2736                            socket_options: core_ctx.to_other_socket_options(ip_options).clone(),
2737                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2738                            original_shutdown: None,
2739                            extra,
2740                        })
2741                    }
2742                };
2743                Ok(DualStackConnectOperation { params, remove_op: None })
2744            }
2745            SocketStateInner::Bound(state) => {
2746                let remove_op = DualStackRemoveOperation::new_from_state(
2747                    core_ctx,
2748                    socket_id,
2749                    ip_options,
2750                    state,
2751                    sharing.clone(),
2752                );
2753
2754                let BoundSocketState { socket_type, original_bound_addr: _ } = state;
2755                match socket_type {
2756                    BoundSocketStateType::Listener(ListenerState {
2757                        addr: ListenerAddr { ip, device },
2758                    }) => {
2759                        match (remote_ip, core_ctx.ds_converter().convert(ip)) {
2760                            // Disallow connecting to the other stack because the
2761                            // existing socket state is in this stack.
2762                            (
2763                                DualStackRemoteIp::OtherStack(_),
2764                                DualStackListenerIpAddr::ThisStack(_),
2765                            ) => Err(ConnectError::RemoteUnexpectedlyMapped),
2766                            // Disallow connecting to this stack because the existing
2767                            // socket state is in the other stack.
2768                            (
2769                                DualStackRemoteIp::ThisStack(_),
2770                                DualStackListenerIpAddr::OtherStack(_),
2771                            ) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
2772                            // Connect in this stack.
2773                            (
2774                                DualStackRemoteIp::ThisStack(remote_ip),
2775                                DualStackListenerIpAddr::ThisStack(ListenerIpAddr {
2776                                    addr,
2777                                    identifier,
2778                                }),
2779                            ) => Ok(DualStackConnectOperation {
2780                                params: EitherStack::ThisStack(ConnectParameters {
2781                                    local_ip: addr.clone(),
2782                                    local_port: Some(*identifier),
2783                                    remote_ip,
2784                                    remote_port,
2785                                    device: device.clone(),
2786                                    sharing: sharing.clone(),
2787                                    common_ip_options: ip_options.common.clone(),
2788                                    socket_options: ip_options.socket_options.clone(),
2789                                    socket_id: S::make_bound_socket_map_id(socket_id),
2790                                    original_shutdown: None,
2791                                    extra,
2792                                }),
2793                                remove_op: Some(remove_op),
2794                            }),
2795                            // Listeners in "both stacks" can connect to either
2796                            // stack. Connect in this stack as specified by the
2797                            // remote.
2798                            (
2799                                DualStackRemoteIp::ThisStack(remote_ip),
2800                                DualStackListenerIpAddr::BothStacks(identifier),
2801                            ) => Ok(DualStackConnectOperation {
2802                                params: EitherStack::ThisStack(ConnectParameters {
2803                                    local_ip: None,
2804                                    local_port: Some(*identifier),
2805                                    remote_ip,
2806                                    remote_port,
2807                                    device: device.clone(),
2808                                    sharing: sharing.clone(),
2809                                    common_ip_options: ip_options.common.clone(),
2810                                    socket_options: ip_options.socket_options.clone(),
2811                                    socket_id: S::make_bound_socket_map_id(socket_id),
2812                                    original_shutdown: None,
2813                                    extra,
2814                                }),
2815                                remove_op: Some(remove_op),
2816                            }),
2817                            // Connect in the other stack.
2818                            (
2819                                DualStackRemoteIp::OtherStack(remote_ip),
2820                                DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
2821                                    addr,
2822                                    identifier,
2823                                }),
2824                            ) => Ok(DualStackConnectOperation {
2825                                params: EitherStack::OtherStack(ConnectParameters {
2826                                    local_ip: addr.clone(),
2827                                    local_port: Some(*identifier),
2828                                    remote_ip,
2829                                    remote_port,
2830                                    device: device.clone(),
2831                                    sharing: sharing.clone(),
2832                                    common_ip_options: ip_options.common.clone(),
2833                                    socket_options: core_ctx
2834                                        .to_other_socket_options(ip_options)
2835                                        .clone(),
2836                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2837                                    original_shutdown: None,
2838                                    extra,
2839                                }),
2840                                remove_op: Some(remove_op),
2841                            }),
2842                            // Listeners in "both stacks" can connect to either
2843                            // stack. Connect in the other stack as specified by
2844                            // the remote.
2845                            (
2846                                DualStackRemoteIp::OtherStack(remote_ip),
2847                                DualStackListenerIpAddr::BothStacks(identifier),
2848                            ) => Ok(DualStackConnectOperation {
2849                                params: EitherStack::OtherStack(ConnectParameters {
2850                                    local_ip: None,
2851                                    local_port: Some(*identifier),
2852                                    remote_ip,
2853                                    remote_port,
2854                                    device: device.clone(),
2855                                    sharing: sharing.clone(),
2856                                    common_ip_options: ip_options.common.clone(),
2857                                    socket_options: core_ctx
2858                                        .to_other_socket_options(ip_options)
2859                                        .clone(),
2860                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2861                                    original_shutdown: None,
2862                                    extra,
2863                                }),
2864                                remove_op: Some(remove_op),
2865                            }),
2866                        }
2867                    }
2868                    BoundSocketStateType::Connected(state) => {
2869                        match (remote_ip, core_ctx.ds_converter().convert(state)) {
2870                            // Disallow connecting to the other stack because the
2871                            // existing socket state is in this stack.
2872                            (
2873                                DualStackRemoteIp::OtherStack(_),
2874                                DualStackConnState::ThisStack(_),
2875                            ) => Err(ConnectError::RemoteUnexpectedlyMapped),
2876                            // Disallow connecting to this stack because the existing
2877                            // socket state is in the other stack.
2878                            (
2879                                DualStackRemoteIp::ThisStack(_),
2880                                DualStackConnState::OtherStack(_),
2881                            ) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
2882                            // Connect in this stack.
2883                            (
2884                                DualStackRemoteIp::ThisStack(remote_ip),
2885                                DualStackConnState::ThisStack(ConnState {
2886                                    socket: _,
2887                                    shutdown,
2888                                    addr:
2889                                        ConnAddr {
2890                                            ip:
2891                                                ConnIpAddr { local: (local_ip, local_id), remote: _ },
2892                                            device,
2893                                        },
2894                                    clear_device_on_disconnect: _,
2895                                    extra: _,
2896                                }),
2897                            ) => Ok(DualStackConnectOperation {
2898                                params: EitherStack::ThisStack(ConnectParameters {
2899                                    local_ip: Some(local_ip.clone()),
2900                                    local_port: Some(*local_id),
2901                                    remote_ip,
2902                                    remote_port,
2903                                    device: device.clone(),
2904                                    sharing: sharing.clone(),
2905                                    common_ip_options: ip_options.common.clone(),
2906                                    socket_options: ip_options.socket_options.clone(),
2907                                    socket_id: S::make_bound_socket_map_id(socket_id),
2908                                    original_shutdown: Some(shutdown.clone()),
2909                                    extra,
2910                                }),
2911                                remove_op: Some(remove_op),
2912                            }),
2913                            // Connect in the other stack.
2914                            (
2915                                DualStackRemoteIp::OtherStack(remote_ip),
2916                                DualStackConnState::OtherStack(ConnState {
2917                                    socket: _,
2918                                    shutdown,
2919                                    addr:
2920                                        ConnAddr {
2921                                            ip:
2922                                                ConnIpAddr { local: (local_ip, local_id), remote: _ },
2923                                            device,
2924                                        },
2925                                    clear_device_on_disconnect: _,
2926                                    extra: _,
2927                                }),
2928                            ) => Ok(DualStackConnectOperation {
2929                                params: EitherStack::OtherStack(ConnectParameters {
2930                                    local_ip: Some(local_ip.clone()),
2931                                    local_port: Some(*local_id),
2932                                    remote_ip,
2933                                    remote_port,
2934                                    device: device.clone(),
2935                                    sharing: sharing.clone(),
2936                                    common_ip_options: ip_options.common.clone(),
2937                                    socket_options: core_ctx
2938                                        .to_other_socket_options(ip_options)
2939                                        .clone(),
2940                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2941                                    original_shutdown: Some(shutdown.clone()),
2942                                    extra,
2943                                }),
2944                                remove_op: Some(remove_op),
2945                            }),
2946                        }
2947                    }
2948                }
2949            }
2950        }
2951    }
2952
2953    /// Performs this operation and connects the socket.
2954    ///
2955    /// This is primarily a wrapper around [`connect_inner`] that establishes the
2956    /// remove/reinsert closures for dual stack removal.
2957    ///
2958    /// Returns a tuple containing the state, and sharing state for the new
2959    /// connection.
2960    fn apply<
2961        BC: DatagramBindingsContext,
2962        CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>
2963            + IpSocketHandler<I::OtherVersion, BC, WeakDeviceId = D, DeviceId = D::Strong>,
2964    >(
2965        self,
2966        core_ctx: &mut CC,
2967        bindings_ctx: &mut BC,
2968        socket_map: &mut BoundDatagramSocketMap<I, D, S>,
2969        other_socket_map: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
2970    ) -> Result<DualStackConnState<I, D, S>, ConnectError> {
2971        let DualStackConnectOperation { params, remove_op } = self;
2972        match params {
2973            EitherStack::ThisStack(params) => {
2974                // NB: Because we're connecting in this stack, we receive this
2975                // stack's sockets as an argument to `remove_fn` and
2976                // `reinsert_fn`. Thus we need to capture + pass through the
2977                // other stack's sockets.
2978                let remove_fn = |sockets: &mut BoundDatagramSocketMap<I, D, S>| {
2979                    remove_op.map(|remove_op| {
2980                        let reinsert_op = remove_op
2981                            .apply(sockets, other_socket_map)
2982                            .expect("Failed to remove listener socket");
2983                        (reinsert_op, other_socket_map)
2984                    })
2985                };
2986                let reinsert_fn = |sockets: &mut BoundDatagramSocketMap<I, D, S>,
2987                                   insert_op: Option<(
2988                    DualStackInsertOperation<I, D, S>,
2989                    &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
2990                )>| {
2991                    if let Some((insert_op, other_sockets)) = insert_op {
2992                        let _: DualStackRemoveOperation<I, D, S> = insert_op
2993                            .apply(sockets, other_sockets)
2994                            .expect("Failed to revert listener socket removal");
2995                    }
2996                };
2997                connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)
2998                    .map(DualStackConnState::ThisStack)
2999            }
3000            EitherStack::OtherStack(params) => {
3001                // NB: Because we're connecting in the other stack, we receive
3002                // the other stack's sockets as an argument to `remove_fn` and
3003                // `reinsert_fn`. Thus we need to capture + pass through this
3004                // stack's sockets.
3005                let remove_fn =
3006                    |other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>| {
3007                        remove_op.map(|remove_op| {
3008                            let reinsert_op = remove_op
3009                                .apply(socket_map, other_sockets)
3010                                .expect("Failed to remove listener socket");
3011                            (reinsert_op, socket_map)
3012                        })
3013                    };
3014                let reinsert_fn =
3015                    |other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
3016                     insert_op: Option<(
3017                        DualStackInsertOperation<I, D, S>,
3018                        &mut BoundDatagramSocketMap<I, D, S>,
3019                    )>| {
3020                        if let Some((insert_op, sockets)) = insert_op {
3021                            let _: DualStackRemoveOperation<I, D, S> = insert_op
3022                                .apply(sockets, other_sockets)
3023                                .expect("Failed to revert listener socket removal");
3024                        }
3025                    };
3026                connect_inner(
3027                    params,
3028                    core_ctx,
3029                    bindings_ctx,
3030                    other_socket_map,
3031                    remove_fn,
3032                    reinsert_fn,
3033                )
3034                .map(DualStackConnState::OtherStack)
3035            }
3036        }
3037    }
3038}
3039
3040/// A connected socket was expected.
3041#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq, Error)]
3042#[generic_over_ip()]
3043#[error("expected connected socket")]
3044pub struct ExpectedConnError;
3045
3046/// An unbound socket was expected.
3047#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq, Error)]
3048#[generic_over_ip()]
3049#[error("expected unbound socket")]
3050pub struct ExpectedUnboundError;
3051
3052/// Converts a connected socket to an unbound socket.
3053///
3054/// Removes the connection's entry from the [`BoundSocketMap`], and returns the
3055/// socket's new state.
3056fn disconnect_to_unbound<
3057    I: IpExt,
3058    BC: DatagramBindingsContext,
3059    CC: DatagramBoundStateContext<I, BC, S>,
3060    S: DatagramSocketSpec,
3061>(
3062    core_ctx: &mut CC,
3063    id: &S::SocketId<I, CC::WeakDeviceId>,
3064    clear_device_on_disconnect: bool,
3065    ip_options: &IpOptions<I, CC::WeakDeviceId, S>,
3066    socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
3067    sharing: S::SharingState,
3068) -> UnboundSocketState<CC::WeakDeviceId> {
3069    match core_ctx.dual_stack_context_mut() {
3070        MaybeDualStack::NotDualStack(nds) => {
3071            let remove_op =
3072                SingleStackRemoveOperation::new_from_state(nds, id, socket_state, sharing);
3073            let _: SingleStackInsertOperation<_, _, _> =
3074                core_ctx.with_bound_sockets_mut(|_core_ctx, bound| {
3075                    remove_op.apply(bound).expect("Failed to remove connected socket entry")
3076                });
3077        }
3078        MaybeDualStack::DualStack(ds) => {
3079            let remove_op =
3080                DualStackRemoveOperation::new_from_state(ds, id, ip_options, socket_state, sharing);
3081            let _: DualStackInsertOperation<_, _, _> =
3082                ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
3083                    remove_op
3084                        .apply(bound, other_bound)
3085                        .expect("Failed to remove connected socket entry")
3086                });
3087        }
3088    };
3089    let device =
3090        if clear_device_on_disconnect { None } else { socket_state.get_device(core_ctx).clone() };
3091    UnboundSocketState { device }
3092}
3093
3094/// Converts a connected socket to a listener socket.
3095///
3096/// Removes the connection's entry from the [`BoundSocketMap`] and returns the
3097/// socket's new state.
3098fn disconnect_to_listener<
3099    I: IpExt,
3100    BC: DatagramBindingsContext,
3101    CC: DatagramBoundStateContext<I, BC, S>,
3102    S: DatagramSocketSpec,
3103>(
3104    core_ctx: &mut CC,
3105    id: &S::SocketId<I, CC::WeakDeviceId>,
3106    listener_ip: S::ListenerIpAddr<I>,
3107    clear_device_on_disconnect: bool,
3108    ip_options: &IpOptions<I, CC::WeakDeviceId, S>,
3109    socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
3110    sharing: S::SharingState,
3111) -> BoundSocketState<I, CC::WeakDeviceId, S> {
3112    let new_device =
3113        if clear_device_on_disconnect { None } else { socket_state.get_device(core_ctx).clone() };
3114
3115    match core_ctx.dual_stack_context_mut() {
3116        MaybeDualStack::NotDualStack(nds) => {
3117            let ListenerIpAddr { addr, identifier } =
3118                nds.nds_converter().convert(listener_ip.clone());
3119            let remove_op =
3120                SingleStackRemoveOperation::new_from_state(nds, id, socket_state, sharing.clone());
3121            core_ctx.with_bound_sockets_mut(|_core_ctx, bound| {
3122                let _: SingleStackInsertOperation<_, _, _> =
3123                    remove_op.apply(bound).expect("Failed to remove connected socket entry");
3124                BoundStateHandler::<_, S, _>::try_insert_listener(
3125                    bound,
3126                    addr,
3127                    identifier,
3128                    new_device.clone(),
3129                    sharing.clone(),
3130                    S::make_bound_socket_map_id(id),
3131                )
3132                .expect("inserting listener for disconnected socket should succeed");
3133            })
3134        }
3135        MaybeDualStack::DualStack(ds) => {
3136            let remove_op = DualStackRemoveOperation::new_from_state(
3137                ds,
3138                id,
3139                ip_options,
3140                socket_state,
3141                sharing.clone(),
3142            );
3143            let other_id = ds.to_other_bound_socket_id(id);
3144            let id = S::make_bound_socket_map_id(id);
3145            let converter = ds.ds_converter();
3146            ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
3147                let _: DualStackInsertOperation<_, _, _> = remove_op
3148                    .apply(bound, other_bound)
3149                    .expect("Failed to remove connected socket entry");
3150
3151                match converter.convert(listener_ip.clone()) {
3152                    DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }) => {
3153                        BoundStateHandler::<_, S, _>::try_insert_listener(
3154                            bound,
3155                            addr,
3156                            identifier,
3157                            new_device.clone(),
3158                            sharing.clone(),
3159                            id,
3160                        )
3161                    }
3162                    DualStackListenerIpAddr::OtherStack(ListenerIpAddr { addr, identifier }) => {
3163                        BoundStateHandler::<_, S, _>::try_insert_listener(
3164                            other_bound,
3165                            addr,
3166                            identifier,
3167                            new_device.clone(),
3168                            sharing.clone(),
3169                            other_id,
3170                        )
3171                    }
3172                    DualStackListenerIpAddr::BothStacks(identifier) => {
3173                        let ids = PairedBoundSocketIds { this: id, other: other_id };
3174                        let mut bound_pair = PairedSocketMapMut { bound, other_bound };
3175                        BoundStateHandler::<_, S, _>::try_insert_listener(
3176                            &mut bound_pair,
3177                            DualStackUnspecifiedAddr,
3178                            identifier,
3179                            new_device.clone(),
3180                            sharing.clone(),
3181                            ids,
3182                        )
3183                    }
3184                }
3185                .expect("inserting listener for disconnected socket should succeed");
3186            })
3187        }
3188    };
3189    BoundSocketState {
3190        original_bound_addr: Some(listener_ip.clone()),
3191        socket_type: BoundSocketStateType::Listener(ListenerState {
3192            addr: ListenerAddr { ip: listener_ip, device: new_device },
3193        }),
3194    }
3195}
3196
3197/// Error encountered when sending a datagram on a socket.
3198#[derive(Debug, GenericOverIp, Error)]
3199#[generic_over_ip()]
3200pub enum SendError<SE: Error> {
3201    /// The socket is not connected,
3202    #[error("socket not connected")]
3203    NotConnected,
3204    /// The socket is not writeable.
3205    #[error("socket not writeable")]
3206    NotWriteable,
3207    /// There was a problem sending the IP packet.
3208    #[error("error sending IP packet: {0}")]
3209    IpSock(#[from] IpSockSendError),
3210    /// There was a problem when serializing the packet.
3211    #[error("error serializing packet: {0:?}")]
3212    SerializeError(#[source] SE),
3213    /// There is no space available on the send buffer.
3214    #[error("send buffer full")]
3215    SendBufferFull,
3216    /// Invalid message length.
3217    #[error("invalid message length")]
3218    InvalidLength,
3219}
3220
3221impl<SE: Error> From<SendBufferError> for SendError<SE> {
3222    fn from(err: SendBufferError) -> Self {
3223        match err {
3224            SendBufferError::SendBufferFull => Self::SendBufferFull,
3225            SendBufferError::InvalidLength => Self::InvalidLength,
3226        }
3227    }
3228}
3229
3230/// An error encountered while sending a datagram packet to an alternate address.
3231#[derive(Debug, Error)]
3232pub enum SendToError<SE: Error> {
3233    /// The socket is not writeable.
3234    #[error("socket not writeable")]
3235    NotWriteable,
3236    /// There was a problem with the remote address relating to its zone.
3237    #[error("problem with zone of remote address: {0}")]
3238    Zone(#[from] ZonedAddressError),
3239    /// An error was encountered while trying to create a temporary IP socket
3240    /// to use for the send operation.
3241    #[error("error creating temporary IP socket for send: {0}")]
3242    CreateAndSend(#[from] IpSockCreateAndSendError),
3243    /// The remote address is mapped (i.e. an ipv4-mapped-ipv6 address), but the
3244    /// socket is not dual-stack enabled.
3245    #[error("remote address is mapped, but socket is not dual-stack enabled")]
3246    RemoteUnexpectedlyMapped,
3247    /// The remote address is non-mapped (i.e not an ipv4-mapped-ipv6 address),
3248    /// but the socket is dual stack enabled and bound to a mapped address.
3249    #[error(
3250        "remote address is non-mapped, but socket is \
3251         dual-stack enabled and bound to mapped address"
3252    )]
3253    RemoteUnexpectedlyNonMapped,
3254    /// The provided buffer is not valid.
3255    #[error("serialize buffer invalid")]
3256    SerializeError(#[source] SE),
3257    /// There is no space available on the send buffer.
3258    #[error("send buffer full")]
3259    SendBufferFull,
3260    /// Invalid message length.
3261    #[error("invalid message length")]
3262    InvalidLength,
3263}
3264
3265impl<SE: Error> From<SendBufferError> for SendToError<SE> {
3266    fn from(err: SendBufferError) -> Self {
3267        match err {
3268            SendBufferError::SendBufferFull => Self::SendBufferFull,
3269            SendBufferError::InvalidLength => Self::InvalidLength,
3270        }
3271    }
3272}
3273
3274struct SendOneshotParameters<
3275    'a,
3276    SockI: IpExt,
3277    WireI: IpExt,
3278    S: DatagramSocketSpec,
3279    D: WeakDeviceIdentifier,
3280> {
3281    local_ip: Option<SocketIpAddr<WireI::Addr>>,
3282    local_id: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
3283    remote_ip: ZonedAddr<SocketIpAddr<WireI::Addr>, D::Strong>,
3284    remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
3285    device: &'a Option<D>,
3286    options: IpOptionsRef<'a, WireI, D>,
3287    id: &'a S::SocketId<SockI, D>,
3288}
3289
3290fn send_oneshot<
3291    SockI: IpExt,
3292    WireI: IpExt,
3293    S: DatagramSocketSpec,
3294    CC: IpSocketHandler<WireI, BC> + CoreTxMetadataContext<TxMetadata<SockI, CC::WeakDeviceId, S>, BC>,
3295    BC: DatagramBindingsContext,
3296    B: BufferMut,
3297>(
3298    core_ctx: &mut CC,
3299    bindings_ctx: &mut BC,
3300    params: SendOneshotParameters<'_, SockI, WireI, S, CC::WeakDeviceId>,
3301    body: B,
3302) -> Result<(), SendToError<S::SerializeError>> {
3303    let SendOneshotParameters { local_ip, local_id, remote_ip, remote_id, device, options, id } =
3304        params;
3305    let device = device.clone().or_else(|| {
3306        remote_ip
3307            .addr()
3308            .addr()
3309            .is_multicast()
3310            .then(|| options.ip_specific.multicast_interface.clone())
3311            .flatten()
3312    });
3313    let (remote_ip, device) = match remote_ip.resolve_addr_with_device(device) {
3314        Ok(addr) => addr,
3315        Err(e) => return Err(SendToError::Zone(e)),
3316    };
3317
3318    let tx_metadata = id.borrow().send_buffer.prepare_for_send::<WireI, _, _, _>(id, &body)?;
3319    let tx_metadata = core_ctx.convert_tx_meta(tx_metadata);
3320
3321    core_ctx
3322        .send_oneshot_ip_packet_with_fallible_serializer(
3323            bindings_ctx,
3324            IpSocketArgs {
3325                device: device.as_ref().map(|d| d.as_ref()),
3326                local_ip: local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
3327                remote_ip,
3328                proto: S::ip_proto::<WireI>(),
3329                options: &options,
3330            },
3331            tx_metadata,
3332            |local_ip| {
3333                S::make_packet::<WireI, _>(
3334                    body,
3335                    &ConnIpAddr {
3336                        local: (local_ip.into(), local_id),
3337                        remote: (remote_ip, remote_id),
3338                    },
3339                )
3340            },
3341        )
3342        .map_err(|err| match err {
3343            SendOneShotIpPacketError::CreateAndSendError { err } => SendToError::CreateAndSend(err),
3344            SendOneShotIpPacketError::SerializeError(err) => SendToError::SerializeError(err),
3345        })
3346}
3347
3348/// Mutably holds the original state of a bound socket required to update the
3349/// bound device.
3350enum SetBoundDeviceParameters<'a, I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
3351    Listener {
3352        ip: &'a ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
3353        device: &'a mut Option<D>,
3354    },
3355    Connected(&'a mut ConnState<I, D, S>),
3356}
3357
3358/// Update the device for a bound socket.
3359///
3360/// The update is applied both to the socket's entry in the given
3361/// [`BoundSocketMap`], and the mutable socket state in the given
3362/// [`SetBoundDeviceParameters`].
3363///
3364/// # Panics
3365///
3366/// Panics if the given `socket_id` is not present in the given `sockets` map.
3367fn set_bound_device_single_stack<
3368    'a,
3369    I: IpExt,
3370    D: WeakDeviceIdentifier,
3371    S: DatagramSocketSpec,
3372    BC: DatagramBindingsContext,
3373    CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>,
3374>(
3375    bindings_ctx: &mut BC,
3376    core_ctx: &mut CC,
3377    params: SetBoundDeviceParameters<'a, I, D, S>,
3378    sockets: &mut BoundDatagramSocketMap<I, D, S>,
3379    socket_id: &BoundDatagramSocketId<I, D, S>,
3380    common_ip_options: &DatagramIpAgnosticOptions,
3381    new_device: Option<&D::Strong>,
3382    sharing: S::SharingState,
3383) -> Result<(), SocketError> {
3384    let (local_ip, remote_ip, old_device) = match &params {
3385        SetBoundDeviceParameters::Listener {
3386            ip: ListenerIpAddr { addr, identifier: _ },
3387            device,
3388        } => (addr.as_ref(), None, device.as_ref()),
3389        SetBoundDeviceParameters::Connected(ConnState {
3390            socket: _,
3391            addr:
3392                ConnAddr {
3393                    ip: ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) },
3394                    device,
3395                },
3396            shutdown: _,
3397            clear_device_on_disconnect: _,
3398            extra: _,
3399        }) => (Some(local_ip), Some(remote_ip), device.as_ref()),
3400    };
3401    // Don't allow changing the device if one of the IP addresses in the
3402    // socket address vector requires a zone (scope ID).
3403    let device_update = SocketDeviceUpdate {
3404        local_ip: local_ip.map(AsRef::<SpecifiedAddr<I::Addr>>::as_ref),
3405        remote_ip: remote_ip.map(AsRef::<SpecifiedAddr<I::Addr>>::as_ref),
3406        old_device,
3407    };
3408    match device_update.check_update(new_device) {
3409        Ok(()) => (),
3410        Err(SocketDeviceUpdateNotAllowedError) => {
3411            return Err(SocketError::Local(LocalAddressError::Zone(
3412                ZonedAddressError::DeviceZoneMismatch,
3413            )));
3414        }
3415    };
3416
3417    let new_device_strong = new_device.map(EitherDeviceId::Strong);
3418    let new_device_weak = new_device.map(|d| d.downgrade());
3419
3420    let (old_addr, new_addr, update_state) = match params {
3421        SetBoundDeviceParameters::Listener { ip, device } => (
3422            AddrVec::Listen(ListenerAddr { ip: ip.clone(), device: device.clone() }),
3423            AddrVec::Listen(ListenerAddr { ip: ip.clone(), device: new_device_weak.clone() }),
3424            Either::Left(move || {
3425                *device = new_device_weak;
3426            }),
3427        ),
3428        SetBoundDeviceParameters::Connected(ConnState {
3429            socket,
3430            addr,
3431            shutdown: _,
3432            clear_device_on_disconnect,
3433            extra: _,
3434        }) => {
3435            let ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) } =
3436                addr.ip;
3437            let new_socket = core_ctx
3438                .new_ip_socket(
3439                    bindings_ctx,
3440                    IpSocketArgs {
3441                        device: new_device_strong,
3442                        local_ip: IpDeviceAddr::new_from_socket_ip_addr(local_ip.clone()),
3443                        remote_ip: remote_ip.clone(),
3444                        proto: socket.proto(),
3445                        options: common_ip_options,
3446                    },
3447                )
3448                .map_err(|_: IpSockCreationError| {
3449                    SocketError::Remote(RemoteAddressError::NoRoute)
3450                })?;
3451            let new_addr = ConnAddr { ip: addr.ip.clone(), device: new_device_weak.clone() };
3452            (
3453                AddrVec::Conn(addr.clone()),
3454                AddrVec::Conn(new_addr.clone()),
3455                Either::Right(move || {
3456                    *socket = new_socket;
3457                    // If this operation explicitly sets the device for the socket, it
3458                    // should no longer be cleared on disconnect.
3459                    if new_device.is_some() {
3460                        *clear_device_on_disconnect = false;
3461                    }
3462                    *addr = new_addr
3463                }),
3464            )
3465        }
3466    };
3467
3468    // Remove old address from the socket map.
3469    let remove_op = SingleStackRemoveOperation::<I, D, S> {
3470        socket_id: socket_id.clone(),
3471        sharing: sharing.clone(),
3472        addr: old_addr,
3473        _marker: PhantomData,
3474    };
3475    let reinsert_op = remove_op.apply(sockets).expect("failed to remove socket in set_device");
3476
3477    // Insert new address into the socket map. This operation may fail, in which case
3478    // we need to reinsert the old address.
3479    let insert_op = SingleStackInsertOperation::<I, D, S> {
3480        socket_id: socket_id.clone(),
3481        sharing: sharing,
3482        addr: new_addr,
3483        _marker: PhantomData,
3484    };
3485    match insert_op.apply(sockets) {
3486        Err(e) => {
3487            let _: SingleStackRemoveOperation<_, _, _> = reinsert_op
3488                .apply(sockets)
3489                .expect("failed to reinsert socket after failed set_device");
3490            return Err(SocketError::Local(e.into()));
3491        }
3492        Ok(_) => {}
3493    }
3494
3495    // Update the socket after updating the socket map.
3496    match update_state {
3497        Either::Left(f) => f(),
3498        Either::Right(f) => f(),
3499    }
3500
3501    Ok(())
3502}
3503
3504/// Update the device for a listener socket in both stacks.
3505///
3506/// Either the update is applied successfully to both stacks, or (in the case of
3507/// an error) both stacks are left in their original state.
3508///
3509/// # Panics
3510///
3511/// Panics if the given socket IDs are not present in the given socket maps.
3512fn set_bound_device_listener_both_stacks<
3513    'a,
3514    I: IpExt,
3515    D: WeakDeviceIdentifier,
3516    S: DatagramSocketSpec,
3517>(
3518    old_device: &mut Option<D>,
3519    identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
3520    sockets: PairedSocketMapMut<'a, I, D, S>,
3521    socket_ids: PairedBoundSocketIds<I, D, S>,
3522    new_device: Option<D>,
3523    sharing: S::SharingState,
3524) -> Result<(), SocketError> {
3525    let PairedSocketMapMut { bound: sockets, other_bound: other_sockets } = sockets;
3526
3527    let remove_op = DualStackListenerRemoveOperation {
3528        identifier,
3529        device: old_device.clone(),
3530        sharing: sharing.clone(),
3531        socket_ids: socket_ids.clone(),
3532        _marker: PhantomData,
3533    };
3534    let reinsert_op =
3535        remove_op.apply(sockets, other_sockets).expect("failed to remove socket in set_device");
3536
3537    let insert_op = DualStackListenerInsertOperation {
3538        identifier,
3539        device: new_device.clone(),
3540        sharing,
3541        socket_ids,
3542        _marker: PhantomData,
3543    };
3544    match insert_op.apply(sockets, other_sockets) {
3545        Err(e) => {
3546            let _: DualStackListenerRemoveOperation<_, _, _> = reinsert_op
3547                .apply(sockets, other_sockets)
3548                .expect("failed to reinsert socket after failed set_device");
3549            return Err(SocketError::Local(e.into()));
3550        }
3551        Ok(_) => {}
3552    };
3553
3554    *old_device = new_device;
3555    return Ok(());
3556}
3557
3558/// Error resulting from attempting to change multicast membership settings for
3559/// a socket.
3560#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
3561pub enum SetMulticastMembershipError {
3562    /// The provided address does not match the provided device.
3563    #[error("provided address does not match the provided device")]
3564    AddressNotAvailable,
3565    /// The device does not exist.
3566    #[error("device does not exist")]
3567    DeviceDoesNotExist,
3568    /// The provided address does not match any address on the host.
3569    #[error("provided address does not match any address on the host")]
3570    NoDeviceWithAddress,
3571    /// No device or address was specified and there is no device with a route to the multicast
3572    /// address.
3573    #[error(
3574        "no device or address was specified and \
3575         there is no device with a route to the multicast address"
3576    )]
3577    NoDeviceAvailable,
3578    /// Tried to join a group again.
3579    #[error("tried to join a group again")]
3580    GroupAlreadyJoined,
3581    /// Tried to leave an unjoined group.
3582    #[error("tried to leave an unjoined group")]
3583    GroupNotJoined,
3584    /// The socket is bound to a device that doesn't match the one specified.
3585    #[error("socket is bound to a device that doesn't match the one specified")]
3586    WrongDevice,
3587}
3588
3589/// Selects the interface for the given remote address, optionally with a
3590/// constraint on the source address.
3591fn pick_interface_for_addr<
3592    A: IpAddress,
3593    S: DatagramSocketSpec,
3594    BC: DatagramBindingsContext,
3595    CC: DatagramBoundStateContext<A::Version, BC, S>,
3596>(
3597    core_ctx: &mut CC,
3598    remote_addr: MulticastAddr<A>,
3599    source_addr: Option<SpecifiedAddr<A>>,
3600    marks: &Marks,
3601) -> Result<CC::DeviceId, SetMulticastMembershipError>
3602where
3603    A::Version: IpExt,
3604{
3605    core_ctx.with_transport_context(|core_ctx| match source_addr {
3606        Some(source_addr) => {
3607            BaseTransportIpContext::<A::Version, _>::with_devices_with_assigned_addr(
3608                core_ctx,
3609                source_addr,
3610                |mut devices| {
3611                    if let Some(d) = devices.next() {
3612                        if devices.next() == None {
3613                            return Ok(d);
3614                        }
3615                    }
3616                    Err(SetMulticastMembershipError::NoDeviceAvailable)
3617                },
3618            )
3619        }
3620        None => {
3621            let device = MulticastMembershipHandler::select_device_for_multicast_group(
3622                core_ctx,
3623                remote_addr,
3624                marks,
3625            )
3626            .map_err(|e| match e {
3627                ResolveRouteError::NoSrcAddr | ResolveRouteError::Unreachable => {
3628                    SetMulticastMembershipError::NoDeviceAvailable
3629                }
3630            })?;
3631            Ok(device)
3632        }
3633    })
3634}
3635
3636/// Selector for the device to affect when changing multicast settings.
3637#[derive(Copy, Clone, Debug, Eq, GenericOverIp, PartialEq)]
3638#[generic_over_ip(A, IpAddress)]
3639pub enum MulticastInterfaceSelector<A: IpAddress, D> {
3640    /// Use the device with the assigned address.
3641    LocalAddress(SpecifiedAddr<A>),
3642    /// Use the device with the specified identifier.
3643    Interface(D),
3644}
3645
3646/// Selector for the device to use when changing multicast membership settings.
3647///
3648/// This is like `Option<MulticastInterfaceSelector` except it specifies the
3649/// semantics of the `None` value as "pick any device".
3650#[derive(Copy, Clone, Debug, Eq, PartialEq, GenericOverIp)]
3651#[generic_over_ip(A, IpAddress)]
3652pub enum MulticastMembershipInterfaceSelector<A: IpAddress, D> {
3653    /// Use the specified interface.
3654    Specified(MulticastInterfaceSelector<A, D>),
3655    /// Pick any device with a route to the multicast target address.
3656    AnyInterfaceWithRoute,
3657}
3658
3659impl<A: IpAddress, D> From<MulticastInterfaceSelector<A, D>>
3660    for MulticastMembershipInterfaceSelector<A, D>
3661{
3662    fn from(selector: MulticastInterfaceSelector<A, D>) -> Self {
3663        Self::Specified(selector)
3664    }
3665}
3666
3667/// The shared datagram socket API.
3668#[derive(RefCast)]
3669#[repr(transparent)]
3670pub struct DatagramApi<I, C, S>(C, PhantomData<(S, I)>);
3671
3672impl<I, C, S> DatagramApi<I, C, S> {
3673    /// Creates a new `DatagramApi` from `ctx`.
3674    pub fn new(ctx: C) -> Self {
3675        Self(ctx, PhantomData)
3676    }
3677
3678    /// Creates a mutable borrow of a `DatagramApi` from a mutable borrow of
3679    /// `C`.
3680    pub fn wrap(ctx: &mut C) -> &mut Self {
3681        Self::ref_cast_mut(ctx)
3682    }
3683}
3684
3685/// A local alias for [`DatagramSocketSpec::SocketId`] for use in
3686/// [`DatagramApi`].
3687///
3688/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3689/// associated type.
3690type DatagramApiSocketId<I, C, S> = <S as DatagramSocketSpec>::SocketId<
3691    I,
3692    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
3693>;
3694/// A local alias for [`DeviceIdContext::DeviceId`] for use in
3695/// [`DatagramApi`].
3696///
3697/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3698/// associated type.
3699type DatagramApiDeviceId<C> =
3700    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId;
3701/// A local alias for [`DeviceIdContext::WeakDeviceId`] for use in
3702/// [`DatagramApi`].
3703///
3704/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3705/// associated type.
3706type DatagramApiWeakDeviceId<C> =
3707    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId;
3708
3709impl<I, C, S> DatagramApi<I, C, S>
3710where
3711    I: IpExt,
3712    C: ContextPair,
3713    C::BindingsContext: DatagramBindingsContext + SettingsContext<S::Settings>,
3714    C::CoreContext: DatagramStateContext<I, C::BindingsContext, S>,
3715    S: DatagramSocketSpec,
3716{
3717    fn core_ctx(&mut self) -> &mut C::CoreContext {
3718        let Self(pair, PhantomData) = self;
3719        pair.core_ctx()
3720    }
3721
3722    fn bindings_ctx(&mut self) -> &mut C::BindingsContext {
3723        let Self(pair, PhantomData) = self;
3724        pair.bindings_ctx()
3725    }
3726
3727    fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
3728        let Self(pair, PhantomData) = self;
3729        pair.contexts()
3730    }
3731
3732    /// Creates a new datagram socket and inserts it into the list of all open
3733    /// datagram sockets for the provided spec `S`.
3734    ///
3735    /// The caller is responsible for calling  [`close`] when it's done with the
3736    /// resource.
3737    pub fn create(
3738        &mut self,
3739        external_data: S::ExternalData<I>,
3740        writable_listener: S::SocketWritableListener,
3741    ) -> S::SocketId<I, DatagramApiWeakDeviceId<C>> {
3742        let primary = {
3743            let settings = self.bindings_ctx().settings();
3744            create_primary_id(external_data, writable_listener, settings.as_ref())
3745        };
3746        let strong = PrimaryRc::clone_strong(&primary);
3747        self.core_ctx().with_all_sockets_mut(move |socket_set| {
3748            let strong = PrimaryRc::clone_strong(&primary);
3749            assert_matches::assert_matches!(socket_set.insert(strong, primary), None);
3750        });
3751        strong.into()
3752    }
3753
3754    /// Like [`DatagramApi::create`], but uses default values.
3755    #[cfg(any(test, feature = "testutils"))]
3756    pub fn create_default(&mut self) -> S::SocketId<I, DatagramApiWeakDeviceId<C>>
3757    where
3758        S::ExternalData<I>: Default,
3759        S::SocketWritableListener: Default,
3760    {
3761        self.create(Default::default(), Default::default())
3762    }
3763
3764    /// Collects all currently opened sockets.
3765    pub fn collect_all_sockets(&mut self) -> Vec<S::SocketId<I, DatagramApiWeakDeviceId<C>>> {
3766        self.core_ctx()
3767            .with_all_sockets(|socket_set| socket_set.keys().map(|s| s.clone().into()).collect())
3768    }
3769
3770    /// Closes the socket.
3771    pub fn close(
3772        &mut self,
3773        id: DatagramApiSocketId<I, C, S>,
3774    ) -> RemoveResourceResultWithContext<S::ExternalData<I>, C::BindingsContext> {
3775        let (core_ctx, bindings_ctx) = self.contexts();
3776        // Remove the socket from the list first to prevent double close.
3777        let primary = core_ctx.with_all_sockets_mut(|all_sockets| {
3778            all_sockets.remove(id.borrow()).expect("socket already closed")
3779        });
3780        core_ctx.with_socket_state(&id, |core_ctx, state| {
3781            let SocketState { ip_options, inner, sharing } = state;
3782            match inner {
3783                SocketStateInner::Unbound(UnboundSocketState { device: _ }) => {}
3784                SocketStateInner::Bound(state) => match core_ctx.dual_stack_context_mut() {
3785                    MaybeDualStack::DualStack(dual_stack) => {
3786                        let op = DualStackRemoveOperation::new_from_state(
3787                            dual_stack,
3788                            &id,
3789                            ip_options,
3790                            state,
3791                            sharing.clone(),
3792                        );
3793                        let _: DualStackInsertOperation<_, _, _> = dual_stack
3794                            .with_both_bound_sockets_mut(|_core_ctx, sockets, other_sockets| {
3795                                op.apply(sockets, other_sockets).expect("Failed to remove socket")
3796                            });
3797                    }
3798                    MaybeDualStack::NotDualStack(not_dual_stack) => {
3799                        let op = SingleStackRemoveOperation::new_from_state(
3800                            not_dual_stack,
3801                            &id,
3802                            state,
3803                            sharing.clone(),
3804                        );
3805                        let _: SingleStackInsertOperation<_, _, _> = core_ctx
3806                            .with_bound_sockets_mut(|_core_ctx, sockets| {
3807                                op.apply(sockets).expect("Failed to remove socket")
3808                            });
3809                    }
3810                },
3811            };
3812            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
3813                leave_all_joined_groups(core_ctx, bindings_ctx, &ip_options.multicast_memberships)
3814            });
3815        });
3816        // Drop the (hopefully last) strong ID before unwrapping the primary
3817        // reference.
3818        core::mem::drop(id);
3819        <C::BindingsContext as ReferenceNotifiersExt>::unwrap_or_notify_with_new_reference_notifier(
3820            primary,
3821            |ReferenceState { external_data, .. }| external_data,
3822        )
3823    }
3824
3825    /// Returns the socket's bound/connection state information.
3826    pub fn get_info(
3827        &mut self,
3828        id: &DatagramApiSocketId<I, C, S>,
3829    ) -> SocketInfo<I::Addr, DatagramApiWeakDeviceId<C>> {
3830        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.to_socket_info())
3831    }
3832
3833    /// Binds the socket to a local address and port.
3834    pub fn listen(
3835        &mut self,
3836        id: &DatagramApiSocketId<I, C, S>,
3837        addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
3838        local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
3839    ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
3840        let (core_ctx, bindings_ctx) = self.contexts();
3841        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
3842            listen_inner::<_, _, _, S>(core_ctx, bindings_ctx, state, id, addr, local_id)
3843        })
3844    }
3845
3846    /// Connects the datagram socket.
3847    pub fn connect(
3848        &mut self,
3849        id: &DatagramApiSocketId<I, C, S>,
3850        remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
3851        remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
3852        extra: S::ConnStateExtra,
3853    ) -> Result<(), ConnectError> {
3854        let (core_ctx, bindings_ctx) = self.contexts();
3855        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
3856            let conn_state = match (
3857                core_ctx.dual_stack_context_mut(),
3858                DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
3859            ) {
3860                (MaybeDualStack::DualStack(ds), remote_ip) => {
3861                    let connect_op = DualStackConnectOperation::new_from_state(
3862                        ds, id, state, remote_ip, remote_id, extra,
3863                    )?;
3864                    let converter = ds.ds_converter();
3865                    let conn_state =
3866                        ds.with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
3867                            connect_op.apply(core_ctx, bindings_ctx, bound, other_bound)
3868                        })?;
3869                    Ok(converter.convert_back(conn_state))
3870                }
3871                (MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
3872                    let connect_op = SingleStackConnectOperation::new_from_state(
3873                        nds, id, state, remote_ip, remote_id, extra,
3874                    );
3875                    let converter = nds.nds_converter();
3876                    let conn_state = core_ctx.with_bound_sockets_mut(|core_ctx, bound| {
3877                        connect_op.apply(core_ctx, bindings_ctx, bound)
3878                    })?;
3879                    Ok(converter.convert_back(conn_state))
3880                }
3881                (MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
3882                    Err(ConnectError::RemoteUnexpectedlyMapped)
3883                }
3884            }?;
3885            let original_bound_addr = match &state.inner {
3886                SocketStateInner::Unbound(_) => None,
3887                SocketStateInner::Bound(BoundSocketState {
3888                    socket_type: _,
3889                    original_bound_addr,
3890                }) => original_bound_addr.clone(),
3891            };
3892            state.inner = SocketStateInner::Bound(BoundSocketState {
3893                socket_type: BoundSocketStateType::Connected(conn_state),
3894                original_bound_addr,
3895            });
3896            Ok(())
3897        })
3898    }
3899
3900    /// Disconnects a connected socket.
3901    pub fn disconnect_connected(
3902        &mut self,
3903        id: &DatagramApiSocketId<I, C, S>,
3904    ) -> Result<(), ExpectedConnError> {
3905        self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
3906            let SocketState { ip_options, inner, sharing } = state;
3907            let inner_state = match inner {
3908                SocketStateInner::Unbound(_) => return Err(ExpectedConnError),
3909                SocketStateInner::Bound(state) => state,
3910            };
3911            let BoundSocketState { socket_type, original_bound_addr } = inner_state;
3912            let conn_state = match socket_type {
3913                BoundSocketStateType::Listener(_) => {
3914                    return Err(ExpectedConnError);
3915                }
3916                BoundSocketStateType::Connected(state) => state,
3917            };
3918
3919            let clear_device_on_disconnect = match core_ctx.dual_stack_context_mut() {
3920                MaybeDualStack::DualStack(dual_stack) => {
3921                    match dual_stack.ds_converter().convert(conn_state) {
3922                        DualStackConnState::ThisStack(conn_state) => {
3923                            conn_state.clear_device_on_disconnect
3924                        }
3925                        DualStackConnState::OtherStack(conn_state) => {
3926                            conn_state.clear_device_on_disconnect
3927                        }
3928                    }
3929                }
3930                MaybeDualStack::NotDualStack(not_dual_stack) => {
3931                    not_dual_stack.nds_converter().convert(conn_state).clear_device_on_disconnect
3932                }
3933            };
3934
3935            state.inner = match original_bound_addr {
3936                None => SocketStateInner::Unbound(disconnect_to_unbound(
3937                    core_ctx,
3938                    id,
3939                    clear_device_on_disconnect,
3940                    &state.ip_options,
3941                    inner_state,
3942                    sharing.clone(),
3943                )),
3944                Some(original_bound_addr) => SocketStateInner::Bound(disconnect_to_listener(
3945                    core_ctx,
3946                    id,
3947                    original_bound_addr.clone(),
3948                    clear_device_on_disconnect,
3949                    &ip_options,
3950                    inner_state,
3951                    sharing.clone(),
3952                )),
3953            };
3954            Ok(())
3955        })
3956    }
3957
3958    /// Returns the socket's shutdown state.
3959    pub fn get_shutdown_connected(
3960        &mut self,
3961        id: &DatagramApiSocketId<I, C, S>,
3962    ) -> Option<ShutdownType> {
3963        self.core_ctx().with_socket_state(id, |core_ctx, state| {
3964            let state = match &state.inner {
3965                SocketStateInner::Unbound(_) => return None,
3966                SocketStateInner::Bound(BoundSocketState {
3967                    socket_type,
3968                    original_bound_addr: _,
3969                }) => match socket_type {
3970                    BoundSocketStateType::Listener(_) => return None,
3971                    BoundSocketStateType::Connected(state) => state,
3972                },
3973            };
3974            let Shutdown { send, receive } = match core_ctx.dual_stack_context_mut() {
3975                MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_ref(),
3976                MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_ref(),
3977            };
3978            ShutdownType::from_send_receive(*send, *receive)
3979        })
3980    }
3981
3982    /// Shuts down the socket.
3983    ///
3984    /// `which` determines the shutdown type.
3985    pub fn shutdown_connected(
3986        &mut self,
3987        id: &DatagramApiSocketId<I, C, S>,
3988        which: ShutdownType,
3989    ) -> Result<(), ExpectedConnError> {
3990        self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
3991            let state = match &mut state.inner {
3992                SocketStateInner::Unbound(_) => return Err(ExpectedConnError),
3993                SocketStateInner::Bound(BoundSocketState {
3994                    socket_type,
3995                    original_bound_addr: _,
3996                }) => match socket_type {
3997                    BoundSocketStateType::Listener(_) => {
3998                        return Err(ExpectedConnError);
3999                    }
4000                    BoundSocketStateType::Connected(state) => state,
4001                },
4002            };
4003            let (shutdown_send, shutdown_receive) = which.to_send_receive();
4004            let Shutdown { send, receive } = match core_ctx.dual_stack_context_mut() {
4005                MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_mut(),
4006                MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_mut(),
4007            };
4008            *send |= shutdown_send;
4009            *receive |= shutdown_receive;
4010            Ok(())
4011        })
4012    }
4013
4014    /// Sends data over a connected datagram socket.
4015    pub fn send_conn<B: BufferMut>(
4016        &mut self,
4017        id: &DatagramApiSocketId<I, C, S>,
4018        body: B,
4019    ) -> Result<(), SendError<S::SerializeError>> {
4020        let (core_ctx, bindings_ctx) = self.contexts();
4021        core_ctx.with_socket_state(id, |core_ctx, state| {
4022            let SocketState { inner, ip_options, sharing: _ } = state;
4023            let state = match inner {
4024                SocketStateInner::Unbound(_) => return Err(SendError::NotConnected),
4025                SocketStateInner::Bound(BoundSocketState {
4026                    socket_type,
4027                    original_bound_addr: _,
4028                }) => match socket_type {
4029                    BoundSocketStateType::Listener(_) => {
4030                        return Err(SendError::NotConnected);
4031                    }
4032                    BoundSocketStateType::Connected(state) => state,
4033                },
4034            };
4035
4036            struct SendParams<
4037                'a,
4038                I: IpExt,
4039                S: DatagramSocketSpec,
4040                D: WeakDeviceIdentifier,
4041                O: SendOptions<I> + RouteResolutionOptions<I>,
4042            > {
4043                socket: &'a IpSock<I, D>,
4044                ip: &'a ConnIpAddr<
4045                    I::Addr,
4046                    <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
4047                    <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
4048                >,
4049                options: O,
4050            }
4051
4052            enum Operation<
4053                'a,
4054                I: DualStackIpExt,
4055                S: DatagramSocketSpec,
4056                D: WeakDeviceIdentifier,
4057                BC: DatagramBindingsContext,
4058                DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
4059                CC: DatagramBoundStateContext<I, BC, S>,
4060                O: SendOptions<I> + RouteResolutionOptions<I>,
4061                OtherO: SendOptions<I::OtherVersion> + RouteResolutionOptions<I::OtherVersion>,
4062            > {
4063                SendToThisStack((SendParams<'a, I, S, D, O>, &'a mut CC)),
4064                SendToOtherStack(
4065                    (SendParams<'a, I::OtherVersion, S, D, OtherO>, &'a mut DualStackSC),
4066                ),
4067                // Allow `Operation` to be generic over `B` and `C` so that they can
4068                // be used in trait bounds for `DualStackSC` and `SC`.
4069                _Phantom((Never, PhantomData<BC>)),
4070            }
4071
4072            let (shutdown, operation) = match core_ctx.dual_stack_context_mut() {
4073                MaybeDualStack::DualStack(dual_stack) => {
4074                    match dual_stack.ds_converter().convert(state) {
4075                        DualStackConnState::ThisStack(ConnState {
4076                            socket,
4077                            clear_device_on_disconnect: _,
4078                            shutdown,
4079                            addr: ConnAddr { ip, device: _ },
4080                            extra: _,
4081                        }) => (
4082                            shutdown,
4083                            Operation::SendToThisStack((
4084                                SendParams {
4085                                    socket,
4086                                    ip,
4087                                    options: ip_options.this_stack_options_ref(),
4088                                },
4089                                core_ctx,
4090                            )),
4091                        ),
4092                        DualStackConnState::OtherStack(ConnState {
4093                            socket,
4094                            clear_device_on_disconnect: _,
4095                            shutdown,
4096                            addr: ConnAddr { ip, device: _ },
4097                            extra: _,
4098                        }) => (
4099                            shutdown,
4100                            Operation::SendToOtherStack((
4101                                SendParams {
4102                                    socket,
4103                                    ip,
4104                                    options: ip_options.other_stack_options_ref(dual_stack),
4105                                },
4106                                dual_stack,
4107                            )),
4108                        ),
4109                    }
4110                }
4111                MaybeDualStack::NotDualStack(not_dual_stack) => {
4112                    let ConnState {
4113                        socket,
4114                        clear_device_on_disconnect: _,
4115                        shutdown,
4116                        addr: ConnAddr { ip, device: _ },
4117                        extra: _,
4118                    } = not_dual_stack.nds_converter().convert(state);
4119                    (
4120                        shutdown,
4121                        Operation::SendToThisStack((
4122                            SendParams { socket, ip, options: ip_options.this_stack_options_ref() },
4123                            core_ctx,
4124                        )),
4125                    )
4126                }
4127            };
4128
4129            let Shutdown { send: shutdown_send, receive: _ } = shutdown;
4130            if *shutdown_send {
4131                return Err(SendError::NotWriteable);
4132            }
4133
4134            match operation {
4135                Operation::SendToThisStack((SendParams { socket, ip, options }, core_ctx)) => {
4136                    let tx_metadata =
4137                        id.borrow().send_buffer.prepare_for_send::<I, _, _, _>(id, &body)?;
4138                    let packet =
4139                        S::make_packet::<I, _>(body, &ip).map_err(SendError::SerializeError)?;
4140                    DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
4141                        let tx_metadata = core_ctx.convert_tx_meta(tx_metadata);
4142                        core_ctx
4143                            .send_ip_packet(bindings_ctx, &socket, packet, &options, tx_metadata)
4144                            .map_err(|send_error| SendError::IpSock(send_error))
4145                    })
4146                }
4147                Operation::SendToOtherStack((SendParams { socket, ip, options }, dual_stack)) => {
4148                    let tx_metadata = id
4149                        .borrow()
4150                        .send_buffer
4151                        .prepare_for_send::<I::OtherVersion, _, _, _>(id, &body)?;
4152                    let packet = S::make_packet::<I::OtherVersion, _>(body, &ip)
4153                        .map_err(SendError::SerializeError)?;
4154                    DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
4155                        dual_stack,
4156                        |core_ctx| {
4157                            let tx_metadata = core_ctx.convert_tx_meta(tx_metadata);
4158                            core_ctx
4159                                .send_ip_packet(
4160                                    bindings_ctx,
4161                                    &socket,
4162                                    packet,
4163                                    &options,
4164                                    tx_metadata,
4165                                )
4166                                .map_err(|send_error| SendError::IpSock(send_error))
4167                        },
4168                    )
4169                }
4170            }
4171        })
4172    }
4173
4174    /// Sends a datagram to the provided remote node.
4175    pub fn send_to<B: BufferMut>(
4176        &mut self,
4177        id: &DatagramApiSocketId<I, C, S>,
4178        remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
4179        remote_identifier: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
4180        body: B,
4181    ) -> Result<(), Either<LocalAddressError, SendToError<S::SerializeError>>> {
4182        let (core_ctx, bindings_ctx) = self.contexts();
4183        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4184            match listen_inner(core_ctx, bindings_ctx, state, id, None, None) {
4185                Ok(()) | Err(Either::Left(ExpectedUnboundError)) => (),
4186                Err(Either::Right(e)) => return Err(Either::Left(e)),
4187            };
4188            let SocketState { inner, ip_options, sharing: _ } = state;
4189            let state = match inner {
4190                SocketStateInner::Unbound(_) => panic!("expected bound socket"),
4191                SocketStateInner::Bound(BoundSocketState {
4192                    socket_type: state,
4193                    original_bound_addr: _,
4194                }) => state,
4195            };
4196
4197            enum Operation<
4198                'a,
4199                I: DualStackIpExt,
4200                S: DatagramSocketSpec,
4201                D: WeakDeviceIdentifier,
4202                BC: DatagramBindingsContext,
4203                DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
4204                CC: DatagramBoundStateContext<I, BC, S>,
4205            > {
4206                SendToThisStack((SendOneshotParameters<'a, I, I, S, D>, &'a mut CC)),
4207
4208                SendToOtherStack(
4209                    (SendOneshotParameters<'a, I, I::OtherVersion, S, D>, &'a mut DualStackSC),
4210                ),
4211                // Allow `Operation` to be generic over `B` and `C` so that they can
4212                // be used in trait bounds for `DualStackSC` and `SC`.
4213                _Phantom((Never, PhantomData<BC>)),
4214            }
4215
4216            let (operation, shutdown) = match (
4217                core_ctx.dual_stack_context_mut(),
4218                DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
4219            ) {
4220                (MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
4221                    return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped));
4222                }
4223                (MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
4224                    match state {
4225                        BoundSocketStateType::Listener(ListenerState {
4226                            addr: ListenerAddr { ip, device },
4227                        }) => {
4228                            let ListenerIpAddr { addr, identifier } =
4229                                nds.nds_converter().convert(ip.clone());
4230                            (
4231                                Operation::SendToThisStack((
4232                                    SendOneshotParameters {
4233                                        local_ip: addr,
4234                                        local_id: identifier,
4235                                        remote_ip,
4236                                        remote_id: remote_identifier,
4237                                        device,
4238                                        options: ip_options.this_stack_options_ref(),
4239                                        id,
4240                                    },
4241                                    core_ctx,
4242                                )),
4243                                None,
4244                            )
4245                        }
4246                        BoundSocketStateType::Connected(state) => {
4247                            let ConnState {
4248                                socket: _,
4249                                clear_device_on_disconnect: _,
4250                                shutdown,
4251                                addr:
4252                                    ConnAddr {
4253                                        ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
4254                                        device,
4255                                    },
4256                                extra: _,
4257                            } = nds.nds_converter().convert(state);
4258                            (
4259                                Operation::SendToThisStack((
4260                                    SendOneshotParameters {
4261                                        local_ip: Some(*local_ip),
4262                                        local_id: *local_id,
4263                                        remote_ip,
4264                                        remote_id: remote_identifier,
4265                                        device,
4266                                        options: ip_options.this_stack_options_ref(),
4267                                        id,
4268                                    },
4269                                    core_ctx,
4270                                )),
4271                                Some(shutdown),
4272                            )
4273                        }
4274                    }
4275                }
4276                (MaybeDualStack::DualStack(ds), remote_ip) => match state {
4277                    BoundSocketStateType::Listener(ListenerState {
4278                        addr: ListenerAddr { ip, device },
4279                    }) => match (ds.ds_converter().convert(ip), remote_ip) {
4280                        (
4281                            DualStackListenerIpAddr::ThisStack(_),
4282                            DualStackRemoteIp::OtherStack(_),
4283                        ) => return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped)),
4284                        (
4285                            DualStackListenerIpAddr::OtherStack(_),
4286                            DualStackRemoteIp::ThisStack(_),
4287                        ) => return Err(Either::Right(SendToError::RemoteUnexpectedlyNonMapped)),
4288                        (
4289                            DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
4290                            DualStackRemoteIp::ThisStack(remote_ip),
4291                        ) => (
4292                            Operation::SendToThisStack((
4293                                SendOneshotParameters {
4294                                    local_ip: *addr,
4295                                    local_id: *identifier,
4296                                    remote_ip,
4297                                    remote_id: remote_identifier,
4298                                    device,
4299                                    options: ip_options.this_stack_options_ref(),
4300                                    id,
4301                                },
4302                                core_ctx,
4303                            )),
4304                            None,
4305                        ),
4306                        (
4307                            DualStackListenerIpAddr::BothStacks(identifier),
4308                            DualStackRemoteIp::ThisStack(remote_ip),
4309                        ) => (
4310                            Operation::SendToThisStack((
4311                                SendOneshotParameters {
4312                                    local_ip: None,
4313                                    local_id: *identifier,
4314                                    remote_ip,
4315                                    remote_id: remote_identifier,
4316                                    device,
4317                                    options: ip_options.this_stack_options_ref(),
4318                                    id,
4319                                },
4320                                core_ctx,
4321                            )),
4322                            None,
4323                        ),
4324                        (
4325                            DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
4326                                addr,
4327                                identifier,
4328                            }),
4329                            DualStackRemoteIp::OtherStack(remote_ip),
4330                        ) => (
4331                            Operation::SendToOtherStack((
4332                                SendOneshotParameters {
4333                                    local_ip: *addr,
4334                                    local_id: *identifier,
4335                                    remote_ip,
4336                                    remote_id: remote_identifier,
4337                                    device,
4338                                    options: ip_options.other_stack_options_ref(ds),
4339                                    id,
4340                                },
4341                                ds,
4342                            )),
4343                            None,
4344                        ),
4345                        (
4346                            DualStackListenerIpAddr::BothStacks(identifier),
4347                            DualStackRemoteIp::OtherStack(remote_ip),
4348                        ) => (
4349                            Operation::SendToOtherStack((
4350                                SendOneshotParameters {
4351                                    local_ip: None,
4352                                    local_id: *identifier,
4353                                    remote_ip,
4354                                    remote_id: remote_identifier,
4355                                    device,
4356                                    options: ip_options.other_stack_options_ref(ds),
4357                                    id,
4358                                },
4359                                ds,
4360                            )),
4361                            None,
4362                        ),
4363                    },
4364                    BoundSocketStateType::Connected(state) => {
4365                        match (ds.ds_converter().convert(state), remote_ip) {
4366                            (
4367                                DualStackConnState::ThisStack(_),
4368                                DualStackRemoteIp::OtherStack(_),
4369                            ) => return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped)),
4370                            (
4371                                DualStackConnState::OtherStack(_),
4372                                DualStackRemoteIp::ThisStack(_),
4373                            ) => {
4374                                return Err(Either::Right(
4375                                    SendToError::RemoteUnexpectedlyNonMapped,
4376                                ));
4377                            }
4378                            (
4379                                DualStackConnState::ThisStack(state),
4380                                DualStackRemoteIp::ThisStack(remote_ip),
4381                            ) => {
4382                                let ConnState {
4383                                    socket: _,
4384                                    clear_device_on_disconnect: _,
4385                                    shutdown,
4386                                    addr,
4387                                    extra: _,
4388                                } = state;
4389                                let ConnAddr {
4390                                    ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
4391                                    device,
4392                                } = addr;
4393                                (
4394                                    Operation::SendToThisStack((
4395                                        SendOneshotParameters {
4396                                            local_ip: Some(*local_ip),
4397                                            local_id: *local_id,
4398                                            remote_ip,
4399                                            remote_id: remote_identifier,
4400                                            device,
4401                                            options: ip_options.this_stack_options_ref(),
4402                                            id,
4403                                        },
4404                                        core_ctx,
4405                                    )),
4406                                    Some(shutdown),
4407                                )
4408                            }
4409                            (
4410                                DualStackConnState::OtherStack(state),
4411                                DualStackRemoteIp::OtherStack(remote_ip),
4412                            ) => {
4413                                let ConnState {
4414                                    socket: _,
4415                                    clear_device_on_disconnect: _,
4416                                    shutdown,
4417                                    addr,
4418                                    extra: _,
4419                                } = state;
4420                                let ConnAddr {
4421                                    ip: ConnIpAddr { local: (local_ip, local_id), .. },
4422                                    device,
4423                                } = addr;
4424                                (
4425                                    Operation::SendToOtherStack((
4426                                        SendOneshotParameters {
4427                                            local_ip: Some(*local_ip),
4428                                            local_id: *local_id,
4429                                            remote_ip,
4430                                            remote_id: remote_identifier,
4431                                            device,
4432                                            options: ip_options.other_stack_options_ref(ds),
4433                                            id,
4434                                        },
4435                                        ds,
4436                                    )),
4437                                    Some(shutdown),
4438                                )
4439                            }
4440                        }
4441                    }
4442                },
4443            };
4444
4445            if let Some(Shutdown { send: shutdown_write, receive: _ }) = shutdown {
4446                if *shutdown_write {
4447                    return Err(Either::Right(SendToError::NotWriteable));
4448                }
4449            }
4450
4451            match operation {
4452                Operation::SendToThisStack((params, core_ctx)) => {
4453                    DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
4454                        send_oneshot(core_ctx, bindings_ctx, params, body)
4455                    })
4456                }
4457                Operation::SendToOtherStack((params, core_ctx)) => {
4458                    DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
4459                        core_ctx,
4460                        |core_ctx| send_oneshot(core_ctx, bindings_ctx, params, body),
4461                    )
4462                }
4463            }
4464            .map_err(Either::Right)
4465        })
4466    }
4467
4468    /// Returns the bound device for the socket.
4469    pub fn get_bound_device(
4470        &mut self,
4471        id: &DatagramApiSocketId<I, C, S>,
4472    ) -> Option<DatagramApiWeakDeviceId<C>> {
4473        self.core_ctx().with_socket_state(id, |core_ctx, state| state.get_device(core_ctx).clone())
4474    }
4475
4476    /// Sets the socket's bound device to `new_device`.
4477    pub fn set_device(
4478        &mut self,
4479        id: &DatagramApiSocketId<I, C, S>,
4480        new_device: Option<&DatagramApiDeviceId<C>>,
4481    ) -> Result<(), SocketError> {
4482        let (core_ctx, bindings_ctx) = self.contexts();
4483        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4484            let SocketState { inner, ip_options, sharing } = state;
4485            match inner {
4486                SocketStateInner::Unbound(state) => {
4487                    let UnboundSocketState { device } = state;
4488                    *device = new_device.map(|d| d.downgrade());
4489                    Ok(())
4490                }
4491                SocketStateInner::Bound(BoundSocketState {
4492                    socket_type,
4493                    original_bound_addr: _,
4494                }) => {
4495                    // Information about the set-device operation for the given
4496                    // socket.
4497                    enum Operation<
4498                        'a,
4499                        I: IpExt,
4500                        D: WeakDeviceIdentifier,
4501                        S: DatagramSocketSpec,
4502                        CC,
4503                        DualStackSC,
4504                    > {
4505                        ThisStack {
4506                            params: SetBoundDeviceParameters<'a, I, D, S>,
4507                            core_ctx: CC,
4508                        },
4509                        OtherStack {
4510                            params: SetBoundDeviceParameters<'a, I::OtherVersion, D, S>,
4511                            core_ctx: DualStackSC,
4512                        },
4513                        ListenerBothStacks {
4514                            identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
4515                            device: &'a mut Option<D>,
4516                            core_ctx: DualStackSC,
4517                        },
4518                    }
4519
4520                    // Determine which operation needs to be applied.
4521                    let op = match core_ctx.dual_stack_context_mut() {
4522                        MaybeDualStack::DualStack(ds) => match socket_type {
4523                            BoundSocketStateType::Listener(ListenerState {
4524                                addr: ListenerAddr { ip, device },
4525                            }) => match ds.ds_converter().convert(ip) {
4526                                DualStackListenerIpAddr::ThisStack(ip) => Operation::ThisStack {
4527                                    params: SetBoundDeviceParameters::Listener { ip, device },
4528                                    core_ctx,
4529                                },
4530                                DualStackListenerIpAddr::OtherStack(ip) => Operation::OtherStack {
4531                                    params: SetBoundDeviceParameters::Listener { ip, device },
4532                                    core_ctx: ds,
4533                                },
4534                                DualStackListenerIpAddr::BothStacks(identifier) => {
4535                                    Operation::ListenerBothStacks {
4536                                        identifier: *identifier,
4537                                        device,
4538                                        core_ctx: ds,
4539                                    }
4540                                }
4541                            },
4542                            BoundSocketStateType::Connected(state) => {
4543                                match ds.ds_converter().convert(state) {
4544                                    DualStackConnState::ThisStack(state) => Operation::ThisStack {
4545                                        params: SetBoundDeviceParameters::Connected(state),
4546                                        core_ctx,
4547                                    },
4548                                    DualStackConnState::OtherStack(state) => {
4549                                        Operation::OtherStack {
4550                                            params: SetBoundDeviceParameters::Connected(state),
4551                                            core_ctx: ds,
4552                                        }
4553                                    }
4554                                }
4555                            }
4556                        },
4557                        MaybeDualStack::NotDualStack(nds) => match socket_type {
4558                            BoundSocketStateType::Listener(ListenerState {
4559                                addr: ListenerAddr { ip, device },
4560                            }) => Operation::ThisStack {
4561                                params: SetBoundDeviceParameters::Listener {
4562                                    ip: nds.nds_converter().convert(ip),
4563                                    device,
4564                                },
4565                                core_ctx,
4566                            },
4567                            BoundSocketStateType::Connected(state) => Operation::ThisStack {
4568                                params: SetBoundDeviceParameters::Connected(
4569                                    nds.nds_converter().convert(state),
4570                                ),
4571                                core_ctx,
4572                            },
4573                        },
4574                    };
4575
4576                    // Apply the operation
4577                    match op {
4578                        Operation::ThisStack { params, core_ctx } => {
4579                            let socket_id = S::make_bound_socket_map_id(id);
4580                            DatagramBoundStateContext::<I, _, _>::with_bound_sockets_mut(
4581                                core_ctx,
4582                                |core_ctx, bound| {
4583                                    set_bound_device_single_stack(
4584                                        bindings_ctx,
4585                                        core_ctx,
4586                                        params,
4587                                        bound,
4588                                        &socket_id,
4589                                        &ip_options.common,
4590                                        new_device,
4591                                        sharing.clone(),
4592                                    )
4593                                },
4594                            )
4595                        }
4596                        Operation::OtherStack { params, core_ctx } => {
4597                            let socket_id = core_ctx.to_other_bound_socket_id(id);
4598                            core_ctx.with_other_bound_sockets_mut(|core_ctx, bound| {
4599                                set_bound_device_single_stack(
4600                                    bindings_ctx,
4601                                    core_ctx,
4602                                    params,
4603                                    bound,
4604                                    &socket_id,
4605                                    &ip_options.common,
4606                                    new_device,
4607                                    sharing.clone(),
4608                                )
4609                            })
4610                        }
4611                        Operation::ListenerBothStacks { identifier, device, core_ctx } => {
4612                            let socket_id = PairedBoundSocketIds::<_, _, S> {
4613                                this: S::make_bound_socket_map_id(id),
4614                                other: core_ctx.to_other_bound_socket_id(id),
4615                            };
4616                            core_ctx.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
4617                                set_bound_device_listener_both_stacks(
4618                                    device,
4619                                    identifier,
4620                                    PairedSocketMapMut { bound, other_bound },
4621                                    socket_id,
4622                                    new_device.map(|d| d.downgrade()),
4623                                    sharing.clone(),
4624                                )
4625                            })
4626                        }
4627                    }
4628                }
4629            }
4630        })
4631    }
4632
4633    /// Sets the specified socket's membership status for the given group.
4634    ///
4635    /// An error is returned if the membership change request is invalid
4636    /// (e.g. leaving a group that was not joined, or joining a group multiple
4637    /// times) or if the device to use to join is unspecified or conflicts with
4638    /// the existing socket state.
4639    pub fn set_multicast_membership(
4640        &mut self,
4641        id: &DatagramApiSocketId<I, C, S>,
4642        multicast_group: MulticastAddr<I::Addr>,
4643        interface: MulticastMembershipInterfaceSelector<I::Addr, DatagramApiDeviceId<C>>,
4644        want_membership: bool,
4645    ) -> Result<(), SetMulticastMembershipError> {
4646        let (core_ctx, bindings_ctx) = self.contexts();
4647        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4648            let ip_options = state.options();
4649            let bound_device = state.get_device(core_ctx);
4650
4651            let interface = match interface {
4652                MulticastMembershipInterfaceSelector::Specified(selector) => match selector {
4653                    MulticastInterfaceSelector::Interface(device) => {
4654                        if bound_device.as_ref().is_some_and(|d| d != &device) {
4655                            return Err(SetMulticastMembershipError::WrongDevice);
4656                        } else {
4657                            EitherDeviceId::Strong(device)
4658                        }
4659                    }
4660                    MulticastInterfaceSelector::LocalAddress(addr) => {
4661                        EitherDeviceId::Strong(pick_interface_for_addr(
4662                            core_ctx,
4663                            multicast_group,
4664                            Some(addr),
4665                            &ip_options.common.marks,
4666                        )?)
4667                    }
4668                },
4669                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
4670                    if let Some(bound_device) = bound_device.as_ref() {
4671                        EitherDeviceId::Weak(bound_device.clone())
4672                    } else {
4673                        EitherDeviceId::Strong(pick_interface_for_addr(
4674                            core_ctx,
4675                            multicast_group,
4676                            None,
4677                            &ip_options.common.marks,
4678                        )?)
4679                    }
4680                }
4681            };
4682
4683            let ip_options = state.options_mut();
4684
4685            let Some(strong_interface) = interface.as_strong() else {
4686                return Err(SetMulticastMembershipError::DeviceDoesNotExist);
4687            };
4688
4689            let change = ip_options
4690                .multicast_memberships
4691                .apply_membership_change(multicast_group, &interface.as_weak(), want_membership)
4692                .ok_or(if want_membership {
4693                    SetMulticastMembershipError::GroupAlreadyJoined
4694                } else {
4695                    SetMulticastMembershipError::GroupNotJoined
4696                })?;
4697
4698            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
4699                match change {
4700                    MulticastMembershipChange::Join => {
4701                        MulticastMembershipHandler::<I, _>::join_multicast_group(
4702                            core_ctx,
4703                            bindings_ctx,
4704                            &strong_interface,
4705                            multicast_group,
4706                        )
4707                    }
4708                    MulticastMembershipChange::Leave => {
4709                        MulticastMembershipHandler::<I, _>::leave_multicast_group(
4710                            core_ctx,
4711                            bindings_ctx,
4712                            &strong_interface,
4713                            multicast_group,
4714                        )
4715                    }
4716                }
4717            });
4718
4719            Ok(())
4720        })
4721    }
4722
4723    /// Updates the socket's IP hop limits.
4724    pub fn update_ip_hop_limit(
4725        &mut self,
4726        id: &DatagramApiSocketId<I, C, S>,
4727        update: impl FnOnce(&mut SocketHopLimits<I>),
4728    ) {
4729        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4730            let options = state.options_mut();
4731
4732            update(&mut options.socket_options.hop_limits)
4733        })
4734    }
4735
4736    /// Returns the socket's IP hop limits.
4737    pub fn get_ip_hop_limits(&mut self, id: &DatagramApiSocketId<I, C, S>) -> HopLimits {
4738        self.core_ctx().with_socket_state(id, |core_ctx, state| {
4739            let options = state.options();
4740            let device = state.get_device(core_ctx);
4741            let device = device.as_ref().and_then(|d| d.upgrade());
4742            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
4743                options.socket_options.hop_limits.get_limits_with_defaults(
4744                    &BaseTransportIpContext::<I, _>::get_default_hop_limits(
4745                        core_ctx,
4746                        device.as_ref(),
4747                    ),
4748                )
4749            })
4750        })
4751    }
4752
4753    /// Calls the callback with mutable access to [`S::OtherStackIpOptions<I,
4754    /// D>`].
4755    ///
4756    /// If the socket is bound, the callback is not called, and instead an
4757    /// `ExpectedUnboundError` is returned.
4758    pub fn with_other_stack_ip_options_mut_if_unbound<R>(
4759        &mut self,
4760        id: &DatagramApiSocketId<I, C, S>,
4761        cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4762    ) -> Result<R, ExpectedUnboundError> {
4763        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4764            let is_unbound = match &state.inner {
4765                SocketStateInner::Unbound(_) => true,
4766                SocketStateInner::Bound(_) => false,
4767            };
4768            if is_unbound {
4769                let options = state.options_mut();
4770                Ok(cb(&mut options.other_stack))
4771            } else {
4772                Err(ExpectedUnboundError)
4773            }
4774        })
4775    }
4776
4777    /// Calls the callback with mutable access to [`S::OtherStackIpOptions<I,
4778    /// D>`].
4779    pub fn with_other_stack_ip_options_mut<R>(
4780        &mut self,
4781        id: &DatagramApiSocketId<I, C, S>,
4782        cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4783    ) -> R {
4784        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4785            let options = state.options_mut();
4786            cb(&mut options.other_stack)
4787        })
4788    }
4789
4790    /// Calls the callback with access to [`S::OtherStackIpOptions<I, D>`].
4791    pub fn with_other_stack_ip_options<R>(
4792        &mut self,
4793        id: &DatagramApiSocketId<I, C, S>,
4794        cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4795    ) -> R {
4796        self.core_ctx().with_socket_state(id, |_core_ctx, state| cb(&state.options().other_stack))
4797    }
4798
4799    /// Calls the callback with access to [`S::OtherStackIpOptions<I, D>`], and the
4800    /// default [`HopLimits`] for `I::OtherVersion`.
4801    ///
4802    /// If dualstack operations are not supported, the callback is not called, and
4803    /// instead `NotDualStackCapableError` is returned.
4804    pub fn with_other_stack_ip_options_and_default_hop_limits<R>(
4805        &mut self,
4806        id: &DatagramApiSocketId<I, C, S>,
4807        cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>, HopLimits) -> R,
4808    ) -> Result<R, NotDualStackCapableError> {
4809        self.core_ctx().with_socket_state(id, |core_ctx, state| {
4810            let options = state.options();
4811            let device = state.get_device(core_ctx).as_ref().and_then(|d| d.upgrade());
4812            match DatagramBoundStateContext::<I, _, _>::dual_stack_context_mut(core_ctx) {
4813                MaybeDualStack::NotDualStack(_) => Err(NotDualStackCapableError),
4814                MaybeDualStack::DualStack(ds) => {
4815                    let default_hop_limits =
4816                        DualStackDatagramBoundStateContext::<I, _, _>::with_transport_context(
4817                            ds,
4818                            |sync_ctx| {
4819                                BaseTransportIpContext::<I, _>::get_default_hop_limits(
4820                                    sync_ctx,
4821                                    device.as_ref(),
4822                                )
4823                            },
4824                        );
4825                    Ok(cb(&options.other_stack, default_hop_limits))
4826                }
4827            }
4828        })
4829    }
4830
4831    /// Calls the callback with mutable access to
4832    /// [`DatagramIpSpecificSocketOptions<I,D>`] and
4833    /// [`S::OtherStackIpOptions<I, D>`].
4834    pub fn with_both_stacks_ip_options_mut<R>(
4835        &mut self,
4836        id: &DatagramApiSocketId<I, C, S>,
4837        cb: impl FnOnce(
4838            &mut DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
4839            &mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
4840        ) -> R,
4841    ) -> R {
4842        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4843            let options = state.options_mut();
4844            cb(&mut options.socket_options, &mut options.other_stack)
4845        })
4846    }
4847
4848    /// Calls the callback with access to [`DatagramIpSpecificSocketOptions<I,
4849    /// D>`] and [`S::OtherStackIpOptions<I, D>`].
4850    pub fn with_both_stacks_ip_options<R>(
4851        &mut self,
4852        id: &DatagramApiSocketId<I, C, S>,
4853        cb: impl FnOnce(
4854            &DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
4855            &S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
4856        ) -> R,
4857    ) -> R {
4858        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
4859            let options = state.options();
4860            cb(&options.socket_options, &options.other_stack)
4861        })
4862    }
4863
4864    /// Updates the socket's sharing state to the result of `f`.
4865    ///
4866    /// `f` is given mutable access to the sharing state and is called under the
4867    /// socket lock, allowing for atomic updates to the sharing state.
4868    pub fn update_sharing(
4869        &mut self,
4870        id: &DatagramApiSocketId<I, C, S>,
4871        f: impl FnOnce(&mut S::SharingState),
4872    ) -> Result<(), ExpectedUnboundError> {
4873        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4874            match &mut state.inner {
4875                SocketStateInner::Unbound(_) => (),
4876                SocketStateInner::Bound(_) => return Err(ExpectedUnboundError),
4877            };
4878
4879            f(&mut state.sharing);
4880            Ok(())
4881        })
4882    }
4883
4884    /// Returns the socket's sharing state.
4885    pub fn get_sharing(&mut self, id: &DatagramApiSocketId<I, C, S>) -> S::SharingState {
4886        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.sharing.clone())
4887    }
4888
4889    /// Sets the IP transparent option.
4890    pub fn set_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
4891        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4892            state.options_mut().common.transparent = value;
4893        })
4894    }
4895
4896    /// Returns the IP transparent option.
4897    pub fn get_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
4898        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.options().common.transparent)
4899    }
4900
4901    /// Sets the socket mark at `domain`.
4902    pub fn set_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain, mark: Mark) {
4903        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4904            *state.options_mut().common.marks.get_mut(domain) = mark;
4905        })
4906    }
4907
4908    /// Returns the socket mark at `domain`.
4909    pub fn get_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain) -> Mark {
4910        self.core_ctx()
4911            .with_socket_state(id, |_core_ctx, state| *state.options().common.marks.get(domain))
4912    }
4913
4914    /// Sets the broadcast option.
4915    pub fn set_broadcast(
4916        &mut self,
4917        id: &DatagramApiSocketId<I, C, S>,
4918        value: Option<I::BroadcastMarker>,
4919    ) {
4920        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4921            state.options_mut().socket_options.allow_broadcast = value;
4922        })
4923    }
4924
4925    /// Returns the broadcast option.
4926    pub fn get_broadcast(
4927        &mut self,
4928        id: &DatagramApiSocketId<I, C, S>,
4929    ) -> Option<I::BroadcastMarker> {
4930        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
4931            state.options().socket_options.allow_broadcast
4932        })
4933    }
4934
4935    /// Sets the multicast interface for outgoing multicast packets.
4936    pub fn set_multicast_interface(
4937        &mut self,
4938        id: &DatagramApiSocketId<I, C, S>,
4939        value: Option<&DatagramApiDeviceId<C>>,
4940    ) {
4941        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4942            state.options_mut().socket_options.multicast_interface = value.map(|v| v.downgrade());
4943        })
4944    }
4945
4946    /// Returns the configured multicast interface.
4947    pub fn get_multicast_interface(
4948        &mut self,
4949        id: &DatagramApiSocketId<I, C, S>,
4950    ) -> Option<DatagramApiWeakDeviceId<C>> {
4951        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
4952            state.options().socket_options.multicast_interface.clone()
4953        })
4954    }
4955
4956    /// Sets the multicast loopback flag.
4957    pub fn set_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
4958        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4959            state.options_mut().socket_options.multicast_loop = value;
4960        })
4961    }
4962
4963    /// Returns the multicast loopback flag.
4964    pub fn get_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
4965        self.core_ctx()
4966            .with_socket_state(id, |_core_ctx, state| state.options().socket_options.multicast_loop)
4967    }
4968
4969    /// Sets the Traffic Class option.
4970    pub fn set_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>, value: DscpAndEcn) {
4971        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4972            state.options_mut().socket_options.dscp_and_ecn = value;
4973        })
4974    }
4975
4976    /// Returns the Traffic Class option.
4977    pub fn get_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>) -> DscpAndEcn {
4978        self.core_ctx()
4979            .with_socket_state(id, |_core_ctx, state| state.options().socket_options.dscp_and_ecn)
4980    }
4981
4982    /// Sets the send buffer maximum size to `size`.
4983    pub fn set_send_buffer(&mut self, id: &DatagramApiSocketId<I, C, S>, size: usize) {
4984        let settings = self.bindings_ctx().settings();
4985        id.borrow().send_buffer.set_capacity(size, settings.as_ref())
4986    }
4987
4988    /// Returns the current maximum send buffer size.
4989    pub fn send_buffer(&mut self, id: &DatagramApiSocketId<I, C, S>) -> usize {
4990        id.borrow().send_buffer.capacity()
4991    }
4992
4993    /// Returns the currently available send buffer space on the socket.
4994    #[cfg(any(test, feature = "testutils"))]
4995    pub fn send_buffer_available(&mut self, id: &DatagramApiSocketId<I, C, S>) -> usize {
4996        id.borrow().send_buffer.available()
4997    }
4998}
4999
5000#[cfg(any(test, feature = "testutils"))]
5001pub(crate) mod testutil {
5002    use super::*;
5003
5004    use alloc::vec;
5005    use net_types::Witness;
5006    use net_types::ip::IpAddr;
5007    use netstack3_base::CtxPair;
5008    use netstack3_base::testutil::{FakeStrongDeviceId, TestIpExt};
5009    use netstack3_ip::socket::testutil::FakeDeviceConfig;
5010
5011    /// Helper function to ensure the Fake CoreCtx and BindingsCtx are setup
5012    /// with [`FakeDeviceConfig`] (one per provided device), with remote/local
5013    /// IPs that support a connection to the given remote_ip.
5014    pub fn setup_fake_ctx_with_dualstack_conn_addrs<CC, BC: Default, D: FakeStrongDeviceId>(
5015        local_ip: IpAddr,
5016        remote_ip: SpecifiedAddr<IpAddr>,
5017        devices: impl IntoIterator<Item = D>,
5018        core_ctx_builder: impl FnOnce(Vec<FakeDeviceConfig<D, SpecifiedAddr<IpAddr>>>) -> CC,
5019    ) -> CtxPair<CC, BC> {
5020        // A conversion helper to unmap ipv4-mapped-ipv6 addresses.
5021        fn unmap_ip(addr: IpAddr) -> IpAddr {
5022            match addr {
5023                IpAddr::V4(v4) => IpAddr::V4(v4),
5024                IpAddr::V6(v6) => match v6.to_ipv4_mapped() {
5025                    Some(v4) => IpAddr::V4(v4),
5026                    None => IpAddr::V6(v6),
5027                },
5028            }
5029        }
5030
5031        // Convert the local/remote IPs into `IpAddr` in their non-mapped form.
5032        let local_ip = unmap_ip(local_ip);
5033        let remote_ip = unmap_ip(remote_ip.get());
5034        // If the given local_ip is unspecified, use the default from
5035        // `TEST_ADDRS`. This ensures we always instantiate the
5036        // FakeDeviceConfig below with at least one local_ip, which is
5037        // required for connect operations to succeed.
5038        let local_ip = SpecifiedAddr::new(local_ip).unwrap_or_else(|| match remote_ip {
5039            IpAddr::V4(_) => Ipv4::TEST_ADDRS.local_ip.into(),
5040            IpAddr::V6(_) => Ipv6::TEST_ADDRS.local_ip.into(),
5041        });
5042        // If the given remote_ip is unspecified, we won't be able to
5043        // connect; abort the test.
5044        let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote-ip should be specified");
5045        CtxPair::with_core_ctx(core_ctx_builder(
5046            devices
5047                .into_iter()
5048                .map(|device| FakeDeviceConfig {
5049                    device,
5050                    local_ips: vec![local_ip],
5051                    remote_ips: vec![remote_ip],
5052                })
5053                .collect(),
5054        ))
5055    }
5056}
5057
5058#[cfg(test)]
5059mod test {
5060    use core::convert::Infallible as Never;
5061
5062    use alloc::vec;
5063    use assert_matches::assert_matches;
5064    use derivative::Derivative;
5065    use ip_test_macro::ip_test;
5066    use net_declare::{net_ip_v4, net_ip_v6};
5067    use net_types::Witness;
5068    use net_types::ip::{IpVersionMarker, Ipv4Addr, Ipv6Addr};
5069    use netstack3_base::socket::{
5070        AddrVec, Bound, IncompatibleError, ListenerAddrInfo, RemoveResult, SocketMapAddrStateSpec,
5071    };
5072    use netstack3_base::socketmap::SocketMap;
5073    use netstack3_base::testutil::{
5074        FakeDeviceId, FakeReferencyDeviceId, FakeSocketWritableListener, FakeStrongDeviceId,
5075        FakeWeakDeviceId, MultipleDevicesId, TestIpExt,
5076    };
5077    use netstack3_base::{ContextProvider, CtxPair, UninstantiableWrapper};
5078    use netstack3_ip::DEFAULT_HOP_LIMITS;
5079    use netstack3_ip::device::IpDeviceStateIpExt;
5080    use netstack3_ip::socket::testutil::{
5081        FakeDeviceConfig, FakeDualStackIpSocketCtx, FakeIpSocketCtx,
5082    };
5083    use netstack3_ip::testutil::DualStackSendIpPacketMeta;
5084    use packet::{Buf, Serializer as _};
5085    use packet_formats::ip::{Ipv4Proto, Ipv6Proto};
5086    use test_case::test_case;
5087
5088    use super::*;
5089    use crate::internal::spec_context;
5090
5091    trait DatagramIpExt<D: FakeStrongDeviceId>:
5092        IpExt + IpDeviceStateIpExt + TestIpExt + DualStackIpExt + DualStackContextsIpExt<D>
5093    {
5094    }
5095    impl<
5096        D: FakeStrongDeviceId,
5097        I: Ip + IpExt + IpDeviceStateIpExt + TestIpExt + DualStackIpExt + DualStackContextsIpExt<D>,
5098    > DatagramIpExt<D> for I
5099    {
5100    }
5101
5102    #[derive(Debug)]
5103    enum FakeAddrSpec {}
5104
5105    impl SocketMapAddrSpec for FakeAddrSpec {
5106        type LocalIdentifier = NonZeroU16;
5107        type RemoteIdentifier = u16;
5108    }
5109
5110    #[derive(Debug)]
5111    enum FakeStateSpec {}
5112
5113    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
5114    struct Tag;
5115
5116    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
5117
5118    enum Sharing {
5119        #[default]
5120        NoConflicts,
5121        // Any attempt to insert a connection with the following remote port
5122        // will conflict.
5123        ConnectionConflicts {
5124            remote_port: u16,
5125        },
5126    }
5127
5128    #[derive(Clone, Debug, Derivative)]
5129    #[derivative(Eq(bound = ""), PartialEq(bound = ""))]
5130    struct Id<I: IpExt, D: WeakDeviceIdentifier>(StrongRc<I, D, FakeStateSpec>);
5131
5132    /// Utilities for accessing locked internal state in tests.
5133    impl<I: IpExt, D: WeakDeviceIdentifier> Id<I, D> {
5134        fn get(&self) -> impl Deref<Target = SocketState<I, D, FakeStateSpec>> + '_ {
5135            let Self(rc) = self;
5136            rc.state.read()
5137        }
5138
5139        fn get_mut(&self) -> impl DerefMut<Target = SocketState<I, D, FakeStateSpec>> + '_ {
5140            let Self(rc) = self;
5141            rc.state.write()
5142        }
5143    }
5144
5145    impl<I: IpExt, D: WeakDeviceIdentifier> From<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
5146        fn from(value: StrongRc<I, D, FakeStateSpec>) -> Self {
5147            Self(value)
5148        }
5149    }
5150
5151    impl<I: IpExt, D: WeakDeviceIdentifier> Borrow<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
5152        fn borrow(&self) -> &StrongRc<I, D, FakeStateSpec> {
5153            let Self(rc) = self;
5154            rc
5155        }
5156    }
5157
5158    #[derive(Debug)]
5159    struct AddrState<T>(T);
5160
5161    struct FakeSocketMapStateSpec<I, D>(PhantomData<(I, D)>, Never);
5162
5163    impl<I: IpExt, D: WeakDeviceIdentifier> SocketMapStateSpec for FakeSocketMapStateSpec<I, D> {
5164        type AddrVecTag = Tag;
5165        type ConnAddrState = AddrState<Self::ConnId>;
5166        type ConnId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5167        type ConnSharingState = Sharing;
5168        type ListenerAddrState = AddrState<Self::ListenerId>;
5169        type ListenerId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5170        type ListenerSharingState = Sharing;
5171        fn listener_tag(_: ListenerAddrInfo, _state: &Self::ListenerAddrState) -> Self::AddrVecTag {
5172            Tag
5173        }
5174        fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
5175            Tag
5176        }
5177    }
5178
5179    const FAKE_DATAGRAM_IPV4_PROTOCOL: Ipv4Proto = Ipv4Proto::Other(253);
5180    const FAKE_DATAGRAM_IPV6_PROTOCOL: Ipv6Proto = Ipv6Proto::Other(254);
5181
5182    impl DatagramSocketSpec for FakeStateSpec {
5183        const NAME: &'static str = "FAKE";
5184        type AddrSpec = FakeAddrSpec;
5185        type SocketId<I: IpExt, D: WeakDeviceIdentifier> = Id<I, D>;
5186        // NB: We don't have use for real weak IDs here since we only need to be
5187        // able to make it upgrade.
5188        type WeakSocketId<I: IpExt, D: WeakDeviceIdentifier> = Id<I, D>;
5189        type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier> =
5190            DatagramIpSpecificSocketOptions<I::OtherVersion, D>;
5191        type SocketMapSpec<I: IpExt, D: WeakDeviceIdentifier> = FakeSocketMapStateSpec<I, D>;
5192        type SharingState = Sharing;
5193        type ListenerIpAddr<I: IpExt> =
5194            I::DualStackListenerIpAddr<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
5195        type ConnIpAddr<I: IpExt> = I::DualStackConnIpAddr<Self>;
5196        type ConnStateExtra = ();
5197        type ConnState<I: IpExt, D: WeakDeviceIdentifier> = I::DualStackConnState<D, Self>;
5198        type Counters<I: Ip> = ();
5199        type ExternalData<I: Ip> = ();
5200        type SocketWritableListener = FakeSocketWritableListener;
5201        type Settings = DatagramSettings;
5202
5203        fn ip_proto<I: IpProtoExt>() -> I::Proto {
5204            I::map_ip((), |()| FAKE_DATAGRAM_IPV4_PROTOCOL, |()| FAKE_DATAGRAM_IPV6_PROTOCOL)
5205        }
5206
5207        fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
5208            s: &Self::SocketId<I, D>,
5209        ) -> <Self::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, Self::AddrSpec>>::BoundSocketId
5210        {
5211            I::into_dual_stack_bound_socket_id(s.clone())
5212        }
5213
5214        type Serializer<I: IpExt, B: BufferMut> = packet::Nested<B, ()>;
5215        type SerializeError = Never;
5216        const FIXED_HEADER_SIZE: usize = 0;
5217        fn make_packet<I: IpExt, B: BufferMut>(
5218            body: B,
5219            _addr: &ConnIpAddr<
5220                I::Addr,
5221                <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5222                <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5223            >,
5224        ) -> Result<Self::Serializer<I, B>, Never> {
5225            Ok(body.wrap_in(()))
5226        }
5227        fn try_alloc_listen_identifier<I: Ip, D: WeakDeviceIdentifier>(
5228            _bindings_ctx: &mut impl RngContext,
5229            is_available: impl Fn(
5230                <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5231            ) -> Result<(), InUseError>,
5232        ) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
5233            (1..=u16::MAX).map(|i| NonZeroU16::new(i).unwrap()).find(|i| is_available(*i).is_ok())
5234        }
5235
5236        fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
5237            state: &Self::ConnState<I, D>,
5238        ) -> ConnInfo<I::Addr, D> {
5239            let ConnAddr { ip, device } = I::conn_addr_from_state(state);
5240            let ConnInfoAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } =
5241                ip.into();
5242            ConnInfo::new(local_ip, local_port, remote_ip, remote_port, || {
5243                device.clone().expect("device must be bound for addresses that require zones")
5244            })
5245        }
5246
5247        fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
5248            bound: &BoundSocketMap<I, D, FakeAddrSpec, FakeSocketMapStateSpec<I, D>>,
5249            _bindings_ctx: &mut BC,
5250            _flow: DatagramFlowId<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
5251        ) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
5252            (1..u16::MAX).find_map(|identifier| {
5253                let identifier = NonZeroU16::new(identifier).unwrap();
5254                bound
5255                    .listeners()
5256                    .could_insert(
5257                        &ListenerAddr {
5258                            device: None,
5259                            ip: ListenerIpAddr { addr: None, identifier },
5260                        },
5261                        &Default::default(),
5262                    )
5263                    .is_ok()
5264                    .then_some(identifier)
5265            })
5266        }
5267
5268        fn upgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
5269            id: &Self::WeakSocketId<I, D>,
5270        ) -> Option<Self::SocketId<I, D>> {
5271            Some(id.clone())
5272        }
5273
5274        fn downgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
5275            id: &Self::SocketId<I, D>,
5276        ) -> Self::WeakSocketId<I, D> {
5277            id.clone()
5278        }
5279    }
5280
5281    impl<I: IpExt, D: WeakDeviceIdentifier> DatagramSocketMapSpec<I, D, FakeAddrSpec>
5282        for FakeSocketMapStateSpec<I, D>
5283    {
5284        type BoundSocketId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5285    }
5286
5287    impl<I: IpExt, D: WeakDeviceIdentifier>
5288        SocketMapConflictPolicy<
5289            ConnAddr<
5290                ConnIpAddr<
5291                    I::Addr,
5292                    <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5293                    <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5294                >,
5295                D,
5296            >,
5297            Sharing,
5298            I,
5299            D,
5300            FakeAddrSpec,
5301        > for FakeSocketMapStateSpec<I, D>
5302    {
5303        fn check_insert_conflicts(
5304            sharing: &Sharing,
5305            addr: &ConnAddr<
5306                ConnIpAddr<
5307                    I::Addr,
5308                    <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5309                    <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5310                >,
5311                D,
5312            >,
5313            _socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
5314        ) -> Result<(), InsertError> {
5315            let ConnAddr { ip: ConnIpAddr { local: _, remote: (_remote_ip, port) }, device: _ } =
5316                addr;
5317            match sharing {
5318                Sharing::NoConflicts => Ok(()),
5319                Sharing::ConnectionConflicts { remote_port } => {
5320                    if remote_port == port {
5321                        Err(InsertError::Exists)
5322                    } else {
5323                        Ok(())
5324                    }
5325                }
5326            }
5327        }
5328    }
5329
5330    impl<I: IpExt, D: WeakDeviceIdentifier>
5331        SocketMapConflictPolicy<
5332            ListenerAddr<
5333                ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
5334                D,
5335            >,
5336            Sharing,
5337            I,
5338            D,
5339            FakeAddrSpec,
5340        > for FakeSocketMapStateSpec<I, D>
5341    {
5342        fn check_insert_conflicts(
5343            sharing: &Sharing,
5344            _addr: &ListenerAddr<
5345                ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
5346                D,
5347            >,
5348            _socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
5349        ) -> Result<(), InsertError> {
5350            match sharing {
5351                Sharing::NoConflicts => Ok(()),
5352                // Since this implementation is strictly for ListenerAddr,
5353                // ignore connection conflicts.
5354                Sharing::ConnectionConflicts { remote_port: _ } => Ok(()),
5355            }
5356        }
5357    }
5358
5359    impl<T: Eq> SocketMapAddrStateSpec for AddrState<T> {
5360        type Id = T;
5361        type SharingState = Sharing;
5362        type Inserter<'a>
5363            = Never
5364        where
5365            Self: 'a;
5366
5367        fn new(_sharing: &Self::SharingState, id: Self::Id) -> Self {
5368            AddrState(id)
5369        }
5370        fn contains_id(&self, id: &Self::Id) -> bool {
5371            let Self(inner) = self;
5372            inner == id
5373        }
5374        fn try_get_inserter<'a, 'b>(
5375            &'b mut self,
5376            _new_sharing_state: &'a Self::SharingState,
5377        ) -> Result<Self::Inserter<'b>, IncompatibleError> {
5378            Err(IncompatibleError)
5379        }
5380        fn could_insert(
5381            &self,
5382            _new_sharing_state: &Self::SharingState,
5383        ) -> Result<(), IncompatibleError> {
5384            Err(IncompatibleError)
5385        }
5386        fn remove_by_id(&mut self, _id: Self::Id) -> RemoveResult {
5387            RemoveResult::IsLast
5388        }
5389    }
5390
5391    #[derive(Derivative, GenericOverIp)]
5392    #[derivative(Default(bound = ""))]
5393    #[generic_over_ip()]
5394    struct FakeBoundSockets<D: FakeStrongDeviceId> {
5395        v4: BoundDatagramSocketMap<Ipv4, FakeWeakDeviceId<D>, FakeStateSpec>,
5396        v6: BoundDatagramSocketMap<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5397    }
5398
5399    impl<D: FakeStrongDeviceId, I: IpExt>
5400        AsRef<
5401            BoundSocketMap<
5402                I,
5403                FakeWeakDeviceId<D>,
5404                FakeAddrSpec,
5405                FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
5406            >,
5407        > for FakeBoundSockets<D>
5408    {
5409        fn as_ref(
5410            &self,
5411        ) -> &BoundSocketMap<
5412            I,
5413            FakeWeakDeviceId<D>,
5414            FakeAddrSpec,
5415            FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
5416        > {
5417            #[derive(GenericOverIp)]
5418            #[generic_over_ip(I, Ip)]
5419            struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
5420                &'a BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5421            );
5422            let Wrap(state) = I::map_ip(self, |state| Wrap(&state.v4), |state| Wrap(&state.v6));
5423            state
5424        }
5425    }
5426
5427    impl<D: FakeStrongDeviceId, I: IpExt>
5428        AsMut<BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>>
5429        for FakeBoundSockets<D>
5430    {
5431        fn as_mut(&mut self) -> &mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec> {
5432            #[derive(GenericOverIp)]
5433            #[generic_over_ip(I, Ip)]
5434            struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
5435                &'a mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5436            );
5437            let Wrap(state) =
5438                I::map_ip(self, |state| Wrap(&mut state.v4), |state| Wrap(&mut state.v6));
5439            state
5440        }
5441    }
5442
5443    type FakeBindingsCtx = netstack3_base::testutil::FakeBindingsCtx<(), (), (), ()>;
5444    type FakeCtx<I, D> = CtxPair<FakeCoreCtx<I, D>, FakeBindingsCtx>;
5445
5446    type FakeSocketSet<I, D> = DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>;
5447
5448    type InnerIpSocketCtx<D> = netstack3_base::testutil::FakeCoreCtx<
5449        FakeDualStackIpSocketCtx<D>,
5450        DualStackSendIpPacketMeta<D>,
5451        D,
5452    >;
5453
5454    /// A trait providing a shortcut to instantiate a [`DatagramApi`] from a context.
5455    trait DatagramApiExt: ContextPair + Sized {
5456        fn datagram_api<I: Ip>(&mut self) -> DatagramApi<I, &mut Self, FakeStateSpec> {
5457            DatagramApi::new(self)
5458        }
5459    }
5460
5461    impl<O> DatagramApiExt for O where O: ContextPair + Sized {}
5462
5463    struct FakeDualStackCoreCtx<D: FakeStrongDeviceId> {
5464        bound_sockets: FakeBoundSockets<D>,
5465        ip_socket_ctx: InnerIpSocketCtx<D>,
5466    }
5467
5468    struct FakeCoreCtx<I: IpExt, D: FakeStrongDeviceId> {
5469        dual_stack: FakeDualStackCoreCtx<D>,
5470        // NB: socket set is last in the struct so all the strong refs are
5471        // dropped before the primary refs contained herein.
5472        socket_set: FakeSocketSet<I, D>,
5473    }
5474
5475    impl<I: IpExt, D: FakeStrongDeviceId> ContextProvider for FakeCoreCtx<I, D> {
5476        type Context = Self;
5477        fn context(&mut self) -> &mut Self::Context {
5478            self
5479        }
5480    }
5481
5482    impl<I: IpExt, D: FakeStrongDeviceId> FakeCoreCtx<I, D> {
5483        fn new() -> Self {
5484            Self::new_with_sockets(Default::default(), Default::default())
5485        }
5486
5487        fn new_with_sockets(
5488            socket_set: FakeSocketSet<I, D>,
5489            bound_sockets: FakeBoundSockets<D>,
5490        ) -> Self {
5491            Self {
5492                socket_set,
5493                dual_stack: FakeDualStackCoreCtx {
5494                    bound_sockets,
5495                    ip_socket_ctx: Default::default(),
5496                },
5497            }
5498        }
5499
5500        fn new_with_ip_socket_ctx(ip_socket_ctx: FakeDualStackIpSocketCtx<D>) -> Self {
5501            Self {
5502                socket_set: Default::default(),
5503                dual_stack: FakeDualStackCoreCtx {
5504                    bound_sockets: Default::default(),
5505                    ip_socket_ctx: InnerIpSocketCtx::with_state(ip_socket_ctx),
5506                },
5507            }
5508        }
5509    }
5510
5511    impl<I: IpExt, D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeCoreCtx<I, D> {
5512        type DeviceId = D;
5513        type WeakDeviceId = FakeWeakDeviceId<D>;
5514    }
5515
5516    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeDualStackCoreCtx<D> {
5517        type DeviceId = D;
5518        type WeakDeviceId = FakeWeakDeviceId<D>;
5519    }
5520
5521    impl<D: FakeStrongDeviceId, I: DatagramIpExt<D>>
5522        spec_context::DatagramSpecStateContext<I, FakeCoreCtx<I, D>, FakeBindingsCtx>
5523        for FakeStateSpec
5524    {
5525        type SocketsStateCtx<'a> = FakeDualStackCoreCtx<D>;
5526
5527        fn with_all_sockets_mut<
5528            O,
5529            F: FnOnce(&mut DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
5530        >(
5531            core_ctx: &mut FakeCoreCtx<I, D>,
5532            cb: F,
5533        ) -> O {
5534            cb(&mut core_ctx.socket_set)
5535        }
5536
5537        fn with_all_sockets<
5538            O,
5539            F: FnOnce(&DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
5540        >(
5541            core_ctx: &mut FakeCoreCtx<I, D>,
5542            cb: F,
5543        ) -> O {
5544            cb(&core_ctx.socket_set)
5545        }
5546
5547        fn with_socket_state<
5548            O,
5549            F: FnOnce(
5550                &mut Self::SocketsStateCtx<'_>,
5551                &SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5552            ) -> O,
5553        >(
5554            core_ctx: &mut FakeCoreCtx<I, D>,
5555            id: &Id<I, FakeWeakDeviceId<D>>,
5556            cb: F,
5557        ) -> O {
5558            cb(&mut core_ctx.dual_stack, &id.get())
5559        }
5560
5561        fn with_socket_state_mut<
5562            O,
5563            F: FnOnce(
5564                &mut Self::SocketsStateCtx<'_>,
5565                &mut SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5566            ) -> O,
5567        >(
5568            core_ctx: &mut FakeCoreCtx<I, D>,
5569            id: &Id<I, FakeWeakDeviceId<D>>,
5570            cb: F,
5571        ) -> O {
5572            cb(&mut core_ctx.dual_stack, &mut id.get_mut())
5573        }
5574
5575        fn for_each_socket<
5576            F: FnMut(
5577                &mut Self::SocketsStateCtx<'_>,
5578                &Id<I, FakeWeakDeviceId<D>>,
5579                &SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5580            ),
5581        >(
5582            core_ctx: &mut FakeCoreCtx<I, D>,
5583            mut cb: F,
5584        ) {
5585            core_ctx.socket_set.keys().for_each(|id| {
5586                let id = Id::from(id.clone());
5587                cb(&mut core_ctx.dual_stack, &id, &id.get());
5588            })
5589        }
5590    }
5591
5592    /// A test-only IpExt trait to specialize the `DualStackContext` and
5593    /// `NonDualStackContext` associated types on the
5594    /// `DatagramBoundStateContext`.
5595    ///
5596    /// This allows us to implement `DatagramBoundStateContext` for all `I`
5597    /// while also assigning its associated types different values for `Ipv4`
5598    /// and `Ipv6`.
5599    trait DualStackContextsIpExt<D: FakeStrongDeviceId>: IpExt {
5600        type DualStackContext: DualStackDatagramBoundStateContext<
5601                Self,
5602                FakeBindingsCtx,
5603                FakeStateSpec,
5604                DeviceId = D,
5605                WeakDeviceId = FakeWeakDeviceId<D>,
5606            >;
5607        type NonDualStackContext: NonDualStackDatagramBoundStateContext<
5608                Self,
5609                FakeBindingsCtx,
5610                FakeStateSpec,
5611                DeviceId = D,
5612                WeakDeviceId = FakeWeakDeviceId<D>,
5613            >;
5614
5615        fn dual_stack_context(
5616            core_ctx: &FakeDualStackCoreCtx<D>,
5617        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext>;
5618
5619        fn dual_stack_context_mut(
5620            core_ctx: &mut FakeDualStackCoreCtx<D>,
5621        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
5622    }
5623
5624    impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv4 {
5625        type DualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
5626        type NonDualStackContext = FakeDualStackCoreCtx<D>;
5627
5628        fn dual_stack_context(
5629            core_ctx: &FakeDualStackCoreCtx<D>,
5630        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5631            MaybeDualStack::NotDualStack(core_ctx)
5632        }
5633
5634        fn dual_stack_context_mut(
5635            core_ctx: &mut FakeDualStackCoreCtx<D>,
5636        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5637            MaybeDualStack::NotDualStack(core_ctx)
5638        }
5639    }
5640
5641    impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv6 {
5642        type DualStackContext = FakeDualStackCoreCtx<D>;
5643        type NonDualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
5644
5645        fn dual_stack_context(
5646            core_ctx: &FakeDualStackCoreCtx<D>,
5647        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5648            MaybeDualStack::DualStack(core_ctx)
5649        }
5650
5651        fn dual_stack_context_mut(
5652            core_ctx: &mut FakeDualStackCoreCtx<D>,
5653        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5654            MaybeDualStack::DualStack(core_ctx)
5655        }
5656    }
5657
5658    impl<D: FakeStrongDeviceId, I: DualStackContextsIpExt<D>>
5659        spec_context::DatagramSpecBoundStateContext<I, FakeDualStackCoreCtx<D>, FakeBindingsCtx>
5660        for FakeStateSpec
5661    {
5662        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
5663        type DualStackContext = I::DualStackContext;
5664        type NonDualStackContext = I::NonDualStackContext;
5665
5666        fn with_bound_sockets<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5667        where
5668            F: FnOnce(
5669                &mut Self::IpSocketsCtx<'_>,
5670                &BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5671            ) -> O,
5672        {
5673            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5674            cb(ip_socket_ctx, bound_sockets.as_ref())
5675        }
5676        fn with_bound_sockets_mut<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5677        where
5678            F: FnOnce(
5679                &mut Self::IpSocketsCtx<'_>,
5680                &mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5681            ) -> O,
5682        {
5683            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5684            cb(ip_socket_ctx, bound_sockets.as_mut())
5685        }
5686
5687        fn with_transport_context<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5688        where
5689            F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O,
5690        {
5691            cb(&mut core_ctx.ip_socket_ctx)
5692        }
5693
5694        fn dual_stack_context(
5695            core_ctx: &FakeDualStackCoreCtx<D>,
5696        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5697            I::dual_stack_context(core_ctx)
5698        }
5699
5700        fn dual_stack_context_mut(
5701            core_ctx: &mut FakeDualStackCoreCtx<D>,
5702        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5703            I::dual_stack_context_mut(core_ctx)
5704        }
5705    }
5706
5707    impl<D: FakeStrongDeviceId>
5708        spec_context::NonDualStackDatagramSpecBoundStateContext<
5709            Ipv4,
5710            FakeDualStackCoreCtx<D>,
5711            FakeBindingsCtx,
5712        > for FakeStateSpec
5713    {
5714        fn nds_converter(
5715            _core_ctx: &FakeDualStackCoreCtx<D>,
5716        ) -> impl NonDualStackConverter<Ipv4, FakeWeakDeviceId<D>, Self> {
5717            ()
5718        }
5719    }
5720
5721    impl<D: FakeStrongDeviceId>
5722        spec_context::DualStackDatagramSpecBoundStateContext<
5723            Ipv6,
5724            FakeDualStackCoreCtx<D>,
5725            FakeBindingsCtx,
5726        > for FakeStateSpec
5727    {
5728        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
5729        fn dual_stack_enabled(
5730            _core_ctx: &FakeDualStackCoreCtx<D>,
5731            _ip_options: &IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5732        ) -> bool {
5733            // For now, it's simplest to have dual-stack unconditionally enabled
5734            // for datagram tests. However, in the future this could be stateful
5735            // and follow an implementation similar to UDP's test fixture.
5736            true
5737        }
5738
5739        fn to_other_socket_options<'a>(
5740            _core_ctx: &FakeDualStackCoreCtx<D>,
5741            state: &'a IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5742        ) -> &'a DatagramIpSpecificSocketOptions<Ipv4, FakeWeakDeviceId<D>> {
5743            let IpOptions { other_stack, .. } = state;
5744            other_stack
5745        }
5746
5747        fn ds_converter(
5748            _core_ctx: &FakeDualStackCoreCtx<D>,
5749        ) -> impl DualStackConverter<Ipv6, FakeWeakDeviceId<D>, Self> {
5750            ()
5751        }
5752
5753        fn to_other_bound_socket_id(
5754            _core_ctx: &FakeDualStackCoreCtx<D>,
5755            id: &Id<Ipv6, D::Weak>,
5756        ) -> EitherIpSocket<D::Weak, FakeStateSpec> {
5757            EitherIpSocket::V6(id.clone())
5758        }
5759
5760        fn with_both_bound_sockets_mut<
5761            O,
5762            F: FnOnce(
5763                &mut Self::IpSocketsCtx<'_>,
5764                &mut BoundSocketsFromSpec<Ipv6, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5765                &mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5766            ) -> O,
5767        >(
5768            core_ctx: &mut FakeDualStackCoreCtx<D>,
5769            cb: F,
5770        ) -> O {
5771            let FakeDualStackCoreCtx { bound_sockets: FakeBoundSockets { v4, v6 }, ip_socket_ctx } =
5772                core_ctx;
5773            cb(ip_socket_ctx, v6, v4)
5774        }
5775
5776        fn with_other_bound_sockets_mut<
5777            O,
5778            F: FnOnce(
5779                &mut Self::IpSocketsCtx<'_>,
5780                &mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5781            ) -> O,
5782        >(
5783            core_ctx: &mut FakeDualStackCoreCtx<D>,
5784            cb: F,
5785        ) -> O {
5786            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5787            cb(ip_socket_ctx, bound_sockets.as_mut())
5788        }
5789
5790        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
5791            core_ctx: &mut FakeDualStackCoreCtx<D>,
5792            cb: F,
5793        ) -> O {
5794            cb(&mut core_ctx.ip_socket_ctx)
5795        }
5796    }
5797
5798    #[ip_test(I)]
5799    fn set_get_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
5800        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5801        let mut api = ctx.datagram_api::<I>();
5802
5803        let unbound = api.create_default();
5804        const EXPECTED_HOP_LIMITS: HopLimits = HopLimits {
5805            unicast: NonZeroU8::new(45).unwrap(),
5806            multicast: NonZeroU8::new(23).unwrap(),
5807        };
5808
5809        api.update_ip_hop_limit(&unbound, |limits| {
5810            *limits = SocketHopLimits {
5811                unicast: Some(EXPECTED_HOP_LIMITS.unicast),
5812                multicast: Some(EXPECTED_HOP_LIMITS.multicast),
5813                version: IpVersionMarker::default(),
5814            }
5815        });
5816
5817        assert_eq!(api.get_ip_hop_limits(&unbound), EXPECTED_HOP_LIMITS);
5818    }
5819
5820    #[ip_test(I)]
5821    fn set_get_device_hop_limits<I: DatagramIpExt<FakeReferencyDeviceId>>() {
5822        let device = FakeReferencyDeviceId::default();
5823        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
5824            FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
5825                device: device.clone(),
5826                local_ips: Default::default(),
5827                remote_ips: Default::default(),
5828            }]),
5829        ));
5830        let mut api = ctx.datagram_api::<I>();
5831
5832        let unbound = api.create_default();
5833        api.set_device(&unbound, Some(&device)).unwrap();
5834
5835        let HopLimits { mut unicast, multicast } = DEFAULT_HOP_LIMITS;
5836        unicast = unicast.checked_add(1).unwrap();
5837        {
5838            let device_state =
5839                api.core_ctx().dual_stack.ip_socket_ctx.state.get_device_state_mut::<I>(&device);
5840            assert_ne!(device_state.default_hop_limit, unicast);
5841            device_state.default_hop_limit = unicast;
5842        }
5843        assert_eq!(api.get_ip_hop_limits(&unbound), HopLimits { unicast, multicast });
5844
5845        // If the device is removed, use default hop limits.
5846        device.mark_removed();
5847        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5848    }
5849
5850    #[ip_test(I)]
5851    fn default_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
5852        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5853        let mut api = ctx.datagram_api::<I>();
5854        let unbound = api.create_default();
5855        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5856
5857        api.update_ip_hop_limit(&unbound, |limits| {
5858            *limits = SocketHopLimits {
5859                unicast: Some(NonZeroU8::new(1).unwrap()),
5860                multicast: Some(NonZeroU8::new(1).unwrap()),
5861                version: IpVersionMarker::default(),
5862            }
5863        });
5864
5865        // The limits no longer match the default.
5866        assert_ne!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5867
5868        // Clear the hop limits set on the socket.
5869        api.update_ip_hop_limit(&unbound, |limits| *limits = Default::default());
5870
5871        // The values should be back at the defaults.
5872        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5873    }
5874
5875    #[ip_test(I)]
5876    fn bind_device_unbound<I: DatagramIpExt<FakeDeviceId>>() {
5877        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5878        let mut api = ctx.datagram_api::<I>();
5879        let unbound = api.create_default();
5880
5881        api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
5882        assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
5883
5884        api.set_device(&unbound, None).unwrap();
5885        assert_eq!(api.get_bound_device(&unbound), None);
5886    }
5887
5888    #[ip_test(I)]
5889    fn send_to_binds_unbound<I: DatagramIpExt<FakeDeviceId>>() {
5890        let mut ctx =
5891            FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new_with_ip_socket_ctx(
5892                FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
5893                    device: FakeDeviceId,
5894                    local_ips: vec![I::TEST_ADDRS.local_ip],
5895                    remote_ips: vec![I::TEST_ADDRS.remote_ip],
5896                }]),
5897            ));
5898        let mut api = ctx.datagram_api::<I>();
5899        let socket = api.create_default();
5900        let body = Buf::new(Vec::new(), ..);
5901
5902        api.send_to(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), 1234, body)
5903            .expect("succeeds");
5904        assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
5905    }
5906
5907    #[ip_test(I)]
5908    fn send_to_no_route_still_binds<I: DatagramIpExt<FakeDeviceId>>() {
5909        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
5910            FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
5911                device: FakeDeviceId,
5912                local_ips: vec![I::TEST_ADDRS.local_ip],
5913                remote_ips: vec![],
5914            }]),
5915        ));
5916        let mut api = ctx.datagram_api::<I>();
5917        let socket = api.create_default();
5918        let body = Buf::new(Vec::new(), ..);
5919
5920        assert_matches!(
5921            api.send_to(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), 1234, body,),
5922            Err(Either::Right(SendToError::CreateAndSend(_)))
5923        );
5924        assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
5925    }
5926
5927    #[ip_test(I)]
5928    #[test_case(true; "remove device b")]
5929    #[test_case(false; "dont remove device b")]
5930    fn multicast_membership_changes<I: DatagramIpExt<FakeReferencyDeviceId> + TestIpExt>(
5931        remove_device_b: bool,
5932    ) {
5933        let device_a = FakeReferencyDeviceId::default();
5934        let device_b = FakeReferencyDeviceId::default();
5935        let mut core_ctx = FakeIpSocketCtx::<I, FakeReferencyDeviceId>::new(
5936            [device_a.clone(), device_b.clone()].into_iter().map(|device| FakeDeviceConfig {
5937                device,
5938                local_ips: Default::default(),
5939                remote_ips: Default::default(),
5940            }),
5941        );
5942        let mut bindings_ctx = FakeBindingsCtx::default();
5943
5944        let multicast_addr1 = I::get_multicast_addr(1);
5945        let mut memberships = MulticastMemberships::default();
5946        assert_eq!(
5947            memberships.apply_membership_change(
5948                multicast_addr1,
5949                &FakeWeakDeviceId(device_a.clone()),
5950                true /* want_membership */
5951            ),
5952            Some(MulticastMembershipChange::Join),
5953        );
5954        core_ctx.join_multicast_group(&mut bindings_ctx, &device_a, multicast_addr1);
5955
5956        let multicast_addr2 = I::get_multicast_addr(2);
5957        assert_eq!(
5958            memberships.apply_membership_change(
5959                multicast_addr2,
5960                &FakeWeakDeviceId(device_b.clone()),
5961                true /* want_membership */
5962            ),
5963            Some(MulticastMembershipChange::Join),
5964        );
5965        core_ctx.join_multicast_group(&mut bindings_ctx, &device_b, multicast_addr2);
5966
5967        for (device, addr, expected) in [
5968            (&device_a, multicast_addr1, true),
5969            (&device_a, multicast_addr2, false),
5970            (&device_b, multicast_addr1, false),
5971            (&device_b, multicast_addr2, true),
5972        ] {
5973            assert_eq!(
5974                core_ctx.get_device_state(device).is_in_multicast_group(&addr),
5975                expected,
5976                "device={:?}, addr={}",
5977                device,
5978                addr,
5979            );
5980        }
5981
5982        if remove_device_b {
5983            device_b.mark_removed();
5984        }
5985
5986        leave_all_joined_groups(&mut core_ctx, &mut bindings_ctx, &memberships);
5987        for (device, addr, expected) in [
5988            (&device_a, multicast_addr1, false),
5989            (&device_a, multicast_addr2, false),
5990            (&device_b, multicast_addr1, false),
5991            // Should not attempt to leave the multicast group on the device if
5992            // the device looks like it was removed. Note that although we mark
5993            // the device as removed, we do not destroy its state so we can
5994            // inspect it here.
5995            (&device_b, multicast_addr2, remove_device_b),
5996        ] {
5997            assert_eq!(
5998                core_ctx.get_device_state(device).is_in_multicast_group(&addr),
5999                expected,
6000                "device={:?}, addr={}",
6001                device,
6002                addr,
6003            );
6004        }
6005    }
6006
6007    #[ip_test(I)]
6008    fn set_get_transparent<I: DatagramIpExt<FakeDeviceId>>() {
6009        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
6010            FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
6011                device: FakeDeviceId,
6012                local_ips: Default::default(),
6013                remote_ips: Default::default(),
6014            }]),
6015        ));
6016        let mut api = ctx.datagram_api::<I>();
6017        let unbound = api.create_default();
6018
6019        assert!(!api.get_ip_transparent(&unbound));
6020
6021        api.set_ip_transparent(&unbound, true);
6022
6023        assert!(api.get_ip_transparent(&unbound));
6024
6025        api.set_ip_transparent(&unbound, false);
6026
6027        assert!(!api.get_ip_transparent(&unbound));
6028    }
6029
6030    #[ip_test(I)]
6031    fn transparent_bind_connect_non_local_src_addr<I: DatagramIpExt<FakeDeviceId>>() {
6032        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
6033            FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
6034                device: FakeDeviceId,
6035                local_ips: vec![],
6036                remote_ips: vec![I::TEST_ADDRS.remote_ip],
6037            }]),
6038        ));
6039        let mut api = ctx.datagram_api::<I>();
6040        let socket = api.create_default();
6041        api.set_ip_transparent(&socket, true);
6042
6043        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6044        const REMOTE_PORT: u16 = 1234;
6045
6046        // Binding to `local_ip` should succeed even though it is not assigned
6047        // to an interface because the socket is transparent.
6048        api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT))
6049            .expect("listen should succeed");
6050
6051        // Connecting to a valid remote should also succeed even though the
6052        // local address of the IP socket is not actually local.
6053        api.connect(
6054            &socket,
6055            Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
6056            REMOTE_PORT,
6057            Default::default(),
6058        )
6059        .expect("connect should succeed");
6060
6061        api.send_to(
6062            &socket,
6063            Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
6064            REMOTE_PORT,
6065            Buf::new(Vec::new(), ..),
6066        )
6067        .expect("send_to should succeed");
6068    }
6069
6070    #[derive(Eq, PartialEq)]
6071    enum OriginalSocketState {
6072        Unbound,
6073        Listener,
6074        Connected,
6075    }
6076
6077    #[ip_test(I)]
6078    #[test_case(OriginalSocketState::Unbound; "reinsert_unbound")]
6079    #[test_case(OriginalSocketState::Listener; "reinsert_listener")]
6080    #[test_case(OriginalSocketState::Connected; "reinsert_connected")]
6081    fn connect_reinserts_on_failure_single_stack<I: DatagramIpExt<FakeDeviceId>>(
6082        original: OriginalSocketState,
6083    ) {
6084        connect_reinserts_on_failure_inner::<I>(
6085            original,
6086            I::TEST_ADDRS.local_ip.get(),
6087            I::TEST_ADDRS.remote_ip,
6088        );
6089    }
6090
6091    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
6092        net_ip_v4!("192.0.2.2"); "reinsert_listener_other_stack")]
6093    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
6094        net_ip_v4!("192.0.2.2"); "reinsert_listener_both_stacks")]
6095    #[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
6096        net_ip_v4!("192.0.2.2"); "reinsert_connected_other_stack")]
6097    fn connect_reinserts_on_failure_dual_stack(
6098        original: OriginalSocketState,
6099        local_ip: Ipv6Addr,
6100        remote_ip: Ipv4Addr,
6101    ) {
6102        let remote_ip = remote_ip.to_ipv6_mapped();
6103        connect_reinserts_on_failure_inner::<Ipv6>(original, local_ip, remote_ip);
6104    }
6105
6106    fn connect_reinserts_on_failure_inner<I: DatagramIpExt<FakeDeviceId>>(
6107        original: OriginalSocketState,
6108        local_ip: I::Addr,
6109        remote_ip: SpecifiedAddr<I::Addr>,
6110    ) {
6111        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6112            local_ip.to_ip_addr(),
6113            remote_ip.into(),
6114            [FakeDeviceId {}],
6115            |device_configs| {
6116                FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6117                    device_configs,
6118                ))
6119            },
6120        );
6121        let mut api = ctx.datagram_api::<I>();
6122        let socket = api.create_default();
6123        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6124        const ORIGINAL_REMOTE_PORT: u16 = 1234;
6125        const NEW_REMOTE_PORT: u16 = 5678;
6126
6127        // Setup the original socket state.
6128        match original {
6129            OriginalSocketState::Unbound => {}
6130            OriginalSocketState::Listener => api
6131                .listen(
6132                    &socket,
6133                    SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
6134                    Some(LOCAL_PORT),
6135                )
6136                .expect("listen should succeed"),
6137            OriginalSocketState::Connected => api
6138                .connect(
6139                    &socket,
6140                    Some(ZonedAddr::Unzoned(remote_ip)),
6141                    ORIGINAL_REMOTE_PORT,
6142                    Default::default(),
6143                )
6144                .expect("connect should succeed"),
6145        }
6146
6147        // Update the sharing state to generate conflicts during the call to `connect`.
6148        api.core_ctx().with_socket_state_mut(
6149            &socket,
6150            |_core_ctx, state: &mut SocketState<I, _, FakeStateSpec>| {
6151                state.sharing = Sharing::ConnectionConflicts { remote_port: NEW_REMOTE_PORT };
6152            },
6153        );
6154
6155        // Try to connect and observe a conflict error.
6156        assert_matches!(
6157            api.connect(
6158                &socket,
6159                Some(ZonedAddr::Unzoned(remote_ip)),
6160                NEW_REMOTE_PORT,
6161                Default::default(),
6162            ),
6163            Err(ConnectError::SockAddrConflict)
6164        );
6165
6166        // Verify the original socket state is intact.
6167        let info = api.get_info(&socket);
6168        match original {
6169            OriginalSocketState::Unbound => assert_matches!(info, SocketInfo::Unbound),
6170            OriginalSocketState::Listener => {
6171                let local_port = assert_matches!(
6172                    info,
6173                    SocketInfo::Listener(ListenerInfo {
6174                        local_ip: _,
6175                        local_identifier,
6176                    }) => local_identifier
6177                );
6178                assert_eq!(LOCAL_PORT, local_port);
6179            }
6180            OriginalSocketState::Connected => {
6181                let remote_port = assert_matches!(
6182                    info,
6183                    SocketInfo::Connected(ConnInfo {
6184                        local_ip: _,
6185                        local_identifier: _,
6186                        remote_ip: _,
6187                        remote_identifier,
6188                    }) => remote_identifier
6189                );
6190                assert_eq!(ORIGINAL_REMOTE_PORT, remote_port);
6191            }
6192        }
6193    }
6194
6195    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Send; "this_stack_send")]
6196    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Receive; "this_stack_receive")]
6197    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::SendAndReceive; "this_stack_send_and_receive")]
6198    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Send; "other_stack_send")]
6199    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Receive; "other_stack_receive")]
6200    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::SendAndReceive; "other_stack_send_and_receive")]
6201    fn set_get_shutdown_dualstack(remote_ip: Ipv6Addr, shutdown: ShutdownType) {
6202        let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote_ip should be specified");
6203        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6204            Ipv6::UNSPECIFIED_ADDRESS.into(),
6205            remote_ip.into(),
6206            [FakeDeviceId {}],
6207            |device_configs| {
6208                FakeCoreCtx::<Ipv6, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6209                    device_configs,
6210                ))
6211            },
6212        );
6213        let mut api = ctx.datagram_api::<Ipv6>();
6214
6215        const REMOTE_PORT: u16 = 1234;
6216        let socket = api.create_default();
6217        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT, Default::default())
6218            .expect("connect should succeed");
6219        assert_eq!(api.get_shutdown_connected(&socket), None);
6220
6221        api.shutdown_connected(&socket, shutdown).expect("shutdown should succeed");
6222        assert_eq!(api.get_shutdown_connected(&socket), Some(shutdown));
6223    }
6224
6225    #[ip_test(I)]
6226    #[test_case(OriginalSocketState::Unbound; "unbound")]
6227    #[test_case(OriginalSocketState::Listener; "listener")]
6228    #[test_case(OriginalSocketState::Connected; "connected")]
6229    fn set_get_device_single_stack<I: DatagramIpExt<MultipleDevicesId>>(
6230        original: OriginalSocketState,
6231    ) {
6232        set_get_device_inner::<I>(original, I::TEST_ADDRS.local_ip.get(), I::TEST_ADDRS.remote_ip);
6233    }
6234
6235    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
6236        net_ip_v4!("192.0.2.2"); "listener_other_stack")]
6237    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
6238        net_ip_v4!("192.0.2.2"); "listener_both_stacks")]
6239    #[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
6240        net_ip_v4!("192.0.2.2"); "connected_other_stack")]
6241    fn set_get_device_dual_stack(
6242        original: OriginalSocketState,
6243        local_ip: Ipv6Addr,
6244        remote_ip: Ipv4Addr,
6245    ) {
6246        let remote_ip = remote_ip.to_ipv6_mapped();
6247        set_get_device_inner::<Ipv6>(original, local_ip, remote_ip);
6248    }
6249
6250    fn set_get_device_inner<I: DatagramIpExt<MultipleDevicesId>>(
6251        original: OriginalSocketState,
6252        local_ip: I::Addr,
6253        remote_ip: SpecifiedAddr<I::Addr>,
6254    ) {
6255        const DEVICE_ID1: MultipleDevicesId = MultipleDevicesId::A;
6256        const DEVICE_ID2: MultipleDevicesId = MultipleDevicesId::B;
6257
6258        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6259            local_ip.to_ip_addr(),
6260            remote_ip.into(),
6261            [DEVICE_ID1, DEVICE_ID2],
6262            |device_configs| {
6263                FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6264                    device_configs,
6265                ))
6266            },
6267        );
6268
6269        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6270        const REMOTE_PORT: u16 = 1234;
6271
6272        let mut api = ctx.datagram_api::<I>();
6273        let socket1 = api.create_default();
6274        let socket2 = api.create_default();
6275
6276        // Initialize each socket to the `original` state, and verify that their
6277        // device can be set.
6278        for (socket, device_id) in [(&socket1, DEVICE_ID1), (&socket2, DEVICE_ID2)] {
6279            match original {
6280                OriginalSocketState::Unbound => {}
6281                OriginalSocketState::Listener => api
6282                    .listen(
6283                        &socket,
6284                        SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
6285                        Some(LOCAL_PORT),
6286                    )
6287                    .expect("listen should succeed"),
6288                OriginalSocketState::Connected => api
6289                    .connect(
6290                        &socket,
6291                        Some(ZonedAddr::Unzoned(remote_ip)),
6292                        REMOTE_PORT,
6293                        Default::default(),
6294                    )
6295                    .expect("connect should succeed"),
6296            }
6297
6298            assert_eq!(api.get_bound_device(socket), None);
6299            api.set_device(socket, Some(&device_id)).expect("set device should succeed");
6300            assert_eq!(api.get_bound_device(socket), Some(FakeWeakDeviceId(device_id)));
6301        }
6302
6303        // For bound sockets, try to bind socket 2 to device 1, and expect it
6304        // it to conflict with socket 1 (They now have identical address keys in
6305        // the bound socket map)
6306        if original != OriginalSocketState::Unbound {
6307            assert_eq!(
6308                api.set_device(&socket2, Some(&DEVICE_ID1)),
6309                Err(SocketError::Local(LocalAddressError::AddressInUse))
6310            );
6311            // Verify both sockets still have their original device.
6312            assert_eq!(api.get_bound_device(&socket1), Some(FakeWeakDeviceId(DEVICE_ID1)));
6313            assert_eq!(api.get_bound_device(&socket2), Some(FakeWeakDeviceId(DEVICE_ID2)));
6314        }
6315
6316        // Verify the device can be unset.
6317        // NB: Close socket2 first, otherwise socket 1 will conflict with it.
6318        api.close(socket2).into_removed();
6319        api.set_device(&socket1, None).expect("set device should succeed");
6320        assert_eq!(api.get_bound_device(&socket1), None,);
6321    }
6322}