Skip to main content

netstack3_datagram/
datagram.rs

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