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