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