netstack3_datagram/
datagram.rs

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