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