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, 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, MulticastMembershipHandler, ResolveRouteError,
49    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>>,
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>>;
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>> {
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 {}
820impl<BT> DatagramBindingsTypes for BT where BT: TxMetadataBindingsTypes {}
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: 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    /// The potential error for serializing a packet. For example, in UDP, this
1469    /// should be infallible but for ICMP, there will be an error if the input
1470    /// is not an echo request.
1471    type SerializeError: Error;
1472
1473    /// Constructs a packet serializer with `addr` and `body`.
1474    fn make_packet<I: IpExt, B: BufferMut>(
1475        body: B,
1476        addr: &ConnIpAddr<
1477            I::Addr,
1478            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1479            <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
1480        >,
1481    ) -> Result<Self::Serializer<I, B>, Self::SerializeError>;
1482
1483    /// Attempts to allocate a local identifier for a listening socket.
1484    ///
1485    /// Returns the identifier on success, or `None` on failure.
1486    fn try_alloc_listen_identifier<I: IpExt, D: WeakDeviceIdentifier>(
1487        rng: &mut impl RngContext,
1488        is_available: impl Fn(
1489            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1490        ) -> Result<(), InUseError>,
1491    ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
1492
1493    /// Retrieves the associated connection info from the connection state.
1494    fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
1495        state: &Self::ConnState<I, D>,
1496    ) -> ConnInfo<I::Addr, D>;
1497
1498    /// Tries to allocate a local identifier.
1499    fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
1500        bound: &BoundDatagramSocketMap<I, D, Self>,
1501        bindings_ctx: &mut BC,
1502        flow: DatagramFlowId<I::Addr, <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
1503    ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
1504
1505    /// Downgrades a `SocketId` into a `WeakSocketId`.
1506    // TODO(https://fxbug.dev/392672414): Replace this with a base trait.
1507    fn downgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
1508        id: &Self::SocketId<I, D>,
1509    ) -> Self::WeakSocketId<I, D>;
1510
1511    /// Attempts to upgrade a `WeakSocketId` into a `SocketId`.
1512    // TODO(https://fxbug.dev/392672414): Replace this with a base trait.
1513    fn upgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
1514        id: &Self::WeakSocketId<I, D>,
1515    ) -> Option<Self::SocketId<I, D>>;
1516}
1517
1518/// The error returned when an identifier (i.e.) port is already in use.
1519pub struct InUseError;
1520
1521/// Creates a primary ID without inserting it into the all socket map.
1522pub fn create_primary_id<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1523    external_data: S::ExternalData<I>,
1524) -> PrimaryRc<I, D, S> {
1525    PrimaryRc::new(ReferenceState {
1526        state: RwLock::new(SocketState {
1527            inner: SocketStateInner::Unbound(UnboundSocketState::default()),
1528            ip_options: Default::default(),
1529            sharing: Default::default(),
1530        }),
1531        external_data,
1532        counters: Default::default(),
1533    })
1534}
1535
1536/// Information associated with a datagram listener.
1537#[derive(GenericOverIp, Debug, Eq, PartialEq)]
1538#[generic_over_ip(A, IpAddress)]
1539pub struct ListenerInfo<A: IpAddress, D> {
1540    /// The local address associated with a datagram listener, or `None` for any
1541    /// address.
1542    pub local_ip: Option<StrictlyZonedAddr<A, SpecifiedAddr<A>, D>>,
1543    /// The local port associated with a datagram listener.
1544    pub local_identifier: NonZeroU16,
1545}
1546
1547impl<A: IpAddress, LA: Into<(Option<SpecifiedAddr<A>>, NonZeroU16)>, D> From<ListenerAddr<LA, D>>
1548    for ListenerInfo<A, D>
1549{
1550    fn from(ListenerAddr { ip, device }: ListenerAddr<LA, D>) -> Self {
1551        let (addr, local_identifier) = ip.into();
1552        Self {
1553            local_ip: addr.map(|addr| {
1554                StrictlyZonedAddr::new_with_zone(addr, || {
1555                    // The invariant that a zone is present if needed is upheld by
1556                    // set_bindtodevice and bind.
1557                    device.expect("device must be bound for addresses that require zones")
1558                })
1559            }),
1560            local_identifier,
1561        }
1562    }
1563}
1564
1565impl<A: IpAddress, D> From<NonZeroU16> for ListenerInfo<A, D> {
1566    fn from(local_identifier: NonZeroU16) -> Self {
1567        Self { local_ip: None, local_identifier }
1568    }
1569}
1570
1571/// Information associated with a datagram connection.
1572#[derive(Debug, GenericOverIp, PartialEq)]
1573#[generic_over_ip(A, IpAddress)]
1574pub struct ConnInfo<A: IpAddress, D> {
1575    /// The local address associated with a datagram connection.
1576    pub local_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
1577    /// The local identifier associated with a datagram connection.
1578    pub local_identifier: NonZeroU16,
1579    /// The remote address associated with a datagram connection.
1580    pub remote_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
1581    /// The remote identifier associated with a datagram connection.
1582    pub remote_identifier: u16,
1583}
1584
1585impl<A: IpAddress, D> ConnInfo<A, D> {
1586    /// Construct a new `ConnInfo`.
1587    pub fn new(
1588        local_ip: SpecifiedAddr<A>,
1589        local_identifier: NonZeroU16,
1590        remote_ip: SpecifiedAddr<A>,
1591        remote_identifier: u16,
1592        mut get_zone: impl FnMut() -> D,
1593    ) -> Self {
1594        Self {
1595            local_ip: StrictlyZonedAddr::new_with_zone(local_ip, &mut get_zone),
1596            local_identifier,
1597            remote_ip: StrictlyZonedAddr::new_with_zone(remote_ip, &mut get_zone),
1598            remote_identifier,
1599        }
1600    }
1601}
1602
1603/// Information about the addresses for a socket.
1604#[derive(GenericOverIp, Debug, PartialEq)]
1605#[generic_over_ip(A, IpAddress)]
1606pub enum SocketInfo<A: IpAddress, D> {
1607    /// The socket is not bound.
1608    Unbound,
1609    /// The socket is listening.
1610    Listener(ListenerInfo<A, D>),
1611    /// The socket is connected.
1612    Connected(ConnInfo<A, D>),
1613}
1614
1615/// A type of an operation that can be performed on a socket entry.
1616trait EntryOperationType {
1617    type ReverseOp: EntryOperationType<ReverseOp = Self>;
1618    type Error: Debug;
1619
1620    /// Applies the operation to the bound socket map. Returns the reverse operation.
1621    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1622        op: SocketEntryOp<I, D, S, Self>,
1623        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1624    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error>;
1625}
1626
1627enum EntryInsertOp {}
1628impl EntryOperationType for EntryInsertOp {
1629    type ReverseOp = EntryRemoveOp;
1630    type Error = InsertError;
1631
1632    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1633        op: SocketEntryOp<I, D, S, Self>,
1634        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1635    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error> {
1636        let SocketEntryOp { socket_id, sharing, addr, _marker } = op;
1637        match &addr {
1638            AddrVec::Listen(addr) => {
1639                let SocketStateEntry { .. } = sockets.listeners_mut().try_insert(
1640                    addr.clone(),
1641                    sharing.clone(),
1642                    socket_id.clone(),
1643                )?;
1644            }
1645            AddrVec::Conn(addr) => {
1646                let SocketStateEntry { .. } = sockets.conns_mut().try_insert(
1647                    addr.clone(),
1648                    sharing.clone(),
1649                    socket_id.clone(),
1650                )?;
1651            }
1652        };
1653        Ok(SocketEntryOp { socket_id, sharing, addr, _marker: PhantomData })
1654    }
1655}
1656
1657enum EntryRemoveOp {}
1658impl EntryOperationType for EntryRemoveOp {
1659    type ReverseOp = EntryInsertOp;
1660    type Error = NotFoundError;
1661
1662    fn apply<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
1663        op: SocketEntryOp<I, D, S, Self>,
1664        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1665    ) -> Result<SocketEntryOp<I, D, S, Self::ReverseOp>, Self::Error> {
1666        let SocketEntryOp { socket_id, sharing, addr, _marker } = op;
1667        match &addr {
1668            AddrVec::Listen(addr) => {
1669                sockets.listeners_mut().remove(&socket_id, &addr)?;
1670            }
1671            AddrVec::Conn(addr) => {
1672                sockets.conns_mut().remove(&socket_id, &addr)?;
1673            }
1674        };
1675        Ok(SocketEntryOp { socket_id, sharing, addr, _marker: PhantomData })
1676    }
1677}
1678
1679/// State associated with insertion or removal operations on the bound socket map.
1680struct SocketEntryOp<
1681    I: IpExt,
1682    D: WeakDeviceIdentifier,
1683    S: DatagramSocketSpec,
1684    O: EntryOperationType + ?Sized,
1685> {
1686    socket_id: <S::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, S::AddrSpec>>::BoundSocketId,
1687    sharing: S::SharingState,
1688    addr: AddrVec<I, D, S::AddrSpec>,
1689    _marker: PhantomData<O>,
1690}
1691
1692type SingleStackRemoveOperation<I, D, S> = SocketEntryOp<I, D, S, EntryRemoveOp>;
1693type SingleStackInsertOperation<I, D, S> = SocketEntryOp<I, D, S, EntryInsertOp>;
1694
1695impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> SingleStackRemoveOperation<I, D, S> {
1696    /// Constructs the remove operation from existing socket state.
1697    fn new_from_state<BC, CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
1698        core_ctx: &mut CC,
1699        socket_id: &S::SocketId<I, D>,
1700        state: &BoundSocketState<I, D, S>,
1701        sharing: S::SharingState,
1702    ) -> Self {
1703        let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
1704        match state {
1705            BoundSocketStateType::Listener(ListenerState { addr: ListenerAddr { ip, device } }) => {
1706                Self {
1707                    addr: AddrVec::Listen(ListenerAddr {
1708                        ip: core_ctx.nds_converter().convert(ip.clone()),
1709                        device: device.clone(),
1710                    }),
1711                    sharing,
1712                    socket_id: S::make_bound_socket_map_id(socket_id),
1713                    _marker: PhantomData,
1714                }
1715            }
1716            BoundSocketStateType::Connected(state) => {
1717                let ConnState {
1718                    addr,
1719                    socket: _,
1720                    clear_device_on_disconnect: _,
1721                    shutdown: _,
1722                    extra: _,
1723                } = core_ctx.nds_converter().convert(state);
1724                Self {
1725                    addr: AddrVec::Conn(addr.clone()),
1726                    sharing,
1727                    socket_id: S::make_bound_socket_map_id(socket_id),
1728                    _marker: PhantomData,
1729                }
1730            }
1731        }
1732    }
1733}
1734
1735impl<I, D, S, O> SocketEntryOp<I, D, S, O>
1736where
1737    I: IpExt,
1738    D: WeakDeviceIdentifier,
1739    S: DatagramSocketSpec,
1740    O: EntryOperationType,
1741{
1742    /// Applies the operation and returns the reverse operation.
1743    fn apply(
1744        self,
1745        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1746    ) -> Result<SocketEntryOp<I, D, S, O::ReverseOp>, O::Error> {
1747        O::apply(self, sockets)
1748    }
1749}
1750
1751struct DualStackListenerOp<I, D, S, O>
1752where
1753    I: IpExt,
1754    D: WeakDeviceIdentifier,
1755    S: DatagramSocketSpec,
1756    O: EntryOperationType,
1757{
1758    identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1759    device: Option<D>,
1760    sharing: S::SharingState,
1761    socket_ids: PairedBoundSocketIds<I, D, S>,
1762    _marker: PhantomData<O>,
1763}
1764
1765type DualStackListenerRemoveOperation<I, D, S> = DualStackListenerOp<I, D, S, EntryRemoveOp>;
1766type DualStackListenerInsertOperation<I, D, S> = DualStackListenerOp<I, D, S, EntryInsertOp>;
1767
1768impl<I, D, S, O> DualStackListenerOp<I, D, S, O>
1769where
1770    I: IpExt,
1771    D: WeakDeviceIdentifier,
1772    S: DatagramSocketSpec,
1773    O: EntryOperationType,
1774{
1775    fn this_stack_op(&self) -> SocketEntryOp<I, D, S, O> {
1776        SocketEntryOp {
1777            addr: AddrVec::Listen(ListenerAddr {
1778                ip: ListenerIpAddr { addr: None, identifier: self.identifier },
1779                device: self.device.clone(),
1780            }),
1781            sharing: self.sharing.clone(),
1782            socket_id: self.socket_ids.this.clone(),
1783            _marker: PhantomData,
1784        }
1785    }
1786
1787    fn other_stack_op(&self) -> SocketEntryOp<I::OtherVersion, D, S, O> {
1788        SocketEntryOp {
1789            addr: AddrVec::Listen(ListenerAddr {
1790                ip: ListenerIpAddr { addr: None, identifier: self.identifier },
1791                device: self.device.clone(),
1792            }),
1793            sharing: self.sharing.clone(),
1794            socket_id: self.socket_ids.other.clone(),
1795            _marker: PhantomData,
1796        }
1797    }
1798
1799    /// Apply this operation to the given `BoundDatagramSocketMap`s. Returns the reverse
1800    /// operation.
1801    fn apply(
1802        self,
1803        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1804        other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
1805    ) -> Result<DualStackListenerOp<I, D, S, O::ReverseOp>, O::Error> {
1806        let this_stack_reverse_op = self.this_stack_op().apply(sockets)?;
1807        match self.other_stack_op().apply(other_sockets) {
1808            Ok(SocketEntryOp::<_, _, _, O::ReverseOp> { .. }) => (),
1809            Err(e) => {
1810                let _: SocketEntryOp<_, _, _, O> = this_stack_reverse_op
1811                    .apply(sockets)
1812                    .expect("Failed to revert socket map operation");
1813                return Err(e);
1814            }
1815        };
1816
1817        let Self { identifier, device, sharing, socket_ids, _marker: _ } = self;
1818        Ok(DualStackListenerOp { identifier, device, sharing, socket_ids, _marker: PhantomData })
1819    }
1820}
1821
1822/// State associated with a dual-stack socket entry operation.
1823enum DualStackSocketEntryOp<I, D, S, O>
1824where
1825    I: IpExt,
1826    D: WeakDeviceIdentifier,
1827    S: DatagramSocketSpec,
1828    O: EntryOperationType,
1829{
1830    CurrentStack(SocketEntryOp<I, D, S, O>),
1831    OtherStack(SocketEntryOp<I::OtherVersion, D, S, O>),
1832    ListenerBothStacks(DualStackListenerOp<I, D, S, O>),
1833}
1834
1835type DualStackRemoveOperation<I, D, S> = DualStackSocketEntryOp<I, D, S, EntryRemoveOp>;
1836type DualStackInsertOperation<I, D, S> = DualStackSocketEntryOp<I, D, S, EntryInsertOp>;
1837
1838impl<I, D, S> DualStackRemoveOperation<I, D, S>
1839where
1840    I: IpExt,
1841    D: WeakDeviceIdentifier,
1842    S: DatagramSocketSpec,
1843{
1844    /// Constructs the removal operation from existing socket state.
1845    fn new_from_state<BC, CC>(
1846        core_ctx: &mut CC,
1847        socket_id: &S::SocketId<I, D>,
1848        ip_options: &IpOptions<I, D, S>,
1849        state: &BoundSocketState<I, D, S>,
1850        sharing: S::SharingState,
1851    ) -> Self
1852    where
1853        BC: DatagramBindingsTypes,
1854        CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
1855    {
1856        let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
1857        match state {
1858            BoundSocketStateType::Listener(ListenerState { addr }) => {
1859                let ListenerAddr { ip, device } = addr.clone();
1860                match (core_ctx.ds_converter().convert(ip), core_ctx.dual_stack_enabled(ip_options))
1861                {
1862                    // Dual-stack enabled, bound in both stacks.
1863                    (DualStackListenerIpAddr::BothStacks(identifier), true) => {
1864                        DualStackSocketEntryOp::ListenerBothStacks(DualStackListenerOp {
1865                            identifier: identifier.clone(),
1866                            device,
1867                            sharing,
1868                            socket_ids: PairedBoundSocketIds {
1869                                this: S::make_bound_socket_map_id(socket_id),
1870                                other: core_ctx.to_other_bound_socket_id(socket_id),
1871                            },
1872                            _marker: PhantomData,
1873                        })
1874                    }
1875                    // Bound in this stack, with/without dual-stack enabled.
1876                    (DualStackListenerIpAddr::ThisStack(addr), true | false) => {
1877                        DualStackSocketEntryOp::CurrentStack(SocketEntryOp {
1878                            addr: AddrVec::Listen(ListenerAddr { ip: addr, device }),
1879                            sharing,
1880                            socket_id: S::make_bound_socket_map_id(socket_id),
1881                            _marker: PhantomData,
1882                        })
1883                    }
1884                    // Dual-stack enabled, bound only in the other stack.
1885                    (DualStackListenerIpAddr::OtherStack(addr), true) => {
1886                        DualStackSocketEntryOp::OtherStack(SocketEntryOp {
1887                            addr: AddrVec::Listen(ListenerAddr { ip: addr, device }),
1888                            sharing,
1889                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
1890                            _marker: PhantomData,
1891                        })
1892                    }
1893                    (DualStackListenerIpAddr::OtherStack(_), false)
1894                    | (DualStackListenerIpAddr::BothStacks(_), false) => {
1895                        unreachable!("dual-stack disabled socket cannot use the other stack")
1896                    }
1897                }
1898            }
1899            BoundSocketStateType::Connected(state) => {
1900                match core_ctx.ds_converter().convert(state) {
1901                    DualStackConnState::ThisStack(ConnState { addr, .. }) => {
1902                        DualStackSocketEntryOp::CurrentStack(SocketEntryOp {
1903                            addr: AddrVec::Conn(addr.clone()),
1904                            sharing,
1905                            socket_id: S::make_bound_socket_map_id(socket_id),
1906                            _marker: PhantomData,
1907                        })
1908                    }
1909                    DualStackConnState::OtherStack(ConnState { addr, .. }) => {
1910                        core_ctx.assert_dual_stack_enabled(&ip_options);
1911                        DualStackSocketEntryOp::OtherStack(SocketEntryOp {
1912                            addr: AddrVec::Conn(addr.clone()),
1913                            sharing,
1914                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
1915                            _marker: PhantomData,
1916                        })
1917                    }
1918                }
1919            }
1920        }
1921    }
1922}
1923
1924impl<I, D, S, O> DualStackSocketEntryOp<I, D, S, O>
1925where
1926    I: IpExt,
1927    D: WeakDeviceIdentifier,
1928    S: DatagramSocketSpec,
1929    O: EntryOperationType,
1930{
1931    /// Apply this operation to the given `BoundSocketMap`s. Returns the reverse
1932    /// operation.
1933    fn apply(
1934        self,
1935        sockets: &mut BoundDatagramSocketMap<I, D, S>,
1936        other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
1937    ) -> Result<DualStackSocketEntryOp<I, D, S, O::ReverseOp>, O::Error> {
1938        let result = match self {
1939            DualStackSocketEntryOp::CurrentStack(remove) => {
1940                DualStackSocketEntryOp::CurrentStack(remove.apply(sockets)?)
1941            }
1942            DualStackSocketEntryOp::OtherStack(remove) => {
1943                DualStackSocketEntryOp::OtherStack(remove.apply(other_sockets)?)
1944            }
1945            DualStackSocketEntryOp::ListenerBothStacks(listener_op) => {
1946                DualStackSocketEntryOp::ListenerBothStacks(
1947                    listener_op.apply(sockets, other_sockets)?,
1948                )
1949            }
1950        };
1951        Ok(result)
1952    }
1953}
1954
1955/// Abstraction for operations over one or two demultiplexing maps.
1956trait BoundStateHandler<I: IpExt, S: DatagramSocketSpec, D: WeakDeviceIdentifier> {
1957    /// The type of address that can be inserted or removed for listeners.
1958    type ListenerAddr: Clone;
1959    /// The type of ID that can be inserted or removed.
1960    type BoundSocketId;
1961
1962    /// Checks whether an entry could be inserted for the specified address and
1963    /// identifier.
1964    ///
1965    /// Returns `true` if a value could be inserted at the specified address and
1966    /// local ID, with the provided sharing state; otherwise returns `false`.
1967    fn is_listener_entry_available(
1968        &self,
1969        addr: Self::ListenerAddr,
1970        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1971        sharing_state: &S::SharingState,
1972    ) -> bool;
1973
1974    /// Inserts `id` at a listener address or returns an error.
1975    ///
1976    /// Inserts the identifier `id` at the listener address for `addr` and
1977    /// local `identifier` with device `device` and the given sharing state. If
1978    /// the insertion conflicts with an existing socket, a `LocalAddressError`
1979    /// is returned.
1980    fn try_insert_listener(
1981        &mut self,
1982        addr: Self::ListenerAddr,
1983        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
1984        device: Option<D>,
1985        sharing: S::SharingState,
1986        id: Self::BoundSocketId,
1987    ) -> Result<(), LocalAddressError>;
1988}
1989
1990/// An alias for `BoundSocketMap` specialized for datagram sockets.
1991pub type BoundDatagramSocketMap<I, D, S> = BoundSocketMap<
1992    I,
1993    D,
1994    <S as DatagramSocketSpec>::AddrSpec,
1995    <S as DatagramSocketSpec>::SocketMapSpec<I, D>,
1996>;
1997
1998type BoundDatagramSocketId<I, D, S> =
1999    <<S as DatagramSocketSpec>::SocketMapSpec<I, D> as DatagramSocketMapSpec<
2000        I,
2001        D,
2002        <S as DatagramSocketSpec>::AddrSpec,
2003    >>::BoundSocketId;
2004
2005/// A sentinel type for the unspecified address in a dual-stack context.
2006///
2007/// This is kind of like [`Ipv6::UNSPECIFIED_ADDRESS`], but makes it clear that
2008/// the value is being used in a dual-stack context.
2009#[derive(Copy, Clone, Debug)]
2010struct DualStackUnspecifiedAddr;
2011
2012/// Implementation of BoundStateHandler for a single demultiplexing map.
2013impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
2014    for BoundDatagramSocketMap<I, D, S>
2015{
2016    type ListenerAddr = Option<SocketIpAddr<I::Addr>>;
2017    type BoundSocketId = BoundDatagramSocketId<I, D, S>;
2018
2019    fn is_listener_entry_available(
2020        &self,
2021        addr: Self::ListenerAddr,
2022        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2023        sharing: &S::SharingState,
2024    ) -> bool {
2025        let check_addr = ListenerAddr { device: None, ip: ListenerIpAddr { identifier, addr } };
2026        match self.listeners().could_insert(&check_addr, sharing) {
2027            Ok(()) => true,
2028            Err(
2029                InsertError::Exists
2030                | InsertError::IndirectConflict
2031                | InsertError::ShadowAddrExists
2032                | InsertError::WouldShadowExisting,
2033            ) => false,
2034        }
2035    }
2036
2037    fn try_insert_listener(
2038        &mut self,
2039        addr: Self::ListenerAddr,
2040        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2041        device: Option<D>,
2042        sharing: S::SharingState,
2043        id: Self::BoundSocketId,
2044    ) -> Result<(), LocalAddressError> {
2045        let _: SocketStateEntry<'_, _, _, _, _, _> = self
2046            .listeners_mut()
2047            .try_insert(
2048                ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device },
2049                sharing,
2050                id,
2051            )
2052            .map_err(Into::<LocalAddressError>::into)?;
2053
2054        Ok(())
2055    }
2056}
2057
2058struct PairedSocketMapMut<'a, I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2059    bound: &'a mut BoundDatagramSocketMap<I, D, S>,
2060    other_bound: &'a mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
2061}
2062
2063#[derive(Derivative)]
2064#[derivative(Clone(bound = ""))]
2065struct PairedBoundSocketIds<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2066    this: BoundDatagramSocketId<I, D, S>,
2067    other: BoundDatagramSocketId<I::OtherVersion, D, S>,
2068}
2069
2070/// Implementation for a pair of demultiplexing maps for different IP versions.
2071impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
2072    for PairedSocketMapMut<'_, I, D, S>
2073{
2074    type ListenerAddr = DualStackUnspecifiedAddr;
2075    type BoundSocketId = PairedBoundSocketIds<I, D, S>;
2076
2077    fn is_listener_entry_available(
2078        &self,
2079        DualStackUnspecifiedAddr: Self::ListenerAddr,
2080        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2081        sharing: &S::SharingState,
2082    ) -> bool {
2083        let PairedSocketMapMut { bound, other_bound } = self;
2084        BoundStateHandler::<I, S, D>::is_listener_entry_available(*bound, None, identifier, sharing)
2085            && BoundStateHandler::<I::OtherVersion, S, D>::is_listener_entry_available(
2086                *other_bound,
2087                None,
2088                identifier,
2089                sharing,
2090            )
2091    }
2092
2093    fn try_insert_listener(
2094        &mut self,
2095        DualStackUnspecifiedAddr: Self::ListenerAddr,
2096        identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
2097        device: Option<D>,
2098        sharing: S::SharingState,
2099        id: Self::BoundSocketId,
2100    ) -> Result<(), LocalAddressError> {
2101        let PairedSocketMapMut { bound: this, other_bound: other } = self;
2102
2103        let op = DualStackListenerInsertOperation {
2104            identifier,
2105            device,
2106            sharing,
2107            socket_ids: id,
2108            _marker: PhantomData,
2109        };
2110
2111        let _: DualStackListenerRemoveOperation<I, D, S> =
2112            op.apply(this, other).map_err(Into::<LocalAddressError>::into)?;
2113
2114        Ok(())
2115    }
2116}
2117
2118fn try_pick_identifier<
2119    I: IpExt,
2120    S: DatagramSocketSpec,
2121    D: WeakDeviceIdentifier,
2122    BS: BoundStateHandler<I, S, D>,
2123    BC: RngContext,
2124>(
2125    addr: BS::ListenerAddr,
2126    bound: &BS,
2127    bindings_ctx: &mut BC,
2128    sharing: &S::SharingState,
2129) -> Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
2130    S::try_alloc_listen_identifier::<I, D>(bindings_ctx, move |identifier| {
2131        bound
2132            .is_listener_entry_available(addr.clone(), identifier, sharing)
2133            .then_some(())
2134            .ok_or(InUseError)
2135    })
2136}
2137
2138fn try_pick_bound_address<
2139    I: IpExt,
2140    CC: TransportIpContext<I, BC>,
2141    BC: DatagramBindingsTypes,
2142    LI,
2143>(
2144    addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
2145    device: &Option<CC::WeakDeviceId>,
2146    core_ctx: &mut CC,
2147    identifier: LI,
2148    transparent: bool,
2149) -> Result<
2150    (Option<SocketIpAddr<I::Addr>>, Option<EitherDeviceId<CC::DeviceId, CC::WeakDeviceId>>, LI),
2151    LocalAddressError,
2152> {
2153    let (addr, device, identifier) = match addr {
2154        Some(addr) => {
2155            // Extract the specified address and the device. The device
2156            // is either the one from the address or the one to which
2157            // the socket was previously bound.
2158            let (addr, device) = addr.resolve_addr_with_device(device.clone())?;
2159
2160            // Binding to multicast addresses is allowed regardless.
2161            // Other addresses can only be bound to if they are assigned
2162            // to the device, or if the socket is transparent.
2163            if !addr.addr().is_multicast() && !transparent {
2164                BaseTransportIpContext::<I, _>::with_devices_with_assigned_addr(
2165                    core_ctx,
2166                    addr.into(),
2167                    |mut assigned_to| {
2168                        if let Some(device) = &device {
2169                            if !assigned_to.any(|d| device == &EitherDeviceId::Strong(d)) {
2170                                return Err(LocalAddressError::AddressMismatch);
2171                            }
2172                        } else {
2173                            if !assigned_to.any(|_: CC::DeviceId| true) {
2174                                return Err(LocalAddressError::CannotBindToAddress);
2175                            }
2176                        }
2177                        Ok(())
2178                    },
2179                )?;
2180            }
2181            (Some(addr), device, identifier)
2182        }
2183        None => (None, device.clone().map(EitherDeviceId::Weak), identifier),
2184    };
2185    Ok((addr, device, identifier))
2186}
2187
2188fn listen_inner<
2189    I: IpExt,
2190    BC: DatagramBindingsContext,
2191    CC: DatagramBoundStateContext<I, BC, S>,
2192    S: DatagramSocketSpec,
2193>(
2194    core_ctx: &mut CC,
2195    bindings_ctx: &mut BC,
2196    state: &mut SocketState<I, CC::WeakDeviceId, S>,
2197    id: &S::SocketId<I, CC::WeakDeviceId>,
2198    addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, CC::DeviceId>>,
2199    local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2200) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
2201    /// Possible operations that might be performed, depending on whether the
2202    /// socket state spec supports dual-stack operation and what the address
2203    /// looks like.
2204    #[derive(Debug, GenericOverIp)]
2205    #[generic_over_ip(I, Ip)]
2206    enum BoundOperation<'a, I: IpExt, DS: DeviceIdContext<AnyDevice>, NDS> {
2207        /// Bind to the "any" address on both stacks.
2208        DualStackAnyAddr(&'a mut DS),
2209        /// Bind to a non-dual-stack address only on the current stack.
2210        OnlyCurrentStack(
2211            MaybeDualStack<&'a mut DS, &'a mut NDS>,
2212            Option<ZonedAddr<SocketIpAddr<I::Addr>, DS::DeviceId>>,
2213        ),
2214        /// Bind to an address only on the other stack.
2215        OnlyOtherStack(
2216            &'a mut DS,
2217            Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, DS::DeviceId>>,
2218        ),
2219    }
2220
2221    let SocketState { inner, ip_options, sharing } = state;
2222    let UnboundSocketState { device } = match inner {
2223        SocketStateInner::Unbound(state) => state,
2224        SocketStateInner::Bound(_) => return Err(Either::Left(ExpectedUnboundError)),
2225    };
2226
2227    let dual_stack = core_ctx.dual_stack_context_mut();
2228    let bound_operation: BoundOperation<'_, I, _, _> = match (dual_stack, addr) {
2229        // Dual-stack support and unspecified address.
2230        (MaybeDualStack::DualStack(dual_stack), None) => {
2231            match dual_stack.dual_stack_enabled(ip_options) {
2232                // Socket is dual-stack enabled, bind in both stacks.
2233                true => BoundOperation::DualStackAnyAddr(dual_stack),
2234                // Dual-stack support but not enabled, so bind unspecified in the
2235                // current stack.
2236                false => {
2237                    BoundOperation::OnlyCurrentStack(MaybeDualStack::DualStack(dual_stack), None)
2238                }
2239            }
2240        }
2241        // There is dual-stack support and the address is not unspecified so how
2242        // to proceed is going to depend on the value of `addr`.
2243        (MaybeDualStack::DualStack(dual_stack), Some(addr)) => {
2244            match DualStackLocalIp::<I, _>::new(addr) {
2245                // `addr` can't be represented in the other stack.
2246                DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
2247                    MaybeDualStack::DualStack(dual_stack),
2248                    Some(addr),
2249                ),
2250                // There's a representation in the other stack, so use that if possible.
2251                DualStackLocalIp::OtherStack(addr) => {
2252                    match dual_stack.dual_stack_enabled(ip_options) {
2253                        true => BoundOperation::OnlyOtherStack(dual_stack, addr),
2254                        false => return Err(Either::Right(LocalAddressError::CannotBindToAddress)),
2255                    }
2256                }
2257            }
2258        }
2259        // No dual-stack support, so only bind on the current stack.
2260        (MaybeDualStack::NotDualStack(single_stack), None) => {
2261            BoundOperation::OnlyCurrentStack(MaybeDualStack::NotDualStack(single_stack), None)
2262        }
2263        // No dual-stack support, so check the address is allowed in the current
2264        // stack.
2265        (MaybeDualStack::NotDualStack(single_stack), Some(addr)) => {
2266            match DualStackLocalIp::<I, _>::new(addr) {
2267                // The address is only representable in the current stack.
2268                DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
2269                    MaybeDualStack::NotDualStack(single_stack),
2270                    Some(addr),
2271                ),
2272                // The address has a representation in the other stack but there's
2273                // no dual-stack support!
2274                DualStackLocalIp::OtherStack(_addr) => {
2275                    let _: Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, _>> =
2276                        _addr;
2277                    return Err(Either::Right(LocalAddressError::CannotBindToAddress));
2278                }
2279            }
2280        }
2281    };
2282
2283    fn try_bind_single_stack<
2284        I: IpExt,
2285        S: DatagramSocketSpec,
2286        CC: TransportIpContext<I, BC>,
2287        BC: DatagramBindingsContext,
2288    >(
2289        core_ctx: &mut CC,
2290        bindings_ctx: &mut BC,
2291        bound: &mut BoundSocketMap<
2292            I,
2293            CC::WeakDeviceId,
2294            S::AddrSpec,
2295            S::SocketMapSpec<I, CC::WeakDeviceId>,
2296        >,
2297        addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
2298        device: &Option<CC::WeakDeviceId>,
2299        local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2300        id: <S::SocketMapSpec<I, CC::WeakDeviceId> as SocketMapStateSpec>::ListenerId,
2301        sharing: S::SharingState,
2302        transparent: bool,
2303    ) -> Result<
2304        ListenerAddr<
2305            ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2306            CC::WeakDeviceId,
2307        >,
2308        LocalAddressError,
2309    > {
2310        let identifier = match local_id {
2311            Some(id) => Some(id),
2312            None => try_pick_identifier::<I, S, _, _, _>(
2313                addr.as_ref().map(ZonedAddr::addr),
2314                bound,
2315                bindings_ctx,
2316                &sharing,
2317            ),
2318        }
2319        .ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
2320        let (addr, device, identifier) =
2321            try_pick_bound_address::<I, _, _, _>(addr, device, core_ctx, identifier, transparent)?;
2322        let weak_device = device.map(|d| d.as_weak().into_owned());
2323
2324        BoundStateHandler::<_, S, _>::try_insert_listener(
2325            bound,
2326            addr,
2327            identifier,
2328            weak_device.clone(),
2329            sharing,
2330            id,
2331        )
2332        .map(|()| ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device: weak_device })
2333    }
2334
2335    let bound_addr: ListenerAddr<S::ListenerIpAddr<I>, CC::WeakDeviceId> = match bound_operation {
2336        BoundOperation::OnlyCurrentStack(either_dual_stack, addr) => {
2337            let converter = match either_dual_stack {
2338                MaybeDualStack::DualStack(ds) => MaybeDualStack::DualStack(ds.ds_converter()),
2339                MaybeDualStack::NotDualStack(nds) => {
2340                    MaybeDualStack::NotDualStack(nds.nds_converter())
2341                }
2342            };
2343            core_ctx
2344                .with_bound_sockets_mut(|core_ctx, bound| {
2345                    let id = S::make_bound_socket_map_id(id);
2346
2347                    try_bind_single_stack::<I, S, _, _>(
2348                        core_ctx,
2349                        bindings_ctx,
2350                        bound,
2351                        addr,
2352                        &device,
2353                        local_id,
2354                        id,
2355                        sharing.clone(),
2356                        ip_options.common.transparent,
2357                    )
2358                })
2359                .map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
2360                    let ip = match converter {
2361                        MaybeDualStack::DualStack(converter) => converter.convert_back(
2362                            DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
2363                        ),
2364                        MaybeDualStack::NotDualStack(converter) => {
2365                            converter.convert_back(ListenerIpAddr { addr, identifier })
2366                        }
2367                    };
2368                    ListenerAddr { ip, device }
2369                })
2370        }
2371        BoundOperation::OnlyOtherStack(core_ctx, addr) => {
2372            let id = core_ctx.to_other_bound_socket_id(id);
2373            core_ctx
2374                .with_other_bound_sockets_mut(|core_ctx, other_bound| {
2375                    try_bind_single_stack::<_, S, _, _>(
2376                        core_ctx,
2377                        bindings_ctx,
2378                        other_bound,
2379                        addr,
2380                        &device,
2381                        local_id,
2382                        id,
2383                        sharing.clone(),
2384                        ip_options.common.transparent,
2385                    )
2386                })
2387                .map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
2388                    ListenerAddr {
2389                        ip: core_ctx.ds_converter().convert_back(
2390                            DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
2391                                addr,
2392                                identifier,
2393                            }),
2394                        ),
2395                        device,
2396                    }
2397                })
2398        }
2399        BoundOperation::DualStackAnyAddr(core_ctx) => {
2400            let ids = PairedBoundSocketIds {
2401                this: S::make_bound_socket_map_id(id),
2402                other: core_ctx.to_other_bound_socket_id(id),
2403            };
2404            core_ctx
2405                .with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
2406                    let mut bound_pair = PairedSocketMapMut { bound, other_bound };
2407                    let sharing = sharing.clone();
2408
2409                    let identifier = match local_id {
2410                        Some(id) => Some(id),
2411                        None => try_pick_identifier::<I, S, _, _, _>(
2412                            DualStackUnspecifiedAddr,
2413                            &bound_pair,
2414                            bindings_ctx,
2415                            &sharing,
2416                        ),
2417                    }
2418                    .ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
2419                    let (_addr, device, identifier) = try_pick_bound_address::<I, _, _, _>(
2420                        None,
2421                        &device,
2422                        core_ctx,
2423                        identifier,
2424                        ip_options.common.transparent,
2425                    )?;
2426                    let weak_device = device.map(|d| d.as_weak().into_owned());
2427
2428                    BoundStateHandler::<_, S, _>::try_insert_listener(
2429                        &mut bound_pair,
2430                        DualStackUnspecifiedAddr,
2431                        identifier,
2432                        weak_device.clone(),
2433                        sharing,
2434                        ids,
2435                    )
2436                    .map(|()| (identifier, weak_device))
2437                })
2438                .map(|(identifier, device)| ListenerAddr {
2439                    ip: core_ctx
2440                        .ds_converter()
2441                        .convert_back(DualStackListenerIpAddr::BothStacks(identifier)),
2442                    device,
2443                })
2444        }
2445    }
2446    .map_err(Either::Right)?;
2447    // Match Linux behavior by only storing the original bound addr when the
2448    // local_id was provided by the caller.
2449    let original_bound_addr = local_id.map(|_id| {
2450        let ListenerAddr { ip, device: _ } = &bound_addr;
2451        ip.clone()
2452    });
2453
2454    // Replace the unbound state only after we're sure the
2455    // insertion has succeeded.
2456    state.inner = SocketStateInner::Bound(BoundSocketState {
2457        socket_type: BoundSocketStateType::Listener(ListenerState { addr: bound_addr }),
2458        original_bound_addr,
2459    });
2460    Ok(())
2461}
2462
2463/// An error when attempting to create a datagram socket.
2464#[derive(Error, Copy, Clone, Debug, Eq, PartialEq)]
2465pub enum ConnectError {
2466    /// An error was encountered creating an IP socket.
2467    #[error(transparent)]
2468    Ip(#[from] IpSockCreationError),
2469    /// No local port was specified, and none could be automatically allocated.
2470    #[error("a local port could not be allocated")]
2471    CouldNotAllocateLocalPort,
2472    /// The specified socket addresses (IP addresses and ports) conflict with an
2473    /// existing socket.
2474    #[error("the socket's IP address and port conflict with an existing socket")]
2475    SockAddrConflict,
2476    /// There was a problem with the provided address relating to its zone.
2477    #[error(transparent)]
2478    Zone(#[from] ZonedAddressError),
2479    /// The remote address is mapped (i.e. an ipv4-mapped-ipv6 address), but the
2480    /// socket is not dual-stack enabled.
2481    #[error("IPv4-mapped-IPv6 addresses are not supported by this socket")]
2482    RemoteUnexpectedlyMapped,
2483    /// The remote address is non-mapped (i.e not an ipv4-mapped-ipv6 address),
2484    /// but the socket is dual stack enabled and bound to a mapped address.
2485    #[error("non IPv4-mapped-Ipv6 addresses are not supported by this socket")]
2486    RemoteUnexpectedlyNonMapped,
2487}
2488
2489/// Parameters required to connect a socket.
2490struct ConnectParameters<WireI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2491    local_ip: Option<SocketIpAddr<WireI::Addr>>,
2492    local_port: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
2493    remote_ip: ZonedAddr<SocketIpAddr<WireI::Addr>, D::Strong>,
2494    remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2495    device: Option<D>,
2496    sharing: S::SharingState,
2497    common_ip_options: DatagramIpAgnosticOptions,
2498    socket_options: DatagramIpSpecificSocketOptions<WireI, D>,
2499    socket_id:
2500        <S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>>::BoundSocketId,
2501    original_shutdown: Option<Shutdown>,
2502    extra: S::ConnStateExtra,
2503}
2504
2505/// Inserts a connected socket into the bound socket map.
2506///
2507/// It accepts two closures that capture the logic required to remove and
2508/// reinsert the original state from/into the bound_socket_map. The original
2509/// state will only be reinserted if an error is encountered during connect.
2510/// The output of `remove_original` is fed into `reinsert_original`.
2511fn connect_inner<
2512    WireI: IpExt,
2513    D: WeakDeviceIdentifier,
2514    S: DatagramSocketSpec,
2515    R,
2516    BC: DatagramBindingsContext,
2517    CC: IpSocketHandler<WireI, BC, WeakDeviceId = D, DeviceId = D::Strong>,
2518>(
2519    connect_params: ConnectParameters<WireI, D, S>,
2520    core_ctx: &mut CC,
2521    bindings_ctx: &mut BC,
2522    sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2523    remove_original: impl FnOnce(
2524        &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2525    ) -> R,
2526    reinsert_original: impl FnOnce(
2527        &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
2528        R,
2529    ),
2530) -> Result<ConnState<WireI, D, S>, ConnectError> {
2531    let ConnectParameters {
2532        local_ip,
2533        local_port,
2534        remote_ip,
2535        remote_port,
2536        device,
2537        sharing,
2538        common_ip_options,
2539        socket_options,
2540        socket_id,
2541        original_shutdown,
2542        extra,
2543    } = connect_params;
2544
2545    // Select multicast device if we are connecting to a multicast address.
2546    let device = device.or_else(|| {
2547        remote_ip
2548            .addr()
2549            .addr()
2550            .is_multicast()
2551            .then(|| socket_options.multicast_interface.clone())
2552            .flatten()
2553    });
2554
2555    let (remote_ip, socket_device) = remote_ip.resolve_addr_with_device(device.clone())?;
2556
2557    let clear_device_on_disconnect = device.is_none() && socket_device.is_some();
2558
2559    let ip_sock = IpSocketHandler::<WireI, _>::new_ip_socket(
2560        core_ctx,
2561        bindings_ctx,
2562        IpSocketArgs {
2563            device: socket_device.as_ref().map(|d| d.as_ref()),
2564            local_ip: local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
2565            remote_ip,
2566            proto: S::ip_proto::<WireI>(),
2567            options: &common_ip_options,
2568        },
2569    )?;
2570
2571    let local_port = match local_port {
2572        Some(id) => id.clone(),
2573        None => S::try_alloc_local_id(
2574            sockets,
2575            bindings_ctx,
2576            DatagramFlowId {
2577                local_ip: SocketIpAddr::from(*ip_sock.local_ip()),
2578                remote_ip: *ip_sock.remote_ip(),
2579                remote_id: remote_port.clone(),
2580            },
2581        )
2582        .ok_or(ConnectError::CouldNotAllocateLocalPort)?,
2583    };
2584    let conn_addr = ConnAddr {
2585        ip: ConnIpAddr {
2586            local: (SocketIpAddr::from(*ip_sock.local_ip()), local_port),
2587            remote: (*ip_sock.remote_ip(), remote_port),
2588        },
2589        device: ip_sock.device().cloned(),
2590    };
2591    // Now that all the other checks have been done, actually remove the
2592    // original state from the socket map.
2593    let reinsert_op = remove_original(sockets);
2594    // Try to insert the new connection, restoring the original state on
2595    // failure.
2596    let bound_addr = match sockets.conns_mut().try_insert(conn_addr, sharing, socket_id) {
2597        Ok(bound_entry) => bound_entry.get_addr().clone(),
2598        Err(
2599            InsertError::Exists
2600            | InsertError::IndirectConflict
2601            | InsertError::ShadowAddrExists
2602            | InsertError::WouldShadowExisting,
2603        ) => {
2604            reinsert_original(sockets, reinsert_op);
2605            return Err(ConnectError::SockAddrConflict);
2606        }
2607    };
2608    Ok(ConnState {
2609        socket: ip_sock,
2610        clear_device_on_disconnect,
2611        shutdown: original_shutdown.unwrap_or_else(Shutdown::default),
2612        addr: bound_addr,
2613        extra,
2614    })
2615}
2616
2617/// State required to perform single-stack connection of a socket.
2618struct SingleStackConnectOperation<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
2619    params: ConnectParameters<I, D, S>,
2620    remove_op: Option<SingleStackRemoveOperation<I, D, S>>,
2621}
2622
2623impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2624    SingleStackConnectOperation<I, D, S>
2625{
2626    /// Constructs the connect operation from existing socket state.
2627    fn new_from_state<
2628        BC,
2629        CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
2630    >(
2631        core_ctx: &mut CC,
2632        socket_id: &S::SocketId<I, D>,
2633        state: &SocketState<I, D, S>,
2634        remote_ip: ZonedAddr<SocketIpAddr<I::Addr>, D::Strong>,
2635        remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2636        extra: S::ConnStateExtra,
2637    ) -> Self {
2638        let SocketState { ip_options, inner, sharing } = state;
2639        match inner {
2640            SocketStateInner::Unbound(UnboundSocketState { device }) => {
2641                SingleStackConnectOperation {
2642                    params: ConnectParameters {
2643                        local_ip: None,
2644                        local_port: None,
2645                        remote_ip,
2646                        remote_port,
2647                        device: device.clone(),
2648                        sharing: sharing.clone(),
2649                        common_ip_options: ip_options.common.clone(),
2650                        socket_options: ip_options.socket_options.clone(),
2651                        socket_id: S::make_bound_socket_map_id(socket_id),
2652                        original_shutdown: None,
2653                        extra,
2654                    },
2655                    remove_op: None,
2656                }
2657            }
2658            SocketStateInner::Bound(state) => {
2659                let remove_op = SingleStackRemoveOperation::new_from_state(
2660                    core_ctx,
2661                    socket_id,
2662                    state,
2663                    sharing.clone(),
2664                );
2665                let BoundSocketState { socket_type, original_bound_addr: _ } = state;
2666                match socket_type {
2667                    BoundSocketStateType::Listener(ListenerState {
2668                        addr: ListenerAddr { ip, device },
2669                    }) => {
2670                        let ListenerIpAddr { addr, identifier } =
2671                            core_ctx.nds_converter().convert(ip);
2672                        SingleStackConnectOperation {
2673                            params: ConnectParameters {
2674                                local_ip: addr.clone(),
2675                                local_port: Some(*identifier),
2676                                remote_ip,
2677                                remote_port,
2678                                device: device.clone(),
2679                                sharing: sharing.clone(),
2680                                common_ip_options: ip_options.common.clone(),
2681                                socket_options: ip_options.socket_options.clone(),
2682                                socket_id: S::make_bound_socket_map_id(socket_id),
2683                                original_shutdown: None,
2684                                extra,
2685                            },
2686                            remove_op: Some(remove_op),
2687                        }
2688                    }
2689                    BoundSocketStateType::Connected(state) => {
2690                        let ConnState {
2691                            socket: _,
2692                            shutdown,
2693                            addr:
2694                                ConnAddr {
2695                                    ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
2696                                    device,
2697                                },
2698                            clear_device_on_disconnect: _,
2699                            extra: _,
2700                        } = core_ctx.nds_converter().convert(state);
2701                        SingleStackConnectOperation {
2702                            params: ConnectParameters {
2703                                local_ip: Some(local_ip.clone()),
2704                                local_port: Some(*local_id),
2705                                remote_ip,
2706                                remote_port,
2707                                device: device.clone(),
2708                                sharing: sharing.clone(),
2709                                common_ip_options: ip_options.common.clone(),
2710                                socket_options: ip_options.socket_options.clone(),
2711                                socket_id: S::make_bound_socket_map_id(socket_id),
2712                                original_shutdown: Some(shutdown.clone()),
2713                                extra,
2714                            },
2715                            remove_op: Some(remove_op),
2716                        }
2717                    }
2718                }
2719            }
2720        }
2721    }
2722
2723    /// Performs this operation and connects the socket.
2724    ///
2725    /// This is primarily a wrapper around `connect_inner` that establishes the
2726    /// remove/reinsert closures for single stack removal.
2727    ///
2728    /// Returns the state for the new connection.
2729    fn apply<
2730        BC: DatagramBindingsContext,
2731        CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>,
2732    >(
2733        self,
2734        core_ctx: &mut CC,
2735        bindings_ctx: &mut BC,
2736        socket_map: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
2737    ) -> Result<ConnState<I, D, S>, ConnectError> {
2738        let SingleStackConnectOperation { params, remove_op } = self;
2739        let remove_fn =
2740            |sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>| {
2741                remove_op.map(|remove_op| {
2742                    remove_op.apply(sockets).expect("Failed to remove listener socket")
2743                })
2744            };
2745        let reinsert_fn =
2746            |sockets: &mut BoundDatagramSocketMap<I, D, S>,
2747             insert_op: Option<SingleStackInsertOperation<I, D, S>>| {
2748                if let Some(insert_op) = insert_op {
2749                    let _: SingleStackRemoveOperation<I, D, S> =
2750                        insert_op.apply(sockets).expect("Failed to revert listener socket removal");
2751                }
2752            };
2753        connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)
2754    }
2755}
2756
2757/// State required to perform dual-stack connection of a socket.
2758struct DualStackConnectOperation<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2759{
2760    params: EitherStack<ConnectParameters<I, D, S>, ConnectParameters<I::OtherVersion, D, S>>,
2761    remove_op: Option<DualStackRemoveOperation<I, D, S>>,
2762}
2763
2764impl<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
2765    DualStackConnectOperation<I, D, S>
2766{
2767    /// Constructs the connect operation from existing socket state.
2768    fn new_from_state<
2769        BC: DatagramBindingsContext,
2770        CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
2771    >(
2772        core_ctx: &mut CC,
2773        socket_id: &S::SocketId<I, D>,
2774        state: &SocketState<I, D, S>,
2775        remote_ip: DualStackRemoteIp<I, D::Strong>,
2776        remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
2777        extra: S::ConnStateExtra,
2778    ) -> Result<Self, ConnectError> {
2779        let SocketState { ip_options, inner, sharing } = state;
2780        match inner {
2781            SocketStateInner::Unbound(UnboundSocketState { device }) => {
2782                // Unbound sockets don't have a predisposition of which stack to
2783                // connect in. Instead, it's dictated entirely by the remote.
2784                let params = match remote_ip {
2785                    DualStackRemoteIp::ThisStack(remote_ip) => {
2786                        EitherStack::ThisStack(ConnectParameters {
2787                            local_ip: None,
2788                            local_port: None,
2789                            remote_ip,
2790                            remote_port,
2791                            device: device.clone(),
2792                            sharing: sharing.clone(),
2793                            common_ip_options: ip_options.common.clone(),
2794                            socket_options: ip_options.socket_options.clone(),
2795                            socket_id: S::make_bound_socket_map_id(socket_id),
2796                            original_shutdown: None,
2797                            extra,
2798                        })
2799                    }
2800                    DualStackRemoteIp::OtherStack(remote_ip) => {
2801                        if !core_ctx.dual_stack_enabled(ip_options) {
2802                            return Err(ConnectError::RemoteUnexpectedlyMapped);
2803                        }
2804                        EitherStack::OtherStack(ConnectParameters {
2805                            local_ip: None,
2806                            local_port: None,
2807                            remote_ip,
2808                            remote_port,
2809                            device: device.clone(),
2810                            sharing: sharing.clone(),
2811                            common_ip_options: ip_options.common.clone(),
2812                            socket_options: core_ctx.to_other_socket_options(ip_options).clone(),
2813                            socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2814                            original_shutdown: None,
2815                            extra,
2816                        })
2817                    }
2818                };
2819                Ok(DualStackConnectOperation { params, remove_op: None })
2820            }
2821            SocketStateInner::Bound(state) => {
2822                let remove_op = DualStackRemoveOperation::new_from_state(
2823                    core_ctx,
2824                    socket_id,
2825                    ip_options,
2826                    state,
2827                    sharing.clone(),
2828                );
2829
2830                let BoundSocketState { socket_type, original_bound_addr: _ } = state;
2831                match socket_type {
2832                    BoundSocketStateType::Listener(ListenerState {
2833                        addr: ListenerAddr { ip, device },
2834                    }) => {
2835                        match (remote_ip, core_ctx.ds_converter().convert(ip)) {
2836                            // Disallow connecting to the other stack because the
2837                            // existing socket state is in this stack.
2838                            (
2839                                DualStackRemoteIp::OtherStack(_),
2840                                DualStackListenerIpAddr::ThisStack(_),
2841                            ) => Err(ConnectError::RemoteUnexpectedlyMapped),
2842                            // Disallow connecting to this stack because the existing
2843                            // socket state is in the other stack.
2844                            (
2845                                DualStackRemoteIp::ThisStack(_),
2846                                DualStackListenerIpAddr::OtherStack(_),
2847                            ) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
2848                            // Connect in this stack.
2849                            (
2850                                DualStackRemoteIp::ThisStack(remote_ip),
2851                                DualStackListenerIpAddr::ThisStack(ListenerIpAddr {
2852                                    addr,
2853                                    identifier,
2854                                }),
2855                            ) => Ok(DualStackConnectOperation {
2856                                params: EitherStack::ThisStack(ConnectParameters {
2857                                    local_ip: addr.clone(),
2858                                    local_port: Some(*identifier),
2859                                    remote_ip,
2860                                    remote_port,
2861                                    device: device.clone(),
2862                                    sharing: sharing.clone(),
2863                                    common_ip_options: ip_options.common.clone(),
2864                                    socket_options: ip_options.socket_options.clone(),
2865                                    socket_id: S::make_bound_socket_map_id(socket_id),
2866                                    original_shutdown: None,
2867                                    extra,
2868                                }),
2869                                remove_op: Some(remove_op),
2870                            }),
2871                            // Listeners in "both stacks" can connect to either
2872                            // stack. Connect in this stack as specified by the
2873                            // remote.
2874                            (
2875                                DualStackRemoteIp::ThisStack(remote_ip),
2876                                DualStackListenerIpAddr::BothStacks(identifier),
2877                            ) => Ok(DualStackConnectOperation {
2878                                params: EitherStack::ThisStack(ConnectParameters {
2879                                    local_ip: None,
2880                                    local_port: Some(*identifier),
2881                                    remote_ip,
2882                                    remote_port,
2883                                    device: device.clone(),
2884                                    sharing: sharing.clone(),
2885                                    common_ip_options: ip_options.common.clone(),
2886                                    socket_options: ip_options.socket_options.clone(),
2887                                    socket_id: S::make_bound_socket_map_id(socket_id),
2888                                    original_shutdown: None,
2889                                    extra,
2890                                }),
2891                                remove_op: Some(remove_op),
2892                            }),
2893                            // Connect in the other stack.
2894                            (
2895                                DualStackRemoteIp::OtherStack(remote_ip),
2896                                DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
2897                                    addr,
2898                                    identifier,
2899                                }),
2900                            ) => Ok(DualStackConnectOperation {
2901                                params: EitherStack::OtherStack(ConnectParameters {
2902                                    local_ip: addr.clone(),
2903                                    local_port: Some(*identifier),
2904                                    remote_ip,
2905                                    remote_port,
2906                                    device: device.clone(),
2907                                    sharing: sharing.clone(),
2908                                    common_ip_options: ip_options.common.clone(),
2909                                    socket_options: core_ctx
2910                                        .to_other_socket_options(ip_options)
2911                                        .clone(),
2912                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2913                                    original_shutdown: None,
2914                                    extra,
2915                                }),
2916                                remove_op: Some(remove_op),
2917                            }),
2918                            // Listeners in "both stacks" can connect to either
2919                            // stack. Connect in the other stack as specified by
2920                            // the remote.
2921                            (
2922                                DualStackRemoteIp::OtherStack(remote_ip),
2923                                DualStackListenerIpAddr::BothStacks(identifier),
2924                            ) => Ok(DualStackConnectOperation {
2925                                params: EitherStack::OtherStack(ConnectParameters {
2926                                    local_ip: None,
2927                                    local_port: Some(*identifier),
2928                                    remote_ip,
2929                                    remote_port,
2930                                    device: device.clone(),
2931                                    sharing: sharing.clone(),
2932                                    common_ip_options: ip_options.common.clone(),
2933                                    socket_options: core_ctx
2934                                        .to_other_socket_options(ip_options)
2935                                        .clone(),
2936                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
2937                                    original_shutdown: None,
2938                                    extra,
2939                                }),
2940                                remove_op: Some(remove_op),
2941                            }),
2942                        }
2943                    }
2944                    BoundSocketStateType::Connected(state) => {
2945                        match (remote_ip, core_ctx.ds_converter().convert(state)) {
2946                            // Disallow connecting to the other stack because the
2947                            // existing socket state is in this stack.
2948                            (
2949                                DualStackRemoteIp::OtherStack(_),
2950                                DualStackConnState::ThisStack(_),
2951                            ) => Err(ConnectError::RemoteUnexpectedlyMapped),
2952                            // Disallow connecting to this stack because the existing
2953                            // socket state is in the other stack.
2954                            (
2955                                DualStackRemoteIp::ThisStack(_),
2956                                DualStackConnState::OtherStack(_),
2957                            ) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
2958                            // Connect in this stack.
2959                            (
2960                                DualStackRemoteIp::ThisStack(remote_ip),
2961                                DualStackConnState::ThisStack(ConnState {
2962                                    socket: _,
2963                                    shutdown,
2964                                    addr:
2965                                        ConnAddr {
2966                                            ip:
2967                                                ConnIpAddr { local: (local_ip, local_id), remote: _ },
2968                                            device,
2969                                        },
2970                                    clear_device_on_disconnect: _,
2971                                    extra: _,
2972                                }),
2973                            ) => Ok(DualStackConnectOperation {
2974                                params: EitherStack::ThisStack(ConnectParameters {
2975                                    local_ip: Some(local_ip.clone()),
2976                                    local_port: Some(*local_id),
2977                                    remote_ip,
2978                                    remote_port,
2979                                    device: device.clone(),
2980                                    sharing: sharing.clone(),
2981                                    common_ip_options: ip_options.common.clone(),
2982                                    socket_options: ip_options.socket_options.clone(),
2983                                    socket_id: S::make_bound_socket_map_id(socket_id),
2984                                    original_shutdown: Some(shutdown.clone()),
2985                                    extra,
2986                                }),
2987                                remove_op: Some(remove_op),
2988                            }),
2989                            // Connect in the other stack.
2990                            (
2991                                DualStackRemoteIp::OtherStack(remote_ip),
2992                                DualStackConnState::OtherStack(ConnState {
2993                                    socket: _,
2994                                    shutdown,
2995                                    addr:
2996                                        ConnAddr {
2997                                            ip:
2998                                                ConnIpAddr { local: (local_ip, local_id), remote: _ },
2999                                            device,
3000                                        },
3001                                    clear_device_on_disconnect: _,
3002                                    extra: _,
3003                                }),
3004                            ) => Ok(DualStackConnectOperation {
3005                                params: EitherStack::OtherStack(ConnectParameters {
3006                                    local_ip: Some(local_ip.clone()),
3007                                    local_port: Some(*local_id),
3008                                    remote_ip,
3009                                    remote_port,
3010                                    device: device.clone(),
3011                                    sharing: sharing.clone(),
3012                                    common_ip_options: ip_options.common.clone(),
3013                                    socket_options: core_ctx
3014                                        .to_other_socket_options(ip_options)
3015                                        .clone(),
3016                                    socket_id: core_ctx.to_other_bound_socket_id(socket_id),
3017                                    original_shutdown: Some(shutdown.clone()),
3018                                    extra,
3019                                }),
3020                                remove_op: Some(remove_op),
3021                            }),
3022                        }
3023                    }
3024                }
3025            }
3026        }
3027    }
3028
3029    /// Performs this operation and connects the socket.
3030    ///
3031    /// This is primarily a wrapper around [`connect_inner`] that establishes the
3032    /// remove/reinsert closures for dual stack removal.
3033    ///
3034    /// Returns a tuple containing the state, and sharing state for the new
3035    /// connection.
3036    fn apply<
3037        BC: DatagramBindingsContext,
3038        CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>
3039            + IpSocketHandler<I::OtherVersion, BC, WeakDeviceId = D, DeviceId = D::Strong>,
3040    >(
3041        self,
3042        core_ctx: &mut CC,
3043        bindings_ctx: &mut BC,
3044        socket_map: &mut BoundDatagramSocketMap<I, D, S>,
3045        other_socket_map: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
3046    ) -> Result<DualStackConnState<I, D, S>, ConnectError> {
3047        let DualStackConnectOperation { params, remove_op } = self;
3048        match params {
3049            EitherStack::ThisStack(params) => {
3050                // NB: Because we're connecting in this stack, we receive this
3051                // stack's sockets as an argument to `remove_fn` and
3052                // `reinsert_fn`. Thus we need to capture + pass through the
3053                // other stack's sockets.
3054                let remove_fn = |sockets: &mut BoundDatagramSocketMap<I, D, S>| {
3055                    remove_op.map(|remove_op| {
3056                        let reinsert_op = remove_op
3057                            .apply(sockets, other_socket_map)
3058                            .expect("Failed to remove listener socket");
3059                        (reinsert_op, other_socket_map)
3060                    })
3061                };
3062                let reinsert_fn = |sockets: &mut BoundDatagramSocketMap<I, D, S>,
3063                                   insert_op: Option<(
3064                    DualStackInsertOperation<I, D, S>,
3065                    &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
3066                )>| {
3067                    if let Some((insert_op, other_sockets)) = insert_op {
3068                        let _: DualStackRemoveOperation<I, D, S> = insert_op
3069                            .apply(sockets, other_sockets)
3070                            .expect("Failed to revert listener socket removal");
3071                    }
3072                };
3073                connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)
3074                    .map(DualStackConnState::ThisStack)
3075            }
3076            EitherStack::OtherStack(params) => {
3077                // NB: Because we're connecting in the other stack, we receive
3078                // the other stack's sockets as an argument to `remove_fn` and
3079                // `reinsert_fn`. Thus we need to capture + pass through this
3080                // stack's sockets.
3081                let remove_fn =
3082                    |other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>| {
3083                        remove_op.map(|remove_op| {
3084                            let reinsert_op = remove_op
3085                                .apply(socket_map, other_sockets)
3086                                .expect("Failed to remove listener socket");
3087                            (reinsert_op, socket_map)
3088                        })
3089                    };
3090                let reinsert_fn =
3091                    |other_sockets: &mut BoundDatagramSocketMap<I::OtherVersion, D, S>,
3092                     insert_op: Option<(
3093                        DualStackInsertOperation<I, D, S>,
3094                        &mut BoundDatagramSocketMap<I, D, S>,
3095                    )>| {
3096                        if let Some((insert_op, sockets)) = insert_op {
3097                            let _: DualStackRemoveOperation<I, D, S> = insert_op
3098                                .apply(sockets, other_sockets)
3099                                .expect("Failed to revert listener socket removal");
3100                        }
3101                    };
3102                connect_inner(
3103                    params,
3104                    core_ctx,
3105                    bindings_ctx,
3106                    other_socket_map,
3107                    remove_fn,
3108                    reinsert_fn,
3109                )
3110                .map(DualStackConnState::OtherStack)
3111            }
3112        }
3113    }
3114}
3115
3116/// A connected socket was expected.
3117#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq, Error)]
3118#[generic_over_ip()]
3119#[error("expected connected socket")]
3120pub struct ExpectedConnError;
3121
3122/// An unbound socket was expected.
3123#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq, Error)]
3124#[generic_over_ip()]
3125#[error("expected unbound socket")]
3126pub struct ExpectedUnboundError;
3127
3128/// Converts a connected socket to an unbound socket.
3129///
3130/// Removes the connection's entry from the [`BoundSocketMap`], and returns the
3131/// socket's new state.
3132fn disconnect_to_unbound<
3133    I: IpExt,
3134    BC: DatagramBindingsContext,
3135    CC: DatagramBoundStateContext<I, BC, S>,
3136    S: DatagramSocketSpec,
3137>(
3138    core_ctx: &mut CC,
3139    id: &S::SocketId<I, CC::WeakDeviceId>,
3140    clear_device_on_disconnect: bool,
3141    ip_options: &IpOptions<I, CC::WeakDeviceId, S>,
3142    socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
3143    sharing: S::SharingState,
3144) -> UnboundSocketState<CC::WeakDeviceId> {
3145    match core_ctx.dual_stack_context_mut() {
3146        MaybeDualStack::NotDualStack(nds) => {
3147            let remove_op =
3148                SingleStackRemoveOperation::new_from_state(nds, id, socket_state, sharing);
3149            let _: SingleStackInsertOperation<_, _, _> =
3150                core_ctx.with_bound_sockets_mut(|_core_ctx, bound| {
3151                    remove_op.apply(bound).expect("Failed to remove connected socket entry")
3152                });
3153        }
3154        MaybeDualStack::DualStack(ds) => {
3155            let remove_op =
3156                DualStackRemoveOperation::new_from_state(ds, id, ip_options, socket_state, sharing);
3157            let _: DualStackInsertOperation<_, _, _> =
3158                ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
3159                    remove_op
3160                        .apply(bound, other_bound)
3161                        .expect("Failed to remove connected socket entry")
3162                });
3163        }
3164    };
3165    let device =
3166        if clear_device_on_disconnect { None } else { socket_state.get_device(core_ctx).clone() };
3167    UnboundSocketState { device }
3168}
3169
3170/// Converts a connected socket to a listener socket.
3171///
3172/// Removes the connection's entry from the [`BoundSocketMap`] and returns the
3173/// socket's new state.
3174fn disconnect_to_listener<
3175    I: IpExt,
3176    BC: DatagramBindingsContext,
3177    CC: DatagramBoundStateContext<I, BC, S>,
3178    S: DatagramSocketSpec,
3179>(
3180    core_ctx: &mut CC,
3181    id: &S::SocketId<I, CC::WeakDeviceId>,
3182    listener_ip: S::ListenerIpAddr<I>,
3183    clear_device_on_disconnect: bool,
3184    ip_options: &IpOptions<I, CC::WeakDeviceId, S>,
3185    socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
3186    sharing: S::SharingState,
3187) -> BoundSocketState<I, CC::WeakDeviceId, S> {
3188    let new_device =
3189        if clear_device_on_disconnect { None } else { socket_state.get_device(core_ctx).clone() };
3190
3191    match core_ctx.dual_stack_context_mut() {
3192        MaybeDualStack::NotDualStack(nds) => {
3193            let ListenerIpAddr { addr, identifier } =
3194                nds.nds_converter().convert(listener_ip.clone());
3195            let remove_op =
3196                SingleStackRemoveOperation::new_from_state(nds, id, socket_state, sharing.clone());
3197            core_ctx.with_bound_sockets_mut(|_core_ctx, bound| {
3198                let _: SingleStackInsertOperation<_, _, _> =
3199                    remove_op.apply(bound).expect("Failed to remove connected socket entry");
3200                BoundStateHandler::<_, S, _>::try_insert_listener(
3201                    bound,
3202                    addr,
3203                    identifier,
3204                    new_device.clone(),
3205                    sharing.clone(),
3206                    S::make_bound_socket_map_id(id),
3207                )
3208                .expect("inserting listener for disconnected socket should succeed");
3209            })
3210        }
3211        MaybeDualStack::DualStack(ds) => {
3212            let remove_op = DualStackRemoveOperation::new_from_state(
3213                ds,
3214                id,
3215                ip_options,
3216                socket_state,
3217                sharing.clone(),
3218            );
3219            let other_id = ds.to_other_bound_socket_id(id);
3220            let id = S::make_bound_socket_map_id(id);
3221            let converter = ds.ds_converter();
3222            ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
3223                let _: DualStackInsertOperation<_, _, _> = remove_op
3224                    .apply(bound, other_bound)
3225                    .expect("Failed to remove connected socket entry");
3226
3227                match converter.convert(listener_ip.clone()) {
3228                    DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }) => {
3229                        BoundStateHandler::<_, S, _>::try_insert_listener(
3230                            bound,
3231                            addr,
3232                            identifier,
3233                            new_device.clone(),
3234                            sharing.clone(),
3235                            id,
3236                        )
3237                    }
3238                    DualStackListenerIpAddr::OtherStack(ListenerIpAddr { addr, identifier }) => {
3239                        BoundStateHandler::<_, S, _>::try_insert_listener(
3240                            other_bound,
3241                            addr,
3242                            identifier,
3243                            new_device.clone(),
3244                            sharing.clone(),
3245                            other_id,
3246                        )
3247                    }
3248                    DualStackListenerIpAddr::BothStacks(identifier) => {
3249                        let ids = PairedBoundSocketIds { this: id, other: other_id };
3250                        let mut bound_pair = PairedSocketMapMut { bound, other_bound };
3251                        BoundStateHandler::<_, S, _>::try_insert_listener(
3252                            &mut bound_pair,
3253                            DualStackUnspecifiedAddr,
3254                            identifier,
3255                            new_device.clone(),
3256                            sharing.clone(),
3257                            ids,
3258                        )
3259                    }
3260                }
3261                .expect("inserting listener for disconnected socket should succeed");
3262            })
3263        }
3264    };
3265    BoundSocketState {
3266        original_bound_addr: Some(listener_ip.clone()),
3267        socket_type: BoundSocketStateType::Listener(ListenerState {
3268            addr: ListenerAddr { ip: listener_ip, device: new_device },
3269        }),
3270    }
3271}
3272
3273/// Error encountered when sending a datagram on a socket.
3274#[derive(Debug, GenericOverIp, Error)]
3275#[generic_over_ip()]
3276pub enum SendError<SE: Error> {
3277    /// The socket is not connected,
3278    #[error("socket not connected")]
3279    NotConnected,
3280    /// The socket is not writeable.
3281    #[error("socket not writeable")]
3282    NotWriteable,
3283    /// There was a problem sending the IP packet.
3284    #[error("error sending IP packet: {0}")]
3285    IpSock(#[from] IpSockSendError),
3286    /// There was a problem when serializing the packet.
3287    #[error("error serializing packet: {0:?}")]
3288    SerializeError(#[source] SE),
3289}
3290
3291/// An error encountered while sending a datagram packet to an alternate address.
3292#[derive(Debug, Error)]
3293pub enum SendToError<SE: Error> {
3294    /// The socket is not writeable.
3295    #[error("socket not writeable")]
3296    NotWriteable,
3297    /// An error was encountered while trying to bind a local address for an
3298    /// unbound socket.
3299    #[error("local address error: {0}")]
3300    LocalAddress(#[from] LocalAddressError),
3301    /// There was a problem with the remote address relating to its zone.
3302    #[error("problem with zone of remote address: {0}")]
3303    Zone(#[from] ZonedAddressError),
3304    /// An error was encountered while trying to create a temporary IP socket
3305    /// to use for the send operation.
3306    #[error("error creating temporary IP socket for send: {0}")]
3307    CreateAndSend(#[from] IpSockCreateAndSendError),
3308    /// The remote address is mapped (i.e. an ipv4-mapped-ipv6 address), but the
3309    /// socket is not dual-stack enabled.
3310    #[error("remote address is mapped, but socket is not dual-stack enabled")]
3311    RemoteUnexpectedlyMapped,
3312    /// The remote address is non-mapped (i.e not an ipv4-mapped-ipv6 address),
3313    /// but the socket is dual stack enabled and bound to a mapped address.
3314    #[error(
3315        "remote address is non-mapped, but socket is \
3316         dual-stack enabled and bound to mapped address"
3317    )]
3318    RemoteUnexpectedlyNonMapped,
3319    /// The provided buffer is not valid.
3320    #[error("serialize buffer invalid")]
3321    SerializeError(#[source] SE),
3322}
3323
3324struct SendOneshotParameters<
3325    'a,
3326    SockI: IpExt,
3327    WireI: IpExt,
3328    S: DatagramSocketSpec,
3329    D: WeakDeviceIdentifier,
3330> {
3331    local_ip: Option<SocketIpAddr<WireI::Addr>>,
3332    local_id: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
3333    remote_ip: ZonedAddr<SocketIpAddr<WireI::Addr>, D::Strong>,
3334    remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
3335    device: &'a Option<D>,
3336    options: IpOptionsRef<'a, WireI, D>,
3337    id: &'a S::SocketId<SockI, D>,
3338    send_token: S::SendToken,
3339}
3340
3341fn send_oneshot<
3342    SockI: IpExt,
3343    WireI: IpExt,
3344    S: DatagramSocketSpec,
3345    CC: IpSocketHandler<WireI, BC> + CoreTxMetadataContext<TxMetadata<SockI, CC::WeakDeviceId, S>, BC>,
3346    BC: DatagramBindingsContext,
3347    B: BufferMut,
3348>(
3349    core_ctx: &mut CC,
3350    bindings_ctx: &mut BC,
3351    params: SendOneshotParameters<'_, SockI, WireI, S, CC::WeakDeviceId>,
3352    body: B,
3353) -> Result<(), SendToError<S::SerializeError>> {
3354    let SendOneshotParameters {
3355        local_ip,
3356        local_id,
3357        remote_ip,
3358        remote_id,
3359        device,
3360        options,
3361        id,
3362        send_token,
3363    } = params;
3364    let device = device.clone().or_else(|| {
3365        remote_ip
3366            .addr()
3367            .addr()
3368            .is_multicast()
3369            .then(|| options.ip_specific.multicast_interface.clone())
3370            .flatten()
3371    });
3372    let (remote_ip, device) = match remote_ip.resolve_addr_with_device(device) {
3373        Ok(addr) => addr,
3374        Err(e) => return Err(SendToError::Zone(e)),
3375    };
3376
3377    let tx_metadata = core_ctx.convert_tx_meta(TxMetadata::new(id, send_token));
3378
3379    core_ctx
3380        .send_oneshot_ip_packet_with_fallible_serializer(
3381            bindings_ctx,
3382            IpSocketArgs {
3383                device: device.as_ref().map(|d| d.as_ref()),
3384                local_ip: local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
3385                remote_ip,
3386                proto: S::ip_proto::<WireI>(),
3387                options: &options,
3388            },
3389            tx_metadata,
3390            |local_ip| {
3391                S::make_packet::<WireI, _>(
3392                    body,
3393                    &ConnIpAddr {
3394                        local: (local_ip.into(), local_id),
3395                        remote: (remote_ip, remote_id),
3396                    },
3397                )
3398            },
3399        )
3400        .map_err(|err| match err {
3401            SendOneShotIpPacketError::CreateAndSendError { err } => SendToError::CreateAndSend(err),
3402            SendOneShotIpPacketError::SerializeError(err) => SendToError::SerializeError(err),
3403        })
3404}
3405
3406/// Mutably holds the original state of a bound socket required to update the
3407/// bound device.
3408enum SetBoundDeviceParameters<'a, I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
3409    Listener {
3410        ip: &'a ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
3411        device: &'a mut Option<D>,
3412    },
3413    Connected(&'a mut ConnState<I, D, S>),
3414}
3415
3416/// Update the device for a bound socket.
3417///
3418/// The update is applied both to the socket's entry in the given
3419/// [`BoundSocketMap`], and the mutable socket state in the given
3420/// [`SetBoundDeviceParameters`].
3421///
3422/// # Panics
3423///
3424/// Panics if the given `socket_id` is not present in the given `sockets` map.
3425fn set_bound_device_single_stack<
3426    'a,
3427    I: IpExt,
3428    D: WeakDeviceIdentifier,
3429    S: DatagramSocketSpec,
3430    BC: DatagramBindingsContext,
3431    CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>,
3432>(
3433    bindings_ctx: &mut BC,
3434    core_ctx: &mut CC,
3435    params: SetBoundDeviceParameters<'a, I, D, S>,
3436    sockets: &mut BoundDatagramSocketMap<I, D, S>,
3437    socket_id: &BoundDatagramSocketId<I, D, S>,
3438    common_ip_options: &DatagramIpAgnosticOptions,
3439    new_device: Option<&D::Strong>,
3440    sharing: S::SharingState,
3441) -> Result<(), SocketError> {
3442    let (local_ip, remote_ip, old_device) = match &params {
3443        SetBoundDeviceParameters::Listener {
3444            ip: ListenerIpAddr { addr, identifier: _ },
3445            device,
3446        } => (addr.as_ref(), None, device.as_ref()),
3447        SetBoundDeviceParameters::Connected(ConnState {
3448            socket: _,
3449            addr:
3450                ConnAddr {
3451                    ip: ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) },
3452                    device,
3453                },
3454            shutdown: _,
3455            clear_device_on_disconnect: _,
3456            extra: _,
3457        }) => (Some(local_ip), Some(remote_ip), device.as_ref()),
3458    };
3459    // Don't allow changing the device if one of the IP addresses in the
3460    // socket address vector requires a zone (scope ID).
3461    let device_update = SocketDeviceUpdate {
3462        local_ip: local_ip.map(AsRef::<SpecifiedAddr<I::Addr>>::as_ref),
3463        remote_ip: remote_ip.map(AsRef::<SpecifiedAddr<I::Addr>>::as_ref),
3464        old_device,
3465    };
3466    match device_update.check_update(new_device) {
3467        Ok(()) => (),
3468        Err(SocketDeviceUpdateNotAllowedError) => {
3469            return Err(SocketError::Local(LocalAddressError::Zone(
3470                ZonedAddressError::DeviceZoneMismatch,
3471            )));
3472        }
3473    };
3474
3475    let new_device_strong = new_device.map(EitherDeviceId::Strong);
3476    let new_device_weak = new_device.map(|d| d.downgrade());
3477
3478    let (old_addr, new_addr, update_state) = match params {
3479        SetBoundDeviceParameters::Listener { ip, device } => (
3480            AddrVec::Listen(ListenerAddr { ip: ip.clone(), device: device.clone() }),
3481            AddrVec::Listen(ListenerAddr { ip: ip.clone(), device: new_device_weak.clone() }),
3482            Either::Left(move || {
3483                *device = new_device_weak;
3484            }),
3485        ),
3486        SetBoundDeviceParameters::Connected(ConnState {
3487            socket,
3488            addr,
3489            shutdown: _,
3490            clear_device_on_disconnect,
3491            extra: _,
3492        }) => {
3493            let ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) } =
3494                addr.ip;
3495            let new_socket = core_ctx
3496                .new_ip_socket(
3497                    bindings_ctx,
3498                    IpSocketArgs {
3499                        device: new_device_strong,
3500                        local_ip: IpDeviceAddr::new_from_socket_ip_addr(local_ip.clone()),
3501                        remote_ip: remote_ip.clone(),
3502                        proto: socket.proto(),
3503                        options: common_ip_options,
3504                    },
3505                )
3506                .map_err(|_: IpSockCreationError| {
3507                    SocketError::Remote(RemoteAddressError::NoRoute)
3508                })?;
3509            let new_addr = ConnAddr { ip: addr.ip.clone(), device: new_device_weak.clone() };
3510            (
3511                AddrVec::Conn(addr.clone()),
3512                AddrVec::Conn(new_addr.clone()),
3513                Either::Right(move || {
3514                    *socket = new_socket;
3515                    // If this operation explicitly sets the device for the socket, it
3516                    // should no longer be cleared on disconnect.
3517                    if new_device.is_some() {
3518                        *clear_device_on_disconnect = false;
3519                    }
3520                    *addr = new_addr
3521                }),
3522            )
3523        }
3524    };
3525
3526    // Remove old address from the socket map.
3527    let remove_op = SingleStackRemoveOperation::<I, D, S> {
3528        socket_id: socket_id.clone(),
3529        sharing: sharing.clone(),
3530        addr: old_addr,
3531        _marker: PhantomData,
3532    };
3533    let reinsert_op = remove_op.apply(sockets).expect("failed to remove socket in set_device");
3534
3535    // Insert new address into the socket map. This operation may fail, in which case
3536    // we need to reinsert the old address.
3537    let insert_op = SingleStackInsertOperation::<I, D, S> {
3538        socket_id: socket_id.clone(),
3539        sharing: sharing,
3540        addr: new_addr,
3541        _marker: PhantomData,
3542    };
3543    match insert_op.apply(sockets) {
3544        Err(e) => {
3545            let _: SingleStackRemoveOperation<_, _, _> = reinsert_op
3546                .apply(sockets)
3547                .expect("failed to reinsert socket after failed set_device");
3548            return Err(SocketError::Local(e.into()));
3549        }
3550        Ok(_) => {}
3551    }
3552
3553    // Update the socket after updating the socket map.
3554    match update_state {
3555        Either::Left(f) => f(),
3556        Either::Right(f) => f(),
3557    }
3558
3559    Ok(())
3560}
3561
3562/// Update the device for a listener socket in both stacks.
3563///
3564/// Either the update is applied successfully to both stacks, or (in the case of
3565/// an error) both stacks are left in their original state.
3566///
3567/// # Panics
3568///
3569/// Panics if the given socket IDs are not present in the given socket maps.
3570fn set_bound_device_listener_both_stacks<
3571    'a,
3572    I: IpExt,
3573    D: WeakDeviceIdentifier,
3574    S: DatagramSocketSpec,
3575>(
3576    old_device: &mut Option<D>,
3577    identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
3578    sockets: PairedSocketMapMut<'a, I, D, S>,
3579    socket_ids: PairedBoundSocketIds<I, D, S>,
3580    new_device: Option<D>,
3581    sharing: S::SharingState,
3582) -> Result<(), SocketError> {
3583    let PairedSocketMapMut { bound: sockets, other_bound: other_sockets } = sockets;
3584
3585    let remove_op = DualStackListenerRemoveOperation {
3586        identifier,
3587        device: old_device.clone(),
3588        sharing: sharing.clone(),
3589        socket_ids: socket_ids.clone(),
3590        _marker: PhantomData,
3591    };
3592    let reinsert_op =
3593        remove_op.apply(sockets, other_sockets).expect("failed to remove socket in set_device");
3594
3595    let insert_op = DualStackListenerInsertOperation {
3596        identifier,
3597        device: new_device.clone(),
3598        sharing,
3599        socket_ids,
3600        _marker: PhantomData,
3601    };
3602    match insert_op.apply(sockets, other_sockets) {
3603        Err(e) => {
3604            let _: DualStackListenerRemoveOperation<_, _, _> = reinsert_op
3605                .apply(sockets, other_sockets)
3606                .expect("failed to reinsert socket after failed set_device");
3607            return Err(SocketError::Local(e.into()));
3608        }
3609        Ok(_) => {}
3610    };
3611
3612    *old_device = new_device;
3613    return Ok(());
3614}
3615
3616/// Error resulting from attempting to change multicast membership settings for
3617/// a socket.
3618#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
3619pub enum SetMulticastMembershipError {
3620    /// The provided address does not match the provided device.
3621    #[error("provided address does not match the provided device")]
3622    AddressNotAvailable,
3623    /// The device does not exist.
3624    #[error("device does not exist")]
3625    DeviceDoesNotExist,
3626    /// The provided address does not match any address on the host.
3627    #[error("provided address does not match any address on the host")]
3628    NoDeviceWithAddress,
3629    /// No device or address was specified and there is no device with a route to the multicast
3630    /// address.
3631    #[error(
3632        "no device or address was specified and \
3633         there is no device with a route to the multicast address"
3634    )]
3635    NoDeviceAvailable,
3636    /// Tried to join a group again.
3637    #[error("tried to join a group again")]
3638    GroupAlreadyJoined,
3639    /// Tried to leave an unjoined group.
3640    #[error("tried to leave an unjoined group")]
3641    GroupNotJoined,
3642    /// The socket is bound to a device that doesn't match the one specified.
3643    #[error("socket is bound to a device that doesn't match the one specified")]
3644    WrongDevice,
3645}
3646
3647/// Selects the interface for the given remote address, optionally with a
3648/// constraint on the source address.
3649fn pick_interface_for_addr<
3650    A: IpAddress,
3651    S: DatagramSocketSpec,
3652    BC: DatagramBindingsContext,
3653    CC: DatagramBoundStateContext<A::Version, BC, S>,
3654>(
3655    core_ctx: &mut CC,
3656    remote_addr: MulticastAddr<A>,
3657    source_addr: Option<SpecifiedAddr<A>>,
3658    marks: &Marks,
3659) -> Result<CC::DeviceId, SetMulticastMembershipError>
3660where
3661    A::Version: IpExt,
3662{
3663    core_ctx.with_transport_context(|core_ctx| match source_addr {
3664        Some(source_addr) => {
3665            BaseTransportIpContext::<A::Version, _>::with_devices_with_assigned_addr(
3666                core_ctx,
3667                source_addr,
3668                |mut devices| {
3669                    if let Some(d) = devices.next() {
3670                        if devices.next() == None {
3671                            return Ok(d);
3672                        }
3673                    }
3674                    Err(SetMulticastMembershipError::NoDeviceAvailable)
3675                },
3676            )
3677        }
3678        None => {
3679            let device = MulticastMembershipHandler::select_device_for_multicast_group(
3680                core_ctx,
3681                remote_addr,
3682                marks,
3683            )
3684            .map_err(|e| match e {
3685                ResolveRouteError::NoSrcAddr | ResolveRouteError::Unreachable => {
3686                    SetMulticastMembershipError::NoDeviceAvailable
3687                }
3688            })?;
3689            Ok(device)
3690        }
3691    })
3692}
3693
3694/// Selector for the device to affect when changing multicast settings.
3695#[derive(Copy, Clone, Debug, Eq, GenericOverIp, PartialEq)]
3696#[generic_over_ip(A, IpAddress)]
3697pub enum MulticastInterfaceSelector<A: IpAddress, D> {
3698    /// Use the device with the assigned address.
3699    LocalAddress(SpecifiedAddr<A>),
3700    /// Use the device with the specified identifier.
3701    Interface(D),
3702}
3703
3704/// Selector for the device to use when changing multicast membership settings.
3705///
3706/// This is like `Option<MulticastInterfaceSelector` except it specifies the
3707/// semantics of the `None` value as "pick any device".
3708#[derive(Copy, Clone, Debug, Eq, PartialEq, GenericOverIp)]
3709#[generic_over_ip(A, IpAddress)]
3710pub enum MulticastMembershipInterfaceSelector<A: IpAddress, D> {
3711    /// Use the specified interface.
3712    Specified(MulticastInterfaceSelector<A, D>),
3713    /// Pick any device with a route to the multicast target address.
3714    AnyInterfaceWithRoute,
3715}
3716
3717impl<A: IpAddress, D> From<MulticastInterfaceSelector<A, D>>
3718    for MulticastMembershipInterfaceSelector<A, D>
3719{
3720    fn from(selector: MulticastInterfaceSelector<A, D>) -> Self {
3721        Self::Specified(selector)
3722    }
3723}
3724
3725/// The shared datagram socket API.
3726#[derive(RefCast)]
3727#[repr(transparent)]
3728pub struct DatagramApi<I, C, S>(C, PhantomData<(S, I)>);
3729
3730impl<I, C, S> DatagramApi<I, C, S> {
3731    /// Creates a new `DatagramApi` from `ctx`.
3732    pub fn new(ctx: C) -> Self {
3733        Self(ctx, PhantomData)
3734    }
3735
3736    /// Creates a mutable borrow of a `DatagramApi` from a mutable borrow of
3737    /// `C`.
3738    pub fn wrap(ctx: &mut C) -> &mut Self {
3739        Self::ref_cast_mut(ctx)
3740    }
3741}
3742
3743/// A local alias for [`DatagramSocketSpec::SocketId`] for use in
3744/// [`DatagramApi`].
3745///
3746/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3747/// associated type.
3748type DatagramApiSocketId<I, C, S> = <S as DatagramSocketSpec>::SocketId<
3749    I,
3750    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
3751>;
3752/// A local alias for [`DeviceIdContext::DeviceId`] for use in
3753/// [`DatagramApi`].
3754///
3755/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3756/// associated type.
3757type DatagramApiDeviceId<C> =
3758    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId;
3759/// A local alias for [`DeviceIdContext::WeakDeviceId`] for use in
3760/// [`DatagramApi`].
3761///
3762/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
3763/// associated type.
3764type DatagramApiWeakDeviceId<C> =
3765    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId;
3766
3767impl<I, C, S> DatagramApi<I, C, S>
3768where
3769    I: IpExt,
3770    C: ContextPair,
3771    C::BindingsContext: DatagramBindingsContext,
3772    C::CoreContext: DatagramStateContext<I, C::BindingsContext, S>,
3773    S: DatagramSocketSpec,
3774{
3775    fn core_ctx(&mut self) -> &mut C::CoreContext {
3776        let Self(pair, PhantomData) = self;
3777        pair.core_ctx()
3778    }
3779
3780    fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
3781        let Self(pair, PhantomData) = self;
3782        pair.contexts()
3783    }
3784
3785    /// Creates a new datagram socket and inserts it into the list of all open
3786    /// datagram sockets for the provided spec `S`.
3787    ///
3788    /// The caller is responsible for calling  [`close`] when it's done with the
3789    /// resource.
3790    pub fn create(
3791        &mut self,
3792        external_data: S::ExternalData<I>,
3793    ) -> S::SocketId<I, DatagramApiWeakDeviceId<C>> {
3794        let primary = create_primary_id(external_data);
3795        let strong = PrimaryRc::clone_strong(&primary);
3796        self.core_ctx().with_all_sockets_mut(move |socket_set| {
3797            let strong = PrimaryRc::clone_strong(&primary);
3798            assert_matches::assert_matches!(socket_set.insert(strong, primary), None);
3799        });
3800        strong.into()
3801    }
3802
3803    /// Like [`DatagramApi::create`], but uses default values.
3804    #[cfg(any(test, feature = "testutils"))]
3805    pub fn create_default(&mut self) -> S::SocketId<I, DatagramApiWeakDeviceId<C>>
3806    where
3807        S::ExternalData<I>: Default,
3808    {
3809        self.create(Default::default())
3810    }
3811
3812    /// Collects all currently opened sockets.
3813    pub fn collect_all_sockets(&mut self) -> Vec<S::SocketId<I, DatagramApiWeakDeviceId<C>>> {
3814        self.core_ctx()
3815            .with_all_sockets(|socket_set| socket_set.keys().map(|s| s.clone().into()).collect())
3816    }
3817
3818    /// Closes the socket and returns a custom payload.
3819    pub fn close<O, F>(
3820        &mut self,
3821        id: DatagramApiSocketId<I, C, S>,
3822        map: F,
3823    ) -> RemoveResourceResultWithContext<O, C::BindingsContext>
3824    where
3825        O: Send,
3826        F: Send + Clone + 'static + FnOnce(ReferenceState<I, DatagramApiWeakDeviceId<C>, S>) -> O,
3827    {
3828        let (core_ctx, bindings_ctx) = self.contexts();
3829        // Remove the socket from the list first to prevent double close.
3830        let primary = core_ctx.with_all_sockets_mut(|all_sockets| {
3831            all_sockets.remove(id.borrow()).expect("socket already closed")
3832        });
3833        core_ctx.with_socket_state(&id, |core_ctx, state| {
3834            let SocketState { ip_options, inner, sharing } = state;
3835            match inner {
3836                SocketStateInner::Unbound(UnboundSocketState { device: _ }) => {}
3837                SocketStateInner::Bound(state) => match core_ctx.dual_stack_context_mut() {
3838                    MaybeDualStack::DualStack(dual_stack) => {
3839                        let op = DualStackRemoveOperation::new_from_state(
3840                            dual_stack,
3841                            &id,
3842                            ip_options,
3843                            state,
3844                            sharing.clone(),
3845                        );
3846                        let _: DualStackInsertOperation<_, _, _> = dual_stack
3847                            .with_both_bound_sockets_mut(|_core_ctx, sockets, other_sockets| {
3848                                op.apply(sockets, other_sockets).expect("Failed to remove socket")
3849                            });
3850                    }
3851                    MaybeDualStack::NotDualStack(not_dual_stack) => {
3852                        let op = SingleStackRemoveOperation::new_from_state(
3853                            not_dual_stack,
3854                            &id,
3855                            state,
3856                            sharing.clone(),
3857                        );
3858                        let _: SingleStackInsertOperation<_, _, _> = core_ctx
3859                            .with_bound_sockets_mut(|_core_ctx, sockets| {
3860                                op.apply(sockets).expect("Failed to remove socket")
3861                            });
3862                    }
3863                },
3864            };
3865            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
3866                leave_all_joined_groups(core_ctx, bindings_ctx, &ip_options.multicast_memberships)
3867            });
3868        });
3869
3870        // Drop the (hopefully last) strong ID before unwrapping the primary
3871        // reference.
3872        core::mem::drop(id);
3873        <C::BindingsContext as ReferenceNotifiersExt>::unwrap_or_notify_with_new_reference_notifier(
3874            primary, map,
3875        )
3876    }
3877
3878    /// Returns the socket's bound/connection state information.
3879    pub fn get_info(
3880        &mut self,
3881        id: &DatagramApiSocketId<I, C, S>,
3882    ) -> SocketInfo<I::Addr, DatagramApiWeakDeviceId<C>> {
3883        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.to_socket_info())
3884    }
3885
3886    /// Binds the socket to a local address and port.
3887    pub fn listen(
3888        &mut self,
3889        id: &DatagramApiSocketId<I, C, S>,
3890        addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
3891        local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
3892    ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
3893        let (core_ctx, bindings_ctx) = self.contexts();
3894        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
3895            listen_inner::<_, _, _, S>(core_ctx, bindings_ctx, state, id, addr, local_id)
3896        })
3897    }
3898
3899    /// Connects the datagram socket.
3900    pub fn connect(
3901        &mut self,
3902        id: &DatagramApiSocketId<I, C, S>,
3903        remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
3904        remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
3905        extra: S::ConnStateExtra,
3906    ) -> Result<(), ConnectError> {
3907        let (core_ctx, bindings_ctx) = self.contexts();
3908        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
3909            let conn_state = match (
3910                core_ctx.dual_stack_context_mut(),
3911                DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
3912            ) {
3913                (MaybeDualStack::DualStack(ds), remote_ip) => {
3914                    let connect_op = DualStackConnectOperation::new_from_state(
3915                        ds, id, state, remote_ip, remote_id, extra,
3916                    )?;
3917                    let converter = ds.ds_converter();
3918                    let conn_state =
3919                        ds.with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
3920                            connect_op.apply(core_ctx, bindings_ctx, bound, other_bound)
3921                        })?;
3922                    Ok(converter.convert_back(conn_state))
3923                }
3924                (MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
3925                    let connect_op = SingleStackConnectOperation::new_from_state(
3926                        nds, id, state, remote_ip, remote_id, extra,
3927                    );
3928                    let converter = nds.nds_converter();
3929                    let conn_state = core_ctx.with_bound_sockets_mut(|core_ctx, bound| {
3930                        connect_op.apply(core_ctx, bindings_ctx, bound)
3931                    })?;
3932                    Ok(converter.convert_back(conn_state))
3933                }
3934                (MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
3935                    Err(ConnectError::RemoteUnexpectedlyMapped)
3936                }
3937            }?;
3938            let original_bound_addr = match &state.inner {
3939                SocketStateInner::Unbound(_) => None,
3940                SocketStateInner::Bound(BoundSocketState {
3941                    socket_type: _,
3942                    original_bound_addr,
3943                }) => original_bound_addr.clone(),
3944            };
3945            state.inner = SocketStateInner::Bound(BoundSocketState {
3946                socket_type: BoundSocketStateType::Connected(conn_state),
3947                original_bound_addr,
3948            });
3949            Ok(())
3950        })
3951    }
3952
3953    /// Disconnects a connected socket.
3954    pub fn disconnect_connected(
3955        &mut self,
3956        id: &DatagramApiSocketId<I, C, S>,
3957    ) -> Result<(), ExpectedConnError> {
3958        self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
3959            let SocketState { ip_options, inner, sharing } = state;
3960            let inner_state = match inner {
3961                SocketStateInner::Unbound(_) => return Err(ExpectedConnError),
3962                SocketStateInner::Bound(state) => state,
3963            };
3964            let BoundSocketState { socket_type, original_bound_addr } = inner_state;
3965            let conn_state = match socket_type {
3966                BoundSocketStateType::Listener(_) => {
3967                    return Err(ExpectedConnError);
3968                }
3969                BoundSocketStateType::Connected(state) => state,
3970            };
3971
3972            let clear_device_on_disconnect = match core_ctx.dual_stack_context_mut() {
3973                MaybeDualStack::DualStack(dual_stack) => {
3974                    match dual_stack.ds_converter().convert(conn_state) {
3975                        DualStackConnState::ThisStack(conn_state) => {
3976                            conn_state.clear_device_on_disconnect
3977                        }
3978                        DualStackConnState::OtherStack(conn_state) => {
3979                            conn_state.clear_device_on_disconnect
3980                        }
3981                    }
3982                }
3983                MaybeDualStack::NotDualStack(not_dual_stack) => {
3984                    not_dual_stack.nds_converter().convert(conn_state).clear_device_on_disconnect
3985                }
3986            };
3987
3988            state.inner = match original_bound_addr {
3989                None => SocketStateInner::Unbound(disconnect_to_unbound(
3990                    core_ctx,
3991                    id,
3992                    clear_device_on_disconnect,
3993                    &state.ip_options,
3994                    inner_state,
3995                    sharing.clone(),
3996                )),
3997                Some(original_bound_addr) => SocketStateInner::Bound(disconnect_to_listener(
3998                    core_ctx,
3999                    id,
4000                    original_bound_addr.clone(),
4001                    clear_device_on_disconnect,
4002                    &ip_options,
4003                    inner_state,
4004                    sharing.clone(),
4005                )),
4006            };
4007            Ok(())
4008        })
4009    }
4010
4011    /// Disconnects any socket (bound or unbound), resetting it to unbound state
4012    /// and clearing the bound device.
4013    pub fn disconnect_any_to_unbound(&mut self, id: &DatagramApiSocketId<I, C, S>) {
4014        self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
4015            let SocketState { ip_options, inner, sharing, .. } = state;
4016            match inner {
4017                SocketStateInner::Unbound(UnboundSocketState { device }) => {
4018                    *device = None;
4019                }
4020                SocketStateInner::Bound(bound_state) => {
4021                    let unbound_state = disconnect_to_unbound(
4022                        core_ctx,
4023                        id,
4024                        true,
4025                        ip_options,
4026                        bound_state,
4027                        sharing.clone(),
4028                    );
4029                    state.inner = SocketStateInner::Unbound(unbound_state);
4030                }
4031            }
4032        });
4033    }
4034
4035    /// Returns the socket's shutdown state.
4036    pub fn get_shutdown_connected(
4037        &mut self,
4038        id: &DatagramApiSocketId<I, C, S>,
4039    ) -> Option<ShutdownType> {
4040        self.core_ctx().with_socket_state(id, |core_ctx, state| {
4041            let state = match &state.inner {
4042                SocketStateInner::Unbound(_) => return None,
4043                SocketStateInner::Bound(BoundSocketState {
4044                    socket_type,
4045                    original_bound_addr: _,
4046                }) => match socket_type {
4047                    BoundSocketStateType::Listener(_) => return None,
4048                    BoundSocketStateType::Connected(state) => state,
4049                },
4050            };
4051            let Shutdown { send, receive } = match core_ctx.dual_stack_context_mut() {
4052                MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_ref(),
4053                MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_ref(),
4054            };
4055            ShutdownType::from_send_receive(*send, *receive)
4056        })
4057    }
4058
4059    /// Shuts down the socket.
4060    ///
4061    /// `which` determines the shutdown type.
4062    pub fn shutdown_connected(
4063        &mut self,
4064        id: &DatagramApiSocketId<I, C, S>,
4065        which: ShutdownType,
4066    ) -> Result<(), ExpectedConnError> {
4067        self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
4068            let state = match &mut state.inner {
4069                SocketStateInner::Unbound(_) => return Err(ExpectedConnError),
4070                SocketStateInner::Bound(BoundSocketState {
4071                    socket_type,
4072                    original_bound_addr: _,
4073                }) => match socket_type {
4074                    BoundSocketStateType::Listener(_) => {
4075                        return Err(ExpectedConnError);
4076                    }
4077                    BoundSocketStateType::Connected(state) => state,
4078                },
4079            };
4080            let (shutdown_send, shutdown_receive) = which.to_send_receive();
4081            let Shutdown { send, receive } = match core_ctx.dual_stack_context_mut() {
4082                MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_mut(),
4083                MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_mut(),
4084            };
4085            *send |= shutdown_send;
4086            *receive |= shutdown_receive;
4087            Ok(())
4088        })
4089    }
4090
4091    /// Sends data over a connected datagram socket.
4092    pub fn send_conn<B: BufferMut>(
4093        &mut self,
4094        id: &DatagramApiSocketId<I, C, S>,
4095        body: B,
4096        send_token: S::SendToken,
4097    ) -> Result<(), SendError<S::SerializeError>> {
4098        let (core_ctx, bindings_ctx) = self.contexts();
4099        core_ctx.with_socket_state(id, |core_ctx, state| {
4100            let SocketState { inner, ip_options, sharing: _ } = state;
4101            let state = match inner {
4102                SocketStateInner::Unbound(_) => return Err(SendError::NotConnected),
4103                SocketStateInner::Bound(BoundSocketState {
4104                    socket_type,
4105                    original_bound_addr: _,
4106                }) => match socket_type {
4107                    BoundSocketStateType::Listener(_) => {
4108                        return Err(SendError::NotConnected);
4109                    }
4110                    BoundSocketStateType::Connected(state) => state,
4111                },
4112            };
4113
4114            struct SendParams<
4115                'a,
4116                I: IpExt,
4117                S: DatagramSocketSpec,
4118                D: WeakDeviceIdentifier,
4119                O: SendOptions<I> + RouteResolutionOptions<I>,
4120            > {
4121                socket: &'a IpSock<I, D>,
4122                ip: &'a ConnIpAddr<
4123                    I::Addr,
4124                    <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
4125                    <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
4126                >,
4127                options: O,
4128            }
4129
4130            enum Operation<
4131                'a,
4132                I: DualStackIpExt,
4133                S: DatagramSocketSpec,
4134                D: WeakDeviceIdentifier,
4135                BC: DatagramBindingsContext,
4136                DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
4137                CC: DatagramBoundStateContext<I, BC, S>,
4138                O: SendOptions<I> + RouteResolutionOptions<I>,
4139                OtherO: SendOptions<I::OtherVersion> + RouteResolutionOptions<I::OtherVersion>,
4140            > {
4141                SendToThisStack((SendParams<'a, I, S, D, O>, &'a mut CC)),
4142                SendToOtherStack(
4143                    (SendParams<'a, I::OtherVersion, S, D, OtherO>, &'a mut DualStackSC),
4144                ),
4145                // Allow `Operation` to be generic over `B` and `C` so that they can
4146                // be used in trait bounds for `DualStackSC` and `SC`.
4147                _Phantom((!, PhantomData<BC>)),
4148            }
4149
4150            let (shutdown, operation) = match core_ctx.dual_stack_context_mut() {
4151                MaybeDualStack::DualStack(dual_stack) => {
4152                    match dual_stack.ds_converter().convert(state) {
4153                        DualStackConnState::ThisStack(ConnState {
4154                            socket,
4155                            clear_device_on_disconnect: _,
4156                            shutdown,
4157                            addr: ConnAddr { ip, device: _ },
4158                            extra: _,
4159                        }) => (
4160                            shutdown,
4161                            Operation::SendToThisStack((
4162                                SendParams {
4163                                    socket,
4164                                    ip,
4165                                    options: ip_options.this_stack_options_ref(),
4166                                },
4167                                core_ctx,
4168                            )),
4169                        ),
4170                        DualStackConnState::OtherStack(ConnState {
4171                            socket,
4172                            clear_device_on_disconnect: _,
4173                            shutdown,
4174                            addr: ConnAddr { ip, device: _ },
4175                            extra: _,
4176                        }) => (
4177                            shutdown,
4178                            Operation::SendToOtherStack((
4179                                SendParams {
4180                                    socket,
4181                                    ip,
4182                                    options: ip_options.other_stack_options_ref(dual_stack),
4183                                },
4184                                dual_stack,
4185                            )),
4186                        ),
4187                    }
4188                }
4189                MaybeDualStack::NotDualStack(not_dual_stack) => {
4190                    let ConnState {
4191                        socket,
4192                        clear_device_on_disconnect: _,
4193                        shutdown,
4194                        addr: ConnAddr { ip, device: _ },
4195                        extra: _,
4196                    } = not_dual_stack.nds_converter().convert(state);
4197                    (
4198                        shutdown,
4199                        Operation::SendToThisStack((
4200                            SendParams { socket, ip, options: ip_options.this_stack_options_ref() },
4201                            core_ctx,
4202                        )),
4203                    )
4204                }
4205            };
4206
4207            let Shutdown { send: shutdown_send, receive: _ } = shutdown;
4208            if *shutdown_send {
4209                return Err(SendError::NotWriteable);
4210            }
4211
4212            let tx_metadata = TxMetadata::<I, _, _>::new(id, send_token);
4213            match operation {
4214                Operation::SendToThisStack((SendParams { socket, ip, options }, core_ctx)) => {
4215                    let packet =
4216                        S::make_packet::<I, _>(body, &ip).map_err(SendError::SerializeError)?;
4217                    DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
4218                        let tx_metadata = core_ctx.convert_tx_meta(tx_metadata);
4219                        core_ctx
4220                            .send_ip_packet(bindings_ctx, &socket, packet, &options, tx_metadata)
4221                            .map_err(|send_error| SendError::IpSock(send_error))
4222                    })
4223                }
4224                Operation::SendToOtherStack((SendParams { socket, ip, options }, dual_stack)) => {
4225                    let packet = S::make_packet::<I::OtherVersion, _>(body, &ip)
4226                        .map_err(SendError::SerializeError)?;
4227                    DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
4228                        dual_stack,
4229                        |core_ctx| {
4230                            let tx_metadata = core_ctx.convert_tx_meta(tx_metadata);
4231                            core_ctx
4232                                .send_ip_packet(
4233                                    bindings_ctx,
4234                                    &socket,
4235                                    packet,
4236                                    &options,
4237                                    tx_metadata,
4238                                )
4239                                .map_err(|send_error| SendError::IpSock(send_error))
4240                        },
4241                    )
4242                }
4243            }
4244        })
4245    }
4246
4247    /// Sends a datagram to the provided remote node.
4248    pub fn send_to<B: BufferMut>(
4249        &mut self,
4250        id: &DatagramApiSocketId<I, C, S>,
4251        remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
4252        remote_identifier: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
4253        body: B,
4254        send_token: S::SendToken,
4255    ) -> Result<(), SendToError<S::SerializeError>> {
4256        let (core_ctx, bindings_ctx) = self.contexts();
4257        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4258            match listen_inner(core_ctx, bindings_ctx, state, id, None, None) {
4259                Ok(()) | Err(Either::Left(ExpectedUnboundError)) => (),
4260                Err(Either::Right(e)) => return Err(SendToError::LocalAddress(e)),
4261            };
4262            let SocketState { inner, ip_options, sharing: _ } = state;
4263            let state = match inner {
4264                SocketStateInner::Unbound(_) => panic!("expected bound socket"),
4265                SocketStateInner::Bound(BoundSocketState {
4266                    socket_type: state,
4267                    original_bound_addr: _,
4268                }) => state,
4269            };
4270
4271            enum Operation<
4272                'a,
4273                I: DualStackIpExt,
4274                S: DatagramSocketSpec,
4275                D: WeakDeviceIdentifier,
4276                BC: DatagramBindingsContext,
4277                DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
4278                CC: DatagramBoundStateContext<I, BC, S>,
4279            > {
4280                SendToThisStack((SendOneshotParameters<'a, I, I, S, D>, &'a mut CC)),
4281
4282                SendToOtherStack(
4283                    (SendOneshotParameters<'a, I, I::OtherVersion, S, D>, &'a mut DualStackSC),
4284                ),
4285                // Allow `Operation` to be generic over `B` and `C` so that they can
4286                // be used in trait bounds for `DualStackSC` and `SC`.
4287                _Phantom((!, PhantomData<BC>)),
4288            }
4289
4290            let (operation, shutdown) = match (
4291                core_ctx.dual_stack_context_mut(),
4292                DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
4293            ) {
4294                (MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
4295                    return Err(SendToError::RemoteUnexpectedlyMapped);
4296                }
4297                (MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
4298                    match state {
4299                        BoundSocketStateType::Listener(ListenerState {
4300                            addr: ListenerAddr { ip, device },
4301                        }) => {
4302                            let ListenerIpAddr { addr, identifier } =
4303                                nds.nds_converter().convert(ip.clone());
4304                            (
4305                                Operation::SendToThisStack((
4306                                    SendOneshotParameters {
4307                                        local_ip: addr,
4308                                        local_id: identifier,
4309                                        remote_ip,
4310                                        remote_id: remote_identifier,
4311                                        device,
4312                                        options: ip_options.this_stack_options_ref(),
4313                                        id,
4314                                        send_token,
4315                                    },
4316                                    core_ctx,
4317                                )),
4318                                None,
4319                            )
4320                        }
4321                        BoundSocketStateType::Connected(state) => {
4322                            let ConnState {
4323                                socket: _,
4324                                clear_device_on_disconnect: _,
4325                                shutdown,
4326                                addr:
4327                                    ConnAddr {
4328                                        ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
4329                                        device,
4330                                    },
4331                                extra: _,
4332                            } = nds.nds_converter().convert(state);
4333                            (
4334                                Operation::SendToThisStack((
4335                                    SendOneshotParameters {
4336                                        local_ip: Some(*local_ip),
4337                                        local_id: *local_id,
4338                                        remote_ip,
4339                                        remote_id: remote_identifier,
4340                                        device,
4341                                        options: ip_options.this_stack_options_ref(),
4342                                        id,
4343                                        send_token,
4344                                    },
4345                                    core_ctx,
4346                                )),
4347                                Some(shutdown),
4348                            )
4349                        }
4350                    }
4351                }
4352                (MaybeDualStack::DualStack(ds), remote_ip) => match state {
4353                    BoundSocketStateType::Listener(ListenerState {
4354                        addr: ListenerAddr { ip, device },
4355                    }) => match (ds.ds_converter().convert(ip), remote_ip) {
4356                        (
4357                            DualStackListenerIpAddr::ThisStack(_),
4358                            DualStackRemoteIp::OtherStack(_),
4359                        ) => return Err(SendToError::RemoteUnexpectedlyMapped),
4360                        (
4361                            DualStackListenerIpAddr::OtherStack(_),
4362                            DualStackRemoteIp::ThisStack(_),
4363                        ) => return Err(SendToError::RemoteUnexpectedlyNonMapped),
4364                        (
4365                            DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
4366                            DualStackRemoteIp::ThisStack(remote_ip),
4367                        ) => (
4368                            Operation::SendToThisStack((
4369                                SendOneshotParameters {
4370                                    local_ip: *addr,
4371                                    local_id: *identifier,
4372                                    remote_ip,
4373                                    remote_id: remote_identifier,
4374                                    device,
4375                                    options: ip_options.this_stack_options_ref(),
4376                                    id,
4377                                    send_token,
4378                                },
4379                                core_ctx,
4380                            )),
4381                            None,
4382                        ),
4383                        (
4384                            DualStackListenerIpAddr::BothStacks(identifier),
4385                            DualStackRemoteIp::ThisStack(remote_ip),
4386                        ) => (
4387                            Operation::SendToThisStack((
4388                                SendOneshotParameters {
4389                                    local_ip: None,
4390                                    local_id: *identifier,
4391                                    remote_ip,
4392                                    remote_id: remote_identifier,
4393                                    device,
4394                                    options: ip_options.this_stack_options_ref(),
4395                                    id,
4396                                    send_token,
4397                                },
4398                                core_ctx,
4399                            )),
4400                            None,
4401                        ),
4402                        (
4403                            DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
4404                                addr,
4405                                identifier,
4406                            }),
4407                            DualStackRemoteIp::OtherStack(remote_ip),
4408                        ) => (
4409                            Operation::SendToOtherStack((
4410                                SendOneshotParameters {
4411                                    local_ip: *addr,
4412                                    local_id: *identifier,
4413                                    remote_ip,
4414                                    remote_id: remote_identifier,
4415                                    device,
4416                                    options: ip_options.other_stack_options_ref(ds),
4417                                    id,
4418                                    send_token,
4419                                },
4420                                ds,
4421                            )),
4422                            None,
4423                        ),
4424                        (
4425                            DualStackListenerIpAddr::BothStacks(identifier),
4426                            DualStackRemoteIp::OtherStack(remote_ip),
4427                        ) => (
4428                            Operation::SendToOtherStack((
4429                                SendOneshotParameters {
4430                                    local_ip: None,
4431                                    local_id: *identifier,
4432                                    remote_ip,
4433                                    remote_id: remote_identifier,
4434                                    device,
4435                                    options: ip_options.other_stack_options_ref(ds),
4436                                    id,
4437                                    send_token,
4438                                },
4439                                ds,
4440                            )),
4441                            None,
4442                        ),
4443                    },
4444                    BoundSocketStateType::Connected(state) => {
4445                        match (ds.ds_converter().convert(state), remote_ip) {
4446                            (
4447                                DualStackConnState::ThisStack(_),
4448                                DualStackRemoteIp::OtherStack(_),
4449                            ) => return Err(SendToError::RemoteUnexpectedlyMapped),
4450                            (
4451                                DualStackConnState::OtherStack(_),
4452                                DualStackRemoteIp::ThisStack(_),
4453                            ) => {
4454                                return Err(SendToError::RemoteUnexpectedlyNonMapped);
4455                            }
4456                            (
4457                                DualStackConnState::ThisStack(state),
4458                                DualStackRemoteIp::ThisStack(remote_ip),
4459                            ) => {
4460                                let ConnState {
4461                                    socket: _,
4462                                    clear_device_on_disconnect: _,
4463                                    shutdown,
4464                                    addr,
4465                                    extra: _,
4466                                } = state;
4467                                let ConnAddr {
4468                                    ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
4469                                    device,
4470                                } = addr;
4471                                (
4472                                    Operation::SendToThisStack((
4473                                        SendOneshotParameters {
4474                                            local_ip: Some(*local_ip),
4475                                            local_id: *local_id,
4476                                            remote_ip,
4477                                            remote_id: remote_identifier,
4478                                            device,
4479                                            options: ip_options.this_stack_options_ref(),
4480                                            id,
4481                                            send_token,
4482                                        },
4483                                        core_ctx,
4484                                    )),
4485                                    Some(shutdown),
4486                                )
4487                            }
4488                            (
4489                                DualStackConnState::OtherStack(state),
4490                                DualStackRemoteIp::OtherStack(remote_ip),
4491                            ) => {
4492                                let ConnState {
4493                                    socket: _,
4494                                    clear_device_on_disconnect: _,
4495                                    shutdown,
4496                                    addr,
4497                                    extra: _,
4498                                } = state;
4499                                let ConnAddr {
4500                                    ip: ConnIpAddr { local: (local_ip, local_id), .. },
4501                                    device,
4502                                } = addr;
4503                                (
4504                                    Operation::SendToOtherStack((
4505                                        SendOneshotParameters {
4506                                            local_ip: Some(*local_ip),
4507                                            local_id: *local_id,
4508                                            remote_ip,
4509                                            remote_id: remote_identifier,
4510                                            device,
4511                                            options: ip_options.other_stack_options_ref(ds),
4512                                            id,
4513                                            send_token,
4514                                        },
4515                                        ds,
4516                                    )),
4517                                    Some(shutdown),
4518                                )
4519                            }
4520                        }
4521                    }
4522                },
4523            };
4524
4525            if let Some(Shutdown { send: shutdown_write, receive: _ }) = shutdown {
4526                if *shutdown_write {
4527                    return Err(SendToError::NotWriteable);
4528                }
4529            }
4530
4531            match operation {
4532                Operation::SendToThisStack((params, core_ctx)) => {
4533                    DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
4534                        send_oneshot(core_ctx, bindings_ctx, params, body)
4535                    })
4536                }
4537                Operation::SendToOtherStack((params, core_ctx)) => {
4538                    DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
4539                        core_ctx,
4540                        |core_ctx| send_oneshot(core_ctx, bindings_ctx, params, body),
4541                    )
4542                }
4543            }
4544        })
4545    }
4546
4547    /// Returns the bound device for the socket.
4548    pub fn get_bound_device(
4549        &mut self,
4550        id: &DatagramApiSocketId<I, C, S>,
4551    ) -> Option<DatagramApiWeakDeviceId<C>> {
4552        self.core_ctx().with_socket_state(id, |core_ctx, state| state.get_device(core_ctx).clone())
4553    }
4554
4555    /// Sets the socket's bound device to `new_device`.
4556    pub fn set_device(
4557        &mut self,
4558        id: &DatagramApiSocketId<I, C, S>,
4559        new_device: Option<&DatagramApiDeviceId<C>>,
4560    ) -> Result<(), SocketError> {
4561        let (core_ctx, bindings_ctx) = self.contexts();
4562        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4563            let SocketState { inner, ip_options, sharing } = state;
4564            match inner {
4565                SocketStateInner::Unbound(state) => {
4566                    let UnboundSocketState { device } = state;
4567                    *device = new_device.map(|d| d.downgrade());
4568                    Ok(())
4569                }
4570                SocketStateInner::Bound(BoundSocketState {
4571                    socket_type,
4572                    original_bound_addr: _,
4573                }) => {
4574                    // Information about the set-device operation for the given
4575                    // socket.
4576                    enum Operation<
4577                        'a,
4578                        I: IpExt,
4579                        D: WeakDeviceIdentifier,
4580                        S: DatagramSocketSpec,
4581                        CC,
4582                        DualStackSC,
4583                    > {
4584                        ThisStack {
4585                            params: SetBoundDeviceParameters<'a, I, D, S>,
4586                            core_ctx: CC,
4587                        },
4588                        OtherStack {
4589                            params: SetBoundDeviceParameters<'a, I::OtherVersion, D, S>,
4590                            core_ctx: DualStackSC,
4591                        },
4592                        ListenerBothStacks {
4593                            identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
4594                            device: &'a mut Option<D>,
4595                            core_ctx: DualStackSC,
4596                        },
4597                    }
4598
4599                    // Determine which operation needs to be applied.
4600                    let op = match core_ctx.dual_stack_context_mut() {
4601                        MaybeDualStack::DualStack(ds) => match socket_type {
4602                            BoundSocketStateType::Listener(ListenerState {
4603                                addr: ListenerAddr { ip, device },
4604                            }) => match ds.ds_converter().convert(ip) {
4605                                DualStackListenerIpAddr::ThisStack(ip) => Operation::ThisStack {
4606                                    params: SetBoundDeviceParameters::Listener { ip, device },
4607                                    core_ctx,
4608                                },
4609                                DualStackListenerIpAddr::OtherStack(ip) => Operation::OtherStack {
4610                                    params: SetBoundDeviceParameters::Listener { ip, device },
4611                                    core_ctx: ds,
4612                                },
4613                                DualStackListenerIpAddr::BothStacks(identifier) => {
4614                                    Operation::ListenerBothStacks {
4615                                        identifier: *identifier,
4616                                        device,
4617                                        core_ctx: ds,
4618                                    }
4619                                }
4620                            },
4621                            BoundSocketStateType::Connected(state) => {
4622                                match ds.ds_converter().convert(state) {
4623                                    DualStackConnState::ThisStack(state) => Operation::ThisStack {
4624                                        params: SetBoundDeviceParameters::Connected(state),
4625                                        core_ctx,
4626                                    },
4627                                    DualStackConnState::OtherStack(state) => {
4628                                        Operation::OtherStack {
4629                                            params: SetBoundDeviceParameters::Connected(state),
4630                                            core_ctx: ds,
4631                                        }
4632                                    }
4633                                }
4634                            }
4635                        },
4636                        MaybeDualStack::NotDualStack(nds) => match socket_type {
4637                            BoundSocketStateType::Listener(ListenerState {
4638                                addr: ListenerAddr { ip, device },
4639                            }) => Operation::ThisStack {
4640                                params: SetBoundDeviceParameters::Listener {
4641                                    ip: nds.nds_converter().convert(ip),
4642                                    device,
4643                                },
4644                                core_ctx,
4645                            },
4646                            BoundSocketStateType::Connected(state) => Operation::ThisStack {
4647                                params: SetBoundDeviceParameters::Connected(
4648                                    nds.nds_converter().convert(state),
4649                                ),
4650                                core_ctx,
4651                            },
4652                        },
4653                    };
4654
4655                    // Apply the operation
4656                    match op {
4657                        Operation::ThisStack { params, core_ctx } => {
4658                            let socket_id = S::make_bound_socket_map_id(id);
4659                            DatagramBoundStateContext::<I, _, _>::with_bound_sockets_mut(
4660                                core_ctx,
4661                                |core_ctx, bound| {
4662                                    set_bound_device_single_stack(
4663                                        bindings_ctx,
4664                                        core_ctx,
4665                                        params,
4666                                        bound,
4667                                        &socket_id,
4668                                        &ip_options.common,
4669                                        new_device,
4670                                        sharing.clone(),
4671                                    )
4672                                },
4673                            )
4674                        }
4675                        Operation::OtherStack { params, core_ctx } => {
4676                            let socket_id = core_ctx.to_other_bound_socket_id(id);
4677                            core_ctx.with_other_bound_sockets_mut(|core_ctx, bound| {
4678                                set_bound_device_single_stack(
4679                                    bindings_ctx,
4680                                    core_ctx,
4681                                    params,
4682                                    bound,
4683                                    &socket_id,
4684                                    &ip_options.common,
4685                                    new_device,
4686                                    sharing.clone(),
4687                                )
4688                            })
4689                        }
4690                        Operation::ListenerBothStacks { identifier, device, core_ctx } => {
4691                            let socket_id = PairedBoundSocketIds::<_, _, S> {
4692                                this: S::make_bound_socket_map_id(id),
4693                                other: core_ctx.to_other_bound_socket_id(id),
4694                            };
4695                            core_ctx.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
4696                                set_bound_device_listener_both_stacks(
4697                                    device,
4698                                    identifier,
4699                                    PairedSocketMapMut { bound, other_bound },
4700                                    socket_id,
4701                                    new_device.map(|d| d.downgrade()),
4702                                    sharing.clone(),
4703                                )
4704                            })
4705                        }
4706                    }
4707                }
4708            }
4709        })
4710    }
4711
4712    /// Sets the specified socket's membership status for the given group.
4713    ///
4714    /// An error is returned if the membership change request is invalid
4715    /// (e.g. leaving a group that was not joined, or joining a group multiple
4716    /// times) or if the device to use to join is unspecified or conflicts with
4717    /// the existing socket state.
4718    pub fn set_multicast_membership(
4719        &mut self,
4720        id: &DatagramApiSocketId<I, C, S>,
4721        multicast_group: MulticastAddr<I::Addr>,
4722        interface: MulticastMembershipInterfaceSelector<I::Addr, DatagramApiDeviceId<C>>,
4723        want_membership: bool,
4724    ) -> Result<(), SetMulticastMembershipError> {
4725        let (core_ctx, bindings_ctx) = self.contexts();
4726        core_ctx.with_socket_state_mut(id, |core_ctx, state| {
4727            let ip_options = state.options();
4728            let bound_device = state.get_device(core_ctx);
4729
4730            let interface = match interface {
4731                MulticastMembershipInterfaceSelector::Specified(selector) => match selector {
4732                    MulticastInterfaceSelector::Interface(device) => {
4733                        if bound_device.as_ref().is_some_and(|d| d != &device) {
4734                            return Err(SetMulticastMembershipError::WrongDevice);
4735                        } else {
4736                            EitherDeviceId::Strong(device)
4737                        }
4738                    }
4739                    MulticastInterfaceSelector::LocalAddress(addr) => {
4740                        EitherDeviceId::Strong(pick_interface_for_addr(
4741                            core_ctx,
4742                            multicast_group,
4743                            Some(addr),
4744                            &ip_options.common.marks,
4745                        )?)
4746                    }
4747                },
4748                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
4749                    if let Some(bound_device) = bound_device.as_ref() {
4750                        EitherDeviceId::Weak(bound_device.clone())
4751                    } else {
4752                        EitherDeviceId::Strong(pick_interface_for_addr(
4753                            core_ctx,
4754                            multicast_group,
4755                            None,
4756                            &ip_options.common.marks,
4757                        )?)
4758                    }
4759                }
4760            };
4761
4762            let ip_options = state.options_mut();
4763
4764            let Some(strong_interface) = interface.as_strong() else {
4765                return Err(SetMulticastMembershipError::DeviceDoesNotExist);
4766            };
4767
4768            let change = ip_options
4769                .multicast_memberships
4770                .apply_membership_change(multicast_group, &interface.as_weak(), want_membership)
4771                .ok_or(if want_membership {
4772                    SetMulticastMembershipError::GroupAlreadyJoined
4773                } else {
4774                    SetMulticastMembershipError::GroupNotJoined
4775                })?;
4776
4777            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
4778                match change {
4779                    MulticastMembershipChange::Join => {
4780                        MulticastMembershipHandler::<I, _>::join_multicast_group(
4781                            core_ctx,
4782                            bindings_ctx,
4783                            &strong_interface,
4784                            multicast_group,
4785                        )
4786                    }
4787                    MulticastMembershipChange::Leave => {
4788                        MulticastMembershipHandler::<I, _>::leave_multicast_group(
4789                            core_ctx,
4790                            bindings_ctx,
4791                            &strong_interface,
4792                            multicast_group,
4793                        )
4794                    }
4795                }
4796            });
4797
4798            Ok(())
4799        })
4800    }
4801
4802    /// Updates the socket's IP hop limits.
4803    pub fn update_ip_hop_limit(
4804        &mut self,
4805        id: &DatagramApiSocketId<I, C, S>,
4806        update: impl FnOnce(&mut SocketHopLimits<I>),
4807    ) {
4808        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4809            let options = state.options_mut();
4810
4811            update(&mut options.socket_options.hop_limits)
4812        })
4813    }
4814
4815    /// Returns the socket's IP hop limits.
4816    pub fn get_ip_hop_limits(&mut self, id: &DatagramApiSocketId<I, C, S>) -> HopLimits {
4817        self.core_ctx().with_socket_state(id, |core_ctx, state| {
4818            let options = state.options();
4819            let device = state.get_device(core_ctx);
4820            let device = device.as_ref().and_then(|d| d.upgrade());
4821            DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
4822                options.socket_options.hop_limits.get_limits_with_defaults(
4823                    &BaseTransportIpContext::<I, _>::get_default_hop_limits(
4824                        core_ctx,
4825                        device.as_ref(),
4826                    ),
4827                )
4828            })
4829        })
4830    }
4831
4832    /// Calls the callback with mutable access to [`S::OtherStackIpOptions<I,
4833    /// D>`].
4834    ///
4835    /// If the socket is bound, the callback is not called, and instead an
4836    /// `ExpectedUnboundError` is returned.
4837    pub fn with_other_stack_ip_options_mut_if_unbound<R>(
4838        &mut self,
4839        id: &DatagramApiSocketId<I, C, S>,
4840        cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4841    ) -> Result<R, ExpectedUnboundError> {
4842        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4843            let is_unbound = match &state.inner {
4844                SocketStateInner::Unbound(_) => true,
4845                SocketStateInner::Bound(_) => false,
4846            };
4847            if is_unbound {
4848                let options = state.options_mut();
4849                Ok(cb(&mut options.other_stack))
4850            } else {
4851                Err(ExpectedUnboundError)
4852            }
4853        })
4854    }
4855
4856    /// Calls the callback with mutable access to [`S::OtherStackIpOptions<I,
4857    /// D>`].
4858    pub fn with_other_stack_ip_options_mut<R>(
4859        &mut self,
4860        id: &DatagramApiSocketId<I, C, S>,
4861        cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4862    ) -> R {
4863        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4864            let options = state.options_mut();
4865            cb(&mut options.other_stack)
4866        })
4867    }
4868
4869    /// Calls the callback with access to [`S::OtherStackIpOptions<I, D>`].
4870    pub fn with_other_stack_ip_options<R>(
4871        &mut self,
4872        id: &DatagramApiSocketId<I, C, S>,
4873        cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
4874    ) -> R {
4875        self.core_ctx().with_socket_state(id, |_core_ctx, state| cb(&state.options().other_stack))
4876    }
4877
4878    /// Calls the callback with access to [`S::OtherStackIpOptions<I, D>`], and the
4879    /// default [`HopLimits`] for `I::OtherVersion`.
4880    ///
4881    /// If dualstack operations are not supported, the callback is not called, and
4882    /// instead `NotDualStackCapableError` is returned.
4883    pub fn with_other_stack_ip_options_and_default_hop_limits<R>(
4884        &mut self,
4885        id: &DatagramApiSocketId<I, C, S>,
4886        cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>, HopLimits) -> R,
4887    ) -> Result<R, NotDualStackCapableError> {
4888        self.core_ctx().with_socket_state(id, |core_ctx, state| {
4889            let options = state.options();
4890            let device = state.get_device(core_ctx).as_ref().and_then(|d| d.upgrade());
4891            match DatagramBoundStateContext::<I, _, _>::dual_stack_context_mut(core_ctx) {
4892                MaybeDualStack::NotDualStack(_) => Err(NotDualStackCapableError),
4893                MaybeDualStack::DualStack(ds) => {
4894                    let default_hop_limits =
4895                        DualStackDatagramBoundStateContext::<I, _, _>::with_transport_context(
4896                            ds,
4897                            |sync_ctx| {
4898                                BaseTransportIpContext::<I, _>::get_default_hop_limits(
4899                                    sync_ctx,
4900                                    device.as_ref(),
4901                                )
4902                            },
4903                        );
4904                    Ok(cb(&options.other_stack, default_hop_limits))
4905                }
4906            }
4907        })
4908    }
4909
4910    /// Calls the callback with mutable access to
4911    /// [`DatagramIpSpecificSocketOptions<I,D>`] and
4912    /// [`S::OtherStackIpOptions<I, D>`].
4913    pub fn with_both_stacks_ip_options_mut<R>(
4914        &mut self,
4915        id: &DatagramApiSocketId<I, C, S>,
4916        cb: impl FnOnce(
4917            &mut DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
4918            &mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
4919        ) -> R,
4920    ) -> R {
4921        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4922            let options = state.options_mut();
4923            cb(&mut options.socket_options, &mut options.other_stack)
4924        })
4925    }
4926
4927    /// Calls the callback with access to [`DatagramIpSpecificSocketOptions<I,
4928    /// D>`] and [`S::OtherStackIpOptions<I, D>`].
4929    pub fn with_both_stacks_ip_options<R>(
4930        &mut self,
4931        id: &DatagramApiSocketId<I, C, S>,
4932        cb: impl FnOnce(
4933            &DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
4934            &S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
4935        ) -> R,
4936    ) -> R {
4937        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
4938            let options = state.options();
4939            cb(&options.socket_options, &options.other_stack)
4940        })
4941    }
4942
4943    /// Updates the socket's sharing state to the result of `f`.
4944    ///
4945    /// `f` is given mutable access to the sharing state and is called under the
4946    /// socket lock, allowing for atomic updates to the sharing state.
4947    pub fn update_sharing(
4948        &mut self,
4949        id: &DatagramApiSocketId<I, C, S>,
4950        f: impl FnOnce(&mut S::SharingState),
4951    ) -> Result<(), ExpectedUnboundError> {
4952        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4953            match &mut state.inner {
4954                SocketStateInner::Unbound(_) => (),
4955                SocketStateInner::Bound(_) => return Err(ExpectedUnboundError),
4956            };
4957
4958            f(&mut state.sharing);
4959            Ok(())
4960        })
4961    }
4962
4963    /// Returns the socket's sharing state.
4964    pub fn get_sharing(&mut self, id: &DatagramApiSocketId<I, C, S>) -> S::SharingState {
4965        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.sharing.clone())
4966    }
4967
4968    /// Sets the IP transparent option.
4969    pub fn set_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
4970        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4971            state.options_mut().common.transparent = value;
4972        })
4973    }
4974
4975    /// Returns the IP transparent option.
4976    pub fn get_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
4977        self.core_ctx().with_socket_state(id, |_core_ctx, state| state.options().common.transparent)
4978    }
4979
4980    /// Sets the socket mark at `domain`.
4981    pub fn set_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain, mark: Mark) {
4982        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
4983            *state.options_mut().common.marks.get_mut(domain) = mark;
4984        })
4985    }
4986
4987    /// Returns the socket mark at `domain`.
4988    pub fn get_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain) -> Mark {
4989        self.core_ctx()
4990            .with_socket_state(id, |_core_ctx, state| *state.options().common.marks.get(domain))
4991    }
4992
4993    /// Sets the broadcast option.
4994    pub fn set_broadcast(
4995        &mut self,
4996        id: &DatagramApiSocketId<I, C, S>,
4997        value: Option<I::BroadcastMarker>,
4998    ) {
4999        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
5000            state.options_mut().socket_options.allow_broadcast = value;
5001        })
5002    }
5003
5004    /// Returns the broadcast option.
5005    pub fn get_broadcast(
5006        &mut self,
5007        id: &DatagramApiSocketId<I, C, S>,
5008    ) -> Option<I::BroadcastMarker> {
5009        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
5010            state.options().socket_options.allow_broadcast
5011        })
5012    }
5013
5014    /// Sets the multicast interface for outgoing multicast packets.
5015    pub fn set_multicast_interface(
5016        &mut self,
5017        id: &DatagramApiSocketId<I, C, S>,
5018        value: Option<&DatagramApiDeviceId<C>>,
5019    ) {
5020        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
5021            state.options_mut().socket_options.multicast_interface = value.map(|v| v.downgrade());
5022        })
5023    }
5024
5025    /// Returns the configured multicast interface.
5026    pub fn get_multicast_interface(
5027        &mut self,
5028        id: &DatagramApiSocketId<I, C, S>,
5029    ) -> Option<DatagramApiWeakDeviceId<C>> {
5030        self.core_ctx().with_socket_state(id, |_core_ctx, state| {
5031            state.options().socket_options.multicast_interface.clone()
5032        })
5033    }
5034
5035    /// Sets the multicast loopback flag.
5036    pub fn set_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
5037        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
5038            state.options_mut().socket_options.multicast_loop = value;
5039        })
5040    }
5041
5042    /// Returns the multicast loopback flag.
5043    pub fn get_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
5044        self.core_ctx()
5045            .with_socket_state(id, |_core_ctx, state| state.options().socket_options.multicast_loop)
5046    }
5047
5048    /// Sets the Traffic Class option.
5049    pub fn set_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>, value: DscpAndEcn) {
5050        self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
5051            state.options_mut().socket_options.dscp_and_ecn = value;
5052        })
5053    }
5054
5055    /// Returns the Traffic Class option.
5056    pub fn get_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>) -> DscpAndEcn {
5057        self.core_ctx()
5058            .with_socket_state(id, |_core_ctx, state| state.options().socket_options.dscp_and_ecn)
5059    }
5060}
5061
5062#[cfg(any(test, feature = "testutils"))]
5063pub(crate) mod testutil {
5064    use super::*;
5065
5066    use alloc::vec;
5067    use net_types::Witness;
5068    use net_types::ip::IpAddr;
5069    use netstack3_base::CtxPair;
5070    use netstack3_base::testutil::{FakeStrongDeviceId, TestIpExt};
5071    use netstack3_ip::socket::testutil::FakeDeviceConfig;
5072
5073    /// Helper function to ensure the Fake CoreCtx and BindingsCtx are setup
5074    /// with [`FakeDeviceConfig`] (one per provided device), with remote/local
5075    /// IPs that support a connection to the given remote_ip.
5076    pub fn setup_fake_ctx_with_dualstack_conn_addrs<CC, BC: Default, D: FakeStrongDeviceId>(
5077        local_ip: IpAddr,
5078        remote_ip: SpecifiedAddr<IpAddr>,
5079        devices: impl IntoIterator<Item = D>,
5080        core_ctx_builder: impl FnOnce(Vec<FakeDeviceConfig<D, SpecifiedAddr<IpAddr>>>) -> CC,
5081    ) -> CtxPair<CC, BC> {
5082        // A conversion helper to unmap ipv4-mapped-ipv6 addresses.
5083        fn unmap_ip(addr: IpAddr) -> IpAddr {
5084            match addr {
5085                IpAddr::V4(v4) => IpAddr::V4(v4),
5086                IpAddr::V6(v6) => match v6.to_ipv4_mapped() {
5087                    Some(v4) => IpAddr::V4(v4),
5088                    None => IpAddr::V6(v6),
5089                },
5090            }
5091        }
5092
5093        // Convert the local/remote IPs into `IpAddr` in their non-mapped form.
5094        let local_ip = unmap_ip(local_ip);
5095        let remote_ip = unmap_ip(remote_ip.get());
5096        // If the given local_ip is unspecified, use the default from
5097        // `TEST_ADDRS`. This ensures we always instantiate the
5098        // FakeDeviceConfig below with at least one local_ip, which is
5099        // required for connect operations to succeed.
5100        let local_ip = SpecifiedAddr::new(local_ip).unwrap_or_else(|| match remote_ip {
5101            IpAddr::V4(_) => Ipv4::TEST_ADDRS.local_ip.into(),
5102            IpAddr::V6(_) => Ipv6::TEST_ADDRS.local_ip.into(),
5103        });
5104        // If the given remote_ip is unspecified, we won't be able to
5105        // connect; abort the test.
5106        let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote-ip should be specified");
5107        CtxPair::with_core_ctx(core_ctx_builder(
5108            devices
5109                .into_iter()
5110                .map(|device| FakeDeviceConfig {
5111                    device,
5112                    local_ips: vec![local_ip],
5113                    remote_ips: vec![remote_ip],
5114                })
5115                .collect(),
5116        ))
5117    }
5118}
5119
5120#[cfg(test)]
5121mod test {
5122
5123    use alloc::vec;
5124    use assert_matches::assert_matches;
5125    use derivative::Derivative;
5126    use ip_test_macro::ip_test;
5127    use net_declare::{net_ip_v4, net_ip_v6};
5128    use net_types::Witness;
5129    use net_types::ip::{IpVersionMarker, Ipv4Addr, Ipv6Addr};
5130    use netstack3_base::socket::{
5131        AddrVec, Bound, IncompatibleError, ListenerAddrInfo, RemoveResult, SocketMapAddrStateSpec,
5132    };
5133    use netstack3_base::socketmap::SocketMap;
5134    use netstack3_base::testutil::{
5135        FakeDeviceId, FakeReferencyDeviceId, FakeSendToken, FakeStrongDeviceId, FakeWeakDeviceId,
5136        MultipleDevicesId, TestIpExt,
5137    };
5138    use netstack3_base::{ContextProvider, CtxPair, UninstantiableWrapper};
5139    use netstack3_ip::DEFAULT_HOP_LIMITS;
5140    use netstack3_ip::device::IpDeviceStateIpExt;
5141    use netstack3_ip::socket::testutil::{
5142        FakeDeviceConfig, FakeDualStackIpSocketCtx, FakeIpSocketCtx,
5143    };
5144    use netstack3_ip::testutil::DualStackSendIpPacketMeta;
5145    use packet::{Buf, NestableSerializer as _};
5146    use packet_formats::ip::{Ipv4Proto, Ipv6Proto};
5147    use test_case::test_case;
5148
5149    use super::*;
5150    use crate::internal::spec_context;
5151
5152    trait DatagramIpExt<D: FakeStrongDeviceId>:
5153        IpExt + IpDeviceStateIpExt + TestIpExt + DualStackIpExt + DualStackContextsIpExt<D>
5154    {
5155    }
5156    impl<
5157        D: FakeStrongDeviceId,
5158        I: Ip + IpExt + IpDeviceStateIpExt + TestIpExt + DualStackIpExt + DualStackContextsIpExt<D>,
5159    > DatagramIpExt<D> for I
5160    {
5161    }
5162
5163    #[derive(Debug)]
5164    enum FakeAddrSpec {}
5165
5166    impl SocketMapAddrSpec for FakeAddrSpec {
5167        type LocalIdentifier = NonZeroU16;
5168        type RemoteIdentifier = u16;
5169    }
5170
5171    #[derive(Debug)]
5172    enum FakeStateSpec {}
5173
5174    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
5175    struct Tag;
5176
5177    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
5178
5179    enum Sharing {
5180        #[default]
5181        NoConflicts,
5182        // Any attempt to insert a connection with the following remote port
5183        // will conflict.
5184        ConnectionConflicts {
5185            remote_port: u16,
5186        },
5187    }
5188
5189    #[derive(Clone, Debug, Derivative)]
5190    #[derivative(Eq(bound = ""), PartialEq(bound = ""))]
5191    struct Id<I: IpExt, D: WeakDeviceIdentifier>(StrongRc<I, D, FakeStateSpec>);
5192
5193    /// Utilities for accessing locked internal state in tests.
5194    impl<I: IpExt, D: WeakDeviceIdentifier> Id<I, D> {
5195        fn get(&self) -> impl Deref<Target = SocketState<I, D, FakeStateSpec>> + '_ {
5196            let Self(rc) = self;
5197            rc.state.read()
5198        }
5199
5200        fn get_mut(&self) -> impl DerefMut<Target = SocketState<I, D, FakeStateSpec>> + '_ {
5201            let Self(rc) = self;
5202            rc.state.write()
5203        }
5204    }
5205
5206    impl<I: IpExt, D: WeakDeviceIdentifier> From<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
5207        fn from(value: StrongRc<I, D, FakeStateSpec>) -> Self {
5208            Self(value)
5209        }
5210    }
5211
5212    impl<I: IpExt, D: WeakDeviceIdentifier> Borrow<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
5213        fn borrow(&self) -> &StrongRc<I, D, FakeStateSpec> {
5214            let Self(rc) = self;
5215            rc
5216        }
5217    }
5218
5219    #[derive(Debug)]
5220    struct AddrState<T>(T);
5221
5222    struct FakeSocketMapStateSpec<I, D>(PhantomData<(I, D)>, !);
5223
5224    impl<I: IpExt, D: WeakDeviceIdentifier> SocketMapStateSpec for FakeSocketMapStateSpec<I, D> {
5225        type AddrVecTag = Tag;
5226        type ConnAddrState = AddrState<Self::ConnId>;
5227        type ConnId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5228        type ConnSharingState = Sharing;
5229        type ListenerAddrState = AddrState<Self::ListenerId>;
5230        type ListenerId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5231        type ListenerSharingState = Sharing;
5232        fn listener_tag(_: ListenerAddrInfo, _state: &Self::ListenerAddrState) -> Self::AddrVecTag {
5233            Tag
5234        }
5235        fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
5236            Tag
5237        }
5238    }
5239
5240    const FAKE_DATAGRAM_IPV4_PROTOCOL: Ipv4Proto = Ipv4Proto::Other(253);
5241    const FAKE_DATAGRAM_IPV6_PROTOCOL: Ipv6Proto = Ipv6Proto::Other(254);
5242
5243    impl DatagramSocketSpec for FakeStateSpec {
5244        const NAME: &'static str = "FAKE";
5245        type AddrSpec = FakeAddrSpec;
5246        type SocketId<I: IpExt, D: WeakDeviceIdentifier> = Id<I, D>;
5247        // NB: We don't have use for real weak IDs here since we only need to be
5248        // able to make it upgrade.
5249        type WeakSocketId<I: IpExt, D: WeakDeviceIdentifier> = Id<I, D>;
5250        type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier> =
5251            DatagramIpSpecificSocketOptions<I::OtherVersion, D>;
5252        type SocketMapSpec<I: IpExt, D: WeakDeviceIdentifier> = FakeSocketMapStateSpec<I, D>;
5253        type SharingState = Sharing;
5254        type ListenerIpAddr<I: IpExt> =
5255            I::DualStackListenerIpAddr<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
5256        type ConnIpAddr<I: IpExt> = I::DualStackConnIpAddr<Self>;
5257        type ConnStateExtra = ();
5258        type ConnState<I: IpExt, D: WeakDeviceIdentifier> = I::DualStackConnState<D, Self>;
5259        type Counters<I: Ip> = ();
5260        type ExternalData<I: Ip> = ();
5261        type SendToken = FakeSendToken;
5262
5263        fn ip_proto<I: IpProtoExt>() -> I::Proto {
5264            I::map_ip((), |()| FAKE_DATAGRAM_IPV4_PROTOCOL, |()| FAKE_DATAGRAM_IPV6_PROTOCOL)
5265        }
5266
5267        fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
5268            s: &Self::SocketId<I, D>,
5269        ) -> <Self::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, Self::AddrSpec>>::BoundSocketId
5270        {
5271            I::into_dual_stack_bound_socket_id(s.clone())
5272        }
5273
5274        type Serializer<I: IpExt, B: BufferMut> = packet::Nested<B, ()>;
5275        type SerializeError = !;
5276        fn make_packet<I: IpExt, B: BufferMut>(
5277            body: B,
5278            _addr: &ConnIpAddr<
5279                I::Addr,
5280                <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5281                <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5282            >,
5283        ) -> Result<Self::Serializer<I, B>, !> {
5284            Ok(body.wrap_in(()))
5285        }
5286        fn try_alloc_listen_identifier<I: Ip, D: WeakDeviceIdentifier>(
5287            _bindings_ctx: &mut impl RngContext,
5288            is_available: impl Fn(
5289                <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5290            ) -> Result<(), InUseError>,
5291        ) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
5292            (1..=u16::MAX).map(|i| NonZeroU16::new(i).unwrap()).find(|i| is_available(*i).is_ok())
5293        }
5294
5295        fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
5296            state: &Self::ConnState<I, D>,
5297        ) -> ConnInfo<I::Addr, D> {
5298            let ConnAddr { ip, device } = I::conn_addr_from_state(state);
5299            let ConnInfoAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } =
5300                ip.into();
5301            ConnInfo::new(local_ip, local_port, remote_ip, remote_port, || {
5302                device.clone().expect("device must be bound for addresses that require zones")
5303            })
5304        }
5305
5306        fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
5307            bound: &BoundSocketMap<I, D, FakeAddrSpec, FakeSocketMapStateSpec<I, D>>,
5308            _bindings_ctx: &mut BC,
5309            _flow: DatagramFlowId<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
5310        ) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
5311            (1..u16::MAX).find_map(|identifier| {
5312                let identifier = NonZeroU16::new(identifier).unwrap();
5313                bound
5314                    .listeners()
5315                    .could_insert(
5316                        &ListenerAddr {
5317                            device: None,
5318                            ip: ListenerIpAddr { addr: None, identifier },
5319                        },
5320                        &Default::default(),
5321                    )
5322                    .is_ok()
5323                    .then_some(identifier)
5324            })
5325        }
5326
5327        fn upgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
5328            id: &Self::WeakSocketId<I, D>,
5329        ) -> Option<Self::SocketId<I, D>> {
5330            Some(id.clone())
5331        }
5332
5333        fn downgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
5334            id: &Self::SocketId<I, D>,
5335        ) -> Self::WeakSocketId<I, D> {
5336            id.clone()
5337        }
5338    }
5339
5340    impl<I: IpExt, D: WeakDeviceIdentifier> DatagramSocketMapSpec<I, D, FakeAddrSpec>
5341        for FakeSocketMapStateSpec<I, D>
5342    {
5343        type BoundSocketId = I::DualStackBoundSocketId<D, FakeStateSpec>;
5344    }
5345
5346    impl<I: IpExt, D: WeakDeviceIdentifier>
5347        SocketMapConflictPolicy<
5348            ConnAddr<
5349                ConnIpAddr<
5350                    I::Addr,
5351                    <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5352                    <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5353                >,
5354                D,
5355            >,
5356            Sharing,
5357            I,
5358            D,
5359            FakeAddrSpec,
5360        > for FakeSocketMapStateSpec<I, D>
5361    {
5362        fn check_insert_conflicts(
5363            sharing: &Sharing,
5364            addr: &ConnAddr<
5365                ConnIpAddr<
5366                    I::Addr,
5367                    <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
5368                    <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
5369                >,
5370                D,
5371            >,
5372            _socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
5373        ) -> Result<(), InsertError> {
5374            let ConnAddr { ip: ConnIpAddr { local: _, remote: (_remote_ip, port) }, device: _ } =
5375                addr;
5376            match sharing {
5377                Sharing::NoConflicts => Ok(()),
5378                Sharing::ConnectionConflicts { remote_port } => {
5379                    if remote_port == port {
5380                        Err(InsertError::Exists)
5381                    } else {
5382                        Ok(())
5383                    }
5384                }
5385            }
5386        }
5387    }
5388
5389    impl<I: IpExt, D: WeakDeviceIdentifier>
5390        SocketMapConflictPolicy<
5391            ListenerAddr<
5392                ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
5393                D,
5394            >,
5395            Sharing,
5396            I,
5397            D,
5398            FakeAddrSpec,
5399        > for FakeSocketMapStateSpec<I, D>
5400    {
5401        fn check_insert_conflicts(
5402            sharing: &Sharing,
5403            _addr: &ListenerAddr<
5404                ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
5405                D,
5406            >,
5407            _socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
5408        ) -> Result<(), InsertError> {
5409            match sharing {
5410                Sharing::NoConflicts => Ok(()),
5411                // Since this implementation is strictly for ListenerAddr,
5412                // ignore connection conflicts.
5413                Sharing::ConnectionConflicts { remote_port: _ } => Ok(()),
5414            }
5415        }
5416    }
5417
5418    impl<T: Eq> SocketMapAddrStateSpec for AddrState<T> {
5419        type Id = T;
5420        type SharingState = Sharing;
5421        type Inserter<'a>
5422            = !
5423        where
5424            Self: 'a;
5425
5426        fn new(_sharing: &Self::SharingState, id: Self::Id) -> Self {
5427            AddrState(id)
5428        }
5429        fn contains_id(&self, id: &Self::Id) -> bool {
5430            let Self(inner) = self;
5431            inner == id
5432        }
5433        fn try_get_inserter<'a, 'b>(
5434            &'b mut self,
5435            _new_sharing_state: &'a Self::SharingState,
5436        ) -> Result<Self::Inserter<'b>, IncompatibleError> {
5437            Err(IncompatibleError)
5438        }
5439        fn could_insert(
5440            &self,
5441            _new_sharing_state: &Self::SharingState,
5442        ) -> Result<(), IncompatibleError> {
5443            Err(IncompatibleError)
5444        }
5445        fn remove_by_id(&mut self, _id: Self::Id) -> RemoveResult {
5446            RemoveResult::IsLast
5447        }
5448        fn sharing_state(&self) -> Self::SharingState {
5449            Sharing::NoConflicts
5450        }
5451    }
5452
5453    #[derive(Derivative, GenericOverIp)]
5454    #[derivative(Default(bound = ""))]
5455    #[generic_over_ip()]
5456    struct FakeBoundSockets<D: FakeStrongDeviceId> {
5457        v4: BoundDatagramSocketMap<Ipv4, FakeWeakDeviceId<D>, FakeStateSpec>,
5458        v6: BoundDatagramSocketMap<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5459    }
5460
5461    impl<D: FakeStrongDeviceId, I: IpExt>
5462        AsRef<
5463            BoundSocketMap<
5464                I,
5465                FakeWeakDeviceId<D>,
5466                FakeAddrSpec,
5467                FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
5468            >,
5469        > for FakeBoundSockets<D>
5470    {
5471        fn as_ref(
5472            &self,
5473        ) -> &BoundSocketMap<
5474            I,
5475            FakeWeakDeviceId<D>,
5476            FakeAddrSpec,
5477            FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
5478        > {
5479            #[derive(GenericOverIp)]
5480            #[generic_over_ip(I, Ip)]
5481            struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
5482                &'a BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5483            );
5484            let Wrap(state) = I::map_ip(self, |state| Wrap(&state.v4), |state| Wrap(&state.v6));
5485            state
5486        }
5487    }
5488
5489    impl<D: FakeStrongDeviceId, I: IpExt>
5490        AsMut<BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>>
5491        for FakeBoundSockets<D>
5492    {
5493        fn as_mut(&mut self) -> &mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec> {
5494            #[derive(GenericOverIp)]
5495            #[generic_over_ip(I, Ip)]
5496            struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
5497                &'a mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5498            );
5499            let Wrap(state) =
5500                I::map_ip(self, |state| Wrap(&mut state.v4), |state| Wrap(&mut state.v6));
5501            state
5502        }
5503    }
5504
5505    type FakeBindingsCtx = netstack3_base::testutil::FakeBindingsCtx<(), (), (), ()>;
5506    type FakeCtx<I, D> = CtxPair<FakeCoreCtx<I, D>, FakeBindingsCtx>;
5507
5508    type FakeSocketSet<I, D> = DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>;
5509
5510    type InnerIpSocketCtx<D> = netstack3_base::testutil::FakeCoreCtx<
5511        FakeDualStackIpSocketCtx<D>,
5512        DualStackSendIpPacketMeta<D>,
5513        D,
5514    >;
5515
5516    /// A trait providing a shortcut to instantiate a [`DatagramApi`] from a context.
5517    trait DatagramApiExt: ContextPair + Sized {
5518        fn datagram_api<I: Ip>(&mut self) -> DatagramApi<I, &mut Self, FakeStateSpec> {
5519            DatagramApi::new(self)
5520        }
5521    }
5522
5523    impl<O> DatagramApiExt for O where O: ContextPair + Sized {}
5524
5525    struct FakeDualStackCoreCtx<D: FakeStrongDeviceId> {
5526        bound_sockets: FakeBoundSockets<D>,
5527        ip_socket_ctx: InnerIpSocketCtx<D>,
5528    }
5529
5530    struct FakeCoreCtx<I: IpExt, D: FakeStrongDeviceId> {
5531        dual_stack: FakeDualStackCoreCtx<D>,
5532        // NB: socket set is last in the struct so all the strong refs are
5533        // dropped before the primary refs contained herein.
5534        socket_set: FakeSocketSet<I, D>,
5535    }
5536
5537    impl<I: IpExt, D: FakeStrongDeviceId> ContextProvider for FakeCoreCtx<I, D> {
5538        type Context = Self;
5539        fn context(&mut self) -> &mut Self::Context {
5540            self
5541        }
5542    }
5543
5544    impl<I: IpExt, D: FakeStrongDeviceId> FakeCoreCtx<I, D> {
5545        fn new() -> Self {
5546            Self::new_with_sockets(Default::default(), Default::default())
5547        }
5548
5549        fn new_with_sockets(
5550            socket_set: FakeSocketSet<I, D>,
5551            bound_sockets: FakeBoundSockets<D>,
5552        ) -> Self {
5553            Self {
5554                socket_set,
5555                dual_stack: FakeDualStackCoreCtx {
5556                    bound_sockets,
5557                    ip_socket_ctx: Default::default(),
5558                },
5559            }
5560        }
5561
5562        fn new_with_ip_socket_ctx(ip_socket_ctx: FakeDualStackIpSocketCtx<D>) -> Self {
5563            Self {
5564                socket_set: Default::default(),
5565                dual_stack: FakeDualStackCoreCtx {
5566                    bound_sockets: Default::default(),
5567                    ip_socket_ctx: InnerIpSocketCtx::with_state(ip_socket_ctx),
5568                },
5569            }
5570        }
5571    }
5572
5573    impl<I: IpExt, D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeCoreCtx<I, D> {
5574        type DeviceId = D;
5575        type WeakDeviceId = FakeWeakDeviceId<D>;
5576    }
5577
5578    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeDualStackCoreCtx<D> {
5579        type DeviceId = D;
5580        type WeakDeviceId = FakeWeakDeviceId<D>;
5581    }
5582
5583    impl<D: FakeStrongDeviceId, I: DatagramIpExt<D>>
5584        spec_context::DatagramSpecStateContext<I, FakeCoreCtx<I, D>, FakeBindingsCtx>
5585        for FakeStateSpec
5586    {
5587        type SocketsStateCtx<'a> = FakeDualStackCoreCtx<D>;
5588
5589        fn with_all_sockets_mut<
5590            O,
5591            F: FnOnce(&mut DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
5592        >(
5593            core_ctx: &mut FakeCoreCtx<I, D>,
5594            cb: F,
5595        ) -> O {
5596            cb(&mut core_ctx.socket_set)
5597        }
5598
5599        fn with_all_sockets<
5600            O,
5601            F: FnOnce(&DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
5602        >(
5603            core_ctx: &mut FakeCoreCtx<I, D>,
5604            cb: F,
5605        ) -> O {
5606            cb(&core_ctx.socket_set)
5607        }
5608
5609        fn with_socket_state<
5610            O,
5611            F: FnOnce(
5612                &mut Self::SocketsStateCtx<'_>,
5613                &SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5614            ) -> O,
5615        >(
5616            core_ctx: &mut FakeCoreCtx<I, D>,
5617            id: &Id<I, FakeWeakDeviceId<D>>,
5618            cb: F,
5619        ) -> O {
5620            cb(&mut core_ctx.dual_stack, &id.get())
5621        }
5622
5623        fn with_socket_state_mut<
5624            O,
5625            F: FnOnce(
5626                &mut Self::SocketsStateCtx<'_>,
5627                &mut SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5628            ) -> O,
5629        >(
5630            core_ctx: &mut FakeCoreCtx<I, D>,
5631            id: &Id<I, FakeWeakDeviceId<D>>,
5632            cb: F,
5633        ) -> O {
5634            cb(&mut core_ctx.dual_stack, &mut id.get_mut())
5635        }
5636
5637        fn for_each_socket<
5638            F: FnMut(
5639                &mut Self::SocketsStateCtx<'_>,
5640                &Id<I, FakeWeakDeviceId<D>>,
5641                &SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5642            ),
5643        >(
5644            core_ctx: &mut FakeCoreCtx<I, D>,
5645            mut cb: F,
5646        ) {
5647            core_ctx.socket_set.keys().for_each(|id| {
5648                let id = Id::from(id.clone());
5649                cb(&mut core_ctx.dual_stack, &id, &id.get());
5650            })
5651        }
5652    }
5653
5654    /// A test-only IpExt trait to specialize the `DualStackContext` and
5655    /// `NonDualStackContext` associated types on the
5656    /// `DatagramBoundStateContext`.
5657    ///
5658    /// This allows us to implement `DatagramBoundStateContext` for all `I`
5659    /// while also assigning its associated types different values for `Ipv4`
5660    /// and `Ipv6`.
5661    trait DualStackContextsIpExt<D: FakeStrongDeviceId>: IpExt {
5662        type DualStackContext: DualStackDatagramBoundStateContext<
5663                Self,
5664                FakeBindingsCtx,
5665                FakeStateSpec,
5666                DeviceId = D,
5667                WeakDeviceId = FakeWeakDeviceId<D>,
5668            >;
5669        type NonDualStackContext: NonDualStackDatagramBoundStateContext<
5670                Self,
5671                FakeBindingsCtx,
5672                FakeStateSpec,
5673                DeviceId = D,
5674                WeakDeviceId = FakeWeakDeviceId<D>,
5675            >;
5676
5677        fn dual_stack_context(
5678            core_ctx: &FakeDualStackCoreCtx<D>,
5679        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext>;
5680
5681        fn dual_stack_context_mut(
5682            core_ctx: &mut FakeDualStackCoreCtx<D>,
5683        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
5684    }
5685
5686    impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv4 {
5687        type DualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
5688        type NonDualStackContext = FakeDualStackCoreCtx<D>;
5689
5690        fn dual_stack_context(
5691            core_ctx: &FakeDualStackCoreCtx<D>,
5692        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5693            MaybeDualStack::NotDualStack(core_ctx)
5694        }
5695
5696        fn dual_stack_context_mut(
5697            core_ctx: &mut FakeDualStackCoreCtx<D>,
5698        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5699            MaybeDualStack::NotDualStack(core_ctx)
5700        }
5701    }
5702
5703    impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv6 {
5704        type DualStackContext = FakeDualStackCoreCtx<D>;
5705        type NonDualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
5706
5707        fn dual_stack_context(
5708            core_ctx: &FakeDualStackCoreCtx<D>,
5709        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5710            MaybeDualStack::DualStack(core_ctx)
5711        }
5712
5713        fn dual_stack_context_mut(
5714            core_ctx: &mut FakeDualStackCoreCtx<D>,
5715        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5716            MaybeDualStack::DualStack(core_ctx)
5717        }
5718    }
5719
5720    impl<D: FakeStrongDeviceId, I: DualStackContextsIpExt<D>>
5721        spec_context::DatagramSpecBoundStateContext<I, FakeDualStackCoreCtx<D>, FakeBindingsCtx>
5722        for FakeStateSpec
5723    {
5724        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
5725        type DualStackContext = I::DualStackContext;
5726        type NonDualStackContext = I::NonDualStackContext;
5727
5728        fn with_bound_sockets<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5729        where
5730            F: FnOnce(
5731                &mut Self::IpSocketsCtx<'_>,
5732                &BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5733            ) -> O,
5734        {
5735            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5736            cb(ip_socket_ctx, bound_sockets.as_ref())
5737        }
5738        fn with_bound_sockets_mut<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5739        where
5740            F: FnOnce(
5741                &mut Self::IpSocketsCtx<'_>,
5742                &mut BoundDatagramSocketMap<I, FakeWeakDeviceId<D>, FakeStateSpec>,
5743            ) -> O,
5744        {
5745            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5746            cb(ip_socket_ctx, bound_sockets.as_mut())
5747        }
5748
5749        fn with_transport_context<O, F>(core_ctx: &mut FakeDualStackCoreCtx<D>, cb: F) -> O
5750        where
5751            F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O,
5752        {
5753            cb(&mut core_ctx.ip_socket_ctx)
5754        }
5755
5756        fn dual_stack_context(
5757            core_ctx: &FakeDualStackCoreCtx<D>,
5758        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
5759            I::dual_stack_context(core_ctx)
5760        }
5761
5762        fn dual_stack_context_mut(
5763            core_ctx: &mut FakeDualStackCoreCtx<D>,
5764        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
5765            I::dual_stack_context_mut(core_ctx)
5766        }
5767    }
5768
5769    impl<D: FakeStrongDeviceId>
5770        spec_context::NonDualStackDatagramSpecBoundStateContext<
5771            Ipv4,
5772            FakeDualStackCoreCtx<D>,
5773            FakeBindingsCtx,
5774        > for FakeStateSpec
5775    {
5776        fn nds_converter(
5777            _core_ctx: &FakeDualStackCoreCtx<D>,
5778        ) -> impl NonDualStackConverter<Ipv4, FakeWeakDeviceId<D>, Self> {
5779            ()
5780        }
5781    }
5782
5783    impl<D: FakeStrongDeviceId>
5784        spec_context::DualStackDatagramSpecBoundStateContext<
5785            Ipv6,
5786            FakeDualStackCoreCtx<D>,
5787            FakeBindingsCtx,
5788        > for FakeStateSpec
5789    {
5790        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
5791        fn dual_stack_enabled(
5792            _core_ctx: &FakeDualStackCoreCtx<D>,
5793            _ip_options: &IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5794        ) -> bool {
5795            // For now, it's simplest to have dual-stack unconditionally enabled
5796            // for datagram tests. However, in the future this could be stateful
5797            // and follow an implementation similar to UDP's test fixture.
5798            true
5799        }
5800
5801        fn to_other_socket_options<'a>(
5802            _core_ctx: &FakeDualStackCoreCtx<D>,
5803            state: &'a IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
5804        ) -> &'a DatagramIpSpecificSocketOptions<Ipv4, FakeWeakDeviceId<D>> {
5805            let IpOptions { other_stack, .. } = state;
5806            other_stack
5807        }
5808
5809        fn ds_converter(
5810            _core_ctx: &FakeDualStackCoreCtx<D>,
5811        ) -> impl DualStackConverter<Ipv6, FakeWeakDeviceId<D>, Self> {
5812            ()
5813        }
5814
5815        fn to_other_bound_socket_id(
5816            _core_ctx: &FakeDualStackCoreCtx<D>,
5817            id: &Id<Ipv6, D::Weak>,
5818        ) -> EitherIpSocket<D::Weak, FakeStateSpec> {
5819            EitherIpSocket::V6(id.clone())
5820        }
5821
5822        fn with_both_bound_sockets_mut<
5823            O,
5824            F: FnOnce(
5825                &mut Self::IpSocketsCtx<'_>,
5826                &mut BoundSocketsFromSpec<Ipv6, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5827                &mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5828            ) -> O,
5829        >(
5830            core_ctx: &mut FakeDualStackCoreCtx<D>,
5831            cb: F,
5832        ) -> O {
5833            let FakeDualStackCoreCtx { bound_sockets: FakeBoundSockets { v4, v6 }, ip_socket_ctx } =
5834                core_ctx;
5835            cb(ip_socket_ctx, v6, v4)
5836        }
5837
5838        fn with_other_bound_sockets_mut<
5839            O,
5840            F: FnOnce(
5841                &mut Self::IpSocketsCtx<'_>,
5842                &mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
5843            ) -> O,
5844        >(
5845            core_ctx: &mut FakeDualStackCoreCtx<D>,
5846            cb: F,
5847        ) -> O {
5848            let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
5849            cb(ip_socket_ctx, bound_sockets.as_mut())
5850        }
5851
5852        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
5853            core_ctx: &mut FakeDualStackCoreCtx<D>,
5854            cb: F,
5855        ) -> O {
5856            cb(&mut core_ctx.ip_socket_ctx)
5857        }
5858    }
5859
5860    #[ip_test(I)]
5861    fn set_get_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
5862        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5863        let mut api = ctx.datagram_api::<I>();
5864
5865        let unbound = api.create_default();
5866        const EXPECTED_HOP_LIMITS: HopLimits = HopLimits {
5867            unicast: NonZeroU8::new(45).unwrap(),
5868            multicast: NonZeroU8::new(23).unwrap(),
5869        };
5870
5871        api.update_ip_hop_limit(&unbound, |limits| {
5872            *limits = SocketHopLimits {
5873                unicast: Some(EXPECTED_HOP_LIMITS.unicast),
5874                multicast: Some(EXPECTED_HOP_LIMITS.multicast),
5875                version: IpVersionMarker::default(),
5876            }
5877        });
5878
5879        assert_eq!(api.get_ip_hop_limits(&unbound), EXPECTED_HOP_LIMITS);
5880    }
5881
5882    #[ip_test(I)]
5883    fn set_get_device_hop_limits<I: DatagramIpExt<FakeReferencyDeviceId>>() {
5884        let device = FakeReferencyDeviceId::default();
5885        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
5886            FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
5887                device: device.clone(),
5888                local_ips: Default::default(),
5889                remote_ips: Default::default(),
5890            }]),
5891        ));
5892        let mut api = ctx.datagram_api::<I>();
5893
5894        let unbound = api.create_default();
5895        api.set_device(&unbound, Some(&device)).unwrap();
5896
5897        let HopLimits { mut unicast, multicast } = DEFAULT_HOP_LIMITS;
5898        unicast = unicast.checked_add(1).unwrap();
5899        {
5900            let device_state =
5901                api.core_ctx().dual_stack.ip_socket_ctx.state.get_device_state_mut::<I>(&device);
5902            assert_ne!(device_state.default_hop_limit, unicast);
5903            device_state.default_hop_limit = unicast;
5904        }
5905        assert_eq!(api.get_ip_hop_limits(&unbound), HopLimits { unicast, multicast });
5906
5907        // If the device is removed, use default hop limits.
5908        device.mark_removed();
5909        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5910    }
5911
5912    #[ip_test(I)]
5913    fn default_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
5914        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5915        let mut api = ctx.datagram_api::<I>();
5916        let unbound = api.create_default();
5917        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5918
5919        api.update_ip_hop_limit(&unbound, |limits| {
5920            *limits = SocketHopLimits {
5921                unicast: Some(NonZeroU8::new(1).unwrap()),
5922                multicast: Some(NonZeroU8::new(1).unwrap()),
5923                version: IpVersionMarker::default(),
5924            }
5925        });
5926
5927        // The limits no longer match the default.
5928        assert_ne!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5929
5930        // Clear the hop limits set on the socket.
5931        api.update_ip_hop_limit(&unbound, |limits| *limits = Default::default());
5932
5933        // The values should be back at the defaults.
5934        assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
5935    }
5936
5937    #[ip_test(I)]
5938    fn bind_device_unbound<I: DatagramIpExt<FakeDeviceId>>() {
5939        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
5940        let mut api = ctx.datagram_api::<I>();
5941        let unbound = api.create_default();
5942
5943        api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
5944        assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
5945
5946        api.set_device(&unbound, None).unwrap();
5947        assert_eq!(api.get_bound_device(&unbound), None);
5948    }
5949
5950    #[ip_test(I)]
5951    fn send_to_binds_unbound<I: DatagramIpExt<FakeDeviceId>>() {
5952        let mut ctx =
5953            FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new_with_ip_socket_ctx(
5954                FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
5955                    device: FakeDeviceId,
5956                    local_ips: vec![I::TEST_ADDRS.local_ip],
5957                    remote_ips: vec![I::TEST_ADDRS.remote_ip],
5958                }]),
5959            ));
5960        let mut api = ctx.datagram_api::<I>();
5961        let socket = api.create_default();
5962        let body = Buf::new(Vec::new(), ..);
5963
5964        api.send_to(
5965            &socket,
5966            Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
5967            1234,
5968            body,
5969            FakeSendToken::default(),
5970        )
5971        .expect("succeeds");
5972        assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
5973    }
5974
5975    #[ip_test(I)]
5976    fn send_to_no_route_still_binds<I: DatagramIpExt<FakeDeviceId>>() {
5977        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
5978            FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
5979                device: FakeDeviceId,
5980                local_ips: vec![I::TEST_ADDRS.local_ip],
5981                remote_ips: vec![],
5982            }]),
5983        ));
5984        let mut api = ctx.datagram_api::<I>();
5985        let socket = api.create_default();
5986        let body = Buf::new(Vec::new(), ..);
5987
5988        assert_matches!(
5989            api.send_to(
5990                &socket,
5991                Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
5992                1234,
5993                body,
5994                FakeSendToken::default(),
5995            ),
5996            Err(SendToError::CreateAndSend(_))
5997        );
5998        assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
5999    }
6000
6001    #[ip_test(I)]
6002    #[test_case(true; "remove device b")]
6003    #[test_case(false; "dont remove device b")]
6004    fn multicast_membership_changes<I: DatagramIpExt<FakeReferencyDeviceId> + TestIpExt>(
6005        remove_device_b: bool,
6006    ) {
6007        let device_a = FakeReferencyDeviceId::default();
6008        let device_b = FakeReferencyDeviceId::default();
6009        let mut core_ctx = FakeIpSocketCtx::<I, FakeReferencyDeviceId>::new(
6010            [device_a.clone(), device_b.clone()].into_iter().map(|device| FakeDeviceConfig {
6011                device,
6012                local_ips: Default::default(),
6013                remote_ips: Default::default(),
6014            }),
6015        );
6016        let mut bindings_ctx = FakeBindingsCtx::default();
6017
6018        let multicast_addr1 = I::get_multicast_addr(1);
6019        let mut memberships = MulticastMemberships::default();
6020        assert_eq!(
6021            memberships.apply_membership_change(
6022                multicast_addr1,
6023                &FakeWeakDeviceId(device_a.clone()),
6024                true /* want_membership */
6025            ),
6026            Some(MulticastMembershipChange::Join),
6027        );
6028        core_ctx.join_multicast_group(&mut bindings_ctx, &device_a, multicast_addr1);
6029
6030        let multicast_addr2 = I::get_multicast_addr(2);
6031        assert_eq!(
6032            memberships.apply_membership_change(
6033                multicast_addr2,
6034                &FakeWeakDeviceId(device_b.clone()),
6035                true /* want_membership */
6036            ),
6037            Some(MulticastMembershipChange::Join),
6038        );
6039        core_ctx.join_multicast_group(&mut bindings_ctx, &device_b, multicast_addr2);
6040
6041        for (device, addr, expected) in [
6042            (&device_a, multicast_addr1, true),
6043            (&device_a, multicast_addr2, false),
6044            (&device_b, multicast_addr1, false),
6045            (&device_b, multicast_addr2, true),
6046        ] {
6047            assert_eq!(
6048                core_ctx.get_device_state(device).is_in_multicast_group(&addr),
6049                expected,
6050                "device={:?}, addr={}",
6051                device,
6052                addr,
6053            );
6054        }
6055
6056        if remove_device_b {
6057            device_b.mark_removed();
6058        }
6059
6060        leave_all_joined_groups(&mut core_ctx, &mut bindings_ctx, &memberships);
6061        for (device, addr, expected) in [
6062            (&device_a, multicast_addr1, false),
6063            (&device_a, multicast_addr2, false),
6064            (&device_b, multicast_addr1, false),
6065            // Should not attempt to leave the multicast group on the device if
6066            // the device looks like it was removed. Note that although we mark
6067            // the device as removed, we do not destroy its state so we can
6068            // inspect it here.
6069            (&device_b, multicast_addr2, remove_device_b),
6070        ] {
6071            assert_eq!(
6072                core_ctx.get_device_state(device).is_in_multicast_group(&addr),
6073                expected,
6074                "device={:?}, addr={}",
6075                device,
6076                addr,
6077            );
6078        }
6079    }
6080
6081    #[ip_test(I)]
6082    fn set_get_transparent<I: DatagramIpExt<FakeDeviceId>>() {
6083        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
6084            FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
6085                device: FakeDeviceId,
6086                local_ips: Default::default(),
6087                remote_ips: Default::default(),
6088            }]),
6089        ));
6090        let mut api = ctx.datagram_api::<I>();
6091        let unbound = api.create_default();
6092
6093        assert!(!api.get_ip_transparent(&unbound));
6094
6095        api.set_ip_transparent(&unbound, true);
6096
6097        assert!(api.get_ip_transparent(&unbound));
6098
6099        api.set_ip_transparent(&unbound, false);
6100
6101        assert!(!api.get_ip_transparent(&unbound));
6102    }
6103
6104    #[ip_test(I)]
6105    fn transparent_bind_connect_non_local_src_addr<I: DatagramIpExt<FakeDeviceId>>() {
6106        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
6107            FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
6108                device: FakeDeviceId,
6109                local_ips: vec![],
6110                remote_ips: vec![I::TEST_ADDRS.remote_ip],
6111            }]),
6112        ));
6113        let mut api = ctx.datagram_api::<I>();
6114        let socket = api.create_default();
6115        api.set_ip_transparent(&socket, true);
6116
6117        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6118        const REMOTE_PORT: u16 = 1234;
6119
6120        // Binding to `local_ip` should succeed even though it is not assigned
6121        // to an interface because the socket is transparent.
6122        api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT))
6123            .expect("listen should succeed");
6124
6125        // Connecting to a valid remote should also succeed even though the
6126        // local address of the IP socket is not actually local.
6127        api.connect(
6128            &socket,
6129            Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
6130            REMOTE_PORT,
6131            Default::default(),
6132        )
6133        .expect("connect should succeed");
6134
6135        api.send_to(
6136            &socket,
6137            Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
6138            REMOTE_PORT,
6139            Buf::new(Vec::new(), ..),
6140            FakeSendToken::default(),
6141        )
6142        .expect("send_to should succeed");
6143    }
6144
6145    #[derive(Eq, PartialEq)]
6146    enum OriginalSocketState {
6147        Unbound,
6148        Listener,
6149        Connected,
6150    }
6151
6152    #[ip_test(I)]
6153    #[test_case(OriginalSocketState::Unbound; "reinsert_unbound")]
6154    #[test_case(OriginalSocketState::Listener; "reinsert_listener")]
6155    #[test_case(OriginalSocketState::Connected; "reinsert_connected")]
6156    fn connect_reinserts_on_failure_single_stack<I: DatagramIpExt<FakeDeviceId>>(
6157        original: OriginalSocketState,
6158    ) {
6159        connect_reinserts_on_failure_inner::<I>(
6160            original,
6161            I::TEST_ADDRS.local_ip.get(),
6162            I::TEST_ADDRS.remote_ip,
6163        );
6164    }
6165
6166    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
6167        net_ip_v4!("192.0.2.2"); "reinsert_listener_other_stack")]
6168    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
6169        net_ip_v4!("192.0.2.2"); "reinsert_listener_both_stacks")]
6170    #[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
6171        net_ip_v4!("192.0.2.2"); "reinsert_connected_other_stack")]
6172    fn connect_reinserts_on_failure_dual_stack(
6173        original: OriginalSocketState,
6174        local_ip: Ipv6Addr,
6175        remote_ip: Ipv4Addr,
6176    ) {
6177        let remote_ip = remote_ip.to_ipv6_mapped();
6178        connect_reinserts_on_failure_inner::<Ipv6>(original, local_ip, remote_ip);
6179    }
6180
6181    fn connect_reinserts_on_failure_inner<I: DatagramIpExt<FakeDeviceId>>(
6182        original: OriginalSocketState,
6183        local_ip: I::Addr,
6184        remote_ip: SpecifiedAddr<I::Addr>,
6185    ) {
6186        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6187            local_ip.to_ip_addr(),
6188            remote_ip.into(),
6189            [FakeDeviceId {}],
6190            |device_configs| {
6191                FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6192                    device_configs,
6193                ))
6194            },
6195        );
6196        let mut api = ctx.datagram_api::<I>();
6197        let socket = api.create_default();
6198        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6199        const ORIGINAL_REMOTE_PORT: u16 = 1234;
6200        const NEW_REMOTE_PORT: u16 = 5678;
6201
6202        // Setup the original socket state.
6203        match original {
6204            OriginalSocketState::Unbound => {}
6205            OriginalSocketState::Listener => api
6206                .listen(
6207                    &socket,
6208                    SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
6209                    Some(LOCAL_PORT),
6210                )
6211                .expect("listen should succeed"),
6212            OriginalSocketState::Connected => api
6213                .connect(
6214                    &socket,
6215                    Some(ZonedAddr::Unzoned(remote_ip)),
6216                    ORIGINAL_REMOTE_PORT,
6217                    Default::default(),
6218                )
6219                .expect("connect should succeed"),
6220        }
6221
6222        // Update the sharing state to generate conflicts during the call to `connect`.
6223        api.core_ctx().with_socket_state_mut(
6224            &socket,
6225            |_core_ctx, state: &mut SocketState<I, _, FakeStateSpec>| {
6226                state.sharing = Sharing::ConnectionConflicts { remote_port: NEW_REMOTE_PORT };
6227            },
6228        );
6229
6230        // Try to connect and observe a conflict error.
6231        assert_matches!(
6232            api.connect(
6233                &socket,
6234                Some(ZonedAddr::Unzoned(remote_ip)),
6235                NEW_REMOTE_PORT,
6236                Default::default(),
6237            ),
6238            Err(ConnectError::SockAddrConflict)
6239        );
6240
6241        // Verify the original socket state is intact.
6242        let info = api.get_info(&socket);
6243        match original {
6244            OriginalSocketState::Unbound => assert_matches!(info, SocketInfo::Unbound),
6245            OriginalSocketState::Listener => {
6246                let local_port = assert_matches!(
6247                    info,
6248                    SocketInfo::Listener(ListenerInfo {
6249                        local_ip: _,
6250                        local_identifier,
6251                    }) => local_identifier
6252                );
6253                assert_eq!(LOCAL_PORT, local_port);
6254            }
6255            OriginalSocketState::Connected => {
6256                let remote_port = assert_matches!(
6257                    info,
6258                    SocketInfo::Connected(ConnInfo {
6259                        local_ip: _,
6260                        local_identifier: _,
6261                        remote_ip: _,
6262                        remote_identifier,
6263                    }) => remote_identifier
6264                );
6265                assert_eq!(ORIGINAL_REMOTE_PORT, remote_port);
6266            }
6267        }
6268    }
6269
6270    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Send; "this_stack_send")]
6271    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Receive; "this_stack_receive")]
6272    #[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::SendAndReceive; "this_stack_send_and_receive")]
6273    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Send; "other_stack_send")]
6274    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Receive; "other_stack_receive")]
6275    #[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::SendAndReceive; "other_stack_send_and_receive")]
6276    fn set_get_shutdown_dualstack(remote_ip: Ipv6Addr, shutdown: ShutdownType) {
6277        let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote_ip should be specified");
6278        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6279            Ipv6::UNSPECIFIED_ADDRESS.into(),
6280            remote_ip.into(),
6281            [FakeDeviceId {}],
6282            |device_configs| {
6283                FakeCoreCtx::<Ipv6, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6284                    device_configs,
6285                ))
6286            },
6287        );
6288        let mut api = ctx.datagram_api::<Ipv6>();
6289
6290        const REMOTE_PORT: u16 = 1234;
6291        let socket = api.create_default();
6292        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT, Default::default())
6293            .expect("connect should succeed");
6294        assert_eq!(api.get_shutdown_connected(&socket), None);
6295
6296        api.shutdown_connected(&socket, shutdown).expect("shutdown should succeed");
6297        assert_eq!(api.get_shutdown_connected(&socket), Some(shutdown));
6298    }
6299
6300    #[ip_test(I)]
6301    #[test_case(OriginalSocketState::Unbound; "unbound")]
6302    #[test_case(OriginalSocketState::Listener; "listener")]
6303    #[test_case(OriginalSocketState::Connected; "connected")]
6304    fn set_get_device_single_stack<I: DatagramIpExt<MultipleDevicesId>>(
6305        original: OriginalSocketState,
6306    ) {
6307        set_get_device_inner::<I>(original, I::TEST_ADDRS.local_ip.get(), I::TEST_ADDRS.remote_ip);
6308    }
6309
6310    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
6311        net_ip_v4!("192.0.2.2"); "listener_other_stack")]
6312    #[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
6313        net_ip_v4!("192.0.2.2"); "listener_both_stacks")]
6314    #[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
6315        net_ip_v4!("192.0.2.2"); "connected_other_stack")]
6316    fn set_get_device_dual_stack(
6317        original: OriginalSocketState,
6318        local_ip: Ipv6Addr,
6319        remote_ip: Ipv4Addr,
6320    ) {
6321        let remote_ip = remote_ip.to_ipv6_mapped();
6322        set_get_device_inner::<Ipv6>(original, local_ip, remote_ip);
6323    }
6324
6325    fn set_get_device_inner<I: DatagramIpExt<MultipleDevicesId>>(
6326        original: OriginalSocketState,
6327        local_ip: I::Addr,
6328        remote_ip: SpecifiedAddr<I::Addr>,
6329    ) {
6330        const DEVICE_ID1: MultipleDevicesId = MultipleDevicesId::A;
6331        const DEVICE_ID2: MultipleDevicesId = MultipleDevicesId::B;
6332
6333        let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
6334            local_ip.to_ip_addr(),
6335            remote_ip.into(),
6336            [DEVICE_ID1, DEVICE_ID2],
6337            |device_configs| {
6338                FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
6339                    device_configs,
6340                ))
6341            },
6342        );
6343
6344        const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(10).unwrap();
6345        const REMOTE_PORT: u16 = 1234;
6346
6347        let mut api = ctx.datagram_api::<I>();
6348        let socket1 = api.create_default();
6349        let socket2 = api.create_default();
6350
6351        // Initialize each socket to the `original` state, and verify that their
6352        // device can be set.
6353        for (socket, device_id) in [(&socket1, DEVICE_ID1), (&socket2, DEVICE_ID2)] {
6354            match original {
6355                OriginalSocketState::Unbound => {}
6356                OriginalSocketState::Listener => api
6357                    .listen(
6358                        &socket,
6359                        SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
6360                        Some(LOCAL_PORT),
6361                    )
6362                    .expect("listen should succeed"),
6363                OriginalSocketState::Connected => api
6364                    .connect(
6365                        &socket,
6366                        Some(ZonedAddr::Unzoned(remote_ip)),
6367                        REMOTE_PORT,
6368                        Default::default(),
6369                    )
6370                    .expect("connect should succeed"),
6371            }
6372
6373            assert_eq!(api.get_bound_device(socket), None);
6374            api.set_device(socket, Some(&device_id)).expect("set device should succeed");
6375            assert_eq!(api.get_bound_device(socket), Some(FakeWeakDeviceId(device_id)));
6376        }
6377
6378        // For bound sockets, try to bind socket 2 to device 1, and expect it
6379        // it to conflict with socket 1 (They now have identical address keys in
6380        // the bound socket map)
6381        if original != OriginalSocketState::Unbound {
6382            assert_eq!(
6383                api.set_device(&socket2, Some(&DEVICE_ID1)),
6384                Err(SocketError::Local(LocalAddressError::AddressInUse))
6385            );
6386            // Verify both sockets still have their original device.
6387            assert_eq!(api.get_bound_device(&socket1), Some(FakeWeakDeviceId(DEVICE_ID1)));
6388            assert_eq!(api.get_bound_device(&socket2), Some(FakeWeakDeviceId(DEVICE_ID2)));
6389        }
6390
6391        // Verify the device can be unset.
6392        // NB: Close socket2 first, otherwise socket 1 will conflict with it.
6393        api.close(socket2, |_: ReferenceState<_, _, _>| ()).into_removed();
6394        api.set_device(&socket1, None).expect("set device should succeed");
6395        assert_eq!(api.get_bound_device(&socket1), None,);
6396    }
6397}