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