Skip to main content

netstack3_udp/
base.rs

1// Copyright 2018 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//! The User Datagram Protocol (UDP).
6
7use alloc::vec::Vec;
8use core::borrow::Borrow;
9use core::fmt::Debug;
10use core::hash::{Hash, Hasher};
11use core::marker::PhantomData;
12use core::num::{NonZeroU8, NonZeroU16, NonZeroUsize};
13use core::ops::RangeInclusive;
14
15use derivative::Derivative;
16use either::Either;
17use lock_order::lock::{DelegatedOrderedLockAccess, OrderedLockAccess, OrderedLockRef};
18use log::{debug, trace};
19use net_types::ip::{GenericOverIp, Ip, IpInvariant, IpVersion, IpVersionMarker, Ipv4, Ipv6};
20use net_types::{MulticastAddr, SpecifiedAddr, Witness, ZonedAddr};
21use netstack3_base::socket::{
22    AddrEntry, AddrIsMappedError, AddrVec, Bound, ConnAddr, ConnInfoAddr, ConnIpAddr,
23    EitherIpProto, FoundSockets, IncompatibleError, InsertError, Inserter, ListenerAddr,
24    ListenerAddrInfo, ListenerIpAddr, MaybeDualStack, NotDualStackCapableError, RemoveResult,
25    ReusePortOption, SetDualStackEnabledError, SharingDomain, ShutdownType, SocketAddrType,
26    SocketCookie, SocketIpAddr, SocketMapAddrSpec, SocketMapAddrStateSpec, SocketMapConflictPolicy,
27    SocketMapStateSpec,
28};
29use netstack3_base::socketmap::{IterShadows as _, SocketMap, Tagged};
30use netstack3_base::sync::{BindingsSyncTypes, RwLock, StrongRc};
31use netstack3_base::{
32    AnyDevice, BidirectionalConverter, ContextPair, CoreTxMetadataContext, CounterContext,
33    DeviceIdContext, GsoInfo, Inspector, InspectorDeviceExt, InstantContext,
34    IpSocketPropertiesMatcher, LocalAddressError, Mark, MarkDomain, Marks, MatcherBindingsTypes,
35    NetworkParsingContext, PacketType, PortAllocImpl, ReferenceNotifiers,
36    RemoveResourceResultWithContext, ResourceCounterContext, RngContext, SocketError,
37    StrongDeviceIdentifier, WeakDeviceIdentifier, ZonedAddressError,
38};
39use netstack3_datagram::{
40    self as datagram, BoundDatagramSocketMap, BoundSocketState as DatagramBoundSocketState,
41    BoundSocketStateType as DatagramBoundSocketStateType, ConnectError, DatagramApi,
42    DatagramBindingsTypes, DatagramBoundStateContext, DatagramFlowId,
43    DatagramIpSpecificSocketOptions, DatagramSocketMapSpec, DatagramSocketSet, DatagramSocketSpec,
44    DatagramSpecBoundStateContext, DatagramSpecStateContext, DatagramStateContext,
45    DualStackBaseIpExt, DualStackConnState, DualStackConverter, DualStackDatagramBoundStateContext,
46    DualStackDatagramSpecBoundStateContext, DualStackIpExt, EitherIpSocket, ExpectedConnError,
47    ExpectedUnboundError, InUseError, IpExt, IpOptions, MulticastMembershipInterfaceSelector,
48    NonDualStackConverter, NonDualStackDatagramBoundStateContext,
49    NonDualStackDatagramSpecBoundStateContext, PendingDatagramSocketError,
50    SendError as DatagramSendError, SetMulticastMembershipError, SocketInfo,
51    SocketState as DatagramSocketState, SocketStateInner as DatagramSocketStateInner,
52    WrapOtherStackIpOptions, WrapOtherStackIpOptionsMut,
53};
54use netstack3_filter::{SocketIngressFilterResult, SocketOpsFilter, SocketOpsFilterBindingContext};
55use netstack3_ip::icmp::IcmpError;
56use netstack3_ip::socket::{
57    IpSockCreateAndSendError, IpSockCreationError, IpSockSendError, SocketHopLimits,
58};
59use netstack3_ip::{
60    HopLimits, IpHeaderInfo, IpTransportContext, LocalDeliveryPacketInfo, MarksBindingsContext,
61    MulticastMembershipHandler, ReceiveIpPacketMeta, SocketMetadata, TransparentLocalDelivery,
62    TransportIpContext,
63};
64use netstack3_trace::trace_duration;
65use packet::{
66    BufferMut, FragmentedByteSlice, NestablePacketBuilder as _, Nested, ParsablePacket, ParseBuffer,
67};
68use packet_formats::ip::{DscpAndEcn, IpProto, IpProtoExt, Ipv4Proto, Ipv6Proto};
69use packet_formats::udp::{UdpPacket, UdpPacketBuilder, UdpPacketRaw, UdpParseArgs};
70use siphasher::sip::SipHasher13;
71use thiserror::Error;
72
73use crate::internal::counters::{
74    CombinedUdpCounters, UdpCounterContext, UdpCountersWithSocket, UdpCountersWithoutSocket,
75};
76use crate::internal::diagnostics::{UdpSocketDiagnostics, UdpSocketDiagnosticsSeed};
77
78/// Convenience alias to make names shorter.
79pub(crate) type UdpBoundSocketMap<I, D, BT> = BoundDatagramSocketMap<I, D, Udp<BT>>;
80/// Tx metadata sent by UDP sockets.
81pub type UdpSocketTxMetadata<I, D, BT> = datagram::TxMetadata<I, D, Udp<BT>>;
82
83/// UDP bound sockets, i.e., the UDP demux.
84#[derive(Derivative, GenericOverIp)]
85#[generic_over_ip(I, Ip)]
86#[derivative(Default(bound = ""))]
87pub struct BoundSockets<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
88    bound_sockets: UdpBoundSocketMap<I, D, BT>,
89}
90
91/// A collection of UDP sockets.
92#[derive(Derivative)]
93#[derivative(Default(bound = ""))]
94pub struct Sockets<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
95    bound: RwLock<BoundSockets<I, D, BT>, BT::RwLock>,
96    // Destroy all_sockets last so the strong references in the demux are
97    // dropped before the primary references in the set.
98    all_sockets: RwLock<UdpSocketSet<I, D, BT>, BT::RwLock>,
99}
100
101impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
102    OrderedLockAccess<BoundSockets<I, D, BT>> for Sockets<I, D, BT>
103{
104    type Lock = RwLock<BoundSockets<I, D, BT>, BT::RwLock>;
105    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
106        OrderedLockRef::new(&self.bound)
107    }
108}
109
110impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
111    OrderedLockAccess<UdpSocketSet<I, D, BT>> for Sockets<I, D, BT>
112{
113    type Lock = RwLock<UdpSocketSet<I, D, BT>, BT::RwLock>;
114    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
115        OrderedLockRef::new(&self.all_sockets)
116    }
117}
118
119/// The state associated with the UDP protocol.
120///
121/// `D` is the device ID type.
122#[derive(Derivative, GenericOverIp)]
123#[generic_over_ip(I, Ip)]
124#[derivative(Default(bound = ""))]
125pub struct UdpState<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
126    /// System's UDP sockets.
127    pub sockets: Sockets<I, D, BT>,
128    /// Stack-wide UDP "with socket" counters.
129    pub counters_with_socket: UdpCountersWithSocket<I>,
130    /// Stack-wide UDP "without socket" counters.
131    pub counters_without_socket: UdpCountersWithoutSocket<I>,
132}
133
134/// Uninstantiatable type for implementing [`DatagramSocketSpec`].
135pub struct Udp<BT>(PhantomData<BT>, !);
136
137/// Produces an iterator over eligible receiving socket addresses.
138#[cfg(test)]
139fn iter_receiving_addrs<I: IpExt, D: WeakDeviceIdentifier>(
140    addr: ConnIpAddr<I::Addr, NonZeroU16, UdpRemotePort>,
141    device: D,
142) -> impl Iterator<Item = AddrVec<I, D, UdpAddrSpec>> {
143    netstack3_base::socket::AddrVecIter::with_device(addr.into(), device)
144}
145
146fn check_posix_sharing<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
147    new_sharing: Sharing,
148    dest: AddrVec<I, D, UdpAddrSpec>,
149    socketmap: &SocketMap<AddrVec<I, D, UdpAddrSpec>, Bound<UdpSocketMapStateSpec<I, D, BT>>>,
150) -> Result<(), InsertError> {
151    // Having a value present at a shadowed address is disqualifying, unless
152    // both the new and existing sockets allow port sharing.
153    if dest.iter_shadows().any(|a| {
154        socketmap.get(&a).is_some_and(|bound| {
155            !bound.tag(&a).to_sharing_options().is_shareable_with_new_state(new_sharing)
156        })
157    }) {
158        return Err(InsertError::ShadowAddrExists);
159    }
160
161    // Likewise, the presence of a value that shadows the target address is
162    // disqualifying unless both allow port sharing.
163    match &dest {
164        AddrVec::Conn(ConnAddr { ip: _, device: None }) | AddrVec::Listen(_) => {
165            if socketmap.descendant_counts(&dest).any(|(tag, _): &(_, NonZeroUsize)| {
166                !tag.to_sharing_options().is_shareable_with_new_state(new_sharing)
167            }) {
168                return Err(InsertError::WouldShadowExisting);
169            }
170        }
171        AddrVec::Conn(ConnAddr { ip: _, device: Some(_) }) => {
172            // No need to check shadows here because there are no addresses
173            // that shadow a ConnAddr with a device.
174            debug_assert_eq!(socketmap.descendant_counts(&dest).len(), 0)
175        }
176    }
177
178    // There are a few combinations of addresses that can conflict with
179    // each other even though there is not a direct shadowing relationship:
180    // - listener address with device and connected address without.
181    // - "any IP" listener with device and specific IP listener without.
182    // - "any IP" listener with device and connected address without.
183    //
184    // The complication is that since these pairs of addresses don't have a
185    // direct shadowing relationship, it's not possible to query for one
186    // from the other in the socketmap without a linear scan. Instead. we
187    // rely on the fact that the tag values in the socket map have different
188    // values for entries with and without device IDs specified.
189    fn conflict_exists<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
190        new_sharing: Sharing,
191        socketmap: &SocketMap<AddrVec<I, D, UdpAddrSpec>, Bound<UdpSocketMapStateSpec<I, D, BT>>>,
192        addr: impl Into<AddrVec<I, D, UdpAddrSpec>>,
193        mut is_conflicting: impl FnMut(&AddrVecTag) -> bool,
194    ) -> bool {
195        socketmap.descendant_counts(&addr.into()).any(|(tag, _): &(_, NonZeroUsize)| {
196            is_conflicting(tag)
197                && !tag.to_sharing_options().is_shareable_with_new_state(new_sharing)
198        })
199    }
200
201    let found_indirect_conflict = match dest {
202        AddrVec::Listen(ListenerAddr {
203            ip: ListenerIpAddr { addr: None, identifier },
204            device: Some(_device),
205        }) => {
206            // An address with a device will shadow an any-IP listener
207            // `dest` with a device so we only need to check for addresses
208            // without a device. Likewise, an any-IP listener will directly
209            // shadow `dest`, so an indirect conflict can only come from a
210            // specific listener or connected socket (without a device).
211            conflict_exists(
212                new_sharing,
213                socketmap,
214                ListenerAddr { ip: ListenerIpAddr { addr: None, identifier }, device: None },
215                |AddrVecTag { has_device, addr_type, sharing: _ }| {
216                    !*has_device
217                        && match addr_type {
218                            SocketAddrType::SpecificListener | SocketAddrType::Connected => true,
219                            SocketAddrType::AnyListener => false,
220                        }
221                },
222            )
223        }
224        AddrVec::Listen(ListenerAddr {
225            ip: ListenerIpAddr { addr: Some(ip), identifier },
226            device: Some(_device),
227        }) => {
228            // A specific-IP listener `dest` with a device will be shadowed
229            // by a connected socket with a device and will shadow
230            // specific-IP addresses without a device and any-IP listeners
231            // with and without devices. That means an indirect conflict can
232            // only come from a connected socket without a device.
233            conflict_exists(
234                new_sharing,
235                socketmap,
236                ListenerAddr { ip: ListenerIpAddr { addr: Some(ip), identifier }, device: None },
237                |AddrVecTag { has_device, addr_type, sharing: _ }| {
238                    !*has_device
239                        && match addr_type {
240                            SocketAddrType::Connected => true,
241                            SocketAddrType::AnyListener | SocketAddrType::SpecificListener => false,
242                        }
243                },
244            )
245        }
246        AddrVec::Listen(ListenerAddr {
247            ip: ListenerIpAddr { addr: Some(_), identifier },
248            device: None,
249        }) => {
250            // A specific-IP listener `dest` without a device will be
251            // shadowed by a specific-IP listener with a device and by any
252            // connected socket (with or without a device).  It will also
253            // shadow an any-IP listener without a device, which means an
254            // indirect conflict can only come from an any-IP listener with
255            // a device.
256            conflict_exists(
257                new_sharing,
258                socketmap,
259                ListenerAddr { ip: ListenerIpAddr { addr: None, identifier }, device: None },
260                |AddrVecTag { has_device, addr_type, sharing: _ }| {
261                    *has_device
262                        && match addr_type {
263                            SocketAddrType::AnyListener => true,
264                            SocketAddrType::SpecificListener | SocketAddrType::Connected => false,
265                        }
266                },
267            )
268        }
269        AddrVec::Conn(ConnAddr {
270            ip: ConnIpAddr { local: (local_ip, local_identifier), remote: _ },
271            device: None,
272        }) => {
273            // A connected socket `dest` without a device shadows listeners
274            // without devices, and is shadowed by a connected socket with
275            // a device. It can indirectly conflict with listening sockets
276            // with devices.
277
278            // Check for specific-IP listeners with devices, which would
279            // indirectly conflict.
280            conflict_exists(
281                new_sharing,
282                socketmap,
283                ListenerAddr {
284                    ip: ListenerIpAddr {
285                        addr: Some(local_ip),
286                        identifier: local_identifier.clone(),
287                    },
288                    device: None,
289                },
290                |AddrVecTag { has_device, addr_type, sharing: _ }| {
291                    *has_device
292                        && match addr_type {
293                            SocketAddrType::SpecificListener => true,
294                            SocketAddrType::AnyListener | SocketAddrType::Connected => false,
295                        }
296                },
297            ) ||
298            // Check for any-IP listeners with devices since they conflict.
299            // Note that this check cannot be combined with the one above
300            // since they examine tag counts for different addresses. While
301            // the counts of tags matched above *will* also be propagated to
302            // the any-IP listener entry, they would be indistinguishable
303            // from non-conflicting counts. For a connected address with
304            // `Some(local_ip)`, the descendant counts at the listener
305            // address with `addr = None` would include any
306            // `SpecificListener` tags for both addresses with
307            // `Some(local_ip)` and `Some(other_local_ip)`. The former
308            // indirectly conflicts with `dest` but the latter does not,
309            // hence this second distinct check.
310            conflict_exists(
311                new_sharing,
312                socketmap,
313                ListenerAddr {
314                    ip: ListenerIpAddr { addr: None, identifier: local_identifier },
315                    device: None,
316                },
317                |AddrVecTag { has_device, addr_type, sharing: _ }| {
318                    *has_device
319                        && match addr_type {
320                            SocketAddrType::AnyListener => true,
321                            SocketAddrType::SpecificListener | SocketAddrType::Connected => false,
322                        }
323                },
324            )
325        }
326        AddrVec::Listen(ListenerAddr {
327            ip: ListenerIpAddr { addr: None, identifier: _ },
328            device: _,
329        }) => false,
330        AddrVec::Conn(ConnAddr { ip: _, device: Some(_device) }) => false,
331    };
332    if found_indirect_conflict { Err(InsertError::IndirectConflict) } else { Ok(()) }
333}
334
335/// The remote port for a UDP socket.
336#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
337pub enum UdpRemotePort {
338    /// The remote port is set to the following value.
339    Set(NonZeroU16),
340    /// The remote port is unset (i.e. "0") value. An unset remote port is
341    /// treated specially in a few places:
342    ///
343    /// 1) Attempting to send to an unset remote port results in a
344    /// [`UdpSerializeError::RemotePortUnset`] error. Note that this behavior
345    /// diverges from Linux, which does allow sending to a remote_port of 0
346    /// (supported by `send` but not `send_to`). The rationale for this
347    /// divergence originates from RFC 8085 Section 5.1:
348    ///
349    ///    A UDP sender SHOULD NOT use a source port value of zero.  A source
350    ///    port number that cannot be easily determined from the address or
351    ///    payload type provides protection at the receiver from data injection
352    ///    attacks by off-path devices. A UDP receiver SHOULD NOT bind to port
353    ///    zero.
354    ///
355    ///    Applications SHOULD implement receiver port and address checks at the
356    ///    application layer or explicitly request that the operating system
357    ///    filter the received packets to prevent receiving packets with an
358    ///    arbitrary port.  This measure is designed to provide additional
359    ///    protection from data injection attacks from an off-path source (where
360    ///    the port values may not be known).
361    ///
362    /// Combined, these two stanzas recommend hosts discard incoming traffic
363    /// destined to remote port 0 for security reasons. Thus we choose to not
364    /// allow hosts to send such packets under the assumption that it will be
365    /// dropped by the receiving end.
366    ///
367    /// 2) A socket connected to a remote host on port 0 will not receive any
368    /// packets from the remote host. This is because the
369    /// [`BoundSocketMap::lookup`] implementation only delivers packets that
370    /// specify a remote port to connected sockets with an exact match. Further,
371    /// packets that don't specify a remote port are only delivered to listener
372    /// sockets. This diverges from Linux (which treats a remote_port of 0) as
373    /// wild card. If and when a concrete need for such behavior is identified,
374    /// the [`BoundSocketMap`] lookup behavior can be adjusted accordingly.
375    Unset,
376}
377
378impl From<NonZeroU16> for UdpRemotePort {
379    fn from(p: NonZeroU16) -> Self {
380        Self::Set(p)
381    }
382}
383
384impl From<u16> for UdpRemotePort {
385    fn from(p: u16) -> Self {
386        NonZeroU16::new(p).map(UdpRemotePort::from).unwrap_or(UdpRemotePort::Unset)
387    }
388}
389
390impl From<UdpRemotePort> for u16 {
391    fn from(p: UdpRemotePort) -> Self {
392        match p {
393            UdpRemotePort::Unset => 0,
394            UdpRemotePort::Set(p) => p.into(),
395        }
396    }
397}
398
399/// Uninstantiatable type for implementing [`SocketMapAddrSpec`].
400pub enum UdpAddrSpec {}
401
402impl SocketMapAddrSpec for UdpAddrSpec {
403    type RemoteIdentifier = UdpRemotePort;
404    type LocalIdentifier = NonZeroU16;
405}
406
407pub struct UdpSocketMapStateSpec<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
408    PhantomData<(I, D, BT)>,
409    !,
410);
411
412impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> SocketMapStateSpec
413    for UdpSocketMapStateSpec<I, D, BT>
414{
415    type ListenerId = I::DualStackBoundSocketId<D, Udp<BT>>;
416    type ConnId = I::DualStackBoundSocketId<D, Udp<BT>>;
417
418    type AddrVecTag = AddrVecTag;
419
420    type ListenerSharingState = Sharing;
421    type ConnSharingState = Sharing;
422
423    type ListenerAddrState = AddrState<Self::ListenerId>;
424
425    type ConnAddrState = AddrState<Self::ConnId>;
426    fn listener_tag(
427        ListenerAddrInfo { has_device, specified_addr }: ListenerAddrInfo,
428        state: &Self::ListenerAddrState,
429    ) -> Self::AddrVecTag {
430        AddrVecTag {
431            has_device,
432            addr_type: specified_addr
433                .then_some(SocketAddrType::SpecificListener)
434                .unwrap_or(SocketAddrType::AnyListener),
435            sharing: state.to_sharing_options(),
436        }
437    }
438    fn connected_tag(has_device: bool, state: &Self::ConnAddrState) -> Self::AddrVecTag {
439        AddrVecTag {
440            has_device,
441            addr_type: SocketAddrType::Connected,
442            sharing: state.to_sharing_options(),
443        }
444    }
445}
446
447impl<AA, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
448    SocketMapConflictPolicy<AA, Sharing, I, D, UdpAddrSpec> for UdpSocketMapStateSpec<I, D, BT>
449where
450    AA: Into<AddrVec<I, D, UdpAddrSpec>> + Clone,
451{
452    fn check_insert_conflicts(
453        new_sharing_state: &Sharing,
454        addr: &AA,
455        socketmap: &SocketMap<AddrVec<I, D, UdpAddrSpec>, Bound<Self>>,
456    ) -> Result<(), InsertError> {
457        check_posix_sharing(*new_sharing_state, addr.clone().into(), socketmap)
458    }
459}
460
461/// State held for IPv6 sockets related to dual-stack operation.
462#[derive(Clone, Derivative)]
463#[derivative(Default(bound = ""), Debug(bound = ""))]
464pub struct DualStackSocketState<D: WeakDeviceIdentifier> {
465    /// Whether dualstack operations are enabled on this socket.
466    /// Match Linux's behavior by enabling dualstack operations by default.
467    #[derivative(Default(value = "true"))]
468    dual_stack_enabled: bool,
469
470    /// Send options used when sending on the IPv4 stack.
471    socket_options: DatagramIpSpecificSocketOptions<Ipv4, D>,
472}
473
474/// Serialization errors for Udp Packets.
475#[derive(Debug, Error)]
476pub enum UdpSerializeError {
477    /// Disallow sending packets with a remote port of 0. See
478    /// [`UdpRemotePort::Unset`] for the rationale.
479    #[error("sending packets with a remote port of 0 is not allowed")]
480    RemotePortUnset,
481}
482
483impl<BT: UdpBindingsTypes> BindingsSyncTypes for Udp<BT> {
484    type Mutex = BT::Mutex;
485    type RwLock = BT::RwLock;
486    type Once = BT::Once;
487}
488
489impl<BT: UdpBindingsTypes> DatagramSocketSpec for Udp<BT> {
490    const NAME: &'static str = "UDP";
491
492    type AddrSpec = UdpAddrSpec;
493    type SocketId<I: IpExt, D: WeakDeviceIdentifier> = UdpSocketId<I, D, BT>;
494    type WeakSocketId<I: IpExt, D: WeakDeviceIdentifier> = WeakUdpSocketId<I, D, BT>;
495    type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier> =
496        I::OtherStackIpOptions<DualStackSocketState<D>>;
497    type ListenerIpAddr<I: IpExt> = I::DualStackListenerIpAddr<NonZeroU16>;
498    type ConnIpAddr<I: IpExt> = I::DualStackConnIpAddr<Self>;
499    type ConnStateExtra = ();
500    type ConnState<I: IpExt, D: WeakDeviceIdentifier> = I::DualStackConnState<D, Self>;
501    type SocketMapSpec<I: IpExt, D: WeakDeviceIdentifier> = UdpSocketMapStateSpec<I, D, BT>;
502    type SharingState = Sharing;
503
504    type Serializer<I: IpExt, B: BufferMut> = Nested<B, UdpPacketBuilder<I::Addr>>;
505    type SerializeError = UdpSerializeError;
506
507    type ExternalData<I: Ip> = BT::ExternalData<I>;
508    type Counters<I: Ip> = UdpCountersWithSocket<I>;
509    type SendToken = BT::SendToken;
510
511    fn ip_proto<I: IpProtoExt>() -> I::Proto {
512        IpProto::Udp.into()
513    }
514
515    fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
516        s: &Self::SocketId<I, D>,
517    ) -> I::DualStackBoundSocketId<D, Udp<BT>> {
518        I::into_dual_stack_bound_socket_id(s.clone())
519    }
520
521    fn make_packet<I: IpExt, B: BufferMut>(
522        body: B,
523        addr: &ConnIpAddr<I::Addr, NonZeroU16, UdpRemotePort>,
524    ) -> Result<Self::Serializer<I, B>, UdpSerializeError> {
525        let ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } = addr;
526        let remote_port = match remote_port {
527            UdpRemotePort::Unset => return Err(UdpSerializeError::RemotePortUnset),
528            UdpRemotePort::Set(remote_port) => *remote_port,
529        };
530        Ok(UdpPacketBuilder::new(local_ip.addr(), remote_ip.addr(), Some(*local_port), remote_port)
531            .wrap_body(body))
532    }
533
534    fn try_alloc_listen_identifier<I: IpExt, D: WeakDeviceIdentifier>(
535        rng: &mut impl RngContext,
536        is_available: impl Fn(NonZeroU16) -> Result<(), InUseError>,
537    ) -> Option<NonZeroU16> {
538        try_alloc_listen_port::<I, D, BT>(rng, is_available)
539    }
540
541    fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
542        state: &Self::ConnState<I, D>,
543    ) -> datagram::ConnInfo<I::Addr, D> {
544        let ConnAddr { ip, device } = I::conn_addr_from_state(state);
545        let ConnInfoAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } =
546            ip.into();
547        datagram::ConnInfo::new(local_ip, local_port, remote_ip, remote_port.into(), || {
548            // The invariant that a zone is present if needed is upheld by connect.
549            device.clone().expect("device must be bound for addresses that require zones")
550        })
551    }
552
553    fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
554        bound: &UdpBoundSocketMap<I, D, BT>,
555        bindings_ctx: &mut BC,
556        flow: datagram::DatagramFlowId<I::Addr, UdpRemotePort>,
557    ) -> Option<NonZeroU16> {
558        let mut rng = bindings_ctx.rng();
559        netstack3_base::simple_randomized_port_alloc(&mut rng, &flow, &UdpPortAlloc(bound), &())
560            .map(|p| NonZeroU16::new(p).expect("ephemeral ports should be non-zero"))
561    }
562
563    fn upgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
564        id: &Self::WeakSocketId<I, D>,
565    ) -> Option<Self::SocketId<I, D>> {
566        id.upgrade()
567    }
568
569    fn downgrade_socket_id<I: IpExt, D: WeakDeviceIdentifier>(
570        id: &Self::SocketId<I, D>,
571    ) -> Self::WeakSocketId<I, D> {
572        UdpSocketId::downgrade(id)
573    }
574}
575
576impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
577    DatagramSocketMapSpec<I, D, UdpAddrSpec> for UdpSocketMapStateSpec<I, D, BT>
578{
579    type BoundSocketId = I::DualStackBoundSocketId<D, Udp<BT>>;
580}
581
582enum LookupResult<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
583    Conn(
584        &'a I::DualStackBoundSocketId<D, Udp<BT>>,
585        ConnAddr<ConnIpAddr<I::Addr, NonZeroU16, UdpRemotePort>, D>,
586    ),
587    Listener(
588        &'a I::DualStackBoundSocketId<D, Udp<BT>>,
589        ListenerAddr<ListenerIpAddr<I::Addr, NonZeroU16>, D>,
590    ),
591}
592
593#[derive(Hash, Copy, Clone)]
594struct SocketSelectorParams<I: Ip, A: AsRef<I::Addr>> {
595    src_ip: I::Addr,
596    dst_ip: A,
597    src_port: u16,
598    dst_port: u16,
599    _ip: IpVersionMarker<I>,
600}
601
602#[derive(Debug, Eq, PartialEq)]
603pub struct LoadBalancedEntry<T> {
604    id: T,
605    sharing_domain: SharingDomain,
606    reuse_addr: bool,
607}
608
609#[derive(Debug, Eq, PartialEq)]
610pub enum AddrState<T> {
611    Exclusive(T),
612    Shared {
613        // Entries with the SO_REUSEADDR flag. If this list is not empty then
614        // new packets are delivered to the last socket in this list.
615        priority: Vec<T>,
616
617        // Entries with the SO_REUSEPORT flag. Some of them may have
618        // SO_REUSEADDR flag set as well. If `priority` list is empty then
619        // incoming packets are load-balanced between sockets in this list.
620        load_balanced: Vec<LoadBalancedEntry<T>>,
621    },
622}
623
624#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Default)]
625pub struct Sharing {
626    reuse_addr: bool,
627    reuse_port: ReusePortOption,
628}
629
630impl Sharing {
631    pub(crate) fn is_shareable_with_new_state(&self, new_state: Sharing) -> bool {
632        let Sharing { reuse_addr, reuse_port } = self;
633        let Sharing { reuse_addr: new_reuse_addr, reuse_port: new_reuse_port } = new_state;
634        (*reuse_addr && new_reuse_addr) || reuse_port.is_shareable_with(&new_reuse_port)
635    }
636}
637
638#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
639pub struct AddrVecTag {
640    pub(crate) has_device: bool,
641    pub(crate) addr_type: SocketAddrType,
642    pub(crate) sharing: Sharing,
643}
644
645pub(crate) trait ToSharingOptions {
646    fn to_sharing_options(&self) -> Sharing;
647}
648
649impl ToSharingOptions for AddrVecTag {
650    fn to_sharing_options(&self) -> Sharing {
651        let AddrVecTag { has_device: _, addr_type: _, sharing } = self;
652        *sharing
653    }
654}
655
656impl<T> ToSharingOptions for AddrState<T> {
657    fn to_sharing_options(&self) -> Sharing {
658        match self {
659            AddrState::Exclusive(_) => {
660                Sharing { reuse_addr: false, reuse_port: ReusePortOption::Disabled }
661            }
662            AddrState::Shared { priority, load_balanced } => {
663                // All sockets in `priority` have `REUSE_ADDR` flag set. Check
664                // that all sockets in `load_balanced` have it set as well.
665                let reuse_addr = load_balanced.iter().all(|e| e.reuse_addr);
666
667                // All sockets in `load_balanced` have `REUSE_PORT` flag set,
668                // while the sockets in `priority` don't. `REUSE_PORT` requires
669                // all sockets to have the same sharing domain.
670                let reuse_port = if priority.is_empty() {
671                    load_balanced
672                        .iter()
673                        .map(|e| Some(e.sharing_domain))
674                        .reduce(|acc, sharing_domain| match (acc, sharing_domain) {
675                            (Some(acc), Some(sharing_domain)) if acc == sharing_domain => {
676                                Some(sharing_domain)
677                            }
678                            _ => None,
679                        })
680                        .flatten()
681                } else {
682                    None
683                };
684                let reuse_port = match reuse_port {
685                    Some(domain) => ReusePortOption::Enabled(domain),
686                    None => ReusePortOption::Disabled,
687                };
688
689                Sharing { reuse_addr, reuse_port }
690            }
691        }
692    }
693}
694
695impl<T> ToSharingOptions for (T, Sharing) {
696    fn to_sharing_options(&self) -> Sharing {
697        let (_state, sharing) = self;
698        *sharing
699    }
700}
701
702pub struct SocketMapAddrInserter<'a, I> {
703    state: &'a mut AddrState<I>,
704    sharing_state: Sharing,
705}
706
707impl<'a, I> Inserter<I> for SocketMapAddrInserter<'a, I> {
708    fn insert(self, id: I) {
709        match self {
710            Self {
711                state: _,
712                sharing_state: Sharing { reuse_addr: false, reuse_port: ReusePortOption::Disabled },
713            }
714            | Self { state: AddrState::Exclusive(_), sharing_state: _ } => {
715                panic!("Can't insert entry in a non-shareable entry")
716            }
717
718            // If only `SO_REUSEADDR` flag is set then insert the entry in the `priority` list.
719            Self {
720                state: AddrState::Shared { priority, load_balanced: _ },
721                sharing_state: Sharing { reuse_addr: true, reuse_port: ReusePortOption::Disabled },
722            } => priority.push(id),
723
724            // If `SO_REUSEPORT` flag is set then insert the entry in the `load_balanced` list.
725            Self {
726                state: AddrState::Shared { priority: _, load_balanced },
727                sharing_state:
728                    Sharing { reuse_addr, reuse_port: ReusePortOption::Enabled(sharing_domain) },
729            } => load_balanced.push(LoadBalancedEntry { id, reuse_addr, sharing_domain }),
730        }
731    }
732}
733
734impl<I: Debug + Eq> SocketMapAddrStateSpec for AddrState<I> {
735    type Id = I;
736    type SharingState = Sharing;
737    type Inserter<'a>
738        = SocketMapAddrInserter<'a, I>
739    where
740        I: 'a;
741
742    fn new(new_sharing_state: &Sharing, id: I) -> Self {
743        match new_sharing_state {
744            Sharing { reuse_addr: false, reuse_port: ReusePortOption::Disabled } => {
745                Self::Exclusive(id)
746            }
747            Sharing { reuse_addr: true, reuse_port: ReusePortOption::Disabled } => {
748                Self::Shared { priority: Vec::from([id]), load_balanced: Vec::new() }
749            }
750            Sharing { reuse_addr, reuse_port: ReusePortOption::Enabled(sharing_domain) } => {
751                Self::Shared {
752                    priority: Vec::new(),
753                    load_balanced: Vec::from([LoadBalancedEntry {
754                        id,
755                        reuse_addr: *reuse_addr,
756                        sharing_domain: *sharing_domain,
757                    }]),
758                }
759            }
760        }
761    }
762
763    fn contains_id(&self, id: &Self::Id) -> bool {
764        match self {
765            Self::Exclusive(x) => id == x,
766            Self::Shared { priority, load_balanced } => {
767                priority.contains(id) || load_balanced.iter().any(|e| e.id == *id)
768            }
769        }
770    }
771
772    fn try_get_inserter<'a, 'b>(
773        &'b mut self,
774        new_sharing_state: &'a Sharing,
775    ) -> Result<SocketMapAddrInserter<'b, I>, IncompatibleError> {
776        self.could_insert(new_sharing_state)?;
777        Ok(SocketMapAddrInserter { state: self, sharing_state: *new_sharing_state })
778    }
779
780    fn could_insert(&self, new_sharing_state: &Sharing) -> Result<(), IncompatibleError> {
781        self.to_sharing_options()
782            .is_shareable_with_new_state(*new_sharing_state)
783            .then_some(())
784            .ok_or(IncompatibleError)
785    }
786
787    fn remove_by_id(&mut self, id: I) -> RemoveResult {
788        match self {
789            Self::Exclusive(_) => RemoveResult::IsLast,
790            Self::Shared { priority, load_balanced } => {
791                if let Some(pos) = priority.iter().position(|i| *i == id) {
792                    let _removed: I = priority.remove(pos);
793                } else {
794                    let pos = load_balanced
795                        .iter()
796                        .position(|e| e.id == id)
797                        .expect("couldn't find ID to remove");
798                    let _removed: LoadBalancedEntry<I> = load_balanced.remove(pos);
799                }
800
801                if priority.is_empty() && load_balanced.is_empty() {
802                    RemoveResult::IsLast
803                } else {
804                    RemoveResult::Success
805                }
806            }
807        }
808    }
809
810    fn sharing_state(&self) -> Self::SharingState {
811        self.to_sharing_options()
812    }
813}
814
815impl<T> AddrState<T> {
816    fn select_receiver<I: Ip, A: AsRef<I::Addr> + Hash>(
817        &self,
818        selector: SocketSelectorParams<I, A>,
819    ) -> &T {
820        match self {
821            AddrState::Exclusive(id) => id,
822            AddrState::Shared { priority, load_balanced } => {
823                if let Some(id) = priority.last() {
824                    id
825                } else if load_balanced.len() == 1 {
826                    &load_balanced[0].id
827                } else {
828                    // NB: The hasher must be deterministic across calls, so
829                    // that all the packets in a flow are delivered to the same
830                    // socket.
831                    let mut hasher = SipHasher13::new();
832                    selector.hash(&mut hasher);
833                    let index: usize = hasher.finish() as usize % load_balanced.len();
834                    &load_balanced[index].id
835                }
836            }
837        }
838    }
839
840    fn first(&self) -> &T {
841        match self {
842            AddrState::Exclusive(id) => id,
843            AddrState::Shared { priority, load_balanced } => {
844                if let Some(id) = priority.last() {
845                    id
846                } else {
847                    &load_balanced[0].id
848                }
849            }
850        }
851    }
852
853    fn collect_all_ids(&self) -> impl Iterator<Item = &'_ T> {
854        match self {
855            AddrState::Exclusive(id) => Either::Left(core::iter::once(id)),
856            AddrState::Shared { priority, load_balanced } => {
857                Either::Right(priority.iter().chain(load_balanced.iter().map(|i| &i.id)))
858            }
859        }
860    }
861}
862
863/// Finds the socket(s) that should receive an incoming packet.
864///
865/// Uses the provided addresses and receiving device to look up sockets that
866/// should receive a matching incoming packet. The returned iterator may
867/// yield 0, 1, or multiple sockets.
868fn lookup<'s, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
869    bound: &'s UdpBoundSocketMap<I, D, BT>,
870    (src_ip, src_port): (Option<SocketIpAddr<I::Addr>>, Option<NonZeroU16>),
871    (dst_ip, dst_port): (SocketIpAddr<I::Addr>, NonZeroU16),
872    device: D,
873    broadcast: Option<I::BroadcastMarker>,
874) -> impl Iterator<Item = LookupResult<'s, I, D, BT>> + 's {
875    let matching_entries = bound.iter_receivers(
876        (src_ip, src_port.map(UdpRemotePort::from)),
877        (dst_ip, dst_port),
878        device,
879        broadcast,
880    );
881    match matching_entries {
882        None => Either::Left(None),
883        Some(FoundSockets::Single(entry)) => {
884            Either::Left(Some(match entry {
885                AddrEntry::Listen(state, l) => {
886                    let selector = SocketSelectorParams::<I, SpecifiedAddr<I::Addr>> {
887                        src_ip: src_ip.map_or(I::UNSPECIFIED_ADDRESS, SocketIpAddr::addr),
888                        dst_ip: dst_ip.into(),
889                        src_port: src_port.map_or(0, NonZeroU16::get),
890                        dst_port: dst_port.get(),
891                        _ip: IpVersionMarker::default(),
892                    };
893                    LookupResult::Listener(state.select_receiver(selector), l)
894                }
895                AddrEntry::Conn(state, c) => {
896                    // Always take the first socket when there are multiple
897                    // connected sockets. We cannot load-balance between
898                    // connected sockets because the source address is always
899                    // the same. This also ensures that we return a result
900                    // consistent with `early_demux_ip_packet()`.
901                    LookupResult::Conn(state.first(), c)
902                }
903            }))
904        }
905
906        Some(FoundSockets::Multicast(entries)) => {
907            Either::Right(entries.into_iter().flat_map(|entry| match entry {
908                AddrEntry::Listen(state, l) => Either::Left(
909                    state.collect_all_ids().map(move |id| LookupResult::Listener(id, l.clone())),
910                ),
911                AddrEntry::Conn(state, c) => Either::Right(
912                    state.collect_all_ids().map(move |id| LookupResult::Conn(id, c.clone())),
913                ),
914            }))
915        }
916    }
917    .into_iter()
918}
919
920/// Helper function to allocate a listen port.
921///
922/// Finds a random ephemeral port that is not in the provided `used_ports` set.
923fn try_alloc_listen_port<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
924    bindings_ctx: &mut impl RngContext,
925    is_available: impl Fn(NonZeroU16) -> Result<(), InUseError>,
926) -> Option<NonZeroU16> {
927    let mut port = UdpPortAlloc::<I, D, BT>::rand_ephemeral(&mut bindings_ctx.rng());
928    for _ in UdpPortAlloc::<I, D, BT>::EPHEMERAL_RANGE {
929        // We can unwrap here because we know that the EPHEMERAL_RANGE doesn't
930        // include 0.
931        let tryport = NonZeroU16::new(port.get()).unwrap();
932        match is_available(tryport) {
933            Ok(()) => return Some(tryport),
934            Err(InUseError {}) => port.next(),
935        }
936    }
937    None
938}
939
940struct UdpPortAlloc<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
941    &'a UdpBoundSocketMap<I, D, BT>,
942);
943
944impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> PortAllocImpl
945    for UdpPortAlloc<'_, I, D, BT>
946{
947    const EPHEMERAL_RANGE: RangeInclusive<u16> = 49152..=65535;
948    type Id = DatagramFlowId<I::Addr, UdpRemotePort>;
949    type PortAvailableArg = ();
950
951    fn is_port_available(&self, id: &Self::Id, local_port: u16, (): &()) -> bool {
952        let Self(socketmap) = self;
953        // We can safely unwrap here, because the ports received in
954        // `is_port_available` are guaranteed to be in `EPHEMERAL_RANGE`.
955        let local_port = NonZeroU16::new(local_port).unwrap();
956        let DatagramFlowId { local_ip, remote_ip, remote_id } = id;
957        let conn = ConnAddr {
958            ip: ConnIpAddr { local: (*local_ip, local_port), remote: (*remote_ip, *remote_id) },
959            device: None,
960        };
961
962        // A port is free if there are no sockets currently using it, and if
963        // there are no sockets that are shadowing it.
964        AddrVec::from(conn).iter_shadows().all(|a| match &a {
965            AddrVec::Listen(l) => socketmap.listeners().get_by_addr(&l).is_none(),
966            AddrVec::Conn(c) => socketmap.conns().get_by_addr(&c).is_none(),
967        } && socketmap.get_shadower_counts(&a) == 0)
968    }
969}
970
971/// A UDP socket.
972#[derive(GenericOverIp, Derivative)]
973#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
974#[generic_over_ip(I, Ip)]
975pub struct UdpSocketId<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
976    datagram::StrongRc<I, D, Udp<BT>>,
977);
978
979impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> UdpSocketId<I, D, BT> {
980    /// Returns `SocketCookie` for the socket.
981    pub fn socket_cookie(&self) -> SocketCookie {
982        let Self(inner) = self;
983        SocketCookie::new(inner.resource_token())
984    }
985
986    /// Returns `SocketInfo` for the socket.
987    pub fn socket_info(&self) -> netstack3_base::socket::SocketInfo {
988        netstack3_base::socket::SocketInfo {
989            proto: I::map_ip(
990                (),
991                |()| EitherIpProto::V4(Ipv4Proto::Proto(IpProto::Udp)),
992                |()| EitherIpProto::V6(Ipv6Proto::Proto(IpProto::Udp)),
993            ),
994            cookie: self.socket_cookie(),
995        }
996    }
997}
998
999impl<CC, I, BT> SocketMetadata<CC> for UdpSocketId<I, CC::WeakDeviceId, BT>
1000where
1001    CC: StateContext<I, BT>,
1002    I: IpExt,
1003    BT: UdpBindingsContext<I, CC::DeviceId>,
1004{
1005    fn socket_info(&self, _core_ctx: &mut CC) -> netstack3_base::socket::SocketInfo {
1006        self.socket_info()
1007    }
1008
1009    fn marks(&self, core_ctx: &mut CC) -> Marks {
1010        core_ctx.with_socket_state(self, |_core_ctx, state| state.options().marks().clone())
1011    }
1012}
1013
1014impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> Clone for UdpSocketId<I, D, BT> {
1015    #[cfg_attr(feature = "instrumented", track_caller)]
1016    fn clone(&self) -> Self {
1017        let Self(rc) = self;
1018        Self(StrongRc::clone(rc))
1019    }
1020}
1021
1022impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
1023    From<datagram::StrongRc<I, D, Udp<BT>>> for UdpSocketId<I, D, BT>
1024{
1025    fn from(value: datagram::StrongRc<I, D, Udp<BT>>) -> Self {
1026        Self(value)
1027    }
1028}
1029
1030impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
1031    Borrow<datagram::StrongRc<I, D, Udp<BT>>> for UdpSocketId<I, D, BT>
1032{
1033    fn borrow(&self) -> &datagram::StrongRc<I, D, Udp<BT>> {
1034        let Self(rc) = self;
1035        rc
1036    }
1037}
1038
1039impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> PartialEq<WeakUdpSocketId<I, D, BT>>
1040    for UdpSocketId<I, D, BT>
1041{
1042    fn eq(&self, other: &WeakUdpSocketId<I, D, BT>) -> bool {
1043        let Self(rc) = self;
1044        let WeakUdpSocketId(weak) = other;
1045        StrongRc::weak_ptr_eq(rc, weak)
1046    }
1047}
1048
1049impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> Debug for UdpSocketId<I, D, BT> {
1050    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1051        let Self(rc) = self;
1052        f.debug_tuple("UdpSocketId").field(&StrongRc::debug_id(rc)).finish()
1053    }
1054}
1055
1056impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>
1057    DelegatedOrderedLockAccess<UdpSocketState<I, D, BT>> for UdpSocketId<I, D, BT>
1058{
1059    type Inner = datagram::ReferenceState<I, D, Udp<BT>>;
1060    fn delegate_ordered_lock_access(&self) -> &Self::Inner {
1061        let Self(rc) = self;
1062        &*rc
1063    }
1064}
1065
1066impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> UdpSocketId<I, D, BT> {
1067    /// Returns the inner state for this socket, sidestepping locking
1068    /// mechanisms.
1069    #[cfg(any(test, feature = "testutils"))]
1070    pub fn state(&self) -> &RwLock<UdpSocketState<I, D, BT>, BT::RwLock> {
1071        let Self(rc) = self;
1072        rc.state()
1073    }
1074
1075    /// Returns a means to debug outstanding references to this socket.
1076    pub fn debug_references(&self) -> impl Debug {
1077        let Self(rc) = self;
1078        StrongRc::debug_references(rc)
1079    }
1080
1081    /// Downgrades this ID to a weak reference.
1082    pub fn downgrade(&self) -> WeakUdpSocketId<I, D, BT> {
1083        let Self(rc) = self;
1084        WeakUdpSocketId(StrongRc::downgrade(rc))
1085    }
1086
1087    /// Returns external data associated with this socket.
1088    pub fn external_data(&self) -> &BT::ExternalData<I> {
1089        let Self(rc) = self;
1090        rc.external_data()
1091    }
1092
1093    /// Returns the counters tracked for this socket.
1094    pub fn counters(&self) -> &UdpCountersWithSocket<I> {
1095        let Self(rc) = self;
1096        rc.counters()
1097    }
1098}
1099
1100/// A weak reference to a UDP socket.
1101#[derive(GenericOverIp, Derivative)]
1102#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""), Clone(bound = ""))]
1103#[generic_over_ip(I, Ip)]
1104pub struct WeakUdpSocketId<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
1105    datagram::WeakRc<I, D, Udp<BT>>,
1106);
1107
1108impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> PartialEq<UdpSocketId<I, D, BT>>
1109    for WeakUdpSocketId<I, D, BT>
1110{
1111    fn eq(&self, other: &UdpSocketId<I, D, BT>) -> bool {
1112        PartialEq::eq(other, self)
1113    }
1114}
1115
1116impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> Debug for WeakUdpSocketId<I, D, BT> {
1117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1118        let Self(rc) = self;
1119        f.debug_tuple("WeakUdpSocketId").field(&rc.debug_id()).finish()
1120    }
1121}
1122
1123impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> WeakUdpSocketId<I, D, BT> {
1124    #[cfg_attr(feature = "instrumented", track_caller)]
1125    pub fn upgrade(&self) -> Option<UdpSocketId<I, D, BT>> {
1126        let Self(rc) = self;
1127        rc.upgrade().map(UdpSocketId)
1128    }
1129}
1130
1131/// A set containing all UDP sockets.
1132pub type UdpSocketSet<I, D, BT> = DatagramSocketSet<I, D, Udp<BT>>;
1133/// A UDP socket's state.
1134pub type UdpSocketState<I, D, BT> = DatagramSocketState<I, D, Udp<BT>>;
1135
1136/// Auxiliary information about an incoming UDP packet.
1137#[derive(Debug, GenericOverIp, Clone, PartialEq, Eq)]
1138#[generic_over_ip(I, Ip)]
1139pub struct UdpPacketMeta<I: Ip> {
1140    /// Source address specified in the IP header.
1141    pub src_ip: I::Addr,
1142
1143    /// Source port.
1144    pub src_port: Option<NonZeroU16>,
1145
1146    /// Destination address specified in the IP header.
1147    pub dst_ip: I::Addr,
1148
1149    /// Destination port.
1150    pub dst_port: NonZeroU16,
1151
1152    /// DSCP and ECN values received in Traffic Class or TOS field.
1153    pub dscp_and_ecn: DscpAndEcn,
1154}
1155
1156impl UdpPacketMeta<Ipv4> {
1157    fn to_ipv6_mapped(&self) -> UdpPacketMeta<Ipv6> {
1158        let Self { dst_ip, dst_port, src_ip, src_port, dscp_and_ecn } = self;
1159        UdpPacketMeta {
1160            dst_ip: dst_ip.to_ipv6_mapped().get(),
1161            dst_port: *dst_port,
1162            src_ip: src_ip.to_ipv6_mapped().get(),
1163            src_port: *src_port,
1164            dscp_and_ecn: *dscp_and_ecn,
1165        }
1166    }
1167}
1168
1169/// Errors that Bindings may encounter when receiving a UDP datagram.
1170pub enum ReceiveUdpError {
1171    /// The socket's receive queue is full and can't hold the datagram.
1172    QueueFull,
1173}
1174
1175/// The bindings context handling received UDP frames.
1176pub trait UdpReceiveBindingsContext<I: IpExt, D: StrongDeviceIdentifier>: UdpBindingsTypes {
1177    /// Receives a UDP packet on a socket.
1178    fn receive_udp(
1179        &mut self,
1180        id: &UdpSocketId<I, D::Weak, Self>,
1181        device_id: &D,
1182        meta: UdpPacketMeta<I>,
1183        body: &[u8],
1184    ) -> Result<(), ReceiveUdpError>;
1185
1186    /// Notifies Bindings that an error was set on a socket.
1187    fn on_socket_error(
1188        &mut self,
1189        id: &UdpSocketId<I, D::Weak, Self>,
1190        err: PendingDatagramSocketError,
1191    );
1192}
1193
1194/// The bindings context providing external types to UDP sockets.
1195///
1196/// # Discussion
1197///
1198/// We'd like this trait to take an `I` type parameter instead of using GAT to
1199/// get the IP version, however we end up with problems due to the shape of
1200/// [`DatagramSocketSpec`] and the underlying support for dual stack sockets.
1201///
1202/// This is completely fine for all known implementations, except for a rough
1203/// edge in fake tests bindings contexts that are already parameterized on I
1204/// themselves. This is still better than relying on `Box<dyn Any>` to keep the
1205/// external data in our references so we take the rough edge.
1206pub trait UdpBindingsTypes: DatagramBindingsTypes + MatcherBindingsTypes + Sized + 'static {
1207    /// Opaque bindings data held by core for a given IP version.
1208    type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
1209    /// A token representing resources allocated for an in-flight send operation.
1210    ///
1211    /// Core holds this token until the packet is either transmitted by the
1212    /// device or dropped along the egress path. This allows bindings to track
1213    /// send buffer capacity or other per-packet resources.
1214    type SendToken: Debug + Send + Sync + 'static;
1215}
1216
1217/// The bindings context for UDP.
1218pub trait UdpBindingsContext<I: IpExt, D: StrongDeviceIdentifier>:
1219    InstantContext
1220    + RngContext
1221    + UdpReceiveBindingsContext<I, D>
1222    + ReferenceNotifiers
1223    + UdpBindingsTypes
1224    + SocketOpsFilterBindingContext<D>
1225    + MatcherBindingsTypes
1226    + MarksBindingsContext
1227{
1228}
1229impl<
1230    I: IpExt,
1231    BC: InstantContext
1232        + RngContext
1233        + UdpReceiveBindingsContext<I, D>
1234        + ReferenceNotifiers
1235        + UdpBindingsTypes
1236        + SocketOpsFilterBindingContext<D>
1237        + MarksBindingsContext,
1238    D: StrongDeviceIdentifier,
1239> UdpBindingsContext<I, D> for BC
1240{
1241}
1242
1243/// An execution context for the UDP protocol which also provides access to state.
1244pub trait BoundStateContext<I: IpExt, BC: UdpBindingsContext<I, Self::DeviceId>>:
1245    DeviceIdContext<AnyDevice> + UdpStateContext
1246{
1247    /// The core context passed to the callback provided to methods.
1248    type IpSocketsCtx<'a>: TransportIpContext<I, BC>
1249        + MulticastMembershipHandler<I, BC>
1250        + CoreTxMetadataContext<UdpSocketTxMetadata<I, Self::WeakDeviceId, BC>, BC>
1251        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
1252
1253    /// The inner dual stack context.
1254    type DualStackContext: DualStackDatagramBoundStateContext<
1255            I,
1256            BC,
1257            Udp<BC>,
1258            DeviceId = Self::DeviceId,
1259            WeakDeviceId = Self::WeakDeviceId,
1260        >;
1261    /// The inner non dual stack context.
1262    type NonDualStackContext: NonDualStackDatagramBoundStateContext<
1263            I,
1264            BC,
1265            Udp<BC>,
1266            DeviceId = Self::DeviceId,
1267            WeakDeviceId = Self::WeakDeviceId,
1268        >;
1269
1270    /// Calls the function with an immutable reference to UDP sockets.
1271    fn with_bound_sockets<
1272        O,
1273        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &BoundSockets<I, Self::WeakDeviceId, BC>) -> O,
1274    >(
1275        &mut self,
1276        cb: F,
1277    ) -> O;
1278
1279    /// Calls the function with a mutable reference to UDP sockets.
1280    fn with_bound_sockets_mut<
1281        O,
1282        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut BoundSockets<I, Self::WeakDeviceId, BC>) -> O,
1283    >(
1284        &mut self,
1285        cb: F,
1286    ) -> O;
1287
1288    /// Returns a context for dual- or non-dual-stack operation.
1289    fn dual_stack_context(
1290        &self,
1291    ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext>;
1292
1293    /// Same as [`BoundStateContext::dual_stack_context`], but returns mutable references.
1294    fn dual_stack_context_mut(
1295        &mut self,
1296    ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
1297
1298    /// Calls the function without access to the UDP bound socket state.
1299    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
1300        &mut self,
1301        cb: F,
1302    ) -> O;
1303}
1304
1305/// Core context abstracting state access to UDP state.
1306pub trait StateContext<I: IpExt, BC: UdpBindingsContext<I, Self::DeviceId>>:
1307    DeviceIdContext<AnyDevice>
1308{
1309    /// The core context passed to the callback.
1310    type SocketStateCtx<'a>: BoundStateContext<I, BC>
1311        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
1312        + UdpStateContext;
1313
1314    /// Calls the function with mutable access to the set with all UDP
1315    /// sockets.
1316    fn with_all_sockets_mut<O, F: FnOnce(&mut UdpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
1317        &mut self,
1318        cb: F,
1319    ) -> O;
1320
1321    /// Calls the function with immutable access to the set with all UDP
1322    /// sockets.
1323    fn with_all_sockets<O, F: FnOnce(&UdpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
1324        &mut self,
1325        cb: F,
1326    ) -> O;
1327
1328    /// Calls the function without access to UDP socket state.
1329    fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
1330        &mut self,
1331        cb: F,
1332    ) -> O;
1333
1334    /// Calls the function with an immutable reference to the given socket's
1335    /// state.
1336    fn with_socket_state<
1337        O,
1338        F: FnOnce(&mut Self::SocketStateCtx<'_>, &UdpSocketState<I, Self::WeakDeviceId, BC>) -> O,
1339    >(
1340        &mut self,
1341        id: &UdpSocketId<I, Self::WeakDeviceId, BC>,
1342        cb: F,
1343    ) -> O;
1344
1345    /// Calls the function with a mutable reference to the given socket's state.
1346    fn with_socket_state_mut<
1347        O,
1348        F: FnOnce(&mut Self::SocketStateCtx<'_>, &mut UdpSocketState<I, Self::WeakDeviceId, BC>) -> O,
1349    >(
1350        &mut self,
1351        id: &UdpSocketId<I, Self::WeakDeviceId, BC>,
1352        cb: F,
1353    ) -> O;
1354
1355    /// Call `f` with each socket's state.
1356    fn for_each_socket<
1357        F: FnMut(
1358            &mut Self::SocketStateCtx<'_>,
1359            &UdpSocketId<I, Self::WeakDeviceId, BC>,
1360            &UdpSocketState<I, Self::WeakDeviceId, BC>,
1361        ),
1362    >(
1363        &mut self,
1364        cb: F,
1365    );
1366}
1367
1368/// Empty trait to work around coherence issues.
1369///
1370/// This serves only to convince the coherence checker that a particular blanket
1371/// trait implementation could only possibly conflict with other blanket impls
1372/// in this crate. It can be safely implemented for any type.
1373/// TODO(https://github.com/rust-lang/rust/issues/97811): Remove this once the
1374/// coherence checker doesn't require it.
1375pub trait UdpStateContext {}
1376
1377/// An execution context for UDP dual-stack operations.
1378pub trait DualStackBoundStateContext<I: IpExt, BC: UdpBindingsContext<I, Self::DeviceId>>:
1379    DeviceIdContext<AnyDevice>
1380{
1381    /// The core context passed to the callbacks to methods.
1382    type IpSocketsCtx<'a>: TransportIpContext<I, BC>
1383        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
1384        + CoreTxMetadataContext<UdpSocketTxMetadata<I, Self::WeakDeviceId, BC>, BC>
1385        // Allow creating IP sockets for the other IP version.
1386        + TransportIpContext<I::OtherVersion, BC>
1387        + CoreTxMetadataContext<UdpSocketTxMetadata<I::OtherVersion, Self::WeakDeviceId, BC>, BC>;
1388
1389    /// Calls the provided callback with mutable access to both the
1390    /// demultiplexing maps.
1391    fn with_both_bound_sockets_mut<
1392        O,
1393        F: FnOnce(
1394            &mut Self::IpSocketsCtx<'_>,
1395            &mut BoundSockets<I, Self::WeakDeviceId, BC>,
1396            &mut BoundSockets<I::OtherVersion, Self::WeakDeviceId, BC>,
1397        ) -> O,
1398    >(
1399        &mut self,
1400        cb: F,
1401    ) -> O;
1402
1403    /// Calls the provided callback with mutable access to the demultiplexing
1404    /// map for the other IP version.
1405    fn with_other_bound_sockets_mut<
1406        O,
1407        F: FnOnce(
1408            &mut Self::IpSocketsCtx<'_>,
1409            &mut BoundSockets<I::OtherVersion, Self::WeakDeviceId, BC>,
1410        ) -> O,
1411    >(
1412        &mut self,
1413        cb: F,
1414    ) -> O;
1415
1416    /// Calls the provided callback with access to the `IpSocketsCtx`.
1417    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
1418        &mut self,
1419        cb: F,
1420    ) -> O;
1421}
1422
1423/// An execution context for UDP non-dual-stack operations.
1424pub trait NonDualStackBoundStateContext<I: IpExt, BC: UdpBindingsContext<I, Self::DeviceId>>:
1425    DeviceIdContext<AnyDevice>
1426{
1427}
1428
1429/// An implementation of [`IpTransportContext`] for UDP.
1430pub enum UdpIpTransportContext {}
1431
1432fn early_demux_ip_packet<
1433    I: IpExt,
1434    B: ParseBuffer,
1435    BC: UdpBindingsContext<I, CC::DeviceId> + UdpBindingsContext<I::OtherVersion, CC::DeviceId>,
1436    CC: StateContext<I, BC>
1437        + StateContext<I::OtherVersion, BC>
1438        + UdpCounterContext<I, CC::WeakDeviceId, BC>
1439        + UdpCounterContext<I::OtherVersion, CC::WeakDeviceId, BC>,
1440>(
1441    core_ctx: &mut CC,
1442    device: &CC::DeviceId,
1443    src_ip: I::Addr,
1444    dst_ip: I::Addr,
1445    mut buffer: B,
1446) -> Option<I::DualStackBoundSocketId<CC::WeakDeviceId, Udp<BC>>> {
1447    trace_duration!(c"udp::early_demux");
1448
1449    let Ok(packet) = buffer.parse_with::<_, UdpPacketRaw<_>>(I::VERSION_MARKER) else {
1450        // If we fail to parse the packet then just return None. Invalid
1451        // packets are handled later.
1452        return None;
1453    };
1454
1455    let src_ip = SocketIpAddr::new(src_ip)?;
1456    let dst_ip = SocketIpAddr::new(dst_ip)?;
1457    let src_port = packet.src_port()?;
1458    let dst_port = packet.dst_port()?;
1459
1460    // Find a connected socket matching the packet.
1461    StateContext::<I, _>::with_bound_state_context(core_ctx, |core_ctx| {
1462        let device_weak = device.downgrade();
1463        DatagramBoundStateContext::<_, _, Udp<_>>::with_bound_sockets(
1464            core_ctx,
1465            |_core_ctx, bound_sockets| {
1466                bound_sockets
1467                    .lookup_connected((src_ip, src_port.into()), (dst_ip, dst_port), device_weak)
1468                    .map(|entry| entry.first().clone())
1469            },
1470        )
1471    })
1472}
1473fn receive_ip_packet<
1474    I: IpExt,
1475    B: BufferMut,
1476    H: IpHeaderInfo<I>,
1477    BC: UdpBindingsContext<I, CC::DeviceId> + UdpBindingsContext<I::OtherVersion, CC::DeviceId>,
1478    CC: StateContext<I, BC>
1479        + StateContext<I::OtherVersion, BC>
1480        + UdpCounterContext<I, CC::WeakDeviceId, BC>
1481        + UdpCounterContext<I::OtherVersion, CC::WeakDeviceId, BC>,
1482>(
1483    core_ctx: &mut CC,
1484    bindings_ctx: &mut BC,
1485    device: &CC::DeviceId,
1486    src_ip: I::RecvSrcAddr,
1487    dst_ip: SpecifiedAddr<I::Addr>,
1488    mut buffer: B,
1489    info: &mut LocalDeliveryPacketInfo<I, H>,
1490    early_demux_socket: Option<DualStackUdpSocketId<I, CC::WeakDeviceId, BC>>,
1491) -> Result<(), (B, I::IcmpError)> {
1492    let LocalDeliveryPacketInfo { meta, header_info, marks, packet_type, gso_info } = info;
1493    let ReceiveIpPacketMeta { broadcast, transparent_override, parsing_context } = meta;
1494
1495    trace_duration!("udp::receive_ip_packet");
1496    CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).rx.increment();
1497    trace!("received UDP packet: {:x?}", buffer.as_mut());
1498    let src_ip: I::Addr = src_ip.into_addr();
1499
1500    let Ok(packet) = buffer.parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1501        src_ip,
1502        dst_ip.get(),
1503        parsing_context,
1504    )) else {
1505        // There isn't much we can do if the UDP packet is
1506        // malformed.
1507        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).rx_malformed.increment();
1508        return Ok(());
1509    };
1510
1511    let src_ip = if let Some(src_ip) = SpecifiedAddr::new(src_ip) {
1512        match src_ip.try_into() {
1513            Ok(addr) => Some(addr),
1514            Err(AddrIsMappedError {}) => {
1515                CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1516                    .rx_mapped_addr
1517                    .increment();
1518                trace!("udp::receive_ip_packet: mapped source address");
1519                return Ok(());
1520            }
1521        }
1522    } else {
1523        None
1524    };
1525
1526    let dst_port = packet.dst_port();
1527    let (delivery_ip, delivery_port, require_transparent) = match transparent_override {
1528        Some(TransparentLocalDelivery { addr, port }) => (*addr, *port, true),
1529        None => (dst_ip, dst_port, false),
1530    };
1531
1532    let delivery_ip = match delivery_ip.try_into() {
1533        Ok(addr) => addr,
1534        Err(AddrIsMappedError {}) => {
1535            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1536                .rx_mapped_addr
1537                .increment();
1538            trace!("udp::receive_ip_packet: mapped destination address");
1539            return Ok(());
1540        }
1541    };
1542
1543    let src_port = packet.src_port();
1544    // Unfortunately, type inference isn't smart enough for us to just do
1545    // packet.parse_metadata().
1546    let parse_meta =
1547        ParsablePacket::<_, UdpParseArgs<I::Addr, &mut NetworkParsingContext>>::parse_metadata(
1548            &packet,
1549        );
1550
1551    /// The maximum number of socket IDs that are expected to receive a given
1552    /// packet. While it's possible for this number to be exceeded, it's
1553    /// unlikely.
1554    const MAX_EXPECTED_IDS: usize = 16;
1555
1556    /// Collection of sockets that will receive a packet.
1557    ///
1558    /// Making this a [`smallvec::SmallVec`] lets us keep all the retrieved ids
1559    /// on the stack most of the time. If there are more than
1560    /// [`MAX_EXPECTED_IDS`], this will spill and allocate on the heap.
1561    type Recipients<Id> = smallvec::SmallVec<[Id; MAX_EXPECTED_IDS]>;
1562
1563    let recipients = if let Some(socket) = early_demux_socket {
1564        Recipients::from_iter([socket])
1565    } else {
1566        StateContext::<I, _>::with_bound_state_context(core_ctx, |core_ctx| {
1567            let device_weak = device.downgrade();
1568            DatagramBoundStateContext::<_, _, Udp<_>>::with_bound_sockets(
1569                core_ctx,
1570                |_core_ctx, bound_sockets| {
1571                    lookup(
1572                        bound_sockets,
1573                        (src_ip, src_port),
1574                        (delivery_ip, delivery_port),
1575                        device_weak,
1576                        *broadcast,
1577                    )
1578                    .map(|result| match result {
1579                        LookupResult::Conn(id, _) | LookupResult::Listener(id, _) => id.clone(),
1580                    })
1581                    // Collect into an array on the stack.
1582                    .collect::<Recipients<_>>()
1583                },
1584            )
1585        })
1586    };
1587
1588    let meta = UdpPacketMeta {
1589        src_ip: src_ip.map_or(I::UNSPECIFIED_ADDRESS, SocketIpAddr::addr),
1590        src_port,
1591        dst_ip: *dst_ip,
1592        dst_port,
1593        dscp_and_ecn: header_info.dscp_and_ecn(),
1594    };
1595    let was_delivered = recipients.into_iter().fold(false, |was_delivered, lookup_result| {
1596        let delivered = try_dual_stack_deliver::<I, BC, CC, H>(
1597            core_ctx,
1598            bindings_ctx,
1599            lookup_result,
1600            device,
1601            &meta,
1602            marks,
1603            *packet_type,
1604            *gso_info,
1605            require_transparent,
1606            header_info,
1607            packet.clone(),
1608        );
1609        was_delivered | delivered
1610    });
1611
1612    if !was_delivered {
1613        buffer.undo_parse(parse_meta);
1614        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1615            .rx_unknown_dest_port
1616            .increment();
1617        Err((buffer, I::IcmpError::port_unreachable()))
1618    } else {
1619        Ok(())
1620    }
1621}
1622
1623/// Tries to deliver the given UDP packet to the given UDP socket.
1624fn try_deliver<
1625    I: IpExt,
1626    CC: StateContext<I, BC> + UdpCounterContext<I, CC::WeakDeviceId, BC>,
1627    BC: UdpBindingsContext<I, CC::DeviceId>,
1628    WireI: IpExt,
1629    H: IpHeaderInfo<WireI>,
1630>(
1631    core_ctx: &mut CC,
1632    bindings_ctx: &mut BC,
1633    id: &UdpSocketId<I, CC::WeakDeviceId, BC>,
1634    device_id: &CC::DeviceId,
1635    meta: UdpPacketMeta<I>,
1636    packet_marks: &Marks,
1637    packet_type: PacketType,
1638    gso_info: Option<GsoInfo>,
1639    require_transparent: bool,
1640    header_info: &H,
1641    packet: UdpPacket<&[u8]>,
1642) -> bool {
1643    let delivered = core_ctx.with_socket_state(&id, |core_ctx, state| {
1644        let should_deliver = match &state.inner {
1645            DatagramSocketStateInner::Bound(DatagramBoundSocketState {
1646                socket_type,
1647                original_bound_addr: _,
1648            }) => match socket_type {
1649                DatagramBoundSocketStateType::Connected(state) => {
1650                    match BoundStateContext::dual_stack_context_mut(core_ctx) {
1651                        MaybeDualStack::DualStack(dual_stack) => {
1652                            match dual_stack.ds_converter().convert(state) {
1653                                DualStackConnState::ThisStack(state) => state.should_receive(),
1654                                DualStackConnState::OtherStack(state) => state.should_receive(),
1655                            }
1656                        }
1657                        MaybeDualStack::NotDualStack(not_dual_stack) => {
1658                            not_dual_stack.nds_converter().convert(state).should_receive()
1659                        }
1660                    }
1661                }
1662                DatagramBoundSocketStateType::Listener(_) => true,
1663            },
1664            DatagramSocketStateInner::Unbound(_) => true,
1665        };
1666
1667        if !should_deliver {
1668            return None;
1669        }
1670
1671        // Transparently proxied packets are only delivered to transparent
1672        // sockets.
1673        if require_transparent && !state.options().transparent() {
1674            return None;
1675        }
1676
1677        let [ip_prefix, ip_options] = header_info.as_bytes();
1678        let [udp_header, data] = packet.as_bytes();
1679        let mut slices = [ip_prefix, ip_options, udp_header, data];
1680        let packet_buf = FragmentedByteSlice::new(&mut slices);
1681        let header_len = ip_prefix.len() + ip_options.len() + udp_header.len();
1682        let marks = BC::update_ingress_marks(*packet_marks, state.options().marks());
1683        let filter_result = bindings_ctx.socket_ops_filter().on_ingress(
1684            WireI::VERSION,
1685            packet_buf,
1686            header_len,
1687            device_id,
1688            id.socket_info(),
1689            &marks,
1690            packet_type,
1691            gso_info,
1692        );
1693
1694        match filter_result {
1695            SocketIngressFilterResult::Accept => {
1696                Some(bindings_ctx.receive_udp(id, device_id, meta, packet.body()))
1697            }
1698            SocketIngressFilterResult::Drop => None,
1699        }
1700    });
1701
1702    match delivered {
1703        None => false,
1704        Some(result) => {
1705            core_ctx.increment_both(id, |c| &c.rx_delivered);
1706            match result {
1707                Ok(()) => {}
1708                Err(ReceiveUdpError::QueueFull) => {
1709                    core_ctx.increment_both(id, |c| &c.rx_queue_full);
1710                }
1711            }
1712            true
1713        }
1714    }
1715}
1716
1717/// A wrapper for [`try_deliver`] that supports dual stack delivery.
1718fn try_dual_stack_deliver<
1719    I: IpExt,
1720    BC: UdpBindingsContext<I, CC::DeviceId> + UdpBindingsContext<I::OtherVersion, CC::DeviceId>,
1721    CC: StateContext<I, BC>
1722        + StateContext<I::OtherVersion, BC>
1723        + UdpCounterContext<I, CC::WeakDeviceId, BC>
1724        + UdpCounterContext<I::OtherVersion, CC::WeakDeviceId, BC>,
1725    H: IpHeaderInfo<I>,
1726>(
1727    core_ctx: &mut CC,
1728    bindings_ctx: &mut BC,
1729    socket: I::DualStackBoundSocketId<CC::WeakDeviceId, Udp<BC>>,
1730    device_id: &CC::DeviceId,
1731    meta: &UdpPacketMeta<I>,
1732    packet_marks: &Marks,
1733    packet_type: PacketType,
1734    gso_info: Option<GsoInfo>,
1735    require_transparent: bool,
1736    header_info: &H,
1737    packet: UdpPacket<&[u8]>,
1738) -> bool {
1739    #[derive(GenericOverIp)]
1740    #[generic_over_ip(I, Ip)]
1741    struct Inputs<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
1742        meta: &'a UdpPacketMeta<I>,
1743        socket: I::DualStackBoundSocketId<D, Udp<BT>>,
1744    }
1745
1746    struct Outputs<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
1747        meta: UdpPacketMeta<I>,
1748        socket: UdpSocketId<I, D, BT>,
1749    }
1750
1751    #[derive(GenericOverIp)]
1752    #[generic_over_ip(I, Ip)]
1753    enum DualStackOutputs<I: DualStackIpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
1754        CurrentStack(Outputs<I, D, BT>),
1755        OtherStack(Outputs<I::OtherVersion, D, BT>),
1756    }
1757
1758    let dual_stack_outputs = I::map_ip(
1759        Inputs { meta, socket },
1760        |Inputs { meta, socket }| match socket {
1761            EitherIpSocket::V4(socket) => {
1762                DualStackOutputs::CurrentStack(Outputs { meta: meta.clone(), socket })
1763            }
1764            EitherIpSocket::V6(socket) => {
1765                DualStackOutputs::OtherStack(Outputs { meta: meta.to_ipv6_mapped(), socket })
1766            }
1767        },
1768        |Inputs { meta, socket }| {
1769            DualStackOutputs::CurrentStack(Outputs { meta: meta.clone(), socket })
1770        },
1771    );
1772
1773    match dual_stack_outputs {
1774        DualStackOutputs::CurrentStack(Outputs { meta, socket }) => try_deliver(
1775            core_ctx,
1776            bindings_ctx,
1777            &socket,
1778            device_id,
1779            meta,
1780            packet_marks,
1781            packet_type,
1782            gso_info,
1783            require_transparent,
1784            header_info,
1785            packet,
1786        ),
1787        DualStackOutputs::OtherStack(Outputs { meta, socket }) => try_deliver(
1788            core_ctx,
1789            bindings_ctx,
1790            &socket,
1791            device_id,
1792            meta,
1793            packet_marks,
1794            packet_type,
1795            gso_info,
1796            require_transparent,
1797            header_info,
1798            packet,
1799        ),
1800    }
1801}
1802
1803fn receive_icmp_error<I, BC, CC>(
1804    core_ctx: &mut CC,
1805    bindings_ctx: &mut BC,
1806    device: &CC::DeviceId,
1807    original_src_ip: Option<SpecifiedAddr<I::Addr>>,
1808    original_dst_ip: SpecifiedAddr<I::Addr>,
1809    original_udp_packet: &[u8],
1810    err: I::ErrorCode,
1811) where
1812    I: IpExt,
1813    BC: UdpBindingsContext<I, CC::DeviceId> + UdpBindingsContext<I::OtherVersion, CC::DeviceId>,
1814    CC: StateContext<I, BC>
1815        + StateContext<I::OtherVersion, BC>
1816        + UdpCounterContext<I, CC::WeakDeviceId, BC>
1817        + UdpCounterContext<I::OtherVersion, CC::WeakDeviceId, BC>,
1818{
1819    let icmp_err = err.into();
1820    let Some(pending_err) = PendingDatagramSocketError::from_hard_icmp(icmp_err) else {
1821        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1822            .rx_icmp_error_soft
1823            .increment();
1824        return;
1825    };
1826
1827    CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1828        .rx_icmp_error_hard
1829        .increment();
1830
1831    let mut buffer = original_udp_packet;
1832    let packet = match buffer.parse_with::<_, UdpPacketRaw<_>>(I::VERSION_MARKER) {
1833        Ok(p) => p,
1834        Err(_) => {
1835            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1836                .rx_icmp_error_hard_malformed
1837                .increment();
1838            return;
1839        }
1840    };
1841
1842    let Some(orig_src_port) = packet.src_port() else {
1843        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1844            .rx_icmp_error_hard_malformed
1845            .increment();
1846        return;
1847    };
1848    let Some(orig_dst_port) = packet.dst_port() else {
1849        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1850            .rx_icmp_error_hard_malformed
1851            .increment();
1852        return;
1853    };
1854    let Some(orig_src_ip) = original_src_ip else {
1855        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1856            .rx_icmp_error_hard_malformed
1857            .increment();
1858        return;
1859    };
1860    let orig_src_ip = match SocketIpAddr::try_from(orig_src_ip) {
1861        Ok(ip) => ip,
1862        Err(AddrIsMappedError {}) => {
1863            debug!("ignoring ICMP error from IPv4-mapped-IPv6 source: {}", orig_src_ip);
1864            return;
1865        }
1866    };
1867    let orig_dst_ip = match SocketIpAddr::try_from(original_dst_ip) {
1868        Ok(ip) => ip,
1869        Err(AddrIsMappedError {}) => {
1870            debug!("ignoring ICMP error to IPv4-mapped-IPv6 destination: {}", original_dst_ip);
1871            return;
1872        }
1873    };
1874
1875    let socket_id = StateContext::<I, _>::with_bound_state_context(core_ctx, |core_ctx| {
1876        let device_weak = device.downgrade();
1877        DatagramBoundStateContext::<_, _, Udp<_>>::with_bound_sockets(
1878            core_ctx,
1879            |_core_ctx, bound_sockets| {
1880                bound_sockets
1881                    .lookup_connected(
1882                        (orig_dst_ip, UdpRemotePort::from(orig_dst_port)),
1883                        (orig_src_ip, orig_src_port),
1884                        device_weak,
1885                    )
1886                    .map(|entry| entry.first().clone())
1887            },
1888        )
1889    });
1890
1891    let Some(socket_id) = socket_id else {
1892        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
1893            .rx_icmp_error_hard_no_socket
1894            .increment();
1895        return;
1896    };
1897
1898    #[derive(GenericOverIp)]
1899    #[generic_over_ip(I, Ip)]
1900    struct Inputs<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
1901        socket_id: I::DualStackBoundSocketId<D, Udp<BT>>,
1902    }
1903
1904    #[derive(GenericOverIp)]
1905    #[generic_over_ip(I, Ip)]
1906    enum DualStackOutputs<I: DualStackIpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> {
1907        CurrentStack(UdpSocketId<I, D, BT>),
1908        OtherStack(UdpSocketId<I::OtherVersion, D, BT>),
1909    }
1910
1911    let dual_stack_outputs = I::map_ip(
1912        Inputs { socket_id },
1913        |Inputs { socket_id }| match socket_id {
1914            EitherIpSocket::V4(socket_id) => DualStackOutputs::CurrentStack(socket_id),
1915            EitherIpSocket::V6(socket_id) => DualStackOutputs::OtherStack(socket_id),
1916        },
1917        |Inputs { socket_id }| DualStackOutputs::CurrentStack(socket_id),
1918    );
1919
1920    match dual_stack_outputs {
1921        DualStackOutputs::CurrentStack(socket_id) => {
1922            core_ctx.increment_both(&socket_id, |c| &c.rx_icmp_error_hard_delivered);
1923            bindings_ctx.on_socket_error(&socket_id, pending_err);
1924        }
1925        DualStackOutputs::OtherStack(socket_id) => {
1926            ResourceCounterContext::<
1927                UdpSocketId<I::OtherVersion, CC::WeakDeviceId, BC>,
1928                UdpCountersWithSocket<I::OtherVersion>,
1929            >::increment_both(core_ctx, &socket_id, |c| {
1930                &c.rx_icmp_error_hard_delivered
1931            });
1932            bindings_ctx.on_socket_error(&socket_id, pending_err);
1933        }
1934    }
1935}
1936
1937/// Enables a blanket implementation of [`IpTransportContext`] for
1938/// [`UdpIpTransportContext`].
1939///
1940/// Implementing this marker trait for a type enables a blanket implementation
1941/// of `IpTransportContext` given the other requirements are met.
1942// For some reason rustc insists that this trait is not used, but it's required
1943// to mark types that want the blanket impl. This should be lifted when this
1944// type is pulled into the UDP crate and the trait is exported.
1945pub trait UseUdpIpTransportContextBlanket {}
1946
1947/// Alias for a SocketId that can reference either V4 or V6 socket.
1948pub type DualStackUdpSocketId<I, D, BT> =
1949    <I as DualStackBaseIpExt>::DualStackBoundSocketId<D, Udp<BT>>;
1950
1951impl<
1952    I: IpExt,
1953    BC: UdpBindingsContext<I, CC::DeviceId> + UdpBindingsContext<I::OtherVersion, CC::DeviceId>,
1954    CC: StateContext<I, BC>
1955        + StateContext<I::OtherVersion, BC>
1956        + UseUdpIpTransportContextBlanket
1957        + UdpCounterContext<I, CC::WeakDeviceId, BC>
1958        + UdpCounterContext<I::OtherVersion, CC::WeakDeviceId, BC>,
1959> IpTransportContext<I, BC, CC> for UdpIpTransportContext
1960{
1961    type EarlyDemuxSocket = DualStackUdpSocketId<I, CC::WeakDeviceId, BC>;
1962
1963    fn early_demux<B: ParseBuffer>(
1964        core_ctx: &mut CC,
1965        device: &CC::DeviceId,
1966        src_ip: I::Addr,
1967        dst_ip: I::Addr,
1968        buffer: B,
1969    ) -> Option<Self::EarlyDemuxSocket> {
1970        early_demux_ip_packet::<I, _, _, _>(core_ctx, device, src_ip, dst_ip, buffer)
1971    }
1972
1973    fn receive_icmp_error(
1974        core_ctx: &mut CC,
1975        bindings_ctx: &mut BC,
1976        device: &CC::DeviceId,
1977        original_src_ip: Option<SpecifiedAddr<I::Addr>>,
1978        original_dst_ip: SpecifiedAddr<I::Addr>,
1979        original_udp_packet: &[u8],
1980        err: I::ErrorCode,
1981    ) {
1982        CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).rx_icmp_error.increment();
1983        debug!(
1984            "UDP received ICMP error {:?} from {:?} to {:?}",
1985            err, original_dst_ip, original_src_ip
1986        );
1987
1988        receive_icmp_error::<I, _, _>(
1989            core_ctx,
1990            bindings_ctx,
1991            device,
1992            original_src_ip,
1993            original_dst_ip,
1994            original_udp_packet,
1995            err,
1996        )
1997    }
1998
1999    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
2000        core_ctx: &mut CC,
2001        bindings_ctx: &mut BC,
2002        device: &CC::DeviceId,
2003        src_ip: I::RecvSrcAddr,
2004        dst_ip: SpecifiedAddr<I::Addr>,
2005        buffer: B,
2006        info: &mut LocalDeliveryPacketInfo<I, H>,
2007        early_demux_socket: Option<Self::EarlyDemuxSocket>,
2008    ) -> Result<(), (B, I::IcmpError)> {
2009        receive_ip_packet::<I, _, _, _, _>(
2010            core_ctx,
2011            bindings_ctx,
2012            device,
2013            src_ip,
2014            dst_ip,
2015            buffer,
2016            info,
2017            early_demux_socket,
2018        )
2019    }
2020}
2021
2022/// An error encountered while sending a UDP packet to an alternate address.
2023#[derive(Error, Debug, PartialEq)]
2024pub enum SendToError {
2025    /// The socket is not writeable.
2026    #[error("not writeable")]
2027    NotWriteable,
2028    /// An error was encountered while trying to bind a local address for an
2029    /// unbound socket.
2030    #[error("local address error: {0}")]
2031    LocalAddress(#[from] LocalAddressError),
2032    /// An error was encountered while trying to create a temporary IP socket
2033    /// to use for the send operation.
2034    #[error("could not create a temporary connection socket: {0}")]
2035    CreateSock(#[from] IpSockCreationError),
2036    /// An error was encountered while trying to send via the temporary IP
2037    /// socket.
2038    #[error("could not send via temporary socket: {0}")]
2039    Send(#[from] IpSockSendError),
2040    /// There was a problem with the remote address relating to its zone.
2041    #[error("zone error: {0}")]
2042    Zone(#[from] ZonedAddressError),
2043    /// Disallow sending packets with a remote port of 0. See
2044    /// [`UdpRemotePort::Unset`] for the rationale.
2045    #[error("the remote port was unset")]
2046    RemotePortUnset,
2047    /// The remote address is mapped (i.e. an ipv4-mapped-ipv6 address), but the
2048    /// socket is not dual-stack enabled.
2049    #[error("the remote ip was unexpectedly an ipv4-mapped-ipv6 address")]
2050    RemoteUnexpectedlyMapped,
2051    /// The remote address is non-mapped (i.e not an ipv4-mapped-ipv6 address),
2052    /// but the socket is dual stack enabled and bound to a mapped address.
2053    #[error("the remote ip was unexpectedly not an ipv4-mapped-ipv6 address")]
2054    RemoteUnexpectedlyNonMapped,
2055}
2056
2057/// The UDP socket API.
2058pub struct UdpApi<I: Ip, C>(C, IpVersionMarker<I>);
2059
2060impl<I: Ip, C> UdpApi<I, C> {
2061    /// Creates a new `UdpApi` from `ctx`.
2062    pub fn new(ctx: C) -> Self {
2063        Self(ctx, IpVersionMarker::new())
2064    }
2065}
2066
2067/// A local alias for [`UdpSocketId`] for use in [`UdpApi`].
2068///
2069/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
2070/// associated type.
2071type UdpApiSocketId<I, C> = UdpSocketId<
2072    I,
2073    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
2074    <C as ContextPair>::BindingsContext,
2075>;
2076
2077impl<I, C> UdpApi<I, C>
2078where
2079    I: IpExt,
2080    C: ContextPair,
2081    C::CoreContext: StateContext<I, C::BindingsContext>
2082        + UdpCounterContext<
2083            I,
2084            <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
2085            C::BindingsContext,
2086        >
2087        // NB: This bound is somewhat redundant to StateContext but it helps the
2088        // compiler know we're using UDP datagram sockets.
2089        + DatagramStateContext<I, C::BindingsContext, Udp<C::BindingsContext>>,
2090    C::BindingsContext:
2091        UdpBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
2092    <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId: netstack3_base::InterfaceProperties<
2093            <C::BindingsContext as MatcherBindingsTypes>::DeviceClass,
2094        >,
2095{
2096    fn core_ctx(&mut self) -> &mut C::CoreContext {
2097        let Self(pair, IpVersionMarker { .. }) = self;
2098        pair.core_ctx()
2099    }
2100
2101    fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
2102        let Self(pair, IpVersionMarker { .. }) = self;
2103        pair.contexts()
2104    }
2105
2106    /// Get diagnostic information for sockets matching the provided matcher.
2107    pub fn bound_sockets_diagnostics<M, E>(&mut self, matcher: &M, results: &mut E)
2108    where
2109        M: IpSocketPropertiesMatcher<<C::BindingsContext as MatcherBindingsTypes>::DeviceClass>
2110            + ?Sized,
2111        E: Extend<UdpSocketDiagnostics<I>>,
2112    {
2113        DatagramStateContext::for_each_socket(self.core_ctx(), |ctx, id, state| {
2114            if !matcher
2115                .matches_ip_socket(&netstack3_datagram::SocketStateForMatching::new(state, id, ctx))
2116            {
2117                return;
2118            }
2119
2120            results.extend(UdpSocketDiagnostics::from_parts(state, id.socket_cookie()));
2121        });
2122    }
2123
2124    /// Disconnects all bound sockets matching the provided matcher.
2125    ///
2126    /// Returns the number of sockets that were disconnected.
2127    pub fn disconnect_bound<M>(&mut self, matcher: &M) -> usize
2128    where
2129        M: IpSocketPropertiesMatcher<<C::BindingsContext as MatcherBindingsTypes>::DeviceClass>
2130            + ?Sized,
2131    {
2132        // It's technically possible to avoid the Vec here by putting the
2133        // disconnection logic into the datagram crate and providing trait hooks
2134        // for specific functionality. This is how bound_socket_diagnostics does
2135        // things. However, this is not a performance-sensitive operation, and
2136        // doing so would add a lot of complexity to the code.
2137        let mut ids = Vec::new();
2138        DatagramStateContext::for_each_socket(self.core_ctx(), |ctx, id, state| {
2139            if matcher
2140                .matches_ip_socket(&netstack3_datagram::SocketStateForMatching::new(state, id, ctx))
2141            {
2142                ids.push(id.clone());
2143            }
2144        });
2145
2146        for id in &ids {
2147            self.datagram().disconnect_any_to_unbound(id);
2148
2149            let (_, bindings_ctx) = self.contexts();
2150            bindings_ctx.on_socket_error(id, PendingDatagramSocketError::Aborted);
2151        }
2152
2153        ids.len()
2154    }
2155
2156    fn datagram(&mut self) -> &mut DatagramApi<I, C, Udp<C::BindingsContext>> {
2157        let Self(pair, IpVersionMarker { .. }) = self;
2158        DatagramApi::wrap(pair)
2159    }
2160
2161    /// Creates a new unbound UDP socket with default external data.
2162    pub fn create(&mut self) -> UdpApiSocketId<I, C>
2163    where
2164        <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>: Default,
2165    {
2166        self.create_with(Default::default())
2167    }
2168
2169    /// Creates a new unbound UDP socket with provided external data.
2170    pub fn create_with(
2171        &mut self,
2172        external_data: <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>,
2173    ) -> UdpApiSocketId<I, C> {
2174        self.datagram().create(external_data)
2175    }
2176
2177    /// Connect a UDP socket
2178    ///
2179    /// `connect` binds `id` as a connection to the remote address and port. It
2180    /// is also bound to a local address and port, meaning that packets sent on
2181    /// this connection will always come from that address and port. The local
2182    /// address will be chosen based on the route to the remote address, and the
2183    /// local port will be chosen from the available ones.
2184    ///
2185    /// # Errors
2186    ///
2187    /// `connect` will fail in the following cases:
2188    /// - If both `local_ip` and `local_port` are specified but conflict with an
2189    ///   existing connection or listener
2190    /// - If one or both are left unspecified but there is still no way to
2191    ///   satisfy the request (e.g., `local_ip` is specified but there are no
2192    ///   available local ports for that address)
2193    /// - If there is no route to `remote_ip`
2194    /// - If `id` belongs to an already-connected socket
2195    pub fn connect(
2196        &mut self,
2197        id: &UdpApiSocketId<I, C>,
2198        remote_ip: Option<
2199            ZonedAddr<
2200                SpecifiedAddr<I::Addr>,
2201                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2202            >,
2203        >,
2204        remote_port: UdpRemotePort,
2205    ) -> Result<(), ConnectError> {
2206        debug!("connect on {id:?} to {remote_ip:?}:{remote_port:?}");
2207        self.datagram().connect(id, remote_ip, remote_port, ())
2208    }
2209
2210    /// Sets the bound device for a socket.
2211    ///
2212    /// Sets the device to be used for sending and receiving packets for a socket.
2213    /// If the socket is not currently bound to a local address and port, the device
2214    /// will be used when binding.
2215    pub fn set_device(
2216        &mut self,
2217        id: &UdpApiSocketId<I, C>,
2218        device_id: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
2219    ) -> Result<(), SocketError> {
2220        debug!("set device on {id:?} to {device_id:?}");
2221        self.datagram().set_device(id, device_id)
2222    }
2223
2224    /// Gets the device the specified socket is bound to.
2225    pub fn get_bound_device(
2226        &mut self,
2227        id: &UdpApiSocketId<I, C>,
2228    ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
2229        self.datagram().get_bound_device(id)
2230    }
2231
2232    /// Enable or disable dual stack operations on the given socket.
2233    ///
2234    /// This is notionally the inverse of the `IPV6_V6ONLY` socket option.
2235    ///
2236    /// # Errors
2237    ///
2238    /// Returns an error if the socket does not support the `IPV6_V6ONLY` socket
2239    /// option (e.g. an IPv4 socket).
2240    pub fn set_dual_stack_enabled(
2241        &mut self,
2242        id: &UdpApiSocketId<I, C>,
2243        enabled: bool,
2244    ) -> Result<(), SetDualStackEnabledError> {
2245        self.datagram()
2246            .with_other_stack_ip_options_mut_if_unbound(id, |other_stack| {
2247                I::map_ip(
2248                    (enabled, WrapOtherStackIpOptionsMut(other_stack)),
2249                    |(_enabled, _v4)| Err(NotDualStackCapableError.into()),
2250                    |(enabled, WrapOtherStackIpOptionsMut(other_stack))| {
2251                        let DualStackSocketState { dual_stack_enabled, .. } = other_stack;
2252                        *dual_stack_enabled = enabled;
2253                        Ok(())
2254                    },
2255                )
2256            })
2257            .map_err(|ExpectedUnboundError| {
2258                // NB: Match Linux and prefer to return `NotCapable` errors over
2259                // `SocketIsBound` errors, for IPv4 sockets.
2260                match I::VERSION {
2261                    IpVersion::V4 => NotDualStackCapableError.into(),
2262                    IpVersion::V6 => SetDualStackEnabledError::SocketIsBound,
2263                }
2264            })?
2265    }
2266
2267    /// Get the enabled state of dual stack operations on the given socket.
2268    ///
2269    /// This is notionally the inverse of the `IPV6_V6ONLY` socket option.
2270    ///
2271    /// # Errors
2272    ///
2273    /// Returns an error if the socket does not support the `IPV6_V6ONLY` socket
2274    /// option (e.g. an IPv4 socket).
2275    pub fn get_dual_stack_enabled(
2276        &mut self,
2277        id: &UdpApiSocketId<I, C>,
2278    ) -> Result<bool, NotDualStackCapableError> {
2279        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2280            I::map_ip(
2281                WrapOtherStackIpOptions(other_stack),
2282                |_v4| Err(NotDualStackCapableError),
2283                |WrapOtherStackIpOptions(other_stack)| {
2284                    let DualStackSocketState { dual_stack_enabled, .. } = other_stack;
2285                    Ok(*dual_stack_enabled)
2286                },
2287            )
2288        })
2289    }
2290
2291    /// Sets the POSIX `SO_REUSEADDR` option for the specified socket.
2292    ///
2293    /// # Errors
2294    ///
2295    /// Returns an error if the socket is already bound.
2296    pub fn set_posix_reuse_addr(
2297        &mut self,
2298        id: &UdpApiSocketId<I, C>,
2299        reuse_addr: bool,
2300    ) -> Result<(), ExpectedUnboundError> {
2301        self.datagram().update_sharing(id, |sharing| {
2302            sharing.reuse_addr = reuse_addr;
2303        })
2304    }
2305
2306    /// Gets the POSIX `SO_REUSEADDR` option for the specified socket.
2307    pub fn get_posix_reuse_addr(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2308        self.datagram().get_sharing(id).reuse_addr
2309    }
2310
2311    /// Sets the POSIX `SO_REUSEPORT` option for the specified socket.
2312    ///
2313    /// # Errors
2314    ///
2315    /// Returns an error if the socket is already bound.
2316    pub fn set_posix_reuse_port(
2317        &mut self,
2318        id: &UdpApiSocketId<I, C>,
2319        reuse_port: ReusePortOption,
2320    ) -> Result<(), ExpectedUnboundError> {
2321        self.datagram().update_sharing(id, |sharing| {
2322            sharing.reuse_port = reuse_port;
2323        })
2324    }
2325
2326    /// Gets the POSIX `SO_REUSEPORT` option for the specified socket.
2327    pub fn get_posix_reuse_port(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2328        self.datagram().get_sharing(id).reuse_port.is_enabled()
2329    }
2330
2331    /// Sets the specified socket's membership status for the given group.
2332    ///
2333    /// An error is returned if the membership change request is invalid
2334    /// (e.g. leaving a group that was not joined, or joining a group multiple
2335    /// times) or if the device to use to join is unspecified or conflicts with
2336    /// the existing socket state.
2337    pub fn set_multicast_membership(
2338        &mut self,
2339        id: &UdpApiSocketId<I, C>,
2340        multicast_group: MulticastAddr<I::Addr>,
2341        interface: MulticastMembershipInterfaceSelector<
2342            I::Addr,
2343            <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2344        >,
2345        want_membership: bool,
2346    ) -> Result<(), SetMulticastMembershipError> {
2347        debug!(
2348            "set multicast membership on {id:?} for group {multicast_group:?} with interface \
2349            selector: {interface:?}: want_membership={want_membership}"
2350        );
2351        self.datagram().set_multicast_membership(id, multicast_group, interface, want_membership)
2352    }
2353
2354    /// Sets the hop limit for packets sent by the socket to a unicast
2355    /// destination.
2356    ///
2357    /// Sets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2358    /// hop limits when `ip_version` is [`IpVersion::V6`].
2359    ///
2360    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2361    /// `ip_version` of [`IpVersion::V6`].
2362    pub fn set_unicast_hop_limit(
2363        &mut self,
2364        id: &UdpApiSocketId<I, C>,
2365        unicast_hop_limit: Option<NonZeroU8>,
2366        ip_version: IpVersion,
2367    ) -> Result<(), NotDualStackCapableError> {
2368        if ip_version == I::VERSION {
2369            return Ok(self
2370                .datagram()
2371                .update_ip_hop_limit(id, SocketHopLimits::set_unicast(unicast_hop_limit)));
2372        }
2373        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2374            I::map_ip(
2375                (IpInvariant(unicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack)),
2376                |(IpInvariant(_unicast_hop_limit), _v4)| Err(NotDualStackCapableError),
2377                |(IpInvariant(unicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack))| {
2378                    let DualStackSocketState {
2379                        socket_options:
2380                            DatagramIpSpecificSocketOptions {
2381                                hop_limits: SocketHopLimits { unicast, multicast: _, version: _ },
2382                                ..
2383                            },
2384                        ..
2385                    } = other_stack;
2386                    *unicast = unicast_hop_limit;
2387                    Ok(())
2388                },
2389            )
2390        })
2391    }
2392
2393    /// Sets the hop limit for packets sent by the socket to a multicast
2394    /// destination.
2395    ///
2396    /// Sets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2397    /// hop limits when `ip_version` is [`IpVersion::V6`].
2398    ///
2399    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2400    /// `ip_version` of [`IpVersion::V6`].
2401    pub fn set_multicast_hop_limit(
2402        &mut self,
2403        id: &UdpApiSocketId<I, C>,
2404        multicast_hop_limit: Option<NonZeroU8>,
2405        ip_version: IpVersion,
2406    ) -> Result<(), NotDualStackCapableError> {
2407        if ip_version == I::VERSION {
2408            return Ok(self
2409                .datagram()
2410                .update_ip_hop_limit(id, SocketHopLimits::set_multicast(multicast_hop_limit)));
2411        }
2412        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2413            I::map_ip(
2414                (IpInvariant(multicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack)),
2415                |(IpInvariant(_multicast_hop_limit), _v4)| Err(NotDualStackCapableError),
2416                |(IpInvariant(multicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack))| {
2417                    let DualStackSocketState {
2418                        socket_options:
2419                            DatagramIpSpecificSocketOptions {
2420                                hop_limits: SocketHopLimits { unicast: _, multicast, version: _ },
2421                                ..
2422                            },
2423                        ..
2424                    } = other_stack;
2425                    *multicast = multicast_hop_limit;
2426                    Ok(())
2427                },
2428            )
2429        })
2430    }
2431
2432    /// Gets the hop limit for packets sent by the socket to a unicast
2433    /// destination.
2434    ///
2435    /// Gets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2436    /// hop limits when `ip_version` is [`IpVersion::V6`].
2437    ///
2438    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2439    /// `ip_version` of [`IpVersion::V6`].
2440    pub fn get_unicast_hop_limit(
2441        &mut self,
2442        id: &UdpApiSocketId<I, C>,
2443        ip_version: IpVersion,
2444    ) -> Result<NonZeroU8, NotDualStackCapableError> {
2445        if ip_version == I::VERSION {
2446            return Ok(self.datagram().get_ip_hop_limits(id).unicast);
2447        }
2448        self.datagram().with_other_stack_ip_options_and_default_hop_limits(
2449            id,
2450            |other_stack, default_hop_limits| {
2451                I::map_ip_in(
2452                    (WrapOtherStackIpOptions(other_stack), IpInvariant(default_hop_limits)),
2453                    |_v4| Err(NotDualStackCapableError),
2454                    |(
2455                        WrapOtherStackIpOptions(other_stack),
2456                        IpInvariant(HopLimits { unicast: default_unicast, multicast: _ }),
2457                    )| {
2458                        let DualStackSocketState {
2459                            socket_options:
2460                                DatagramIpSpecificSocketOptions {
2461                                    hop_limits:
2462                                        SocketHopLimits { unicast, multicast: _, version: _ },
2463                                    ..
2464                                },
2465                            ..
2466                        } = other_stack;
2467                        Ok(unicast.unwrap_or(default_unicast))
2468                    },
2469                )
2470            },
2471        )?
2472    }
2473
2474    /// Gets the hop limit for packets sent by the socket to a multicast
2475    /// destination.
2476    ///
2477    /// Gets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2478    /// hop limits when `ip_version` is [`IpVersion::V6`].
2479    ///
2480    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2481    /// `ip_version` of [`IpVersion::V6`].
2482    pub fn get_multicast_hop_limit(
2483        &mut self,
2484        id: &UdpApiSocketId<I, C>,
2485        ip_version: IpVersion,
2486    ) -> Result<NonZeroU8, NotDualStackCapableError> {
2487        if ip_version == I::VERSION {
2488            return Ok(self.datagram().get_ip_hop_limits(id).multicast);
2489        }
2490        self.datagram().with_other_stack_ip_options_and_default_hop_limits(
2491            id,
2492            |other_stack, default_hop_limits| {
2493                I::map_ip_in(
2494                    (WrapOtherStackIpOptions(other_stack), IpInvariant(default_hop_limits)),
2495                    |_v4| Err(NotDualStackCapableError),
2496                    |(
2497                        WrapOtherStackIpOptions(other_stack),
2498                        IpInvariant(HopLimits { unicast: _, multicast: default_multicast }),
2499                    )| {
2500                        let DualStackSocketState {
2501                            socket_options:
2502                                DatagramIpSpecificSocketOptions {
2503                                    hop_limits:
2504                                        SocketHopLimits { unicast: _, multicast, version: _ },
2505                                    ..
2506                                },
2507                            ..
2508                        } = other_stack;
2509                        Ok(multicast.unwrap_or(default_multicast))
2510                    },
2511                )
2512            },
2513        )?
2514    }
2515
2516    /// Returns the configured multicast interface for the socket.
2517    pub fn get_multicast_interface(
2518        &mut self,
2519        id: &UdpApiSocketId<I, C>,
2520        ip_version: IpVersion,
2521    ) -> Result<
2522        Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2523        NotDualStackCapableError,
2524    > {
2525        if ip_version == I::VERSION {
2526            return Ok(self.datagram().get_multicast_interface(id));
2527        };
2528
2529        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2530            I::map_ip_in(
2531                WrapOtherStackIpOptions(other_stack),
2532                |_v4| Err(NotDualStackCapableError),
2533                |WrapOtherStackIpOptions(other_stack)| {
2534                    Ok(other_stack.socket_options.multicast_interface.clone())
2535                },
2536            )
2537        })
2538    }
2539
2540    /// Sets the multicast interface to `interface` for a socket.
2541    pub fn set_multicast_interface(
2542        &mut self,
2543        id: &UdpApiSocketId<I, C>,
2544        interface: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
2545        ip_version: IpVersion,
2546    ) -> Result<(), NotDualStackCapableError> {
2547        if ip_version == I::VERSION {
2548            self.datagram().set_multicast_interface(id, interface);
2549            return Ok(());
2550        };
2551
2552        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2553            I::map_ip(
2554                (IpInvariant(interface), WrapOtherStackIpOptionsMut(other_stack)),
2555                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2556                |(IpInvariant(interface), WrapOtherStackIpOptionsMut(other_stack))| {
2557                    other_stack.socket_options.multicast_interface =
2558                        interface.map(|device| device.downgrade());
2559                    Ok(())
2560                },
2561            )
2562        })
2563    }
2564
2565    /// Gets the transparent option.
2566    pub fn get_transparent(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2567        self.datagram().get_ip_transparent(id)
2568    }
2569
2570    /// Sets the transparent option.
2571    pub fn set_transparent(&mut self, id: &UdpApiSocketId<I, C>, value: bool) {
2572        self.datagram().set_ip_transparent(id, value)
2573    }
2574
2575    /// Gets the socket mark at the mark domain.
2576    pub fn get_mark(&mut self, id: &UdpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
2577        self.datagram().get_mark(id, domain)
2578    }
2579
2580    /// Sets the socket mark at the mark domain.
2581    pub fn set_mark(&mut self, id: &UdpApiSocketId<I, C>, domain: MarkDomain, mark: Mark) {
2582        self.datagram().set_mark(id, domain, mark)
2583    }
2584
2585    /// Gets the broadcast option.
2586    pub fn get_broadcast(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2587        self.datagram().with_both_stacks_ip_options(id, |this_stack, other_stack| {
2588            I::map_ip_in(
2589                (this_stack, WrapOtherStackIpOptions(other_stack)),
2590                |(this_stack, _)| this_stack.allow_broadcast.is_some(),
2591                |(_, WrapOtherStackIpOptions(other_stack))| {
2592                    other_stack.socket_options.allow_broadcast.is_some()
2593                },
2594            )
2595        })
2596    }
2597
2598    /// Sets the broadcast option.
2599    pub fn set_broadcast(&mut self, id: &UdpApiSocketId<I, C>, value: bool) {
2600        self.datagram().with_both_stacks_ip_options_mut(id, |this_stack, other_stack| {
2601            let value = value.then_some(());
2602            I::map_ip_in(
2603                (this_stack, WrapOtherStackIpOptionsMut(other_stack)),
2604                |(this_stack, _)| this_stack.allow_broadcast = value,
2605                |(_, WrapOtherStackIpOptionsMut(other_stack))| {
2606                    other_stack.socket_options.allow_broadcast = value;
2607                },
2608            )
2609        })
2610    }
2611
2612    /// Gets the loopback multicast option.
2613    pub fn get_multicast_loop(
2614        &mut self,
2615        id: &UdpApiSocketId<I, C>,
2616        ip_version: IpVersion,
2617    ) -> Result<bool, NotDualStackCapableError> {
2618        if ip_version == I::VERSION {
2619            return Ok(self.datagram().get_multicast_loop(id));
2620        };
2621
2622        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2623            I::map_ip_in(
2624                WrapOtherStackIpOptions(other_stack),
2625                |_v4| Err(NotDualStackCapableError),
2626                |WrapOtherStackIpOptions(other_stack)| {
2627                    Ok(other_stack.socket_options.multicast_loop)
2628                },
2629            )
2630        })
2631    }
2632
2633    /// Sets the loopback multicast option.
2634    pub fn set_multicast_loop(
2635        &mut self,
2636        id: &UdpApiSocketId<I, C>,
2637        value: bool,
2638        ip_version: IpVersion,
2639    ) -> Result<(), NotDualStackCapableError> {
2640        if ip_version == I::VERSION {
2641            self.datagram().set_multicast_loop(id, value);
2642            return Ok(());
2643        };
2644
2645        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2646            I::map_ip(
2647                (IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack)),
2648                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2649                |(IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack))| {
2650                    other_stack.socket_options.multicast_loop = value;
2651                    Ok(())
2652                },
2653            )
2654        })
2655    }
2656
2657    /// Gets the TCLASS/TOS option.
2658    pub fn get_dscp_and_ecn(
2659        &mut self,
2660        id: &UdpApiSocketId<I, C>,
2661        ip_version: IpVersion,
2662    ) -> Result<DscpAndEcn, NotDualStackCapableError> {
2663        if ip_version == I::VERSION {
2664            return Ok(self.datagram().get_dscp_and_ecn(id));
2665        };
2666
2667        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2668            I::map_ip_in(
2669                WrapOtherStackIpOptions(other_stack),
2670                |_v4| Err(NotDualStackCapableError),
2671                |WrapOtherStackIpOptions(other_stack)| Ok(other_stack.socket_options.dscp_and_ecn),
2672            )
2673        })
2674    }
2675
2676    /// Sets the TCLASS/TOS option.
2677    pub fn set_dscp_and_ecn(
2678        &mut self,
2679        id: &UdpApiSocketId<I, C>,
2680        value: DscpAndEcn,
2681        ip_version: IpVersion,
2682    ) -> Result<(), NotDualStackCapableError> {
2683        if ip_version == I::VERSION {
2684            self.datagram().set_dscp_and_ecn(id, value);
2685            return Ok(());
2686        };
2687
2688        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2689            I::map_ip(
2690                (IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack)),
2691                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2692                |(IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack))| {
2693                    other_stack.socket_options.dscp_and_ecn = value;
2694                    Ok(())
2695                },
2696            )
2697        })
2698    }
2699
2700    /// Disconnects a connected UDP socket.
2701    ///
2702    /// `disconnect` removes an existing connected socket and replaces it with a
2703    /// listening socket bound to the same local address and port.
2704    ///
2705    /// # Errors
2706    ///
2707    /// Returns an error if the socket is not connected.
2708    pub fn disconnect(&mut self, id: &UdpApiSocketId<I, C>) -> Result<(), ExpectedConnError> {
2709        debug!("disconnect {id:?}");
2710        self.datagram().disconnect_connected(id)
2711    }
2712
2713    /// Shuts down a socket for reading and/or writing.
2714    ///
2715    /// # Errors
2716    ///
2717    /// Returns an error if the socket is not connected.
2718    pub fn shutdown(
2719        &mut self,
2720        id: &UdpApiSocketId<I, C>,
2721        which: ShutdownType,
2722    ) -> Result<(), ExpectedConnError> {
2723        debug!("shutdown {id:?} {which:?}");
2724        self.datagram().shutdown_connected(id, which)
2725    }
2726
2727    /// Get the shutdown state for a socket.
2728    ///
2729    /// If the socket is not connected, or if `shutdown` was not called on it,
2730    /// returns `None`.
2731    pub fn get_shutdown(&mut self, id: &UdpApiSocketId<I, C>) -> Option<ShutdownType> {
2732        self.datagram().get_shutdown_connected(id)
2733    }
2734
2735    /// Removes a socket that was previously created.
2736    pub fn close(
2737        &mut self,
2738        id: UdpApiSocketId<I, C>,
2739    ) -> RemoveResourceResultWithContext<
2740        (
2741            UdpSocketDiagnosticsSeed<
2742                I,
2743                <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
2744                C::BindingsContext,
2745            >,
2746            <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>,
2747        ),
2748        C::BindingsContext,
2749    > {
2750        debug!("close {id:?}");
2751        let cookie = id.socket_cookie();
2752        self.datagram().close(id, move |reference_state| {
2753            let (state, external_data) = reference_state.into_state_and_external_data();
2754            (UdpSocketDiagnosticsSeed { state, cookie }, external_data)
2755        })
2756    }
2757
2758    /// Gets the [`SocketInfo`] associated with the UDP socket referenced by
2759    /// `id`.
2760    pub fn get_info(
2761        &mut self,
2762        id: &UdpApiSocketId<I, C>,
2763    ) -> SocketInfo<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
2764        self.datagram().get_info(id)
2765    }
2766
2767    /// Use an existing socket to listen for incoming UDP packets.
2768    ///
2769    /// `listen_udp` converts `id` into a listening socket and registers the new
2770    /// socket as a listener for incoming UDP packets on the given `port`. If
2771    /// `addr` is `None`, the listener is a "wildcard listener", and is bound to
2772    /// all local addresses. See the [`crate::transport`] module documentation
2773    /// for more details.
2774    ///
2775    /// If `addr` is `Some``, and `addr` is already bound on the given port
2776    /// (either by a listener or a connection), `listen_udp` will fail. If
2777    /// `addr` is `None`, and a wildcard listener is already bound to the given
2778    /// port, `listen_udp` will fail.
2779    ///
2780    /// # Errors
2781    ///
2782    /// Returns an error if the socket is not currently unbound.
2783    pub fn listen(
2784        &mut self,
2785        id: &UdpApiSocketId<I, C>,
2786        addr: Option<
2787            ZonedAddr<
2788                SpecifiedAddr<I::Addr>,
2789                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2790            >,
2791        >,
2792        port: Option<NonZeroU16>,
2793    ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
2794        debug!("listen on {id:?} on {addr:?}:{port:?}");
2795        self.datagram().listen(id, addr, port)
2796    }
2797
2798    /// Sends a UDP packet on an existing socket.
2799    ///
2800    /// # Errors
2801    ///
2802    /// Returns an error if the socket is not connected or the packet cannot be
2803    /// sent.
2804    pub fn send<B: BufferMut>(
2805        &mut self,
2806        id: &UdpApiSocketId<I, C>,
2807        body: B,
2808        send_token: <C::BindingsContext as UdpBindingsTypes>::SendToken,
2809    ) -> Result<(), Either<SendError, ExpectedConnError>> {
2810        self.core_ctx().increment_both(id, |c| &c.tx);
2811        self.datagram().send_conn(id, body, send_token).map_err(|err| {
2812            self.core_ctx().increment_both(id, |c| &c.tx_error);
2813            match err {
2814                DatagramSendError::NotConnected => Either::Right(ExpectedConnError),
2815                DatagramSendError::NotWriteable => Either::Left(SendError::NotWriteable),
2816                DatagramSendError::IpSock(err) => Either::Left(SendError::IpSock(err)),
2817                DatagramSendError::SerializeError(err) => match err {
2818                    UdpSerializeError::RemotePortUnset => Either::Left(SendError::RemotePortUnset),
2819                },
2820            }
2821        })
2822    }
2823
2824    /// Sends a UDP packet to the provided destination address.
2825    ///
2826    /// If this is called with an unbound socket, the socket will be implicitly
2827    /// bound. If that succeeds, the ID for the new socket is returned.
2828    ///
2829    /// # Errors
2830    ///
2831    /// Returns an error if the socket is unbound and connecting fails, or if the
2832    /// packet could not be sent. If the socket is unbound and connecting succeeds
2833    /// but sending fails, the socket remains connected.
2834    pub fn send_to<B: BufferMut>(
2835        &mut self,
2836        id: &UdpApiSocketId<I, C>,
2837        remote_ip: Option<
2838            ZonedAddr<
2839                SpecifiedAddr<I::Addr>,
2840                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2841            >,
2842        >,
2843        remote_port: UdpRemotePort,
2844        body: B,
2845        send_token: <C::BindingsContext as UdpBindingsTypes>::SendToken,
2846    ) -> Result<(), SendToError> {
2847        // Match Linux's behavior and verify the remote port is set.
2848        match remote_port {
2849            UdpRemotePort::Unset => return Err(SendToError::RemotePortUnset),
2850            UdpRemotePort::Set(_) => {}
2851        }
2852
2853        self.core_ctx().increment_both(id, |c| &c.tx);
2854        self.datagram().send_to(id, remote_ip, remote_port, body, send_token).map_err(|e| {
2855            self.core_ctx().increment_both(id, |c| &c.tx_error);
2856            match e {
2857                datagram::SendToError::LocalAddress(e) => SendToError::LocalAddress(e),
2858                datagram::SendToError::SerializeError(err) => match err {
2859                    UdpSerializeError::RemotePortUnset => SendToError::RemotePortUnset,
2860                },
2861                datagram::SendToError::NotWriteable => SendToError::NotWriteable,
2862                datagram::SendToError::Zone(e) => SendToError::Zone(e),
2863                datagram::SendToError::CreateAndSend(e) => match e {
2864                    IpSockCreateAndSendError::Send(e) => SendToError::Send(e),
2865                    IpSockCreateAndSendError::Create(e) => SendToError::CreateSock(e),
2866                },
2867                datagram::SendToError::RemoteUnexpectedlyMapped => {
2868                    SendToError::RemoteUnexpectedlyMapped
2869                }
2870                datagram::SendToError::RemoteUnexpectedlyNonMapped => {
2871                    SendToError::RemoteUnexpectedlyNonMapped
2872                }
2873            }
2874        })
2875    }
2876
2877    /// Collects all currently opened sockets, returning a cloned reference for
2878    /// each one.
2879    pub fn collect_all_sockets(&mut self) -> Vec<UdpApiSocketId<I, C>> {
2880        self.datagram().collect_all_sockets()
2881    }
2882
2883    /// Provides inspect data for UDP sockets.
2884    pub fn inspect<N>(&mut self, inspector: &mut N)
2885    where
2886        N: Inspector
2887            + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2888        for<'a> N::ChildInspector<'a>:
2889            InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2890    {
2891        DatagramStateContext::for_each_socket(self.core_ctx(), |_ctx, socket_id, socket_state| {
2892            inspector.record_debug_child(socket_id, |inspector| {
2893                socket_state.record_common_info(inspector);
2894                inspector.record_child("Counters", |inspector| {
2895                    inspector.delegate_inspectable(&CombinedUdpCounters {
2896                        with_socket: socket_id.counters(),
2897                        without_socket: None,
2898                    });
2899                });
2900            });
2901        });
2902    }
2903}
2904
2905/// Error when sending a packet on a socket.
2906#[derive(Copy, Clone, Debug, Eq, PartialEq, GenericOverIp, Error)]
2907#[generic_over_ip()]
2908pub enum SendError {
2909    /// The socket is not writeable.
2910    #[error("socket not writable")]
2911    NotWriteable,
2912    /// The packet couldn't be sent.
2913    #[error("packet couldn't be sent: {0}")]
2914    IpSock(#[from] IpSockSendError),
2915    /// Disallow sending packets with a remote port of 0. See
2916    /// [`UdpRemotePort::Unset`] for the rationale.
2917    #[error("remote port unset")]
2918    RemotePortUnset,
2919}
2920
2921impl<I: IpExt, BC: UdpBindingsContext<I, CC::DeviceId>, CC: StateContext<I, BC>>
2922    DatagramSpecStateContext<I, CC, BC> for Udp<BC>
2923{
2924    type SocketsStateCtx<'a> = CC::SocketStateCtx<'a>;
2925
2926    fn with_all_sockets_mut<O, F: FnOnce(&mut UdpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
2927        core_ctx: &mut CC,
2928        cb: F,
2929    ) -> O {
2930        StateContext::with_all_sockets_mut(core_ctx, cb)
2931    }
2932
2933    fn with_all_sockets<O, F: FnOnce(&UdpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
2934        core_ctx: &mut CC,
2935        cb: F,
2936    ) -> O {
2937        StateContext::with_all_sockets(core_ctx, cb)
2938    }
2939
2940    fn with_socket_state<
2941        O,
2942        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &UdpSocketState<I, CC::WeakDeviceId, BC>) -> O,
2943    >(
2944        core_ctx: &mut CC,
2945        id: &UdpSocketId<I, CC::WeakDeviceId, BC>,
2946        cb: F,
2947    ) -> O {
2948        StateContext::with_socket_state(core_ctx, id, cb)
2949    }
2950
2951    fn with_socket_state_mut<
2952        O,
2953        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut UdpSocketState<I, CC::WeakDeviceId, BC>) -> O,
2954    >(
2955        core_ctx: &mut CC,
2956        id: &UdpSocketId<I, CC::WeakDeviceId, BC>,
2957        cb: F,
2958    ) -> O {
2959        StateContext::with_socket_state_mut(core_ctx, id, cb)
2960    }
2961
2962    fn for_each_socket<
2963        F: FnMut(
2964            &mut Self::SocketsStateCtx<'_>,
2965            &UdpSocketId<I, CC::WeakDeviceId, BC>,
2966            &UdpSocketState<I, CC::WeakDeviceId, BC>,
2967        ),
2968    >(
2969        core_ctx: &mut CC,
2970        cb: F,
2971    ) {
2972        StateContext::for_each_socket(core_ctx, cb)
2973    }
2974}
2975
2976impl<
2977    I: IpExt,
2978    BC: UdpBindingsContext<I, CC::DeviceId>,
2979    CC: BoundStateContext<I, BC> + UdpStateContext,
2980> DatagramSpecBoundStateContext<I, CC, BC> for Udp<BC>
2981{
2982    type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
2983
2984    fn with_bound_sockets<O, F>(core_ctx: &mut CC, cb: F) -> O
2985    where
2986        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &UdpBoundSocketMap<I, CC::WeakDeviceId, BC>) -> O,
2987    {
2988        core_ctx.with_bound_sockets(|core_ctx, BoundSockets { bound_sockets }| {
2989            cb(core_ctx, bound_sockets)
2990        })
2991    }
2992
2993    fn with_bound_sockets_mut<O, F>(core_ctx: &mut CC, cb: F) -> O
2994    where
2995        F: FnOnce(
2996            &mut Self::IpSocketsCtx<'_>,
2997            &mut UdpBoundSocketMap<I, CC::WeakDeviceId, BC>,
2998        ) -> O,
2999    {
3000        core_ctx.with_bound_sockets_mut(|core_ctx, BoundSockets { bound_sockets }| {
3001            cb(core_ctx, bound_sockets)
3002        })
3003    }
3004
3005    type DualStackContext = CC::DualStackContext;
3006    type NonDualStackContext = CC::NonDualStackContext;
3007    fn dual_stack_context_mut(
3008        core_ctx: &mut CC,
3009    ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
3010        BoundStateContext::dual_stack_context_mut(core_ctx)
3011    }
3012
3013    fn dual_stack_context(
3014        core_ctx: &CC,
3015    ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
3016        BoundStateContext::dual_stack_context(core_ctx)
3017    }
3018
3019    fn with_transport_context<O, F>(core_ctx: &mut CC, cb: F) -> O
3020    where
3021        F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O,
3022    {
3023        core_ctx.with_transport_context(cb)
3024    }
3025}
3026
3027impl<
3028    BC: UdpBindingsContext<Ipv6, CC::DeviceId> + UdpBindingsContext<Ipv4, CC::DeviceId>,
3029    CC: DualStackBoundStateContext<Ipv6, BC> + UdpStateContext,
3030> DualStackDatagramSpecBoundStateContext<Ipv6, CC, BC> for Udp<BC>
3031{
3032    type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
3033    fn dual_stack_enabled(
3034        _core_ctx: &CC,
3035        ip_options: &IpOptions<Ipv6, CC::WeakDeviceId, Udp<BC>>,
3036    ) -> bool {
3037        let DualStackSocketState { dual_stack_enabled, .. } = ip_options.other_stack();
3038        *dual_stack_enabled
3039    }
3040
3041    fn to_other_socket_options<'a>(
3042        _core_ctx: &CC,
3043        state: &'a IpOptions<Ipv6, CC::WeakDeviceId, Udp<BC>>,
3044    ) -> &'a DatagramIpSpecificSocketOptions<Ipv4, CC::WeakDeviceId> {
3045        &state.other_stack().socket_options
3046    }
3047
3048    fn ds_converter(_core_ctx: &CC) -> impl DualStackConverter<Ipv6, CC::WeakDeviceId, Self> {
3049        ()
3050    }
3051
3052    fn to_other_bound_socket_id(
3053        _core_ctx: &CC,
3054        id: &UdpSocketId<Ipv6, CC::WeakDeviceId, BC>,
3055    ) -> EitherIpSocket<CC::WeakDeviceId, Udp<BC>> {
3056        EitherIpSocket::V6(id.clone())
3057    }
3058
3059    fn with_both_bound_sockets_mut<O, F>(core_ctx: &mut CC, cb: F) -> O
3060    where
3061        F: FnOnce(
3062            &mut Self::IpSocketsCtx<'_>,
3063            &mut UdpBoundSocketMap<Ipv6, CC::WeakDeviceId, BC>,
3064            &mut UdpBoundSocketMap<Ipv4, CC::WeakDeviceId, BC>,
3065        ) -> O,
3066    {
3067        core_ctx.with_both_bound_sockets_mut(
3068            |core_ctx,
3069             BoundSockets { bound_sockets: bound_first },
3070             BoundSockets { bound_sockets: bound_second }| {
3071                cb(core_ctx, bound_first, bound_second)
3072            },
3073        )
3074    }
3075
3076    fn with_other_bound_sockets_mut<
3077        O,
3078        F: FnOnce(
3079            &mut Self::IpSocketsCtx<'_>,
3080            &mut UdpBoundSocketMap<Ipv4, CC::WeakDeviceId, BC>,
3081        ) -> O,
3082    >(
3083        core_ctx: &mut CC,
3084        cb: F,
3085    ) -> O {
3086        core_ctx.with_other_bound_sockets_mut(|core_ctx, BoundSockets { bound_sockets }| {
3087            cb(core_ctx, bound_sockets)
3088        })
3089    }
3090
3091    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3092        core_ctx: &mut CC,
3093        cb: F,
3094    ) -> O {
3095        core_ctx.with_transport_context(|core_ctx| cb(core_ctx))
3096    }
3097}
3098
3099impl<
3100    BC: UdpBindingsContext<Ipv4, CC::DeviceId>,
3101    CC: BoundStateContext<Ipv4, BC> + NonDualStackBoundStateContext<Ipv4, BC> + UdpStateContext,
3102> NonDualStackDatagramSpecBoundStateContext<Ipv4, CC, BC> for Udp<BC>
3103{
3104    fn nds_converter(_core_ctx: &CC) -> impl NonDualStackConverter<Ipv4, CC::WeakDeviceId, Self> {
3105        ()
3106    }
3107}
3108
3109#[cfg(test)]
3110pub(crate) mod testutils {
3111    use alloc::borrow::ToOwned;
3112    use alloc::vec;
3113    use core::ops::{Deref, DerefMut};
3114    use netstack3_ip::IpLayerIpExt;
3115
3116    use net_types::ip::{IpAddr, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr, Ipv6SourceAddr};
3117    use netstack3_base::testutil::{
3118        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSendToken, FakeStrongDeviceId,
3119        FakeWeakDeviceId,
3120    };
3121    use netstack3_base::{CtxPair, ResourceCounterContext, UninstantiableWrapper};
3122    use netstack3_hashmap::HashMap;
3123    use netstack3_ip::device::IpDeviceStateIpExt;
3124    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeDualStackIpSocketCtx};
3125    use netstack3_ip::testutil::DualStackSendIpPacketMeta;
3126
3127    use super::*;
3128    /// A packet received on a socket.
3129    #[derive(Debug, Derivative, PartialEq)]
3130    #[derivative(Default(bound = ""))]
3131    pub(crate) struct SocketReceived<I: Ip> {
3132        pub(crate) packets: Vec<ReceivedPacket<I>>,
3133        #[derivative(Default(value = "usize::MAX"))]
3134        pub(crate) max_size: usize,
3135    }
3136
3137    #[derive(Debug, PartialEq)]
3138    pub(crate) struct ReceivedPacket<I: Ip> {
3139        pub(crate) meta: UdpPacketMeta<I>,
3140        pub(crate) body: Vec<u8>,
3141    }
3142
3143    impl<D: FakeStrongDeviceId> FakeUdpCoreCtx<D> {
3144        pub(crate) fn new_with_device<I: TestIpExt>(device: D) -> Self {
3145            Self::with_local_remote_ip_addrs_and_device(
3146                vec![local_ip::<I>()],
3147                vec![remote_ip::<I>()],
3148                device,
3149            )
3150        }
3151
3152        fn with_local_remote_ip_addrs_and_device<A: Into<SpecifiedAddr<IpAddr>>>(
3153            local_ips: Vec<A>,
3154            remote_ips: Vec<A>,
3155            device: D,
3156        ) -> Self {
3157            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
3158                device,
3159                local_ips,
3160                remote_ips,
3161            }]))
3162        }
3163
3164        pub(crate) fn with_ip_socket_ctx_state(state: FakeDualStackIpSocketCtx<D>) -> Self {
3165            Self {
3166                all_sockets: Default::default(),
3167                bound_sockets: FakeUdpBoundSocketsCtx {
3168                    bound_sockets: Default::default(),
3169                    ip_socket_ctx: InnerIpSocketCtx::with_state(state),
3170                },
3171            }
3172        }
3173    }
3174
3175    impl FakeUdpCoreCtx<FakeDeviceId> {
3176        pub(crate) fn new_fake_device<I: TestIpExt>() -> Self {
3177            Self::new_with_device::<I>(FakeDeviceId)
3178        }
3179
3180        pub(crate) fn with_local_remote_ip_addrs<A: Into<SpecifiedAddr<IpAddr>>>(
3181            local_ips: Vec<A>,
3182            remote_ips: Vec<A>,
3183        ) -> Self {
3184            Self::with_local_remote_ip_addrs_and_device(local_ips, remote_ips, FakeDeviceId)
3185        }
3186    }
3187
3188    /// UDP tests context pair.
3189    pub(crate) type FakeUdpCtx<D> = CtxPair<FakeUdpCoreCtx<D>, FakeUdpBindingsCtx<D>>;
3190
3191    #[derive(Derivative)]
3192    #[derivative(Default(bound = ""))]
3193    pub(crate) struct FakeBoundSockets<D: StrongDeviceIdentifier> {
3194        v4: BoundSockets<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
3195        v6: BoundSockets<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
3196    }
3197
3198    impl<D: StrongDeviceIdentifier> FakeBoundSockets<D> {
3199        fn bound_sockets<I: IpExt>(&self) -> &BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
3200            I::map_ip_out(self, |state| &state.v4, |state| &state.v6)
3201        }
3202
3203        fn bound_sockets_mut<I: IpExt>(
3204            &mut self,
3205        ) -> &mut BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
3206            I::map_ip_out(self, |state| &mut state.v4, |state| &mut state.v6)
3207        }
3208    }
3209
3210    pub(crate) struct FakeUdpBoundSocketsCtx<D: FakeStrongDeviceId> {
3211        pub(crate) bound_sockets: FakeBoundSockets<D>,
3212        pub(crate) ip_socket_ctx: InnerIpSocketCtx<D>,
3213    }
3214
3215    /// `FakeBindingsCtx` specialized for UDP.
3216    pub(crate) type FakeUdpBindingsCtx<D> = FakeBindingsCtx<(), (), FakeBindingsCtxState<D>, ()>;
3217
3218    /// The inner context providing a fake IP socket context to
3219    /// [`FakeUdpBoundSocketsCtx`].
3220    type InnerIpSocketCtx<D> =
3221        FakeCoreCtx<FakeDualStackIpSocketCtx<D>, DualStackSendIpPacketMeta<D>, D>;
3222
3223    pub(crate) type UdpFakeDeviceCtx = FakeUdpCtx<FakeDeviceId>;
3224    pub(crate) type UdpFakeDeviceCoreCtx = FakeUdpCoreCtx<FakeDeviceId>;
3225
3226    #[derive(Derivative)]
3227    #[derivative(Default(bound = ""))]
3228    pub(crate) struct FakeBindingsCtxState<D: StrongDeviceIdentifier> {
3229        received_v4:
3230            HashMap<WeakUdpSocketId<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv4>>,
3231        received_v6:
3232            HashMap<WeakUdpSocketId<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv6>>,
3233        pending_errors_v4: HashMap<
3234            WeakUdpSocketId<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
3235            Option<PendingDatagramSocketError>,
3236        >,
3237        pending_errors_v6: HashMap<
3238            WeakUdpSocketId<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
3239            Option<PendingDatagramSocketError>,
3240        >,
3241    }
3242
3243    impl<D: StrongDeviceIdentifier> FakeBindingsCtxState<D> {
3244        pub(crate) fn received<I: TestIpExt>(
3245            &self,
3246        ) -> &HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
3247        {
3248            #[derive(GenericOverIp)]
3249            #[generic_over_ip(I, Ip)]
3250            struct Wrap<'a, I: TestIpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
3251                &'a HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
3252            );
3253            let Wrap(map) = I::map_ip_out(
3254                self,
3255                |state| Wrap(&state.received_v4),
3256                |state| Wrap(&state.received_v6),
3257            );
3258            map
3259        }
3260
3261        pub(crate) fn received_mut<I: IpExt>(
3262            &mut self,
3263        ) -> &mut HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
3264        {
3265            #[derive(GenericOverIp)]
3266            #[generic_over_ip(I, Ip)]
3267            struct Wrap<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
3268                &'a mut HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
3269            );
3270            let Wrap(map) = I::map_ip_out(
3271                self,
3272                |state| Wrap(&mut state.received_v4),
3273                |state| Wrap(&mut state.received_v6),
3274            );
3275            map
3276        }
3277
3278        pub(crate) fn pending_errors_mut<I: IpExt>(
3279            &mut self,
3280        ) -> &mut HashMap<
3281            WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>,
3282            Option<PendingDatagramSocketError>,
3283        > {
3284            #[derive(GenericOverIp)]
3285            #[generic_over_ip(I, Ip)]
3286            struct Wrap<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
3287                &'a mut HashMap<WeakUdpSocketId<I, D, BT>, Option<PendingDatagramSocketError>>,
3288            );
3289            let Wrap(map) = I::map_ip_out(
3290                self,
3291                |state| Wrap(&mut state.pending_errors_v4),
3292                |state| Wrap(&mut state.pending_errors_v6),
3293            );
3294            map
3295        }
3296
3297        pub(crate) fn take_pending_error<I: IpExt>(
3298            &mut self,
3299            id: &WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>,
3300        ) -> Option<PendingDatagramSocketError> {
3301            self.pending_errors_mut::<I>().remove(id).flatten()
3302        }
3303
3304        pub(crate) fn socket_data<I: TestIpExt>(
3305            &self,
3306        ) -> HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, Vec<&'_ [u8]>> {
3307            self.received::<I>()
3308                .iter()
3309                .map(|(id, SocketReceived { packets, .. })| {
3310                    (
3311                        id.clone(),
3312                        packets.iter().map(|ReceivedPacket { meta: _, body }| &body[..]).collect(),
3313                    )
3314                })
3315                .collect()
3316        }
3317    }
3318
3319    impl<I: IpExt, D: StrongDeviceIdentifier> UdpReceiveBindingsContext<I, D>
3320        for FakeUdpBindingsCtx<D>
3321    {
3322        fn receive_udp(
3323            &mut self,
3324            id: &UdpSocketId<I, D::Weak, Self>,
3325            _device_id: &D,
3326            meta: UdpPacketMeta<I>,
3327            body: &[u8],
3328        ) -> Result<(), ReceiveUdpError> {
3329            let SocketReceived { packets, max_size } =
3330                self.state.received_mut::<I>().entry(id.downgrade()).or_default();
3331            if packets.len() < *max_size {
3332                packets.push(ReceivedPacket { meta, body: body.to_owned() });
3333                Ok(())
3334            } else {
3335                Err(ReceiveUdpError::QueueFull)
3336            }
3337        }
3338
3339        fn on_socket_error(
3340            &mut self,
3341            id: &UdpSocketId<I, D::Weak, Self>,
3342            err: PendingDatagramSocketError,
3343        ) {
3344            let _ = self.state.pending_errors_mut::<I>().insert(id.downgrade(), Some(err));
3345        }
3346    }
3347
3348    impl<D: StrongDeviceIdentifier> UdpBindingsTypes for FakeUdpBindingsCtx<D> {
3349        type ExternalData<I: Ip> = ();
3350        type SendToken = FakeSendToken;
3351    }
3352
3353    /// Utilities for accessing locked internal state in tests.
3354    impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> UdpSocketId<I, D, BT> {
3355        fn get(&self) -> impl Deref<Target = UdpSocketState<I, D, BT>> + '_ {
3356            self.state().read()
3357        }
3358
3359        fn get_mut(&self) -> impl DerefMut<Target = UdpSocketState<I, D, BT>> + '_ {
3360            self.state().write()
3361        }
3362    }
3363
3364    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpCoreCtx<D> {
3365        type DeviceId = D;
3366        type WeakDeviceId = FakeWeakDeviceId<D>;
3367    }
3368
3369    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpBoundSocketsCtx<D> {
3370        type DeviceId = D;
3371        type WeakDeviceId = FakeWeakDeviceId<D>;
3372    }
3373
3374    impl<I: TestIpExt, D: FakeStrongDeviceId> StateContext<I, FakeUdpBindingsCtx<D>>
3375        for FakeUdpCoreCtx<D>
3376    {
3377        type SocketStateCtx<'a> = FakeUdpBoundSocketsCtx<D>;
3378
3379        fn with_all_sockets_mut<
3380            O,
3381            F: FnOnce(&mut UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
3382        >(
3383            &mut self,
3384            cb: F,
3385        ) -> O {
3386            cb(self.all_sockets.socket_set_mut())
3387        }
3388
3389        fn with_all_sockets<
3390            O,
3391            F: FnOnce(&UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
3392        >(
3393            &mut self,
3394            cb: F,
3395        ) -> O {
3396            cb(self.all_sockets.socket_set())
3397        }
3398
3399        fn with_socket_state<
3400            O,
3401            F: FnOnce(
3402                &mut Self::SocketStateCtx<'_>,
3403                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3404            ) -> O,
3405        >(
3406            &mut self,
3407            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3408            cb: F,
3409        ) -> O {
3410            cb(&mut self.bound_sockets, &id.get())
3411        }
3412
3413        fn with_socket_state_mut<
3414            O,
3415            F: FnOnce(
3416                &mut Self::SocketStateCtx<'_>,
3417                &mut UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3418            ) -> O,
3419        >(
3420            &mut self,
3421            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3422            cb: F,
3423        ) -> O {
3424            cb(&mut self.bound_sockets, &mut id.get_mut())
3425        }
3426
3427        fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
3428            &mut self,
3429            cb: F,
3430        ) -> O {
3431            cb(&mut self.bound_sockets)
3432        }
3433
3434        fn for_each_socket<
3435            F: FnMut(
3436                &mut Self::SocketStateCtx<'_>,
3437                &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3438                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3439            ),
3440        >(
3441            &mut self,
3442            mut cb: F,
3443        ) {
3444            self.all_sockets.socket_set().keys().for_each(|id| {
3445                let id = UdpSocketId::from(id.clone());
3446                cb(&mut self.bound_sockets, &id, &id.get());
3447            })
3448        }
3449    }
3450
3451    impl<I: TestIpExt, D: FakeStrongDeviceId> BoundStateContext<I, FakeUdpBindingsCtx<D>>
3452        for FakeUdpBoundSocketsCtx<D>
3453    {
3454        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3455        type DualStackContext = I::UdpDualStackBoundStateContext<D>;
3456        type NonDualStackContext = I::UdpNonDualStackBoundStateContext<D>;
3457
3458        fn with_bound_sockets<
3459            O,
3460            F: FnOnce(
3461                &mut Self::IpSocketsCtx<'_>,
3462                &BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3463            ) -> O,
3464        >(
3465            &mut self,
3466            cb: F,
3467        ) -> O {
3468            let Self { bound_sockets, ip_socket_ctx } = self;
3469            cb(ip_socket_ctx, bound_sockets.bound_sockets())
3470        }
3471
3472        fn with_bound_sockets_mut<
3473            O,
3474            F: FnOnce(
3475                &mut Self::IpSocketsCtx<'_>,
3476                &mut BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3477            ) -> O,
3478        >(
3479            &mut self,
3480            cb: F,
3481        ) -> O {
3482            let Self { bound_sockets, ip_socket_ctx } = self;
3483            cb(ip_socket_ctx, bound_sockets.bound_sockets_mut())
3484        }
3485
3486        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3487            &mut self,
3488            cb: F,
3489        ) -> O {
3490            cb(&mut self.ip_socket_ctx)
3491        }
3492
3493        fn dual_stack_context(
3494            &self,
3495        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
3496            struct Wrap<'a, I: TestIpExt, D: FakeStrongDeviceId + 'static>(
3497                MaybeDualStack<
3498                    &'a I::UdpDualStackBoundStateContext<D>,
3499                    &'a I::UdpNonDualStackBoundStateContext<D>,
3500                >,
3501            );
3502            // TODO(https://fxbug.dev/42082123): Replace this with a derived impl.
3503            impl<'a, I: TestIpExt, NewIp: TestIpExt, D: FakeStrongDeviceId + 'static>
3504                GenericOverIp<NewIp> for Wrap<'a, I, D>
3505            {
3506                type Type = Wrap<'a, NewIp, D>;
3507            }
3508
3509            let Wrap(context) = I::map_ip_out(
3510                self,
3511                |this| Wrap(MaybeDualStack::NotDualStack(this)),
3512                |this| Wrap(MaybeDualStack::DualStack(this)),
3513            );
3514            context
3515        }
3516
3517        fn dual_stack_context_mut(
3518            &mut self,
3519        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
3520            struct Wrap<'a, I: TestIpExt, D: FakeStrongDeviceId + 'static>(
3521                MaybeDualStack<
3522                    &'a mut I::UdpDualStackBoundStateContext<D>,
3523                    &'a mut I::UdpNonDualStackBoundStateContext<D>,
3524                >,
3525            );
3526            // TODO(https://fxbug.dev/42082123): Replace this with a derived impl.
3527            impl<'a, I: TestIpExt, NewIp: TestIpExt, D: FakeStrongDeviceId + 'static>
3528                GenericOverIp<NewIp> for Wrap<'a, I, D>
3529            {
3530                type Type = Wrap<'a, NewIp, D>;
3531            }
3532
3533            let Wrap(context) = I::map_ip_out(
3534                self,
3535                |this| Wrap(MaybeDualStack::NotDualStack(this)),
3536                |this| Wrap(MaybeDualStack::DualStack(this)),
3537            );
3538            context
3539        }
3540    }
3541
3542    impl<D: FakeStrongDeviceId + 'static> UdpStateContext for FakeUdpBoundSocketsCtx<D> {}
3543
3544    impl<D: FakeStrongDeviceId> NonDualStackBoundStateContext<Ipv4, FakeUdpBindingsCtx<D>>
3545        for FakeUdpBoundSocketsCtx<D>
3546    {
3547    }
3548
3549    impl<D: FakeStrongDeviceId> DualStackBoundStateContext<Ipv6, FakeUdpBindingsCtx<D>>
3550        for FakeUdpBoundSocketsCtx<D>
3551    {
3552        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3553
3554        fn with_both_bound_sockets_mut<
3555            O,
3556            F: FnOnce(
3557                &mut Self::IpSocketsCtx<'_>,
3558                &mut BoundSockets<Ipv6, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3559                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3560            ) -> O,
3561        >(
3562            &mut self,
3563            cb: F,
3564        ) -> O {
3565            let Self { ip_socket_ctx, bound_sockets: FakeBoundSockets { v4, v6 } } = self;
3566            cb(ip_socket_ctx, v6, v4)
3567        }
3568
3569        fn with_other_bound_sockets_mut<
3570            O,
3571            F: FnOnce(
3572                &mut Self::IpSocketsCtx<'_>,
3573                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3574            ) -> O,
3575        >(
3576            &mut self,
3577            cb: F,
3578        ) -> O {
3579            DualStackBoundStateContext::with_both_bound_sockets_mut(
3580                self,
3581                |core_ctx, _bound, other_bound| cb(core_ctx, other_bound),
3582            )
3583        }
3584
3585        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3586            &mut self,
3587            cb: F,
3588        ) -> O {
3589            cb(&mut self.ip_socket_ctx)
3590        }
3591    }
3592
3593    /// Ip packet delivery for the [`FakeUdpCoreCtx`].
3594    impl<I: IpLayerIpExt + TestIpExt, D: FakeStrongDeviceId>
3595        IpTransportContext<I, FakeUdpBindingsCtx<D>, FakeUdpCoreCtx<D>> for UdpIpTransportContext
3596    {
3597        type EarlyDemuxSocket = DualStackUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>;
3598
3599        fn early_demux<B: ParseBuffer>(
3600            core_ctx: &mut FakeUdpCoreCtx<D>,
3601            device: &D,
3602            src_ip: I::Addr,
3603            dst_ip: I::Addr,
3604            buffer: B,
3605        ) -> Option<Self::EarlyDemuxSocket> {
3606            early_demux_ip_packet::<I, _, _, _>(core_ctx, device, src_ip, dst_ip, buffer)
3607        }
3608
3609        fn receive_icmp_error(
3610            core_ctx: &mut FakeUdpCoreCtx<D>,
3611            bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3612            device: &D,
3613            original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3614            original_dst_ip: SpecifiedAddr<I::Addr>,
3615            original_body: &[u8],
3616            err: I::ErrorCode,
3617        ) {
3618            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx)
3619                .rx_icmp_error
3620                .increment();
3621            receive_icmp_error::<I, _, _>(
3622                core_ctx,
3623                bindings_ctx,
3624                device,
3625                original_src_ip,
3626                original_dst_ip,
3627                original_body,
3628                err,
3629            )
3630        }
3631
3632        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3633            core_ctx: &mut FakeUdpCoreCtx<D>,
3634            bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3635            device: &D,
3636            src_ip: I::RecvSrcAddr,
3637            dst_ip: SpecifiedAddr<I::Addr>,
3638            buffer: B,
3639            info: &mut LocalDeliveryPacketInfo<I, H>,
3640            early_demux_socket: Option<Self::EarlyDemuxSocket>,
3641        ) -> Result<(), (B, I::IcmpError)> {
3642            receive_ip_packet::<I, _, _, _, _>(
3643                core_ctx,
3644                bindings_ctx,
3645                device,
3646                src_ip,
3647                dst_ip,
3648                buffer,
3649                info,
3650                early_demux_socket,
3651            )
3652        }
3653    }
3654
3655    #[derive(Derivative)]
3656    #[derivative(Default(bound = ""))]
3657    pub(crate) struct FakeDualStackSocketState<D: StrongDeviceIdentifier> {
3658        v4: UdpSocketSet<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
3659        v6: UdpSocketSet<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
3660        udpv4_counters_with_socket: UdpCountersWithSocket<Ipv4>,
3661        udpv6_counters_with_socket: UdpCountersWithSocket<Ipv6>,
3662        udpv4_counters_without_socket: UdpCountersWithoutSocket<Ipv4>,
3663        udpv6_counters_without_socket: UdpCountersWithoutSocket<Ipv6>,
3664    }
3665
3666    impl<D: StrongDeviceIdentifier> FakeDualStackSocketState<D> {
3667        fn socket_set<I: IpExt>(&self) -> &UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3668            I::map_ip_out(self, |dual| &dual.v4, |dual| &dual.v6)
3669        }
3670
3671        fn socket_set_mut<I: IpExt>(
3672            &mut self,
3673        ) -> &mut UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3674            I::map_ip_out(self, |dual| &mut dual.v4, |dual| &mut dual.v6)
3675        }
3676
3677        fn udp_counters_with_socket<I: Ip>(&self) -> &UdpCountersWithSocket<I> {
3678            I::map_ip_out(
3679                self,
3680                |dual| &dual.udpv4_counters_with_socket,
3681                |dual| &dual.udpv6_counters_with_socket,
3682            )
3683        }
3684        fn udp_counters_without_socket<I: Ip>(&self) -> &UdpCountersWithoutSocket<I> {
3685            I::map_ip_out(
3686                self,
3687                |dual| &dual.udpv4_counters_without_socket,
3688                |dual| &dual.udpv6_counters_without_socket,
3689            )
3690        }
3691    }
3692    pub(crate) struct FakeUdpCoreCtx<D: FakeStrongDeviceId> {
3693        pub(crate) bound_sockets: FakeUdpBoundSocketsCtx<D>,
3694        // NB: socket sets are last in the struct so all the strong refs are
3695        // dropped before the primary refs contained herein.
3696        pub(crate) all_sockets: FakeDualStackSocketState<D>,
3697    }
3698
3699    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithSocket<I>> for FakeUdpCoreCtx<D> {
3700        fn counters(&self) -> &UdpCountersWithSocket<I> {
3701            &self.all_sockets.udp_counters_with_socket()
3702        }
3703    }
3704
3705    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithoutSocket<I>>
3706        for FakeUdpCoreCtx<D>
3707    {
3708        fn counters(&self) -> &UdpCountersWithoutSocket<I> {
3709            &self.all_sockets.udp_counters_without_socket()
3710        }
3711    }
3712
3713    impl<I: DualStackIpExt, D: FakeStrongDeviceId>
3714        ResourceCounterContext<
3715            UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3716            UdpCountersWithSocket<I>,
3717        > for FakeUdpCoreCtx<D>
3718    {
3719        fn per_resource_counters<'a>(
3720            &'a self,
3721            resource: &'a UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3722        ) -> &'a UdpCountersWithSocket<I> {
3723            resource.counters()
3724        }
3725    }
3726
3727    pub(crate) fn local_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3728        I::get_other_ip_address(1)
3729    }
3730
3731    pub(crate) fn remote_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3732        I::get_other_ip_address(2)
3733    }
3734
3735    pub(crate) trait BaseTestIpExt:
3736        netstack3_base::testutil::TestIpExt + IpExt + IpDeviceStateIpExt
3737    {
3738        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3739            DualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3740        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3741            NonDualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3742        fn into_recv_src_addr(addr: Self::Addr) -> Self::RecvSrcAddr;
3743    }
3744
3745    impl BaseTestIpExt for Ipv4 {
3746        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3747            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3748
3749        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3750            FakeUdpBoundSocketsCtx<D>;
3751
3752        fn into_recv_src_addr(addr: Ipv4Addr) -> Ipv4SourceAddr {
3753            Ipv4SourceAddr::new(addr).unwrap_or_else(|| panic!("{addr} is not a valid source addr"))
3754        }
3755    }
3756
3757    impl BaseTestIpExt for Ipv6 {
3758        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3759            FakeUdpBoundSocketsCtx<D>;
3760        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3761            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3762
3763        fn into_recv_src_addr(addr: Ipv6Addr) -> Ipv6SourceAddr {
3764            Ipv6SourceAddr::new(addr).unwrap_or_else(|| panic!("{addr} is not a valid source addr"))
3765        }
3766    }
3767
3768    pub(crate) trait TestIpExt: BaseTestIpExt<OtherVersion: BaseTestIpExt> {}
3769    impl<I: BaseTestIpExt<OtherVersion: BaseTestIpExt>> TestIpExt for I {}
3770}
3771
3772#[cfg(test)]
3773mod tests {
3774    use alloc::borrow::ToOwned;
3775    use alloc::vec;
3776    use core::convert::TryInto as _;
3777    use core::num::NonZeroU16;
3778    use packet_formats::icmp::{Icmpv4DestUnreachableCode, Icmpv6DestUnreachableCode};
3779
3780    use assert_matches::assert_matches;
3781    use ip_test_macro::ip_test;
3782    use itertools::Itertools as _;
3783    use net_declare::{net_ip_v4 as ip_v4, net_ip_v6};
3784    use net_types::ip::{IpAddr, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
3785    use net_types::{
3786        AddrAndZone, LinkLocalAddr, MulticastAddr, Scope as _, ScopeableAddress as _, ZonedAddr,
3787    };
3788    use netstack3_base::socket::{SocketIpAddrExt as _, StrictlyZonedAddr};
3789    use netstack3_base::sync::PrimaryRc;
3790    use netstack3_base::testutil::{
3791        FakeDeviceId, FakeReferencyDeviceId, FakeSendToken, FakeStrongDeviceId, FakeWeakDeviceId,
3792        MultipleDevicesId, TestIpExt as _, set_logger_for_test,
3793    };
3794    use netstack3_base::{
3795        CounterCollection, Icmpv4ErrorCode, Icmpv6ErrorCode, Mark, MarkDomain,
3796        NetworkSerializationContext, RemoteAddressError, SendFrameErrorReason,
3797    };
3798    use netstack3_datagram::MulticastInterfaceSelector;
3799    use netstack3_hashmap::{HashMap, HashSet};
3800    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeDualStackIpSocketCtx};
3801    use netstack3_ip::testutil::{DualStackSendIpPacketMeta, FakeIpHeaderInfo};
3802    use netstack3_ip::{IpLayerIpExt, IpPacketDestination, ResolveRouteError, SendIpPacketMeta};
3803    use packet::{Buf, Serializer};
3804    use test_case::test_case;
3805
3806    use crate::internal::counters::testutil::{
3807        CounterExpectationsWithSocket, CounterExpectationsWithoutSocket,
3808    };
3809
3810    use super::testutils::{
3811        FakeUdpBindingsCtx, FakeUdpCoreCtx, FakeUdpCtx, ReceivedPacket, SocketReceived, TestIpExt,
3812        UdpFakeDeviceCoreCtx, UdpFakeDeviceCtx, local_ip, remote_ip,
3813    };
3814    use super::*;
3815
3816    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
3817    enum EarlyDemuxMode {
3818        Enabled,
3819        Disabled,
3820    }
3821    use EarlyDemuxMode::{Disabled as NoEarlyDemux, Enabled as WithEarlyDemux};
3822
3823    /// Helper function to inject an UDP packet with the provided parameters.
3824    fn receive_udp_packet<I, D, CC>(
3825        core_ctx: &mut CC,
3826        bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3827        device: D,
3828        meta: UdpPacketMeta<I>,
3829        body: &[u8],
3830        early_demux_mode: EarlyDemuxMode,
3831    ) -> Result<(), I::IcmpError>
3832    where
3833        UdpIpTransportContext: IpTransportContext<I, FakeUdpBindingsCtx<D>, CC>,
3834        I: IpLayerIpExt + TestIpExt,
3835        D: FakeStrongDeviceId,
3836        CC: DeviceIdContext<AnyDevice, DeviceId = D>,
3837    {
3838        let UdpPacketMeta { src_ip, src_port, dst_ip, dst_port, dscp_and_ecn } = meta;
3839        let builder = UdpPacketBuilder::new(src_ip, dst_ip, src_port, dst_port);
3840
3841        let buffer = builder
3842            .wrap_body(Buf::new(body.to_owned(), ..))
3843            .serialize_vec_outer(&mut NetworkSerializationContext::default())
3844            .unwrap()
3845            .into_inner();
3846
3847        let early_demux_socket = match early_demux_mode {
3848            EarlyDemuxMode::Enabled => {
3849                <UdpIpTransportContext as IpTransportContext<I, _, _>>::early_demux(
3850                    core_ctx,
3851                    &device,
3852                    src_ip,
3853                    dst_ip,
3854                    buffer.as_ref(),
3855                )
3856            }
3857            EarlyDemuxMode::Disabled => None,
3858        };
3859
3860        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
3861            core_ctx,
3862            bindings_ctx,
3863            &device,
3864            I::into_recv_src_addr(src_ip),
3865            SpecifiedAddr::new(dst_ip).unwrap(),
3866            buffer,
3867            &mut LocalDeliveryPacketInfo {
3868                header_info: FakeIpHeaderInfo { dscp_and_ecn, ..Default::default() },
3869                ..Default::default()
3870            },
3871            early_demux_socket,
3872        )
3873        .map_err(|(_buffer, e)| e)
3874    }
3875
3876    const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(100).unwrap();
3877    const OTHER_LOCAL_PORT: NonZeroU16 = LOCAL_PORT.checked_add(1).unwrap();
3878    const REMOTE_PORT: NonZeroU16 = NonZeroU16::new(200).unwrap();
3879    const OTHER_REMOTE_PORT: NonZeroU16 = REMOTE_PORT.checked_add(1).unwrap();
3880
3881    fn conn_addr<I>(
3882        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3883    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3884    where
3885        I: TestIpExt,
3886    {
3887        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3888        let remote_ip = SocketIpAddr::try_from(remote_ip::<I>()).unwrap();
3889        ConnAddr {
3890            ip: ConnIpAddr {
3891                local: (local_ip, LOCAL_PORT),
3892                remote: (remote_ip, REMOTE_PORT.into()),
3893            },
3894            device,
3895        }
3896        .into()
3897    }
3898
3899    fn local_listener<I>(
3900        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3901    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3902    where
3903        I: TestIpExt,
3904    {
3905        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3906        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: Some(local_ip) }, device }
3907            .into()
3908    }
3909
3910    fn wildcard_listener<I>(
3911        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3912    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3913    where
3914        I: TestIpExt,
3915    {
3916        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: None }, device }.into()
3917    }
3918
3919    #[track_caller]
3920    fn assert_counters<
3921        'a,
3922        I: IpExt,
3923        D: WeakDeviceIdentifier,
3924        BT: UdpBindingsTypes,
3925        CC: UdpCounterContext<I, D, BT>,
3926    >(
3927        core_ctx: &CC,
3928        with_socket_expects: CounterExpectationsWithSocket,
3929        without_socket_expects: CounterExpectationsWithoutSocket,
3930        per_socket_expects: impl IntoIterator<
3931            Item = (&'a UdpSocketId<I, D, BT>, CounterExpectationsWithSocket),
3932        >,
3933    ) {
3934        assert_eq!(
3935            CounterContext::<UdpCountersWithSocket<I>>::counters(core_ctx).cast(),
3936            with_socket_expects
3937        );
3938        assert_eq!(
3939            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).cast(),
3940            without_socket_expects
3941        );
3942        for (id, expects) in per_socket_expects.into_iter() {
3943            assert_eq!(core_ctx.per_resource_counters(id).cast(), expects);
3944        }
3945    }
3946
3947    #[ip_test(I)]
3948    #[test_case(conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))), [
3949            conn_addr(None), local_listener(Some(FakeWeakDeviceId(FakeDeviceId))), local_listener(None),
3950            wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)
3951        ]; "conn with device")]
3952    #[test_case(local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3953        [local_listener(None), wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)];
3954        "local listener with device")]
3955    #[test_case(wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), [wildcard_listener(None)];
3956        "wildcard listener with device")]
3957    #[test_case(conn_addr(None), [local_listener(None), wildcard_listener(None)]; "conn no device")]
3958    #[test_case(local_listener(None), [wildcard_listener(None)]; "local listener no device")]
3959    #[test_case(wildcard_listener(None), []; "wildcard listener no device")]
3960    fn test_udp_addr_vec_iter_shadows_conn<I: IpExt, D: WeakDeviceIdentifier, const N: usize>(
3961        addr: AddrVec<I, D, UdpAddrSpec>,
3962        expected_shadows: [AddrVec<I, D, UdpAddrSpec>; N],
3963    ) {
3964        assert_eq!(addr.iter_shadows().collect::<HashSet<_>>(), HashSet::from(expected_shadows));
3965    }
3966
3967    #[ip_test(I)]
3968    fn test_iter_receiving_addrs<I: TestIpExt>() {
3969        let addr = ConnIpAddr {
3970            local: (SocketIpAddr::try_from(local_ip::<I>()).unwrap(), LOCAL_PORT),
3971            remote: (SocketIpAddr::try_from(remote_ip::<I>()).unwrap(), REMOTE_PORT.into()),
3972        };
3973        assert_eq!(
3974            iter_receiving_addrs::<I, _>(addr, FakeWeakDeviceId(FakeDeviceId)).collect::<Vec<_>>(),
3975            vec![
3976                // A socket connected on exactly the receiving vector has precedence.
3977                conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))),
3978                // Connected takes precedence over listening with device match.
3979                conn_addr(None),
3980                local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3981                // Specific IP takes precedence over device match.
3982                local_listener(None),
3983                wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3984                // Fallback to least specific
3985                wildcard_listener(None)
3986            ]
3987        );
3988    }
3989
3990    /// Tests UDP listeners over different IP versions.
3991    ///
3992    /// Tests that a listener can be created, that the context receives packet
3993    /// notifications for that listener, and that we can send data using that
3994    /// listener.
3995    #[ip_test(I)]
3996    fn test_listen_udp<I: TestIpExt>() {
3997        set_logger_for_test();
3998        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3999        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4000        let local_ip = local_ip::<I>();
4001        let remote_ip = remote_ip::<I>();
4002        let socket = api.create();
4003        // Create a listener on the local port, bound to the local IP:
4004        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4005            .expect("listen_udp failed");
4006
4007        // Inject a packet and check that the context receives it:
4008        let body = [1, 2, 3, 4, 5];
4009        let (core_ctx, bindings_ctx) = api.contexts();
4010        let meta = UdpPacketMeta::<I> {
4011            src_ip: remote_ip.get(),
4012            src_port: Some(REMOTE_PORT),
4013            dst_ip: local_ip.get(),
4014            dst_port: LOCAL_PORT,
4015            dscp_and_ecn: DscpAndEcn::default(),
4016        };
4017        receive_udp_packet(
4018            core_ctx,
4019            bindings_ctx,
4020            FakeDeviceId,
4021            meta.clone(),
4022            &body[..],
4023            WithEarlyDemux,
4024        )
4025        .expect("receive udp packet should succeed");
4026
4027        assert_eq!(
4028            bindings_ctx.state.received::<I>(),
4029            &HashMap::from([(
4030                socket.downgrade(),
4031                SocketReceived {
4032                    packets: vec![ReceivedPacket { meta, body: body.into() }],
4033                    max_size: usize::MAX
4034                }
4035            )])
4036        );
4037
4038        // Send a packet providing a local ip:
4039        api.send_to(
4040            &socket,
4041            Some(ZonedAddr::Unzoned(remote_ip)),
4042            REMOTE_PORT.into(),
4043            Buf::new(body.to_vec(), ..),
4044            FakeSendToken::default(),
4045        )
4046        .expect("send_to suceeded");
4047
4048        // And send a packet that doesn't:
4049        api.send_to(
4050            &socket,
4051            Some(ZonedAddr::Unzoned(remote_ip)),
4052            REMOTE_PORT.into(),
4053            Buf::new(body.to_vec(), ..),
4054            FakeSendToken::default(),
4055        )
4056        .expect("send_to succeeded");
4057        let frames = api.core_ctx().bound_sockets.ip_socket_ctx.frames();
4058        assert_eq!(frames.len(), 2);
4059        let check_frame =
4060            |(meta, frame_body): &(DualStackSendIpPacketMeta<FakeDeviceId>, Vec<u8>)| {
4061                let SendIpPacketMeta {
4062                    device: _,
4063                    src_ip,
4064                    dst_ip,
4065                    destination,
4066                    proto,
4067                    ttl: _,
4068                    mtu: _,
4069                    dscp_and_ecn: _,
4070                } = meta.try_as::<I>().unwrap();
4071                assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
4072                assert_eq!(src_ip, &local_ip);
4073                assert_eq!(dst_ip, &remote_ip);
4074                assert_eq!(proto, &IpProto::Udp.into());
4075                let mut buf = &frame_body[..];
4076                let udp_packet =
4077                    UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
4078                        .expect("Parsed sent UDP packet");
4079                assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
4080                assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
4081                assert_eq!(udp_packet.body(), &body[..]);
4082            };
4083        check_frame(&frames[0]);
4084        check_frame(&frames[1]);
4085    }
4086
4087    #[ip_test(I)]
4088    fn test_receive_udp_queue_full<I: TestIpExt>() {
4089        set_logger_for_test();
4090        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4091        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4092        let local_ip = local_ip::<I>();
4093        let remote_ip = remote_ip::<I>();
4094        let socket = api.create();
4095
4096        // Create a listener on the local port, bound to the local IP:
4097        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4098            .expect("listen_udp failed");
4099
4100        let (core_ctx, bindings_ctx) = api.contexts();
4101        // Simulate a full RX queue.
4102        {
4103            let received =
4104                bindings_ctx.state.received_mut::<I>().entry(socket.downgrade()).or_default();
4105            received.max_size = 0;
4106        }
4107
4108        // Inject a packet.
4109        let body = [1, 2, 3, 4, 5];
4110        let meta = UdpPacketMeta::<I> {
4111            src_ip: remote_ip.get(),
4112            src_port: Some(REMOTE_PORT),
4113            dst_ip: local_ip.get(),
4114            dst_port: LOCAL_PORT,
4115            dscp_and_ecn: DscpAndEcn::default(),
4116        };
4117        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..], WithEarlyDemux)
4118            .expect("receive udp packet should succeed");
4119
4120        assert_counters(
4121            api.core_ctx(),
4122            CounterExpectationsWithSocket {
4123                rx_delivered: 1,
4124                rx_queue_full: 1,
4125                ..Default::default()
4126            },
4127            CounterExpectationsWithoutSocket { rx: 1, ..Default::default() },
4128            [(
4129                &socket,
4130                CounterExpectationsWithSocket {
4131                    rx_delivered: 1,
4132                    rx_queue_full: 1,
4133                    ..Default::default()
4134                },
4135            )],
4136        )
4137    }
4138
4139    /// Tests that UDP packets without a connection are dropped.
4140    ///
4141    /// Tests that receiving a UDP packet on a port over which there isn't a
4142    /// listener causes the packet to be dropped correctly.
4143    #[ip_test(I)]
4144    fn test_udp_drop<I: TestIpExt>() {
4145        set_logger_for_test();
4146        let UdpFakeDeviceCtx { mut core_ctx, mut bindings_ctx } =
4147            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4148        let local_ip = local_ip::<I>();
4149        let remote_ip = remote_ip::<I>();
4150
4151        let meta = UdpPacketMeta::<I> {
4152            src_ip: remote_ip.get(),
4153            src_port: Some(REMOTE_PORT),
4154            dst_ip: local_ip.get(),
4155            dst_port: LOCAL_PORT,
4156            dscp_and_ecn: DscpAndEcn::default(),
4157        };
4158        let body = [1, 2, 3, 4, 5];
4159        assert_eq!(
4160            receive_udp_packet(
4161                &mut core_ctx,
4162                &mut bindings_ctx,
4163                FakeDeviceId,
4164                meta,
4165                &body[..],
4166                WithEarlyDemux,
4167            ),
4168            Err(I::IcmpError::port_unreachable())
4169        );
4170        assert_eq!(&bindings_ctx.state.socket_data::<I>(), &HashMap::new());
4171    }
4172
4173    /// Tests that UDP connections can be created and data can be transmitted
4174    /// over it.
4175    ///
4176    /// Only tests with specified local port and address bounds.
4177    #[ip_test(I)]
4178    #[test_case(EarlyDemuxMode::Enabled; "with early demux")]
4179    #[test_case(EarlyDemuxMode::Disabled; "without early demux")]
4180    fn test_udp_conn_basic<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
4181        set_logger_for_test();
4182        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4183        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4184        let local_ip = local_ip::<I>();
4185        let remote_ip = remote_ip::<I>();
4186        let socket = api.create();
4187        // Create a UDP connection with a specified local port and local IP.
4188        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4189            .expect("listen_udp failed");
4190        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4191            .expect("connect failed");
4192
4193        // Inject a UDP packet and see if we receive it on the context.
4194        let meta = UdpPacketMeta::<I> {
4195            src_ip: remote_ip.get(),
4196            src_port: Some(REMOTE_PORT),
4197            dst_ip: local_ip.get(),
4198            dst_port: LOCAL_PORT,
4199            dscp_and_ecn: DscpAndEcn::default(),
4200        };
4201        let body = [1, 2, 3, 4, 5];
4202        let (core_ctx, bindings_ctx) = api.contexts();
4203        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..], early_demux_mode)
4204            .expect("receive udp packet should succeed");
4205
4206        assert_eq!(
4207            bindings_ctx.state.socket_data(),
4208            HashMap::from([(socket.downgrade(), vec![&body[..]])])
4209        );
4210
4211        // Now try to send something over this new connection.
4212        api.send(&socket, Buf::new(body.to_vec(), ..), FakeSendToken::default())
4213            .expect("send_udp_conn returned an error");
4214
4215        let (meta, frame_body) =
4216            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
4217        // Check first frame.
4218        let SendIpPacketMeta {
4219            device: _,
4220            src_ip,
4221            dst_ip,
4222            destination,
4223            proto,
4224            ttl: _,
4225            mtu: _,
4226            dscp_and_ecn: _,
4227        } = meta.try_as::<I>().unwrap();
4228        assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
4229        assert_eq!(src_ip, &local_ip);
4230        assert_eq!(dst_ip, &remote_ip);
4231        assert_eq!(proto, &IpProto::Udp.into());
4232        let mut buf = &frame_body[..];
4233        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
4234            .expect("Parsed sent UDP packet");
4235        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
4236        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
4237        assert_eq!(udp_packet.body(), &body[..]);
4238
4239        let expects_with_socket =
4240            || CounterExpectationsWithSocket { rx_delivered: 1, tx: 1, ..Default::default() };
4241        assert_counters(
4242            api.core_ctx(),
4243            expects_with_socket(),
4244            CounterExpectationsWithoutSocket { rx: 1, ..Default::default() },
4245            [(&socket, expects_with_socket())],
4246        )
4247    }
4248
4249    /// Tests that UDP connections fail with an appropriate error for
4250    /// non-routable remote addresses.
4251    #[ip_test(I)]
4252    fn test_udp_conn_unroutable<I: TestIpExt>() {
4253        set_logger_for_test();
4254        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4255        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4256        // Set fake context callback to treat all addresses as unroutable.
4257        let remote_ip = I::get_other_ip_address(127);
4258        // Create a UDP connection with a specified local port and local IP.
4259        let unbound = api.create();
4260        let conn_err = api
4261            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4262            .unwrap_err();
4263
4264        assert_eq!(conn_err, ConnectError::Ip(ResolveRouteError::Unreachable.into()));
4265    }
4266
4267    /// Tests that UDP listener creation fails with an appropriate error when
4268    /// local address is non-local.
4269    #[ip_test(I)]
4270    fn test_udp_conn_cannot_bind<I: TestIpExt>() {
4271        set_logger_for_test();
4272        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4273        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4274
4275        // Use remote address to trigger IpSockCreationError::LocalAddrNotAssigned.
4276        let remote_ip = remote_ip::<I>();
4277        // Create a UDP listener with a specified local port and local ip:
4278        let unbound = api.create();
4279        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT));
4280
4281        assert_eq!(result, Err(Either::Right(LocalAddressError::CannotBindToAddress)));
4282    }
4283
4284    #[test]
4285    fn test_udp_conn_picks_link_local_source_address() {
4286        set_logger_for_test();
4287        // When the remote address has global scope but the source address
4288        // is link-local, make sure that the socket implicitly has its bound
4289        // device set.
4290        set_logger_for_test();
4291        let local_ip = SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap();
4292        let remote_ip = SpecifiedAddr::new(net_ip_v6!("1:2:3:4::")).unwrap();
4293        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
4294            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
4295        );
4296        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
4297        let socket = api.create();
4298        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4299            .expect("can connect");
4300
4301        let info = api.get_info(&socket);
4302        let (conn_local_ip, conn_remote_ip) = assert_matches!(
4303            info,
4304            SocketInfo::Connected(datagram::ConnInfo {
4305                local_ip: conn_local_ip,
4306                remote_ip: conn_remote_ip,
4307                local_identifier: _,
4308                remote_identifier: _,
4309            }) => (conn_local_ip, conn_remote_ip)
4310        );
4311        assert_eq!(
4312            conn_local_ip,
4313            StrictlyZonedAddr::new_with_zone(local_ip, || FakeWeakDeviceId(FakeDeviceId)),
4314        );
4315        assert_eq!(conn_remote_ip, StrictlyZonedAddr::new_unzoned_or_panic(remote_ip));
4316
4317        // Double-check that the bound device can't be changed after being set
4318        // implicitly.
4319        assert_eq!(
4320            api.set_device(&socket, None),
4321            Err(SocketError::Local(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)))
4322        );
4323    }
4324
4325    #[ip_test(I)]
4326    #[test_case(
4327        true,
4328        Err(IpSockCreationError::Route(ResolveRouteError::Unreachable).into()); "remove device")]
4329    #[test_case(false, Ok(()); "dont remove device")]
4330    fn test_udp_conn_device_removed<I: TestIpExt>(
4331        remove_device: bool,
4332        expected: Result<(), ConnectError>,
4333    ) {
4334        set_logger_for_test();
4335        let device = FakeReferencyDeviceId::default();
4336        let mut ctx =
4337            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
4338        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4339
4340        let unbound = api.create();
4341        api.set_device(&unbound, Some(&device)).unwrap();
4342
4343        if remove_device {
4344            device.mark_removed();
4345        }
4346
4347        let remote_ip = remote_ip::<I>();
4348        assert_eq!(
4349            api.connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
4350            expected,
4351        );
4352    }
4353
4354    /// Tests that UDP connections fail with an appropriate error when local
4355    /// ports are exhausted.
4356    #[ip_test(I)]
4357    fn test_udp_conn_exhausted<I: TestIpExt>() {
4358        // NB: We don't enable logging for this test because it's very spammy.
4359        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4360        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4361
4362        let local_ip = local_ip::<I>();
4363        // Exhaust local ports to trigger FailedToAllocateLocalPort error.
4364        for port_num in FakePortAlloc::<I>::EPHEMERAL_RANGE {
4365            let socket = api.create();
4366            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), NonZeroU16::new(port_num))
4367                .unwrap();
4368        }
4369
4370        let remote_ip = remote_ip::<I>();
4371        let unbound = api.create();
4372        let conn_err = api
4373            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4374            .unwrap_err();
4375
4376        assert_eq!(conn_err, ConnectError::CouldNotAllocateLocalPort);
4377    }
4378
4379    #[ip_test(I)]
4380    fn test_connect_success<I: TestIpExt>() {
4381        set_logger_for_test();
4382        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4383        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4384
4385        let local_ip = local_ip::<I>();
4386        let remote_ip = remote_ip::<I>();
4387        let multicast_addr = I::get_multicast_addr(3);
4388        let socket = api.create();
4389        let sharing_domain = SharingDomain::new(1);
4390
4391        // Set some properties on the socket that should be preserved.
4392        api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4393            .expect("is unbound");
4394        api.set_multicast_membership(
4395            &socket,
4396            multicast_addr,
4397            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4398            true,
4399        )
4400        .expect("join multicast group should succeed");
4401
4402        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4403            .expect("Initial call to listen_udp was expected to succeed");
4404
4405        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4406            .expect("connect should succeed");
4407
4408        // Check that socket options set on the listener are propagated to the
4409        // connected socket.
4410        assert!(api.get_posix_reuse_port(&socket));
4411        assert_eq!(
4412            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
4413            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
4414        );
4415        assert_eq!(
4416            api.set_multicast_membership(
4417                &socket,
4418                multicast_addr,
4419                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4420                true
4421            ),
4422            Err(SetMulticastMembershipError::GroupAlreadyJoined)
4423        );
4424    }
4425
4426    #[ip_test(I)]
4427    fn test_connect_fails<I: TestIpExt>() {
4428        set_logger_for_test();
4429        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4430        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4431        let local_ip = local_ip::<I>();
4432        let remote_ip = I::get_other_ip_address(127);
4433        let multicast_addr = I::get_multicast_addr(3);
4434        let socket = api.create();
4435
4436        // Set some properties on the socket that should be preserved.
4437        let sharing_domain = SharingDomain::new(1);
4438        api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4439            .expect("is unbound");
4440        api.set_multicast_membership(
4441            &socket,
4442            multicast_addr,
4443            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4444            true,
4445        )
4446        .expect("join multicast group should succeed");
4447
4448        // Create a UDP connection with a specified local port and local IP.
4449        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4450            .expect("Initial call to listen_udp was expected to succeed");
4451
4452        assert_matches!(
4453            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
4454            Err(ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable)))
4455        );
4456
4457        // Check that the listener was unchanged by the failed connection.
4458        assert!(api.get_posix_reuse_port(&socket));
4459        assert_eq!(
4460            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
4461            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
4462        );
4463        assert_eq!(
4464            api.set_multicast_membership(
4465                &socket,
4466                multicast_addr,
4467                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4468                true
4469            ),
4470            Err(SetMulticastMembershipError::GroupAlreadyJoined)
4471        );
4472    }
4473
4474    #[ip_test(I)]
4475    fn test_reconnect_udp_conn_success<I: TestIpExt>() {
4476        set_logger_for_test();
4477
4478        let local_ip = local_ip::<I>();
4479        let remote_ip = remote_ip::<I>();
4480        let other_remote_ip = I::get_other_ip_address(3);
4481
4482        let mut ctx =
4483            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4484                vec![local_ip],
4485                vec![remote_ip, other_remote_ip],
4486            ));
4487        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4488
4489        let socket = api.create();
4490        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4491            .expect("listen should succeed");
4492
4493        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4494            .expect("connect was expected to succeed");
4495
4496        api.connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
4497            .expect("connect should succeed");
4498        assert_eq!(
4499            api.get_info(&socket),
4500            SocketInfo::Connected(datagram::ConnInfo {
4501                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
4502                local_identifier: LOCAL_PORT,
4503                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(other_remote_ip),
4504                remote_identifier: OTHER_REMOTE_PORT.into(),
4505            })
4506        );
4507    }
4508
4509    #[ip_test(I)]
4510    fn test_reconnect_udp_conn_fails<I: TestIpExt>() {
4511        set_logger_for_test();
4512        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4513        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4514        let local_ip = local_ip::<I>();
4515        let remote_ip = remote_ip::<I>();
4516        let other_remote_ip = I::get_other_ip_address(3);
4517
4518        let socket = api.create();
4519        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4520            .expect("listen should succeed");
4521
4522        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4523            .expect("connect was expected to succeed");
4524        let error = api
4525            .connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
4526            .expect_err("connect should fail");
4527        assert_matches!(
4528            error,
4529            ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable))
4530        );
4531
4532        assert_eq!(
4533            api.get_info(&socket),
4534            SocketInfo::Connected(datagram::ConnInfo {
4535                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
4536                local_identifier: LOCAL_PORT,
4537                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip),
4538                remote_identifier: REMOTE_PORT.into()
4539            })
4540        );
4541    }
4542
4543    #[ip_test(I)]
4544    fn test_send_to<I: TestIpExt>() {
4545        set_logger_for_test();
4546
4547        let local_ip = local_ip::<I>();
4548        let remote_ip = remote_ip::<I>();
4549        let other_remote_ip = I::get_other_ip_address(3);
4550
4551        let mut ctx =
4552            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4553                vec![local_ip],
4554                vec![remote_ip, other_remote_ip],
4555            ));
4556        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4557
4558        let socket = api.create();
4559        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4560            .expect("listen should succeed");
4561        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4562            .expect("connect should succeed");
4563
4564        let body = [1, 2, 3, 4, 5];
4565        // Try to send something with send_to
4566        api.send_to(
4567            &socket,
4568            Some(ZonedAddr::Unzoned(other_remote_ip)),
4569            REMOTE_PORT.into(),
4570            Buf::new(body.to_vec(), ..),
4571            FakeSendToken::default(),
4572        )
4573        .expect("send_to failed");
4574
4575        // The socket should not have been affected.
4576        let info = api.get_info(&socket);
4577        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
4578        assert_eq!(info.local_ip.into_inner(), ZonedAddr::Unzoned(local_ip));
4579        assert_eq!(info.remote_ip.into_inner(), ZonedAddr::Unzoned(remote_ip));
4580        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
4581
4582        // Check first frame.
4583        let (meta, frame_body) =
4584            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
4585        let SendIpPacketMeta {
4586            device: _,
4587            src_ip,
4588            dst_ip,
4589            destination,
4590            proto,
4591            ttl: _,
4592            mtu: _,
4593            dscp_and_ecn: _,
4594        } = meta.try_as::<I>().unwrap();
4595
4596        assert_eq!(destination, &IpPacketDestination::Neighbor(other_remote_ip));
4597        assert_eq!(src_ip, &local_ip);
4598        assert_eq!(dst_ip, &other_remote_ip);
4599        assert_eq!(proto, &I::Proto::from(IpProto::Udp));
4600        let mut buf = &frame_body[..];
4601        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
4602            .expect("Parsed sent UDP packet");
4603        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
4604        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
4605        assert_eq!(udp_packet.body(), &body[..]);
4606    }
4607
4608    /// Tests that UDP send failures are propagated as errors.
4609    ///
4610    /// Only tests with specified local port and address bounds.
4611    #[ip_test(I)]
4612    fn test_send_udp_conn_failure<I: TestIpExt>() {
4613        set_logger_for_test();
4614        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4615        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4616        let remote_ip = remote_ip::<I>();
4617        // Create a UDP connection with a specified local port and local IP.
4618        let socket = api.create();
4619        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4620            .expect("connect failed");
4621
4622        // Instruct the fake frame context to throw errors.
4623        api.core_ctx().bound_sockets.ip_socket_ctx.frames.set_should_error_for_frame(
4624            |_frame_meta| Some(SendFrameErrorReason::SizeConstraintsViolation),
4625        );
4626
4627        // Now try to send something over this new connection:
4628        let send_err =
4629            api.send(&socket, Buf::new(Vec::new(), ..), FakeSendToken::default()).unwrap_err();
4630        assert_eq!(send_err, Either::Left(SendError::IpSock(IpSockSendError::Mtu)));
4631
4632        let expects_with_socket =
4633            || CounterExpectationsWithSocket { tx: 1, tx_error: 1, ..Default::default() };
4634        assert_counters(
4635            api.core_ctx(),
4636            expects_with_socket(),
4637            Default::default(),
4638            [(&socket, expects_with_socket())],
4639        )
4640    }
4641
4642    #[ip_test(I)]
4643    fn test_send_udp_conn_device_removed<I: TestIpExt>() {
4644        set_logger_for_test();
4645        let device = FakeReferencyDeviceId::default();
4646        let mut ctx =
4647            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
4648        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4649        let remote_ip = remote_ip::<I>();
4650        let socket = api.create();
4651        api.set_device(&socket, Some(&device)).unwrap();
4652        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4653            .expect("connect failed");
4654
4655        for (device_removed, expected_res) in [
4656            (false, Ok(())),
4657            (
4658                true,
4659                Err(Either::Left(SendError::IpSock(IpSockSendError::Unroutable(
4660                    ResolveRouteError::Unreachable,
4661                )))),
4662            ),
4663        ] {
4664            if device_removed {
4665                device.mark_removed();
4666            }
4667
4668            assert_eq!(
4669                api.send(&socket, Buf::new(Vec::new(), ..), FakeSendToken::default()),
4670                expected_res
4671            )
4672        }
4673    }
4674
4675    #[ip_test(I)]
4676    #[test_case(false, ShutdownType::Send; "shutdown send then send")]
4677    #[test_case(false, ShutdownType::SendAndReceive; "shutdown both then send")]
4678    #[test_case(true, ShutdownType::Send; "shutdown send then sendto")]
4679    #[test_case(true, ShutdownType::SendAndReceive; "shutdown both then sendto")]
4680    fn test_send_udp_after_shutdown<I: TestIpExt>(send_to: bool, shutdown: ShutdownType) {
4681        set_logger_for_test();
4682
4683        #[derive(Debug)]
4684        struct NotWriteableError;
4685
4686        let send = |remote_ip, api: &mut UdpApi<_, _>, id| -> Result<(), NotWriteableError> {
4687            match remote_ip {
4688                Some(remote_ip) => api.send_to(
4689                    id,
4690                    Some(remote_ip),
4691                    REMOTE_PORT.into(),
4692                    Buf::new(Vec::new(), ..),
4693                    FakeSendToken::default(),
4694                )
4695                .map_err(
4696                    |e| assert_matches!(e, SendToError::NotWriteable => NotWriteableError)
4697                ),
4698                None => api.send(
4699                    id,
4700                    Buf::new(Vec::new(), ..),
4701                    FakeSendToken::default(),
4702                )
4703                .map_err(|e| assert_matches!(e, Either::Left(SendError::NotWriteable) => NotWriteableError)),
4704            }
4705        };
4706
4707        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4708        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4709
4710        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
4711        let send_to_ip = send_to.then_some(remote_ip);
4712
4713        let socket = api.create();
4714        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
4715
4716        send(send_to_ip, &mut api, &socket).expect("can send");
4717        api.shutdown(&socket, shutdown).expect("is connected");
4718
4719        assert_matches!(send(send_to_ip, &mut api, &socket), Err(NotWriteableError));
4720    }
4721
4722    #[ip_test(I, test = false)]
4723    #[test_case::test_matrix(
4724        [ShutdownType::Receive, ShutdownType::SendAndReceive],
4725        [EarlyDemuxMode::Enabled, EarlyDemuxMode::Disabled]
4726    )]
4727    fn test_marked_for_receive_shutdown<I: TestIpExt>(
4728        which: ShutdownType,
4729        early_demux_mode: EarlyDemuxMode,
4730    ) {
4731        set_logger_for_test();
4732
4733        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4734        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4735
4736        let socket = api.create();
4737        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
4738            .expect("can bind");
4739        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
4740            .expect("can connect");
4741
4742        // Receive once, then set the shutdown flag, then receive again and
4743        // check that it doesn't get to the socket.
4744
4745        let meta = UdpPacketMeta::<I> {
4746            src_ip: remote_ip::<I>().get(),
4747            src_port: Some(REMOTE_PORT),
4748            dst_ip: local_ip::<I>().get(),
4749            dst_port: LOCAL_PORT,
4750            dscp_and_ecn: DscpAndEcn::default(),
4751        };
4752        let packet = [1, 1, 1, 1];
4753        let (core_ctx, bindings_ctx) = api.contexts();
4754
4755        receive_udp_packet(
4756            core_ctx,
4757            bindings_ctx,
4758            FakeDeviceId,
4759            meta.clone(),
4760            &packet[..],
4761            early_demux_mode,
4762        )
4763        .expect("receive udp packet should succeed");
4764
4765        assert_eq!(
4766            bindings_ctx.state.socket_data(),
4767            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4768        );
4769        api.shutdown(&socket, which).expect("is connected");
4770        let (core_ctx, bindings_ctx) = api.contexts();
4771        assert_eq!(
4772            receive_udp_packet(
4773                core_ctx,
4774                bindings_ctx,
4775                FakeDeviceId,
4776                meta.clone(),
4777                &packet[..],
4778                early_demux_mode
4779            ),
4780            Err(I::IcmpError::port_unreachable())
4781        );
4782        assert_eq!(
4783            bindings_ctx.state.socket_data(),
4784            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4785        );
4786
4787        // Calling shutdown for the send direction doesn't change anything.
4788        api.shutdown(&socket, ShutdownType::Send).expect("is connected");
4789        let (core_ctx, bindings_ctx) = api.contexts();
4790        assert_eq!(
4791            receive_udp_packet(
4792                core_ctx,
4793                bindings_ctx,
4794                FakeDeviceId,
4795                meta,
4796                &packet[..],
4797                early_demux_mode
4798            ),
4799            Err(I::IcmpError::port_unreachable())
4800        );
4801        assert_eq!(
4802            bindings_ctx.state.socket_data(),
4803            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4804        );
4805    }
4806
4807    /// Tests that if we have multiple listeners and connections, demuxing the
4808    /// flows is performed correctly.
4809    #[ip_test(I)]
4810    #[test_case(WithEarlyDemux; "with early demux")]
4811    #[test_case(NoEarlyDemux; "without early demux")]
4812    fn test_udp_demux<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
4813        set_logger_for_test();
4814        let local_ip = local_ip::<I>();
4815        let remote_ip_a = I::get_other_ip_address(70);
4816        let remote_ip_b = I::get_other_ip_address(72);
4817        let local_port_a = NonZeroU16::new(100).unwrap();
4818        let local_port_b = NonZeroU16::new(101).unwrap();
4819        let local_port_c = NonZeroU16::new(102).unwrap();
4820        let local_port_d = NonZeroU16::new(103).unwrap();
4821
4822        let mut ctx =
4823            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4824                vec![local_ip],
4825                vec![remote_ip_a, remote_ip_b],
4826            ));
4827        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4828
4829        let sharing_domain = SharingDomain::new(1);
4830
4831        // Create some UDP connections and listeners:
4832        // conn2 has just a remote addr different than conn1, which requires
4833        // allowing them to share the local port.
4834        let [conn1, conn2] = [remote_ip_a, remote_ip_b].map(|remote_ip| {
4835            let socket = api.create();
4836            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4837                .expect("is unbound");
4838            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_d))
4839                .expect("listen_udp failed");
4840            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4841                .expect("connect failed");
4842            socket
4843        });
4844        let list1 = api.create();
4845        api.listen(&list1, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_a))
4846            .expect("listen_udp failed");
4847        let list2 = api.create();
4848        api.listen(&list2, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_b))
4849            .expect("listen_udp failed");
4850        let wildcard_list = api.create();
4851        api.listen(&wildcard_list, None, Some(local_port_c)).expect("listen_udp failed");
4852
4853        let mut expectations = HashMap::<WeakUdpSocketId<I, _, _>, SocketReceived<I>>::new();
4854        // Now inject UDP packets that each of the created connections should
4855        // receive.
4856        let meta = UdpPacketMeta {
4857            src_ip: remote_ip_a.get(),
4858            src_port: Some(REMOTE_PORT),
4859            dst_ip: local_ip.get(),
4860            dst_port: local_port_d,
4861            dscp_and_ecn: DscpAndEcn::default(),
4862        };
4863        let body_conn1 = [1, 1, 1, 1];
4864        let (core_ctx, bindings_ctx) = api.contexts();
4865        receive_udp_packet(
4866            core_ctx,
4867            bindings_ctx,
4868            FakeDeviceId,
4869            meta.clone(),
4870            &body_conn1[..],
4871            early_demux_mode,
4872        )
4873        .expect("receive udp packet should succeed");
4874        expectations
4875            .entry(conn1.downgrade())
4876            .or_default()
4877            .packets
4878            .push(ReceivedPacket { meta: meta, body: body_conn1.into() });
4879        assert_eq!(bindings_ctx.state.received(), &expectations);
4880
4881        let meta = UdpPacketMeta {
4882            src_ip: remote_ip_b.get(),
4883            src_port: Some(REMOTE_PORT),
4884            dst_ip: local_ip.get(),
4885            dst_port: local_port_d,
4886            dscp_and_ecn: DscpAndEcn::default(),
4887        };
4888        let body_conn2 = [2, 2, 2, 2];
4889        receive_udp_packet(
4890            core_ctx,
4891            bindings_ctx,
4892            FakeDeviceId,
4893            meta.clone(),
4894            &body_conn2[..],
4895            early_demux_mode,
4896        )
4897        .expect("receive udp packet should succeed");
4898        expectations
4899            .entry(conn2.downgrade())
4900            .or_default()
4901            .packets
4902            .push(ReceivedPacket { meta: meta, body: body_conn2.into() });
4903        assert_eq!(bindings_ctx.state.received(), &expectations);
4904
4905        let meta = UdpPacketMeta {
4906            src_ip: remote_ip_a.get(),
4907            src_port: Some(REMOTE_PORT),
4908            dst_ip: local_ip.get(),
4909            dst_port: local_port_a,
4910            dscp_and_ecn: DscpAndEcn::default(),
4911        };
4912        let body_list1 = [3, 3, 3, 3];
4913        receive_udp_packet(
4914            core_ctx,
4915            bindings_ctx,
4916            FakeDeviceId,
4917            meta.clone(),
4918            &body_list1[..],
4919            early_demux_mode,
4920        )
4921        .expect("receive udp packet should succeed");
4922        expectations
4923            .entry(list1.downgrade())
4924            .or_default()
4925            .packets
4926            .push(ReceivedPacket { meta: meta, body: body_list1.into() });
4927        assert_eq!(bindings_ctx.state.received(), &expectations);
4928
4929        let meta = UdpPacketMeta {
4930            src_ip: remote_ip_a.get(),
4931            src_port: Some(REMOTE_PORT),
4932            dst_ip: local_ip.get(),
4933            dst_port: local_port_b,
4934            dscp_and_ecn: DscpAndEcn::default(),
4935        };
4936        let body_list2 = [4, 4, 4, 4];
4937        receive_udp_packet(
4938            core_ctx,
4939            bindings_ctx,
4940            FakeDeviceId,
4941            meta.clone(),
4942            &body_list2[..],
4943            early_demux_mode,
4944        )
4945        .expect("receive udp packet should succeed");
4946        expectations
4947            .entry(list2.downgrade())
4948            .or_default()
4949            .packets
4950            .push(ReceivedPacket { meta: meta, body: body_list2.into() });
4951        assert_eq!(bindings_ctx.state.received(), &expectations);
4952
4953        let meta = UdpPacketMeta {
4954            src_ip: remote_ip_a.get(),
4955            src_port: Some(REMOTE_PORT),
4956            dst_ip: local_ip.get(),
4957            dst_port: local_port_c,
4958            dscp_and_ecn: DscpAndEcn::default(),
4959        };
4960        let body_wildcard_list = [5, 5, 5, 5];
4961        receive_udp_packet(
4962            core_ctx,
4963            bindings_ctx,
4964            FakeDeviceId,
4965            meta.clone(),
4966            &body_wildcard_list[..],
4967            early_demux_mode,
4968        )
4969        .expect("receive udp packet should succeed");
4970        expectations
4971            .entry(wildcard_list.downgrade())
4972            .or_default()
4973            .packets
4974            .push(ReceivedPacket { meta: meta, body: body_wildcard_list.into() });
4975        assert_eq!(bindings_ctx.state.received(), &expectations);
4976    }
4977
4978    /// Tests UDP wildcard listeners for different IP versions.
4979    #[ip_test(I)]
4980    #[test_case(WithEarlyDemux; "with early demux")]
4981    #[test_case(NoEarlyDemux; "without early demux")]
4982    fn test_wildcard_listeners<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
4983        set_logger_for_test();
4984        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4985        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4986        let local_ip_a = I::get_other_ip_address(1);
4987        let local_ip_b = I::get_other_ip_address(2);
4988        let remote_ip_a = I::get_other_ip_address(70);
4989        let remote_ip_b = I::get_other_ip_address(72);
4990        let listener = api.create();
4991        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4992
4993        let body = [1, 2, 3, 4, 5];
4994        let (core_ctx, bindings_ctx) = api.contexts();
4995        let meta_1 = UdpPacketMeta {
4996            src_ip: remote_ip_a.get(),
4997            src_port: Some(REMOTE_PORT),
4998            dst_ip: local_ip_a.get(),
4999            dst_port: LOCAL_PORT,
5000            dscp_and_ecn: DscpAndEcn::default(),
5001        };
5002        receive_udp_packet(
5003            core_ctx,
5004            bindings_ctx,
5005            FakeDeviceId,
5006            meta_1.clone(),
5007            &body[..],
5008            early_demux_mode,
5009        )
5010        .expect("receive udp packet should succeed");
5011
5012        // Receive into a different local IP.
5013        let meta_2 = UdpPacketMeta {
5014            src_ip: remote_ip_b.get(),
5015            src_port: Some(REMOTE_PORT),
5016            dst_ip: local_ip_b.get(),
5017            dst_port: LOCAL_PORT,
5018            dscp_and_ecn: DscpAndEcn::default(),
5019        };
5020        receive_udp_packet(
5021            core_ctx,
5022            bindings_ctx,
5023            FakeDeviceId,
5024            meta_2.clone(),
5025            &body[..],
5026            early_demux_mode,
5027        )
5028        .expect("receive udp packet should succeed");
5029
5030        // Check that we received both packets for the listener.
5031        assert_eq!(
5032            bindings_ctx.state.received::<I>(),
5033            &HashMap::from([(
5034                listener.downgrade(),
5035                SocketReceived {
5036                    packets: vec![
5037                        ReceivedPacket { meta: meta_1, body: body.into() },
5038                        ReceivedPacket { meta: meta_2, body: body.into() }
5039                    ],
5040                    max_size: usize::MAX,
5041                }
5042            )])
5043        );
5044    }
5045
5046    #[ip_test(I)]
5047    #[test_case(WithEarlyDemux; "with early demux")]
5048    #[test_case(NoEarlyDemux; "without early demux")]
5049    fn test_receive_source_port_zero_on_listener<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
5050        set_logger_for_test();
5051        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5052        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5053        let listener = api.create();
5054        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5055
5056        let body = [];
5057        let meta = UdpPacketMeta::<I> {
5058            src_ip: I::TEST_ADDRS.remote_ip.get(),
5059            src_port: None,
5060            dst_ip: I::TEST_ADDRS.local_ip.get(),
5061            dst_port: LOCAL_PORT,
5062            dscp_and_ecn: DscpAndEcn::default(),
5063        };
5064
5065        let (core_ctx, bindings_ctx) = api.contexts();
5066        receive_udp_packet(
5067            core_ctx,
5068            bindings_ctx,
5069            FakeDeviceId,
5070            meta.clone(),
5071            &body[..],
5072            early_demux_mode,
5073        )
5074        .expect("receive udp packet should succeed");
5075        // Check that we received both packets for the listener.
5076        assert_eq!(
5077            bindings_ctx.state.received(),
5078            &HashMap::from([(
5079                listener.downgrade(),
5080                SocketReceived {
5081                    packets: vec![ReceivedPacket { meta, body: vec![] }],
5082                    max_size: usize::MAX
5083                }
5084            )])
5085        );
5086    }
5087
5088    #[ip_test(I)]
5089    #[test_case(WithEarlyDemux; "with early demux")]
5090    #[test_case(NoEarlyDemux; "without early demux")]
5091    fn test_receive_source_addr_unspecified_on_listener<I: TestIpExt>(
5092        early_demux_mode: EarlyDemuxMode,
5093    ) {
5094        set_logger_for_test();
5095        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5096        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5097        let listener = api.create();
5098        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5099
5100        let meta = UdpPacketMeta::<I> {
5101            src_ip: I::UNSPECIFIED_ADDRESS,
5102            src_port: Some(REMOTE_PORT),
5103            dst_ip: I::TEST_ADDRS.local_ip.get(),
5104            dst_port: LOCAL_PORT,
5105            dscp_and_ecn: DscpAndEcn::default(),
5106        };
5107        let body = [];
5108        let (core_ctx, bindings_ctx) = api.contexts();
5109        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..], early_demux_mode)
5110            .expect("receive udp packet should succeed");
5111        // Check that we received the packet on the listener.
5112        assert_eq!(
5113            bindings_ctx.state.socket_data(),
5114            HashMap::from([(listener.downgrade(), vec![&body[..]])])
5115        );
5116    }
5117
5118    #[ip_test(I)]
5119    #[test_case(NonZeroU16::new(u16::MAX).unwrap(), Ok(NonZeroU16::new(u16::MAX).unwrap()); "ephemeral available")]
5120    #[test_case(NonZeroU16::new(100).unwrap(), Err(LocalAddressError::FailedToAllocateLocalPort);
5121        "no ephemeral available")]
5122    fn test_bind_picked_port_all_others_taken<I: TestIpExt>(
5123        available_port: NonZeroU16,
5124        expected_result: Result<NonZeroU16, LocalAddressError>,
5125    ) {
5126        // NB: We don't enable logging for this test because it's very spammy.
5127        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5128        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5129
5130        for port in 1..=u16::MAX {
5131            let port = NonZeroU16::new(port).unwrap();
5132            if port == available_port {
5133                continue;
5134            }
5135            let unbound = api.create();
5136            api.listen(&unbound, None, Some(port)).expect("uncontested bind");
5137        }
5138
5139        // Now that all but the LOCAL_PORT are occupied, ask the stack to
5140        // select a port.
5141        let socket = api.create();
5142        let result = api
5143            .listen(&socket, None, None)
5144            .map(|()| {
5145                let info = api.get_info(&socket);
5146                assert_matches!(info, SocketInfo::Listener(info) => info.local_identifier)
5147            })
5148            .map_err(Either::unwrap_right);
5149        assert_eq!(result, expected_result);
5150    }
5151
5152    #[ip_test(I)]
5153    #[test_case(WithEarlyDemux; "with early demux")]
5154    #[test_case(NoEarlyDemux; "without early demux")]
5155    fn test_receive_multicast_packet<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
5156        set_logger_for_test();
5157        let local_ip = local_ip::<I>();
5158        let remote_ip = I::get_other_ip_address(70);
5159        let multicast_addr = I::get_multicast_addr(0);
5160        let multicast_addr_other = I::get_multicast_addr(1);
5161
5162        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
5163            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
5164        );
5165        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5166
5167        let sharing_domain = SharingDomain::new(1);
5168
5169        // Create 3 sockets: one listener for all IPs, two listeners on the same
5170        // local address.
5171        let any_listener = {
5172            let socket = api.create();
5173            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
5174                .expect("is unbound");
5175            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5176            socket
5177        };
5178
5179        let specific_listeners = [(); 2].map(|()| {
5180            let socket = api.create();
5181            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
5182                .expect("is unbound");
5183            api.listen(
5184                &socket,
5185                Some(ZonedAddr::Unzoned(multicast_addr.into_specified())),
5186                Some(LOCAL_PORT),
5187            )
5188            .expect("listen_udp failed");
5189            socket
5190        });
5191
5192        let (core_ctx, bindings_ctx) = api.contexts();
5193        let mut receive_packet = |body, local_ip: MulticastAddr<I::Addr>| {
5194            let meta = UdpPacketMeta::<I> {
5195                src_ip: remote_ip.get(),
5196                src_port: Some(REMOTE_PORT),
5197                dst_ip: local_ip.get(),
5198                dst_port: LOCAL_PORT,
5199                dscp_and_ecn: DscpAndEcn::default(),
5200            };
5201            let body = [body];
5202            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body, early_demux_mode)
5203                .expect("receive udp packet should succeed")
5204        };
5205
5206        // These packets should be received by all listeners.
5207        receive_packet(1, multicast_addr);
5208        receive_packet(2, multicast_addr);
5209
5210        // This packet should be received only by the all-IPs listener.
5211        receive_packet(3, multicast_addr_other);
5212
5213        assert_eq!(
5214            bindings_ctx.state.socket_data(),
5215            HashMap::from([
5216                (specific_listeners[0].downgrade(), vec![[1].as_slice(), &[2]]),
5217                (specific_listeners[1].downgrade(), vec![&[1], &[2]]),
5218                (any_listener.downgrade(), vec![&[1], &[2], &[3]]),
5219            ]),
5220        );
5221
5222        assert_counters(
5223            api.core_ctx(),
5224            CounterExpectationsWithSocket { rx_delivered: 7, ..Default::default() },
5225            CounterExpectationsWithoutSocket { rx: 3, ..Default::default() },
5226            [
5227                (
5228                    &any_listener,
5229                    CounterExpectationsWithSocket { rx_delivered: 3, ..Default::default() },
5230                ),
5231                (
5232                    &specific_listeners[0],
5233                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
5234                ),
5235                (
5236                    &specific_listeners[1],
5237                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
5238                ),
5239            ],
5240        )
5241    }
5242
5243    type UdpMultipleDevicesCtx = FakeUdpCtx<MultipleDevicesId>;
5244    type UdpMultipleDevicesCoreCtx = FakeUdpCoreCtx<MultipleDevicesId>;
5245    type UdpMultipleDevicesBindingsCtx = FakeUdpBindingsCtx<MultipleDevicesId>;
5246
5247    impl FakeUdpCoreCtx<MultipleDevicesId> {
5248        fn new_multiple_devices<I: TestIpExt>() -> Self {
5249            let remote_ips = vec![I::get_other_remote_ip_address(1)];
5250            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5251                MultipleDevicesId::all().into_iter().enumerate().map(|(i, device)| {
5252                    FakeDeviceConfig {
5253                        device,
5254                        local_ips: vec![Self::local_ip(i)],
5255                        remote_ips: remote_ips.clone(),
5256                    }
5257                }),
5258            ))
5259        }
5260
5261        fn local_ip<A: IpAddress>(index: usize) -> SpecifiedAddr<A>
5262        where
5263            A::Version: TestIpExt,
5264        {
5265            A::Version::get_other_ip_address((index + 1).try_into().unwrap())
5266        }
5267    }
5268
5269    /// Tests that if sockets are bound to devices, they will only receive
5270    /// packets that are received on those devices.
5271    #[ip_test(I)]
5272    #[test_case(WithEarlyDemux; "with early demux")]
5273    #[test_case(NoEarlyDemux; "without early demux")]
5274    fn test_bound_to_device_receive<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
5275        set_logger_for_test();
5276        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5277            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5278        );
5279        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5280        let bound_first_device = api.create();
5281        api.listen(
5282            &bound_first_device,
5283            Some(ZonedAddr::Unzoned(local_ip::<I>())),
5284            Some(LOCAL_PORT),
5285        )
5286        .expect("listen should succeed");
5287        api.connect(
5288            &bound_first_device,
5289            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5290            REMOTE_PORT.into(),
5291        )
5292        .expect("connect should succeed");
5293        api.set_device(&bound_first_device, Some(&MultipleDevicesId::A))
5294            .expect("bind should succeed");
5295
5296        let bound_second_device = api.create();
5297        api.set_device(&bound_second_device, Some(&MultipleDevicesId::B)).unwrap();
5298        api.listen(&bound_second_device, None, Some(LOCAL_PORT)).expect("listen should succeed");
5299
5300        // Inject a packet received on `MultipleDevicesId::A` from the specified
5301        // remote; this should go to the first socket.
5302        let meta = UdpPacketMeta::<I> {
5303            src_ip: I::get_other_remote_ip_address(1).get(),
5304            src_port: Some(REMOTE_PORT),
5305            dst_ip: local_ip::<I>().get(),
5306            dst_port: LOCAL_PORT,
5307            dscp_and_ecn: DscpAndEcn::default(),
5308        };
5309        let body = [1, 2, 3, 4, 5];
5310        let (core_ctx, bindings_ctx) = api.contexts();
5311        receive_udp_packet(
5312            core_ctx,
5313            bindings_ctx,
5314            MultipleDevicesId::A,
5315            meta.clone(),
5316            &body[..],
5317            early_demux_mode,
5318        )
5319        .expect("receive udp packet should succeed");
5320
5321        // A second packet received on `MultipleDevicesId::B` will go to the
5322        // second socket.
5323        receive_udp_packet(
5324            core_ctx,
5325            bindings_ctx,
5326            MultipleDevicesId::B,
5327            meta,
5328            &body[..],
5329            early_demux_mode,
5330        )
5331        .expect("receive udp packet should succeed");
5332        assert_eq!(
5333            bindings_ctx.state.socket_data(),
5334            HashMap::from([
5335                (bound_first_device.downgrade(), vec![&body[..]]),
5336                (bound_second_device.downgrade(), vec![&body[..]])
5337            ])
5338        );
5339    }
5340
5341    /// Tests that if sockets are bound to devices, they will send packets out
5342    /// of those devices.
5343    #[ip_test(I)]
5344    fn test_bound_to_device_send<I: TestIpExt>() {
5345        set_logger_for_test();
5346        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5347            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5348        );
5349        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5350        let bound_on_devices = MultipleDevicesId::all().map(|device| {
5351            let socket = api.create();
5352            api.set_device(&socket, Some(&device)).unwrap();
5353            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
5354            socket
5355        });
5356
5357        // Send a packet from each socket.
5358        let body = [1, 2, 3, 4, 5];
5359        for socket in bound_on_devices {
5360            api.send_to(
5361                &socket,
5362                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5363                REMOTE_PORT.into(),
5364                Buf::new(body.to_vec(), ..),
5365                FakeSendToken::default(),
5366            )
5367            .expect("send should succeed");
5368        }
5369
5370        let mut received_devices = api
5371            .core_ctx()
5372            .bound_sockets
5373            .ip_socket_ctx
5374            .frames()
5375            .iter()
5376            .map(|(meta, _body)| {
5377                let SendIpPacketMeta {
5378                    device,
5379                    src_ip: _,
5380                    dst_ip,
5381                    destination: _,
5382                    proto,
5383                    ttl: _,
5384                    mtu: _,
5385                    dscp_and_ecn: _,
5386                } = meta.try_as::<I>().unwrap();
5387                assert_eq!(proto, &IpProto::Udp.into());
5388                assert_eq!(dst_ip, &I::get_other_remote_ip_address(1));
5389                *device
5390            })
5391            .collect::<Vec<_>>();
5392        received_devices.sort();
5393        assert_eq!(received_devices, &MultipleDevicesId::all());
5394    }
5395
5396    fn receive_packet_on<I: TestIpExt>(
5397        core_ctx: &mut UdpMultipleDevicesCoreCtx,
5398        bindings_ctx: &mut UdpMultipleDevicesBindingsCtx,
5399        device: MultipleDevicesId,
5400        early_demux_mode: EarlyDemuxMode,
5401    ) -> Result<(), I::IcmpError> {
5402        let meta = UdpPacketMeta::<I> {
5403            src_ip: I::get_other_remote_ip_address(1).get(),
5404            src_port: Some(REMOTE_PORT),
5405            dst_ip: local_ip::<I>().get(),
5406            dst_port: LOCAL_PORT,
5407            dscp_and_ecn: DscpAndEcn::default(),
5408        };
5409        const BODY: [u8; 5] = [1, 2, 3, 4, 5];
5410        receive_udp_packet(core_ctx, bindings_ctx, device, meta, &BODY[..], early_demux_mode)
5411    }
5412
5413    /// Check that sockets can be bound to and unbound from devices.
5414    #[ip_test(I)]
5415    #[test_case(WithEarlyDemux; "with early demux")]
5416    #[test_case(NoEarlyDemux; "without early demux")]
5417    fn test_bind_unbind_device<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
5418        set_logger_for_test();
5419        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5420            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5421        );
5422        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5423
5424        // Start with `socket` bound to a device.
5425        let socket = api.create();
5426        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
5427        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen failed");
5428
5429        // Since it is bound, it does not receive a packet from another device.
5430        let (core_ctx, bindings_ctx) = api.contexts();
5431        assert_eq!(
5432            receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B, early_demux_mode),
5433            Err(I::IcmpError::port_unreachable())
5434        );
5435        assert_eq!(bindings_ctx.state.socket_data::<I>(), HashMap::new());
5436
5437        // When unbound, the socket can receive packets on the other device.
5438        api.set_device(&socket, None).expect("clearing bound device failed");
5439        let (core_ctx, bindings_ctx) = api.contexts();
5440        receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B, early_demux_mode)
5441            .expect("receive udp packet should succeed");
5442        let received = bindings_ctx.state.received::<I>().iter().collect::<Vec<_>>();
5443        let (rx_socket, socket_received) =
5444            assert_matches!(received[..], [(rx_socket, packets)] => (rx_socket, packets));
5445        assert_eq!(rx_socket, &socket);
5446        assert_matches!(socket_received.packets[..], [_]);
5447    }
5448
5449    /// Check that bind fails as expected when it would cause illegal shadowing.
5450    #[ip_test(I)]
5451    fn test_unbind_device_fails<I: TestIpExt>() {
5452        set_logger_for_test();
5453        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5454            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5455        );
5456        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5457
5458        let bound_on_devices = MultipleDevicesId::all().map(|device| {
5459            let socket = api.create();
5460            api.set_device(&socket, Some(&device)).unwrap();
5461            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
5462            socket
5463        });
5464
5465        // Clearing the bound device is not allowed for either socket since it
5466        // would then be shadowed by the other socket.
5467        for socket in bound_on_devices {
5468            assert_matches!(
5469                api.set_device(&socket, None),
5470                Err(SocketError::Local(LocalAddressError::AddressInUse))
5471            );
5472        }
5473    }
5474
5475    /// Check that binding a device fails if it would make a connected socket
5476    /// unroutable.
5477    #[ip_test(I)]
5478    fn test_bind_conn_socket_device_fails<I: TestIpExt>() {
5479        set_logger_for_test();
5480        let device_configs = HashMap::<_, _>::from(
5481            [(MultipleDevicesId::A, 1), (MultipleDevicesId::B, 2)].map(|(device, i)| {
5482                (
5483                    device,
5484                    FakeDeviceConfig {
5485                        device,
5486                        local_ips: vec![I::get_other_ip_address(i)],
5487                        remote_ips: vec![I::get_other_remote_ip_address(i)],
5488                    },
5489                )
5490            }),
5491        );
5492        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5493            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5494                device_configs.iter().map(|(_, v)| v).cloned(),
5495            )),
5496        );
5497        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5498        let socket = api.create();
5499        api.connect(
5500            &socket,
5501            Some(ZonedAddr::Unzoned(device_configs[&MultipleDevicesId::A].remote_ips[0])),
5502            REMOTE_PORT.into(),
5503        )
5504        .expect("connect should succeed");
5505
5506        // `socket` is not explicitly bound to device `A` but its route must
5507        // go through it because of the destination address. Therefore binding
5508        // to device `B` wil not work.
5509        assert_matches!(
5510            api.set_device(&socket, Some(&MultipleDevicesId::B)),
5511            Err(SocketError::Remote(RemoteAddressError::NoRoute))
5512        );
5513
5514        // Binding to device `A` should be fine.
5515        api.set_device(&socket, Some(&MultipleDevicesId::A)).expect("routing picked A already");
5516    }
5517
5518    #[ip_test(I)]
5519    #[test_case(WithEarlyDemux; "with early demux")]
5520    #[test_case(NoEarlyDemux; "without early demux")]
5521    fn test_bound_device_receive_multicast_packet<I: TestIpExt>(early_demux_mode: EarlyDemuxMode) {
5522        set_logger_for_test();
5523        let remote_ip = I::get_other_ip_address(1);
5524        let multicast_addr = I::get_multicast_addr(0);
5525
5526        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5527            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5528        );
5529        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5530
5531        let sharing_domain = SharingDomain::new(1);
5532
5533        // Create 3 sockets: one listener bound on each device and one not bound
5534        // to a device.
5535
5536        let bound_on_devices = MultipleDevicesId::all().map(|device| {
5537            let listener = api.create();
5538            api.set_device(&listener, Some(&device)).unwrap();
5539            api.set_posix_reuse_port(&listener, ReusePortOption::Enabled(sharing_domain))
5540                .expect("is unbound");
5541            api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
5542
5543            (device, listener)
5544        });
5545
5546        let listener = api.create();
5547        api.set_posix_reuse_port(&listener, ReusePortOption::Enabled(sharing_domain))
5548            .expect("is unbound");
5549        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
5550
5551        fn index_for_device(id: MultipleDevicesId) -> u8 {
5552            match id {
5553                MultipleDevicesId::A => 0,
5554                MultipleDevicesId::B => 1,
5555                MultipleDevicesId::C => 2,
5556            }
5557        }
5558
5559        let (core_ctx, bindings_ctx) = api.contexts();
5560        let mut receive_packet = |remote_ip: SpecifiedAddr<I::Addr>, device: MultipleDevicesId| {
5561            let meta = UdpPacketMeta::<I> {
5562                src_ip: remote_ip.get(),
5563                src_port: Some(REMOTE_PORT),
5564                dst_ip: multicast_addr.get(),
5565                dst_port: LOCAL_PORT,
5566                dscp_and_ecn: DscpAndEcn::default(),
5567            };
5568            let body = vec![index_for_device(device)];
5569            receive_udp_packet(core_ctx, bindings_ctx, device, meta, &body, early_demux_mode)
5570                .expect("receive udp packet should succeed")
5571        };
5572
5573        // Receive packets from the remote IP on each device (2 packets total).
5574        // Listeners bound on devices should receive one, and the other listener
5575        // should receive both.
5576        for device in MultipleDevicesId::all() {
5577            receive_packet(remote_ip, device);
5578        }
5579
5580        let per_socket_data = bindings_ctx.state.socket_data();
5581        for (device, listener) in bound_on_devices {
5582            assert_eq!(per_socket_data[&listener.downgrade()], vec![&[index_for_device(device)]]);
5583        }
5584        let expected_listener_data = &MultipleDevicesId::all().map(|d| vec![index_for_device(d)]);
5585        assert_eq!(&per_socket_data[&listener.downgrade()], expected_listener_data);
5586    }
5587
5588    /// Tests establishing a UDP connection without providing a local IP
5589    #[ip_test(I)]
5590    fn test_conn_unspecified_local_ip<I: TestIpExt>() {
5591        set_logger_for_test();
5592        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5593        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5594        let socket = api.create();
5595        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5596        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5597            .expect("connect failed");
5598        let info = api.get_info(&socket);
5599        assert_eq!(
5600            info,
5601            SocketInfo::Connected(datagram::ConnInfo {
5602                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip::<I>()),
5603                local_identifier: LOCAL_PORT,
5604                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<I>()),
5605                remote_identifier: REMOTE_PORT.into(),
5606            })
5607        );
5608    }
5609
5610    #[ip_test(I)]
5611    fn test_multicast_sendto<I: TestIpExt>() {
5612        set_logger_for_test();
5613
5614        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5615            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5616        );
5617
5618        // Add multicsat route for every device.
5619        for device in MultipleDevicesId::all().iter() {
5620            ctx.core_ctx
5621                .bound_sockets
5622                .ip_socket_ctx
5623                .state
5624                .add_subnet_route(*device, I::MULTICAST_SUBNET);
5625        }
5626
5627        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5628        let socket = api.create();
5629
5630        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
5631            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
5632                .expect("bind should succeed");
5633
5634            let multicast_ip = I::get_multicast_addr(i.try_into().unwrap());
5635            api.send_to(
5636                &socket,
5637                Some(ZonedAddr::Unzoned(multicast_ip.into())),
5638                REMOTE_PORT.into(),
5639                Buf::new(b"packet".to_vec(), ..),
5640                FakeSendToken::default(),
5641            )
5642            .expect("send should succeed");
5643
5644            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
5645            assert_eq!(packets.len(), 1usize);
5646            for (meta, _body) in packets {
5647                let meta = meta.try_as::<I>().unwrap();
5648                assert_eq!(meta.device, *target_device);
5649                assert_eq!(meta.proto, IpProto::Udp.into());
5650                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
5651                assert_eq!(meta.dst_ip, multicast_ip.into());
5652                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
5653            }
5654        }
5655    }
5656
5657    #[ip_test(I)]
5658    fn test_multicast_send<I: TestIpExt>() {
5659        set_logger_for_test();
5660
5661        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5662            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5663        );
5664
5665        // Add multicsat route for every device.
5666        for device in MultipleDevicesId::all().iter() {
5667            ctx.core_ctx
5668                .bound_sockets
5669                .ip_socket_ctx
5670                .state
5671                .add_subnet_route(*device, I::MULTICAST_SUBNET);
5672        }
5673
5674        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5675        let multicast_ip = I::get_multicast_addr(42);
5676
5677        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
5678            let socket = api.create();
5679
5680            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
5681                .expect("set_multicast_interface should succeed");
5682
5683            api.connect(&socket, Some(ZonedAddr::Unzoned(multicast_ip.into())), REMOTE_PORT.into())
5684                .expect("send should succeed");
5685
5686            api.send(&socket, Buf::new(b"packet".to_vec(), ..), FakeSendToken::default())
5687                .expect("send should succeed");
5688
5689            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
5690            assert_eq!(packets.len(), 1usize);
5691            for (meta, _body) in packets {
5692                let meta = meta.try_as::<I>().unwrap();
5693                assert_eq!(meta.device, *target_device);
5694                assert_eq!(meta.proto, IpProto::Udp.into());
5695                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
5696                assert_eq!(meta.dst_ip, multicast_ip.into());
5697                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
5698            }
5699        }
5700    }
5701
5702    /// Tests local port allocation for [`connect`].
5703    ///
5704    /// Tests that calling [`connect`] causes a valid local port to be
5705    /// allocated.
5706    #[ip_test(I)]
5707    fn test_udp_local_port_alloc<I: TestIpExt>() {
5708        let local_ip = local_ip::<I>();
5709        let ip_a = I::get_other_ip_address(100);
5710        let ip_b = I::get_other_ip_address(200);
5711
5712        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
5713            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ip_a, ip_b]),
5714        );
5715        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5716
5717        let conn_a = api.create();
5718        api.connect(&conn_a, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
5719            .expect("connect failed");
5720        let conn_b = api.create();
5721        api.connect(&conn_b, Some(ZonedAddr::Unzoned(ip_b)), REMOTE_PORT.into())
5722            .expect("connect failed");
5723        let conn_c = api.create();
5724        api.connect(&conn_c, Some(ZonedAddr::Unzoned(ip_a)), OTHER_REMOTE_PORT.into())
5725            .expect("connect failed");
5726        let conn_d = api.create();
5727        api.connect(&conn_d, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
5728            .expect("connect failed");
5729        let valid_range = &FakePortAlloc::<I>::EPHEMERAL_RANGE;
5730        let mut get_conn_port = |id| {
5731            let info = api.get_info(&id);
5732            let info = assert_matches!(info, SocketInfo::Connected(info) => info);
5733            let datagram::ConnInfo {
5734                local_ip: _,
5735                local_identifier,
5736                remote_ip: _,
5737                remote_identifier: _,
5738            } = info;
5739            local_identifier
5740        };
5741        let port_a = get_conn_port(conn_a).get();
5742        let port_b = get_conn_port(conn_b).get();
5743        let port_c = get_conn_port(conn_c).get();
5744        let port_d = get_conn_port(conn_d).get();
5745        assert!(valid_range.contains(&port_a));
5746        assert!(valid_range.contains(&port_b));
5747        assert!(valid_range.contains(&port_c));
5748        assert!(valid_range.contains(&port_d));
5749        assert_ne!(port_a, port_b);
5750        assert_ne!(port_a, port_c);
5751        assert_ne!(port_a, port_d);
5752    }
5753
5754    /// Tests that if `listen_udp` fails, it can be retried later.
5755    #[ip_test(I)]
5756    fn test_udp_retry_listen_after_removing_conflict<I: TestIpExt>() {
5757        set_logger_for_test();
5758        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5759        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5760
5761        let listen_unbound = |api: &mut UdpApi<_, _>, socket: &UdpSocketId<_, _, _>| {
5762            api.listen(socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5763        };
5764
5765        // Tie up the address so the second call to `connect` fails.
5766        let listener = api.create();
5767        listen_unbound(&mut api, &listener)
5768            .expect("Initial call to listen_udp was expected to succeed");
5769
5770        // Trying to connect on the same address should fail.
5771        let unbound = api.create();
5772        assert_eq!(
5773            listen_unbound(&mut api, &unbound),
5774            Err(Either::Right(LocalAddressError::AddressInUse))
5775        );
5776
5777        // Once the first listener is removed, the second socket can be
5778        // connected.
5779        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(listener).into_removed();
5780
5781        listen_unbound(&mut api, &unbound).expect("listen should succeed");
5782    }
5783
5784    /// Tests local port allocation for [`listen_udp`].
5785    ///
5786    /// Tests that calling [`listen_udp`] causes a valid local port to be
5787    /// allocated when no local port is passed.
5788    #[ip_test(I)]
5789    fn test_udp_listen_port_alloc<I: TestIpExt>() {
5790        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5791        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5792        let local_ip = local_ip::<I>();
5793
5794        let wildcard_list = api.create();
5795        api.listen(&wildcard_list, None, None).expect("listen_udp failed");
5796        let specified_list = api.create();
5797        api.listen(&specified_list, Some(ZonedAddr::Unzoned(local_ip)), None)
5798            .expect("listen_udp failed");
5799        let mut get_listener_port = |id| {
5800            let info = api.get_info(&id);
5801            let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5802            let datagram::ListenerInfo { local_ip: _, local_identifier } = info;
5803            local_identifier
5804        };
5805        let wildcard_port = get_listener_port(wildcard_list);
5806        let specified_port = get_listener_port(specified_list);
5807        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&wildcard_port.get()));
5808        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&specified_port.get()));
5809        assert_ne!(wildcard_port, specified_port);
5810    }
5811
5812    #[ip_test(I)]
5813    fn test_bind_multiple_reuse_port<I: TestIpExt>() {
5814        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5815        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5816        let listeners = [(), ()].map(|()| {
5817            let socket = api.create();
5818            let sharing_domain = SharingDomain::new(1);
5819            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
5820                .expect("is unbound");
5821            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5822            socket
5823        });
5824
5825        for listener in listeners {
5826            assert_eq!(
5827                api.get_info(&listener),
5828                SocketInfo::Listener(datagram::ListenerInfo {
5829                    local_ip: None,
5830                    local_identifier: LOCAL_PORT
5831                })
5832            );
5833        }
5834    }
5835
5836    #[ip_test(I)]
5837    fn select_receiver_load_balances_deterministically<I: TestIpExt>() {
5838        const NUM_SOCKETS: usize = 4;
5839        const NUM_FLOWS: u16 = 100;
5840
5841        let state = AddrState::Shared {
5842            priority: Vec::new(),
5843            load_balanced: (0..NUM_SOCKETS)
5844                .map(|id| LoadBalancedEntry {
5845                    id,
5846                    sharing_domain: SharingDomain::new(1),
5847                    reuse_addr: false,
5848                })
5849                .collect(),
5850        };
5851        let selector = |src_port| SocketSelectorParams::<I, SpecifiedAddr<I::Addr>> {
5852            src_ip: remote_ip::<I>().get(),
5853            dst_ip: local_ip::<I>(),
5854            src_port,
5855            dst_port: LOCAL_PORT.get(),
5856            _ip: IpVersionMarker::default(),
5857        };
5858
5859        let selected = (0..NUM_FLOWS)
5860            .map(|src_port| {
5861                let selected = *state.select_receiver(selector(src_port));
5862                // All the packets in a flow are delivered to the same socket.
5863                assert_eq!(*state.select_receiver(selector(src_port)), selected);
5864                selected
5865            })
5866            .collect::<HashSet<_>>();
5867        // Different flows are spread across all the sockets.
5868        assert_eq!(selected.len(), NUM_SOCKETS);
5869    }
5870
5871    #[ip_test(I)]
5872    fn test_set_unset_reuse_port_unbound<I: TestIpExt>() {
5873        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5874        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5875        let unbound = api.create();
5876        let sharing_domain = SharingDomain::new(1);
5877        api.set_posix_reuse_port(&unbound, ReusePortOption::Enabled(sharing_domain))
5878            .expect("is unbound");
5879        api.set_posix_reuse_port(&unbound, ReusePortOption::Disabled).expect("is unbound");
5880        api.listen(&unbound, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5881
5882        // Because there is already a listener bound without `SO_REUSEPORT` set,
5883        // the next bind to the same address should fail.
5884        assert_eq!(
5885            {
5886                let unbound = api.create();
5887                api.listen(&unbound, None, Some(LOCAL_PORT))
5888            },
5889            Err(Either::Right(LocalAddressError::AddressInUse))
5890        );
5891    }
5892
5893    #[ip_test(I)]
5894    #[test_case(bind_as_listener)]
5895    #[test_case(bind_as_connected)]
5896    fn test_set_unset_reuse_port_bound<I: TestIpExt>(
5897        set_up_socket: impl FnOnce(
5898            &mut UdpMultipleDevicesCtx,
5899            &UdpSocketId<
5900                I,
5901                FakeWeakDeviceId<MultipleDevicesId>,
5902                FakeUdpBindingsCtx<MultipleDevicesId>,
5903            >,
5904        ),
5905    ) {
5906        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5907            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5908        );
5909        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5910        set_up_socket(&mut ctx, &socket);
5911
5912        // Per src/connectivity/network/netstack3/docs/POSIX_COMPATIBILITY.md,
5913        // Netstack3 only allows setting SO_REUSEPORT on unbound sockets.
5914        assert_matches!(
5915            UdpApi::<I, _>::new(ctx.as_mut())
5916                .set_posix_reuse_port(&socket, ReusePortOption::Disabled),
5917            Err(ExpectedUnboundError)
5918        )
5919    }
5920
5921    /// Tests [`remove_udp`]
5922    #[ip_test(I)]
5923    fn test_remove_udp_conn<I: TestIpExt>() {
5924        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5925        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5926
5927        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5928        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
5929        let socket = api.create();
5930        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).unwrap();
5931        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
5932        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(socket).into_removed();
5933    }
5934
5935    /// Tests [`remove_udp`]
5936    #[ip_test(I)]
5937    fn test_remove_udp_listener<I: TestIpExt>() {
5938        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5939        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5940        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5941
5942        // Test removing a specified listener.
5943        let specified = api.create();
5944        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5945        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(specified).into_removed();
5946
5947        // Test removing a wildcard listener.
5948        let wildcard = api.create();
5949        api.listen(&wildcard, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5950        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(wildcard).into_removed();
5951    }
5952
5953    fn try_join_leave_multicast<I: TestIpExt>(
5954        mcast_addr: MulticastAddr<I::Addr>,
5955        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
5956        set_up_ctx: impl FnOnce(&mut UdpMultipleDevicesCtx),
5957        set_up_socket: impl FnOnce(
5958            &mut UdpMultipleDevicesCtx,
5959            &UdpSocketId<
5960                I,
5961                FakeWeakDeviceId<MultipleDevicesId>,
5962                FakeUdpBindingsCtx<MultipleDevicesId>,
5963            >,
5964        ),
5965    ) -> (
5966        Result<(), SetMulticastMembershipError>,
5967        HashMap<(MultipleDevicesId, MulticastAddr<I::Addr>), NonZeroUsize>,
5968    ) {
5969        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5970            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5971        );
5972        set_up_ctx(&mut ctx);
5973
5974        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5975        set_up_socket(&mut ctx, &socket);
5976        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5977        let result = api.set_multicast_membership(&socket, mcast_addr, interface, true);
5978
5979        let memberships_snapshot =
5980            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>();
5981        if let Ok(()) = result {
5982            api.set_multicast_membership(&socket, mcast_addr, interface, false)
5983                .expect("leaving group failed");
5984        }
5985        assert_eq!(
5986            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5987            HashMap::default()
5988        );
5989
5990        (result, memberships_snapshot)
5991    }
5992
5993    fn leave_unbound<I: TestIpExt>(
5994        _ctx: &mut UdpMultipleDevicesCtx,
5995        _unbound: &UdpSocketId<
5996            I,
5997            FakeWeakDeviceId<MultipleDevicesId>,
5998            FakeUdpBindingsCtx<MultipleDevicesId>,
5999        >,
6000    ) {
6001    }
6002
6003    fn bind_as_listener<I: TestIpExt>(
6004        ctx: &mut UdpMultipleDevicesCtx,
6005        unbound: &UdpSocketId<
6006            I,
6007            FakeWeakDeviceId<MultipleDevicesId>,
6008            FakeUdpBindingsCtx<MultipleDevicesId>,
6009        >,
6010    ) {
6011        UdpApi::<I, _>::new(ctx.as_mut())
6012            .listen(unbound, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
6013            .expect("listen should succeed")
6014    }
6015
6016    fn bind_as_connected<I: TestIpExt>(
6017        ctx: &mut UdpMultipleDevicesCtx,
6018        unbound: &UdpSocketId<
6019            I,
6020            FakeWeakDeviceId<MultipleDevicesId>,
6021            FakeUdpBindingsCtx<MultipleDevicesId>,
6022        >,
6023    ) {
6024        UdpApi::<I, _>::new(ctx.as_mut())
6025            .connect(
6026                unbound,
6027                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
6028                REMOTE_PORT.into(),
6029            )
6030            .expect("connect should succeed")
6031    }
6032
6033    fn iface_id<A: IpAddress>(
6034        id: MultipleDevicesId,
6035    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
6036        MulticastInterfaceSelector::Interface(id).into()
6037    }
6038    fn iface_addr<A: IpAddress>(
6039        addr: SpecifiedAddr<A>,
6040    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
6041        MulticastInterfaceSelector::LocalAddress(addr).into()
6042    }
6043
6044    #[ip_test(I)]
6045    #[test_case(iface_id(MultipleDevicesId::A), leave_unbound::<I>; "device_no_addr_unbound")]
6046    #[test_case(iface_addr(local_ip::<I>()), leave_unbound::<I>; "addr_no_device_unbound")]
6047    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, leave_unbound::<I>;
6048        "any_interface_unbound")]
6049    #[test_case(iface_id(MultipleDevicesId::A), bind_as_listener::<I>; "device_no_addr_listener")]
6050    #[test_case(iface_addr(local_ip::<I>()), bind_as_listener::<I>; "addr_no_device_listener")]
6051    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_listener::<I>;
6052        "any_interface_listener")]
6053    #[test_case(iface_id(MultipleDevicesId::A), bind_as_connected::<I>; "device_no_addr_connected")]
6054    #[test_case(iface_addr(local_ip::<I>()), bind_as_connected::<I>; "addr_no_device_connected")]
6055    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_connected::<I>;
6056        "any_interface_connected")]
6057    fn test_join_leave_multicast_succeeds<I: TestIpExt>(
6058        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
6059        set_up_socket: impl FnOnce(
6060            &mut UdpMultipleDevicesCtx,
6061            &UdpSocketId<
6062                I,
6063                FakeWeakDeviceId<MultipleDevicesId>,
6064                FakeUdpBindingsCtx<MultipleDevicesId>,
6065            >,
6066        ),
6067    ) {
6068        let mcast_addr = I::get_multicast_addr(3);
6069
6070        let set_up_ctx = |ctx: &mut UdpMultipleDevicesCtx| {
6071            // Ensure there is a route to the multicast address, if the interface
6072            // selector requires it.
6073            match interface {
6074                MulticastMembershipInterfaceSelector::Specified(_) => {}
6075                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
6076                    ctx.core_ctx
6077                        .bound_sockets
6078                        .ip_socket_ctx
6079                        .state
6080                        .add_route(MultipleDevicesId::A, mcast_addr.into_specified().into());
6081                }
6082            }
6083        };
6084
6085        let (result, ip_options) =
6086            try_join_leave_multicast(mcast_addr, interface, set_up_ctx, set_up_socket);
6087        assert_eq!(result, Ok(()));
6088        assert_eq!(
6089            ip_options,
6090            HashMap::from([((MultipleDevicesId::A, mcast_addr), NonZeroUsize::new(1).unwrap())])
6091        );
6092    }
6093
6094    #[ip_test(I)]
6095    #[test_case(leave_unbound::<I>; "unbound")]
6096    #[test_case(bind_as_listener::<I>; "listener")]
6097    #[test_case(bind_as_connected::<I>; "connected")]
6098    fn test_join_multicast_fails_without_route<I: TestIpExt>(
6099        set_up_socket: impl FnOnce(
6100            &mut UdpMultipleDevicesCtx,
6101            &UdpSocketId<
6102                I,
6103                FakeWeakDeviceId<MultipleDevicesId>,
6104                FakeUdpBindingsCtx<MultipleDevicesId>,
6105            >,
6106        ),
6107    ) {
6108        let mcast_addr = I::get_multicast_addr(3);
6109
6110        let (result, ip_options) = try_join_leave_multicast(
6111            mcast_addr,
6112            MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
6113            |_: &mut UdpMultipleDevicesCtx| { /* Don't install a route to `mcast_addr` */ },
6114            set_up_socket,
6115        );
6116        assert_eq!(result, Err(SetMulticastMembershipError::NoDeviceAvailable));
6117        assert_eq!(ip_options, HashMap::new());
6118    }
6119
6120    #[ip_test(I)]
6121    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), leave_unbound, Ok(());
6122        "with_ip_unbound")]
6123    #[test_case(MultipleDevicesId::A, None, leave_unbound, Ok(());
6124        "without_ip_unbound")]
6125    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_listener, Ok(());
6126        "with_ip_listener")]
6127    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_connected, Ok(());
6128        "with_ip_connected")]
6129    fn test_join_leave_multicast_interface_inferred_from_bound_device<I: TestIpExt>(
6130        bound_device: MultipleDevicesId,
6131        interface_addr: Option<SpecifiedAddr<I::Addr>>,
6132        set_up_socket: impl FnOnce(
6133            &mut UdpMultipleDevicesCtx,
6134            &UdpSocketId<
6135                I,
6136                FakeWeakDeviceId<MultipleDevicesId>,
6137                FakeUdpBindingsCtx<MultipleDevicesId>,
6138            >,
6139        ),
6140        expected_result: Result<(), SetMulticastMembershipError>,
6141    ) {
6142        let mcast_addr = I::get_multicast_addr(3);
6143        let (result, ip_options) = try_join_leave_multicast(
6144            mcast_addr,
6145            interface_addr
6146                .map(MulticastInterfaceSelector::LocalAddress)
6147                .map(Into::into)
6148                .unwrap_or(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute),
6149            |_: &mut UdpMultipleDevicesCtx| { /* No ctx setup required */ },
6150            |ctx, unbound| {
6151                UdpApi::<I, _>::new(ctx.as_mut())
6152                    .set_device(&unbound, Some(&bound_device))
6153                    .unwrap();
6154                set_up_socket(ctx, &unbound)
6155            },
6156        );
6157        assert_eq!(result, expected_result);
6158        assert_eq!(
6159            ip_options,
6160            expected_result.map_or_else(
6161                |_| HashMap::default(),
6162                |()| HashMap::from([((bound_device, mcast_addr), NonZeroUsize::new(1).unwrap())])
6163            )
6164        );
6165    }
6166
6167    #[ip_test(I)]
6168    fn test_multicast_membership_with_removed_device<I: TestIpExt>() {
6169        let device = FakeReferencyDeviceId::default();
6170        let mut ctx =
6171            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
6172        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6173
6174        let unbound = api.create();
6175        api.set_device(&unbound, Some(&device)).unwrap();
6176
6177        device.mark_removed();
6178
6179        let group = I::get_multicast_addr(4);
6180        assert_eq!(
6181            api.set_multicast_membership(
6182                &unbound,
6183                group,
6184                // Will use the socket's bound device.
6185                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
6186                true,
6187            ),
6188            Err(SetMulticastMembershipError::DeviceDoesNotExist),
6189        );
6190
6191        // Should not have updated the device's multicast state.
6192        //
6193        // Note that even though we mock the device being removed above, its
6194        // state still exists in the fake IP socket context so we can inspect
6195        // it here.
6196        assert_eq!(
6197            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6198            HashMap::default(),
6199        );
6200    }
6201
6202    #[ip_test(I)]
6203    fn test_remove_udp_unbound_leaves_multicast_groups<I: TestIpExt>() {
6204        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6205            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
6206        );
6207        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6208
6209        let unbound = api.create();
6210        let group = I::get_multicast_addr(4);
6211        api.set_multicast_membership(
6212            &unbound,
6213            group,
6214            MulticastInterfaceSelector::LocalAddress(local_ip::<I>()).into(),
6215            true,
6216        )
6217        .expect("join group failed");
6218
6219        assert_eq!(
6220            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6221            HashMap::from([((MultipleDevicesId::A, group), NonZeroUsize::new(1).unwrap())])
6222        );
6223
6224        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(unbound).into_removed();
6225        assert_eq!(
6226            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6227            HashMap::default()
6228        );
6229    }
6230
6231    #[ip_test(I)]
6232    fn test_remove_udp_listener_leaves_multicast_groups<I: TestIpExt>() {
6233        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6234            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
6235        );
6236        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6237        let local_ip = local_ip::<I>();
6238
6239        let socket = api.create();
6240        let first_group = I::get_multicast_addr(4);
6241        api.set_multicast_membership(
6242            &socket,
6243            first_group,
6244            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
6245            true,
6246        )
6247        .expect("join group failed");
6248
6249        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
6250            .expect("listen_udp failed");
6251        let second_group = I::get_multicast_addr(5);
6252        api.set_multicast_membership(
6253            &socket,
6254            second_group,
6255            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
6256            true,
6257        )
6258        .expect("join group failed");
6259
6260        assert_eq!(
6261            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6262            HashMap::from([
6263                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
6264                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
6265            ])
6266        );
6267
6268        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(socket).into_removed();
6269        assert_eq!(
6270            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6271            HashMap::default()
6272        );
6273    }
6274
6275    #[ip_test(I)]
6276    fn test_remove_udp_connected_leaves_multicast_groups<I: TestIpExt>() {
6277        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6278            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
6279        );
6280        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6281        let local_ip = local_ip::<I>();
6282
6283        let socket = api.create();
6284        let first_group = I::get_multicast_addr(4);
6285        api.set_multicast_membership(
6286            &socket,
6287            first_group,
6288            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
6289            true,
6290        )
6291        .expect("join group failed");
6292
6293        api.connect(
6294            &socket,
6295            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
6296            REMOTE_PORT.into(),
6297        )
6298        .expect("connect failed");
6299
6300        let second_group = I::get_multicast_addr(5);
6301        api.set_multicast_membership(
6302            &socket,
6303            second_group,
6304            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
6305            true,
6306        )
6307        .expect("join group failed");
6308
6309        assert_eq!(
6310            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6311            HashMap::from([
6312                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
6313                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
6314            ])
6315        );
6316
6317        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(socket).into_removed();
6318        assert_eq!(
6319            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
6320            HashMap::default()
6321        );
6322    }
6323
6324    #[ip_test(I)]
6325    #[should_panic(expected = "listen again failed")]
6326    fn test_listen_udp_removes_unbound<I: TestIpExt>() {
6327        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6328        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6329        let local_ip = local_ip::<I>();
6330        let socket = api.create();
6331
6332        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
6333            .expect("listen_udp failed");
6334
6335        // Attempting to create a new listener from the same unbound ID should
6336        // panic since the unbound socket ID is now invalid.
6337        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(OTHER_LOCAL_PORT))
6338            .expect("listen again failed");
6339    }
6340
6341    #[ip_test(I)]
6342    fn test_get_conn_info<I: TestIpExt>() {
6343        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6344        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6345        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
6346        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
6347        // Create a UDP connection with a specified local port and local IP.
6348        let socket = api.create();
6349        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
6350        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
6351        let info = api.get_info(&socket);
6352        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
6353        assert_eq!(info.local_ip.into_inner(), local_ip.map_zone(FakeWeakDeviceId));
6354        assert_eq!(info.local_identifier, LOCAL_PORT);
6355        assert_eq!(info.remote_ip.into_inner(), remote_ip.map_zone(FakeWeakDeviceId));
6356        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
6357    }
6358
6359    #[ip_test(I)]
6360    fn test_get_listener_info<I: TestIpExt>() {
6361        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6362        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6363        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
6364
6365        // Check getting info on specified listener.
6366        let specified = api.create();
6367        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
6368        let info = api.get_info(&specified);
6369        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
6370        assert_eq!(info.local_ip.unwrap().into_inner(), local_ip.map_zone(FakeWeakDeviceId));
6371        assert_eq!(info.local_identifier, LOCAL_PORT);
6372
6373        // Check getting info on wildcard listener.
6374        let wildcard = api.create();
6375        api.listen(&wildcard, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
6376        let info = api.get_info(&wildcard);
6377        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
6378        assert_eq!(info.local_ip, None);
6379        assert_eq!(info.local_identifier, OTHER_LOCAL_PORT);
6380    }
6381
6382    #[ip_test(I)]
6383    fn test_get_reuse_port<I: TestIpExt>() {
6384        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6385        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6386        let first = api.create();
6387        assert_eq!(api.get_posix_reuse_port(&first), false);
6388
6389        let sharing_domain = SharingDomain::new(1);
6390        api.set_posix_reuse_port(&first, ReusePortOption::Enabled(sharing_domain))
6391            .expect("is unbound");
6392
6393        assert_eq!(api.get_posix_reuse_port(&first), true);
6394
6395        api.listen(&first, Some(ZonedAddr::Unzoned(local_ip::<I>())), None).expect("listen failed");
6396        assert_eq!(api.get_posix_reuse_port(&first), true);
6397        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(first).into_removed();
6398
6399        let second = api.create();
6400        api.set_posix_reuse_port(&second, ReusePortOption::Enabled(sharing_domain))
6401            .expect("is unbound");
6402        api.connect(&second, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
6403            .expect("connect failed");
6404
6405        assert_eq!(api.get_posix_reuse_port(&second), true);
6406    }
6407
6408    #[ip_test(I)]
6409    fn test_get_bound_device_unbound<I: TestIpExt>() {
6410        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6411        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6412        let unbound = api.create();
6413
6414        assert_eq!(api.get_bound_device(&unbound), None);
6415
6416        api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
6417        assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
6418    }
6419
6420    #[ip_test(I)]
6421    fn test_get_bound_device_listener<I: TestIpExt>() {
6422        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6423        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6424        let socket = api.create();
6425
6426        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
6427        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
6428            .expect("failed to listen");
6429        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6430
6431        api.set_device(&socket, None).expect("failed to set device");
6432        assert_eq!(api.get_bound_device(&socket), None);
6433    }
6434
6435    #[ip_test(I)]
6436    fn test_get_bound_device_connected<I: TestIpExt>() {
6437        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6438        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6439        let socket = api.create();
6440        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
6441        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
6442            .expect("failed to connect");
6443        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6444        api.set_device(&socket, None).expect("failed to set device");
6445        assert_eq!(api.get_bound_device(&socket), None);
6446    }
6447
6448    #[ip_test(I)]
6449    fn test_listen_udp_forwards_errors<I: TestIpExt>() {
6450        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6451        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6452        let remote_ip = remote_ip::<I>();
6453
6454        // Check listening to a non-local IP fails.
6455        let unbound = api.create();
6456        let listen_err = api
6457            .listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT))
6458            .expect_err("listen_udp unexpectedly succeeded");
6459        assert_eq!(listen_err, Either::Right(LocalAddressError::CannotBindToAddress));
6460
6461        let unbound = api.create();
6462        let _ = api.listen(&unbound, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
6463        let unbound = api.create();
6464        let listen_err = api
6465            .listen(&unbound, None, Some(OTHER_LOCAL_PORT))
6466            .expect_err("listen_udp unexpectedly succeeded");
6467        assert_eq!(listen_err, Either::Right(LocalAddressError::AddressInUse));
6468    }
6469
6470    const IPV6_LINK_LOCAL_ADDR: Ipv6Addr = net_ip_v6!("fe80::1234");
6471    #[test_case(IPV6_LINK_LOCAL_ADDR, IPV6_LINK_LOCAL_ADDR; "unicast")]
6472    #[test_case(IPV6_LINK_LOCAL_ADDR, MulticastAddr::new(net_ip_v6!("ff02::1234")).unwrap().get(); "multicast")]
6473    fn test_listen_udp_ipv6_link_local_requires_zone(
6474        interface_addr: Ipv6Addr,
6475        bind_addr: Ipv6Addr,
6476    ) {
6477        type I = Ipv6;
6478        let interface_addr = LinkLocalAddr::new(interface_addr).unwrap().into_specified();
6479
6480        let mut ctx =
6481            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
6482                vec![interface_addr],
6483                vec![remote_ip::<I>()],
6484            ));
6485        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6486
6487        let bind_addr = LinkLocalAddr::new(bind_addr).unwrap().into_specified();
6488        assert!(bind_addr.scope().can_have_zone());
6489
6490        let unbound = api.create();
6491        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(bind_addr)), Some(LOCAL_PORT));
6492        assert_eq!(
6493            result,
6494            Err(Either::Right(LocalAddressError::Zone(ZonedAddressError::RequiredZoneNotProvided)))
6495        );
6496    }
6497
6498    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
6499    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "not matching")]
6500    fn test_listen_udp_ipv6_link_local_with_bound_device_set(
6501        zone_id: MultipleDevicesId,
6502        expected_result: Result<(), LocalAddressError>,
6503    ) {
6504        type I = Ipv6;
6505        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6506        assert!(ll_addr.scope().can_have_zone());
6507
6508        let remote_ips = vec![remote_ip::<I>()];
6509        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6510            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6511                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6512                    |(device, local_ip)| FakeDeviceConfig {
6513                        device,
6514                        local_ips: vec![local_ip],
6515                        remote_ips: remote_ips.clone(),
6516                    },
6517                ),
6518            )),
6519        );
6520        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6521
6522        let socket = api.create();
6523        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
6524
6525        let result = api
6526            .listen(
6527                &socket,
6528                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
6529                Some(LOCAL_PORT),
6530            )
6531            .map_err(Either::unwrap_right);
6532        assert_eq!(result, expected_result);
6533    }
6534
6535    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
6536    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::AddressMismatch); "not matching")]
6537    fn test_listen_udp_ipv6_link_local_with_zone_requires_addr_assigned_to_device(
6538        zone_id: MultipleDevicesId,
6539        expected_result: Result<(), LocalAddressError>,
6540    ) {
6541        type I = Ipv6;
6542        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6543        assert!(ll_addr.scope().can_have_zone());
6544
6545        let remote_ips = vec![remote_ip::<I>()];
6546        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6547            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6548                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6549                    |(device, local_ip)| FakeDeviceConfig {
6550                        device,
6551                        local_ips: vec![local_ip],
6552                        remote_ips: remote_ips.clone(),
6553                    },
6554                ),
6555            )),
6556        );
6557        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6558
6559        let socket = api.create();
6560        let result = api
6561            .listen(
6562                &socket,
6563                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
6564                Some(LOCAL_PORT),
6565            )
6566            .map_err(Either::unwrap_right);
6567        assert_eq!(result, expected_result);
6568    }
6569
6570    #[test_case(None, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "clear device")]
6571    #[test_case(Some(MultipleDevicesId::A), Ok(()); "set same device")]
6572    #[test_case(Some(MultipleDevicesId::B),
6573                Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "change device")]
6574    fn test_listen_udp_ipv6_listen_link_local_update_bound_device(
6575        new_device: Option<MultipleDevicesId>,
6576        expected_result: Result<(), LocalAddressError>,
6577    ) {
6578        type I = Ipv6;
6579        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6580        assert!(ll_addr.scope().can_have_zone());
6581
6582        let remote_ips = vec![remote_ip::<I>()];
6583        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6584            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6585                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6586                    |(device, local_ip)| FakeDeviceConfig {
6587                        device,
6588                        local_ips: vec![local_ip],
6589                        remote_ips: remote_ips.clone(),
6590                    },
6591                ),
6592            )),
6593        );
6594        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6595
6596        let socket = api.create();
6597        api.listen(
6598            &socket,
6599            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap())),
6600            Some(LOCAL_PORT),
6601        )
6602        .expect("listen failed");
6603
6604        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(MultipleDevicesId::A)));
6605
6606        assert_eq!(
6607            api.set_device(&socket, new_device.as_ref()),
6608            expected_result.map_err(SocketError::Local),
6609        );
6610    }
6611
6612    #[test_case(None; "bind all IPs")]
6613    #[test_case(Some(ZonedAddr::Unzoned(local_ip::<Ipv6>())); "bind unzoned")]
6614    #[test_case(Some(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6615        MultipleDevicesId::A).unwrap())); "bind with same zone")]
6616    fn test_udp_ipv6_connect_with_unzoned(
6617        bound_addr: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>>,
6618    ) {
6619        let remote_ips = vec![remote_ip::<Ipv6>()];
6620
6621        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6622            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
6623                FakeDeviceConfig {
6624                    device: MultipleDevicesId::A,
6625                    local_ips: vec![
6626                        local_ip::<Ipv6>(),
6627                        SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6628                    ],
6629                    remote_ips: remote_ips.clone(),
6630                },
6631                FakeDeviceConfig {
6632                    device: MultipleDevicesId::B,
6633                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
6634                    remote_ips: remote_ips,
6635                },
6636            ])),
6637        );
6638        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6639
6640        let socket = api.create();
6641
6642        api.listen(&socket, bound_addr, Some(LOCAL_PORT)).unwrap();
6643
6644        assert_matches!(
6645            api.connect(
6646                &socket,
6647                Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())),
6648                REMOTE_PORT.into(),
6649            ),
6650            Ok(())
6651        );
6652    }
6653
6654    #[test]
6655    fn test_udp_ipv6_connect_zoned_get_info() {
6656        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6657        assert!(ll_addr.must_have_zone());
6658
6659        let remote_ips = vec![remote_ip::<Ipv6>()];
6660        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6661            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6662                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<Ipv6>())].map(
6663                    |(device, local_ip)| FakeDeviceConfig {
6664                        device,
6665                        local_ips: vec![local_ip],
6666                        remote_ips: remote_ips.clone(),
6667                    },
6668                ),
6669            )),
6670        );
6671
6672        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6673        let socket = api.create();
6674        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
6675
6676        let zoned_local_addr =
6677            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
6678        api.listen(&socket, Some(zoned_local_addr), Some(LOCAL_PORT)).unwrap();
6679
6680        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())), REMOTE_PORT.into())
6681            .expect("connect should succeed");
6682
6683        assert_eq!(
6684            api.get_info(&socket),
6685            SocketInfo::Connected(datagram::ConnInfo {
6686                local_ip: StrictlyZonedAddr::new_with_zone(ll_addr, || FakeWeakDeviceId(
6687                    MultipleDevicesId::A
6688                )),
6689                local_identifier: LOCAL_PORT,
6690                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<Ipv6>()),
6691                remote_identifier: REMOTE_PORT.into(),
6692            })
6693        );
6694    }
6695
6696    #[test_case(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap(),
6697        MultipleDevicesId::B).unwrap()),
6698        Err(ConnectError::Zone(ZonedAddressError::DeviceZoneMismatch));
6699        "connect to different zone")]
6700    #[test_case(ZonedAddr::Unzoned(SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()),
6701        Ok(FakeWeakDeviceId(MultipleDevicesId::A)); "connect implicit zone")]
6702    fn test_udp_ipv6_bind_zoned(
6703        remote_addr: ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>,
6704        expected: Result<FakeWeakDeviceId<MultipleDevicesId>, ConnectError>,
6705    ) {
6706        let remote_ips = vec![SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()];
6707
6708        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6709            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
6710                FakeDeviceConfig {
6711                    device: MultipleDevicesId::A,
6712                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()],
6713                    remote_ips: remote_ips.clone(),
6714                },
6715                FakeDeviceConfig {
6716                    device: MultipleDevicesId::B,
6717                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
6718                    remote_ips: remote_ips,
6719                },
6720            ])),
6721        );
6722
6723        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6724
6725        let socket = api.create();
6726
6727        api.listen(
6728            &socket,
6729            Some(ZonedAddr::Zoned(
6730                AddrAndZone::new(
6731                    SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6732                    MultipleDevicesId::A,
6733                )
6734                .unwrap(),
6735            )),
6736            Some(LOCAL_PORT),
6737        )
6738        .unwrap();
6739
6740        let result = api
6741            .connect(&socket, Some(remote_addr), REMOTE_PORT.into())
6742            .map(|()| api.get_bound_device(&socket).unwrap());
6743        assert_eq!(result, expected);
6744    }
6745
6746    #[ip_test(I)]
6747    fn test_listen_udp_loopback_no_zone_is_required<I: TestIpExt>() {
6748        let loopback_addr = I::LOOPBACK_ADDRESS;
6749        let remote_ips = vec![remote_ip::<I>()];
6750
6751        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6752            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6753                [(MultipleDevicesId::A, loopback_addr), (MultipleDevicesId::B, local_ip::<I>())]
6754                    .map(|(device, local_ip)| FakeDeviceConfig {
6755                        device,
6756                        local_ips: vec![local_ip],
6757                        remote_ips: remote_ips.clone(),
6758                    }),
6759            )),
6760        );
6761        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6762
6763        let unbound = api.create();
6764        api.set_device(&unbound, Some(&MultipleDevicesId::A)).unwrap();
6765
6766        let result =
6767            api.listen(&unbound, Some(ZonedAddr::Unzoned(loopback_addr)), Some(LOCAL_PORT));
6768        assert_matches!(result, Ok(_));
6769    }
6770
6771    #[test_case(None, true, Ok(()); "connected success")]
6772    #[test_case(None, false, Ok(()); "listening success")]
6773    #[test_case(Some(MultipleDevicesId::A), true, Ok(()); "conn bind same device")]
6774    #[test_case(Some(MultipleDevicesId::A), false, Ok(()); "listen bind same device")]
6775    #[test_case(
6776        Some(MultipleDevicesId::B),
6777        true,
6778        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
6779        "conn bind different device")]
6780    #[test_case(
6781        Some(MultipleDevicesId::B),
6782        false,
6783        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
6784        "listen bind different device")]
6785    fn test_udp_ipv6_send_to_zoned(
6786        bind_device: Option<MultipleDevicesId>,
6787        connect: bool,
6788        expected: Result<(), SendToError>,
6789    ) {
6790        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6791        assert!(ll_addr.must_have_zone());
6792        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
6793
6794        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6795            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6796                [
6797                    (MultipleDevicesId::A, Ipv6::get_other_ip_address(1)),
6798                    (MultipleDevicesId::B, Ipv6::get_other_ip_address(2)),
6799                ]
6800                .map(|(device, local_ip)| FakeDeviceConfig {
6801                    device,
6802                    local_ips: vec![local_ip],
6803                    remote_ips: vec![ll_addr, conn_remote_ip],
6804                }),
6805            )),
6806        );
6807
6808        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6809        let socket = api.create();
6810
6811        if let Some(device) = bind_device {
6812            api.set_device(&socket, Some(&device)).unwrap();
6813        }
6814
6815        let send_to_remote_addr =
6816            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
6817        let result = if connect {
6818            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6819                .expect("connect should succeed");
6820            api.send_to(
6821                &socket,
6822                Some(send_to_remote_addr),
6823                REMOTE_PORT.into(),
6824                Buf::new(Vec::new(), ..),
6825                FakeSendToken::default(),
6826            )
6827        } else {
6828            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
6829            api.send_to(
6830                &socket,
6831                Some(send_to_remote_addr),
6832                REMOTE_PORT.into(),
6833                Buf::new(Vec::new(), ..),
6834                FakeSendToken::default(),
6835            )
6836        };
6837
6838        assert_eq!(result, expected);
6839    }
6840
6841    #[test_case(true; "connected")]
6842    #[test_case(false; "listening")]
6843    fn test_udp_ipv6_bound_zoned_send_to_zoned(connect: bool) {
6844        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::5678")).unwrap().into_specified();
6845        let device_a_local_ip = net_ip_v6!("fe80::1111");
6846        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
6847
6848        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6849            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6850                [
6851                    (MultipleDevicesId::A, device_a_local_ip),
6852                    (MultipleDevicesId::B, net_ip_v6!("fe80::2222")),
6853                ]
6854                .map(|(device, local_ip)| FakeDeviceConfig {
6855                    device,
6856                    local_ips: vec![LinkLocalAddr::new(local_ip).unwrap().into_specified()],
6857                    remote_ips: vec![ll_addr, conn_remote_ip],
6858                }),
6859            )),
6860        );
6861        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6862
6863        let socket = api.create();
6864        api.listen(
6865            &socket,
6866            Some(ZonedAddr::Zoned(
6867                AddrAndZone::new(
6868                    SpecifiedAddr::new(device_a_local_ip).unwrap(),
6869                    MultipleDevicesId::A,
6870                )
6871                .unwrap(),
6872            )),
6873            Some(LOCAL_PORT),
6874        )
6875        .expect("listen should succeed");
6876
6877        // Use a remote address on device B, while the socket is listening on
6878        // device A. This should cause a failure when sending.
6879        let send_to_remote_addr =
6880            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::B).unwrap());
6881
6882        let result = if connect {
6883            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6884                .expect("connect should succeed");
6885            api.send_to(
6886                &socket,
6887                Some(send_to_remote_addr),
6888                REMOTE_PORT.into(),
6889                Buf::new(Vec::new(), ..),
6890                FakeSendToken::default(),
6891            )
6892        } else {
6893            api.send_to(
6894                &socket,
6895                Some(send_to_remote_addr),
6896                REMOTE_PORT.into(),
6897                Buf::new(Vec::new(), ..),
6898                FakeSendToken::default(),
6899            )
6900        };
6901
6902        assert_matches!(result, Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch)));
6903    }
6904
6905    #[test_case(None; "removes implicit")]
6906    #[test_case(Some(FakeDeviceId); "preserves implicit")]
6907    fn test_connect_disconnect_affects_bound_device(bind_device: Option<FakeDeviceId>) {
6908        // If a socket is bound to an unzoned address, whether or not it has a
6909        // bound device should be restored after `connect` then `disconnect`.
6910        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6911        assert!(ll_addr.must_have_zone());
6912
6913        let local_ip = local_ip::<Ipv6>();
6914        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6915            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6916        );
6917        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6918
6919        let socket = api.create();
6920        api.set_device(&socket, bind_device.as_ref()).unwrap();
6921
6922        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6923        api.connect(
6924            &socket,
6925            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6926            REMOTE_PORT.into(),
6927        )
6928        .expect("connect should succeed");
6929
6930        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6931
6932        api.disconnect(&socket).expect("was connected");
6933
6934        assert_eq!(api.get_bound_device(&socket), bind_device.map(FakeWeakDeviceId));
6935    }
6936
6937    #[test]
6938    fn test_bind_zoned_addr_connect_disconnect() {
6939        // If a socket is bound to a zoned address, the address's device should
6940        // be retained after `connect` then `disconnect`.
6941        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6942        assert!(ll_addr.must_have_zone());
6943
6944        let remote_ip = remote_ip::<Ipv6>();
6945        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6946            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![ll_addr], vec![remote_ip]),
6947        );
6948
6949        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6950
6951        let socket = api.create();
6952        api.listen(
6953            &socket,
6954            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6955            Some(LOCAL_PORT),
6956        )
6957        .unwrap();
6958        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
6959            .expect("connect should succeed");
6960
6961        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6962
6963        api.disconnect(&socket).expect("was connected");
6964        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6965    }
6966
6967    #[test]
6968    fn test_bind_device_after_connect_persists_after_disconnect() {
6969        // If a socket is bound to an unzoned address, connected to a zoned address, and then has
6970        // its device set, the device should be *retained* after `disconnect`.
6971        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6972        assert!(ll_addr.must_have_zone());
6973
6974        let local_ip = local_ip::<Ipv6>();
6975        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6976            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6977        );
6978        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6979        let socket = api.create();
6980        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6981        api.connect(
6982            &socket,
6983            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6984            REMOTE_PORT.into(),
6985        )
6986        .expect("connect should succeed");
6987
6988        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6989
6990        // This is a no-op functionally since the socket is already bound to the
6991        // device but it implies that we shouldn't unbind the device on
6992        // disconnect.
6993        api.set_device(&socket, Some(&FakeDeviceId)).expect("binding same device should succeed");
6994
6995        api.disconnect(&socket).expect("was connected");
6996        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6997    }
6998
6999    #[ip_test(I)]
7000    fn test_remove_udp_unbound<I: TestIpExt>() {
7001        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
7002        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
7003        let unbound = api.create();
7004        let _: (UdpSocketDiagnosticsSeed<I, _, _>, ()) = api.close(unbound).into_removed();
7005    }
7006
7007    #[ip_test(I)]
7008    fn test_hop_limits_used_for_sending_packets<I: TestIpExt>() {
7009        let some_multicast_addr: MulticastAddr<I::Addr> = I::map_ip(
7010            (),
7011            |()| Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
7012            |()| MulticastAddr::new(net_ip_v6!("ff0e::1")).unwrap(),
7013        );
7014
7015        let mut ctx =
7016            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
7017                vec![local_ip::<I>()],
7018                vec![remote_ip::<I>(), some_multicast_addr.into_specified()],
7019            ));
7020        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
7021        let listener = api.create();
7022
7023        const UNICAST_HOPS: NonZeroU8 = NonZeroU8::new(23).unwrap();
7024        const MULTICAST_HOPS: NonZeroU8 = NonZeroU8::new(98).unwrap();
7025        api.set_unicast_hop_limit(&listener, Some(UNICAST_HOPS), I::VERSION).unwrap();
7026        api.set_multicast_hop_limit(&listener, Some(MULTICAST_HOPS), I::VERSION).unwrap();
7027
7028        api.listen(&listener, None, None).expect("listen failed");
7029
7030        let mut send_and_get_ttl = |remote_ip| {
7031            api.send_to(
7032                &listener,
7033                Some(ZonedAddr::Unzoned(remote_ip)),
7034                REMOTE_PORT.into(),
7035                Buf::new(vec![], ..),
7036                FakeSendToken::default(),
7037            )
7038            .expect("send failed");
7039
7040            let (meta, _body) = api.core_ctx().bound_sockets.ip_socket_ctx.frames().last().unwrap();
7041            let SendIpPacketMeta { dst_ip, ttl, .. } = meta.try_as::<I>().unwrap();
7042            assert_eq!(*dst_ip, remote_ip);
7043            *ttl
7044        };
7045
7046        assert_eq!(send_and_get_ttl(some_multicast_addr.into_specified()), Some(MULTICAST_HOPS));
7047        assert_eq!(send_and_get_ttl(remote_ip::<I>()), Some(UNICAST_HOPS));
7048    }
7049
7050    const DUAL_STACK_ANY_ADDR: Ipv6Addr = net_ip_v6!("::");
7051    const DUAL_STACK_V4_ANY_ADDR: Ipv6Addr = net_ip_v6!("::FFFF:0.0.0.0");
7052
7053    #[derive(Copy, Clone, Debug)]
7054    enum DualStackBindAddr {
7055        Any,
7056        V4Any,
7057        V4Specific,
7058    }
7059
7060    impl DualStackBindAddr {
7061        const fn v6_addr(&self) -> Option<Ipv6Addr> {
7062            match self {
7063                Self::Any => Some(DUAL_STACK_ANY_ADDR),
7064                Self::V4Any => Some(DUAL_STACK_V4_ANY_ADDR),
7065                Self::V4Specific => None,
7066            }
7067        }
7068    }
7069    const V4_LOCAL_IP: Ipv4Addr = ip_v4!("192.168.1.10");
7070    const V4_LOCAL_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:192.168.1.10");
7071    const V6_LOCAL_IP: Ipv6Addr = net_ip_v6!("2201::1");
7072    const V6_REMOTE_IP: SpecifiedAddr<Ipv6Addr> =
7073        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("2001:db8::1")) };
7074    const V4_REMOTE_IP_MAPPED: SpecifiedAddr<Ipv6Addr> =
7075        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("::FFFF:192.0.2.1")) };
7076
7077    fn get_dual_stack_context<
7078        'a,
7079        BC: UdpBindingsTypes + 'a,
7080        CC: DatagramBoundStateContext<Ipv6, BC, Udp<BC>>,
7081    >(
7082        core_ctx: &'a mut CC,
7083    ) -> &'a mut CC::DualStackContext {
7084        match core_ctx.dual_stack_context_mut() {
7085            MaybeDualStack::NotDualStack(_) => unreachable!("UDP is a dual stack enabled protocol"),
7086            MaybeDualStack::DualStack(ds) => ds,
7087        }
7088    }
7089
7090    #[test_case::test_matrix(
7091        [DualStackBindAddr::Any, DualStackBindAddr::V4Any, DualStackBindAddr::V4Specific],
7092        [WithEarlyDemux, NoEarlyDemux]
7093    )]
7094    fn dual_stack_delivery(bind_addr: DualStackBindAddr, early_demux_mode: EarlyDemuxMode) {
7095        const REMOTE_IP: Ipv4Addr = ip_v4!("8.8.8.8");
7096        const REMOTE_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
7097        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
7098        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7099            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
7100            vec![SpecifiedAddr::new(REMOTE_IP).unwrap()],
7101        ));
7102
7103        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7104        let listener = api.create();
7105        api.listen(
7106            &listener,
7107            SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
7108            Some(LOCAL_PORT),
7109        )
7110        .expect("can bind");
7111
7112        const BODY: &[u8] = b"abcde";
7113        let (core_ctx, bindings_ctx) = api.contexts();
7114        receive_udp_packet(
7115            core_ctx,
7116            bindings_ctx,
7117            FakeDeviceId,
7118            UdpPacketMeta::<Ipv4> {
7119                src_ip: REMOTE_IP,
7120                src_port: Some(REMOTE_PORT),
7121                dst_ip: V4_LOCAL_IP,
7122                dst_port: LOCAL_PORT,
7123                dscp_and_ecn: DscpAndEcn::default(),
7124            },
7125            BODY,
7126            early_demux_mode,
7127        )
7128        .expect("receive udp packet should succeed");
7129
7130        assert_eq!(
7131            bindings_ctx.state.received::<Ipv6>(),
7132            &HashMap::from([(
7133                listener.downgrade(),
7134                SocketReceived {
7135                    packets: vec![ReceivedPacket {
7136                        body: BODY.into(),
7137                        meta: UdpPacketMeta::<Ipv6> {
7138                            src_ip: REMOTE_IP_MAPPED,
7139                            src_port: Some(REMOTE_PORT),
7140                            dst_ip: V4_LOCAL_IP_MAPPED,
7141                            dst_port: LOCAL_PORT,
7142                            dscp_and_ecn: DscpAndEcn::default(),
7143                        }
7144                    }],
7145                    max_size: usize::MAX,
7146                }
7147            )])
7148        );
7149    }
7150
7151    #[test_case(DualStackBindAddr::Any, true; "dual-stack any bind v4 first")]
7152    #[test_case(DualStackBindAddr::V4Any, true; "v4 any bind v4 first")]
7153    #[test_case(DualStackBindAddr::V4Specific, true; "v4 specific bind v4 first")]
7154    #[test_case(DualStackBindAddr::Any, false; "dual-stack any bind v4 second")]
7155    #[test_case(DualStackBindAddr::V4Any, false; "v4 any bind v4 second")]
7156    #[test_case(DualStackBindAddr::V4Specific, false; "v4 specific bind v4 second")]
7157    fn dual_stack_bind_conflict(bind_addr: DualStackBindAddr, bind_v4_first: bool) {
7158        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7159            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
7160            vec![],
7161        ));
7162
7163        let v4_listener = UdpApi::<Ipv4, _>::new(ctx.as_mut()).create();
7164        let v6_listener = UdpApi::<Ipv6, _>::new(ctx.as_mut()).create();
7165
7166        let bind_v4 = |mut api: UdpApi<Ipv4, _>| {
7167            api.listen(
7168                &v4_listener,
7169                SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
7170                Some(LOCAL_PORT),
7171            )
7172        };
7173        let bind_v6 = |mut api: UdpApi<Ipv6, _>| {
7174            api.listen(
7175                &v6_listener,
7176                SpecifiedAddr::new(bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED))
7177                    .map(ZonedAddr::Unzoned),
7178                Some(LOCAL_PORT),
7179            )
7180        };
7181
7182        let second_bind_error = if bind_v4_first {
7183            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect("no conflict");
7184            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect_err("should conflict")
7185        } else {
7186            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect("no conflict");
7187            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect_err("should conflict")
7188        };
7189        assert_eq!(second_bind_error, Either::Right(LocalAddressError::AddressInUse));
7190    }
7191
7192    // Verifies that port availability in both the IPv4 and IPv6 bound socket
7193    // maps is considered when allocating a local port for a dual-stack UDP
7194    // socket listening in both stacks.
7195    #[test_case(IpVersion::V4; "v4_is_constrained")]
7196    #[test_case(IpVersion::V6; "v6_is_constrained")]
7197    fn dual_stack_local_port_alloc(ip_version_with_constrained_ports: IpVersion) {
7198        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7199            vec![
7200                SpecifiedAddr::new(V4_LOCAL_IP.to_ip_addr()).unwrap(),
7201                SpecifiedAddr::new(V6_LOCAL_IP.to_ip_addr()).unwrap(),
7202            ],
7203            vec![],
7204        ));
7205
7206        // Specifically selected to be in the `EPHEMERAL_RANGE`.
7207        const AVAILABLE_PORT: NonZeroU16 = NonZeroU16::new(54321).unwrap();
7208
7209        // Densely pack the port space for one IP Version.
7210        for port in 1..=u16::MAX {
7211            let port = NonZeroU16::new(port).unwrap();
7212            if port == AVAILABLE_PORT {
7213                continue;
7214            }
7215            match ip_version_with_constrained_ports {
7216                IpVersion::V4 => {
7217                    let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
7218                    let listener = api.create();
7219                    api.listen(
7220                        &listener,
7221                        SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
7222                        Some(port),
7223                    )
7224                    .expect("listen v4 should succeed")
7225                }
7226                IpVersion::V6 => {
7227                    let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7228                    let listener = api.create();
7229                    api.listen(
7230                        &listener,
7231                        SpecifiedAddr::new(V6_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
7232                        Some(port),
7233                    )
7234                    .expect("listen v6 should succeed")
7235                }
7236            }
7237        }
7238
7239        // Create a listener on the dualstack any address, expecting it to be
7240        // allocated `AVAILABLE_PORT`.
7241        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7242        let listener = api.create();
7243        api.listen(&listener, None, None).expect("dualstack listen should succeed");
7244        let port = assert_matches!(api.get_info(&listener), SocketInfo::Listener(info) => info.local_identifier);
7245        assert_eq!(port, AVAILABLE_PORT);
7246    }
7247
7248    #[test_case(DualStackBindAddr::V4Any; "v4 any")]
7249    #[test_case(DualStackBindAddr::V4Specific; "v4 specific")]
7250    fn dual_stack_enable(bind_addr: DualStackBindAddr) {
7251        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7252            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
7253            vec![],
7254        ));
7255        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7256
7257        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
7258        let listener = api.create();
7259
7260        assert_eq!(api.get_dual_stack_enabled(&listener), Ok(true));
7261        api.set_dual_stack_enabled(&listener, false).expect("can set dual-stack enabled");
7262
7263        // With dual-stack behavior disabled, the IPv6 socket can't bind to
7264        // an IPv4-mapped IPv6 address.
7265        assert_eq!(
7266            api.listen(
7267                &listener,
7268                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
7269                Some(LOCAL_PORT),
7270            ),
7271            Err(Either::Right(LocalAddressError::CannotBindToAddress))
7272        );
7273        api.set_dual_stack_enabled(&listener, true).expect("can set dual-stack enabled");
7274        // Try again now that dual-stack sockets are enabled.
7275        assert_eq!(
7276            api.listen(
7277                &listener,
7278                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
7279                Some(LOCAL_PORT),
7280            ),
7281            Ok(())
7282        );
7283    }
7284
7285    #[test]
7286    fn dual_stack_bind_unassigned_v4_address() {
7287        const NOT_ASSIGNED_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
7288        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7289            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
7290            vec![],
7291        ));
7292        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7293
7294        let listener = api.create();
7295        assert_eq!(
7296            api.listen(
7297                &listener,
7298                SpecifiedAddr::new(NOT_ASSIGNED_MAPPED).map(|a| ZonedAddr::Unzoned(a)),
7299                Some(LOCAL_PORT),
7300            ),
7301            Err(Either::Right(LocalAddressError::CannotBindToAddress))
7302        );
7303    }
7304
7305    // Calling `connect` on an already bound socket will cause the existing
7306    // `listener` entry in the bound state map to be upgraded to a `connected`
7307    // entry. Dual-stack listeners may exist in both the IPv4 and IPv6 bound
7308    // state maps, so make sure both entries are properly removed.
7309    #[test]
7310    fn dual_stack_connect_cleans_up_existing_listener() {
7311        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7312            vec![Ipv6::TEST_ADDRS.local_ip],
7313            vec![Ipv6::TEST_ADDRS.remote_ip],
7314        ));
7315
7316        const DUAL_STACK_ANY_ADDR: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, FakeDeviceId>> = None;
7317
7318        fn assert_listeners(core_ctx: &mut FakeUdpCoreCtx<FakeDeviceId>, expect_present: bool) {
7319            const V4_LISTENER_ADDR: ListenerAddr<
7320                ListenerIpAddr<Ipv4Addr, NonZeroU16>,
7321                FakeWeakDeviceId<FakeDeviceId>,
7322            > = ListenerAddr {
7323                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
7324                device: None,
7325            };
7326            const V6_LISTENER_ADDR: ListenerAddr<
7327                ListenerIpAddr<Ipv6Addr, NonZeroU16>,
7328                FakeWeakDeviceId<FakeDeviceId>,
7329            > = ListenerAddr {
7330                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
7331                device: None,
7332            };
7333
7334            DualStackBoundStateContext::with_both_bound_sockets_mut(
7335                get_dual_stack_context(&mut core_ctx.bound_sockets),
7336                |_core_ctx, v6_sockets, v4_sockets| {
7337                    let v4 = v4_sockets.bound_sockets.listeners().get_by_addr(&V4_LISTENER_ADDR);
7338                    let v6 = v6_sockets.bound_sockets.listeners().get_by_addr(&V6_LISTENER_ADDR);
7339                    if expect_present {
7340                        assert_matches!(v4, Some(_));
7341                        assert_matches!(v6, Some(_));
7342                    } else {
7343                        assert_matches!(v4, None);
7344                        assert_matches!(v6, None);
7345                    }
7346                },
7347            );
7348        }
7349
7350        // Create a socket and listen on the IPv6 any address. Verify we have
7351        // listener state for both IPv4 and IPv6.
7352        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7353        let socket = api.create();
7354        assert_eq!(api.listen(&socket, DUAL_STACK_ANY_ADDR, Some(LOCAL_PORT)), Ok(()));
7355        assert_listeners(api.core_ctx(), true);
7356
7357        // Connect the socket to a remote V6 address and verify that both
7358        // the IPv4 and IPv6 listener state has been removed.
7359        assert_eq!(
7360            api.connect(
7361                &socket,
7362                Some(ZonedAddr::Unzoned(Ipv6::TEST_ADDRS.remote_ip)),
7363                REMOTE_PORT.into(),
7364            ),
7365            Ok(())
7366        );
7367        assert_matches!(api.get_info(&socket), SocketInfo::Connected(_));
7368        assert_listeners(api.core_ctx(), false);
7369    }
7370
7371    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
7372    #[test_case(net_ip_v6!("::"), false; "v6 any")]
7373    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
7374    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
7375    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
7376    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
7377    fn dual_stack_get_info(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
7378        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
7379            SpecifiedAddr<IpAddr>,
7380        >(
7381            vec![
7382                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
7383                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
7384            ],
7385            vec![],
7386        ));
7387        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7388
7389        let listener = api.create();
7390        api.set_dual_stack_enabled(&listener, enable_dual_stack)
7391            .expect("can set dual-stack enabled");
7392        let bind_addr = SpecifiedAddr::new(bind_addr);
7393        assert_eq!(
7394            api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT),),
7395            Ok(())
7396        );
7397
7398        assert_eq!(
7399            api.get_info(&listener),
7400            SocketInfo::Listener(datagram::ListenerInfo {
7401                local_ip: bind_addr.map(StrictlyZonedAddr::new_unzoned_or_panic),
7402                local_identifier: LOCAL_PORT,
7403            })
7404        );
7405    }
7406
7407    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
7408    #[test_case(net_ip_v6!("::"), false; "v6 any")]
7409    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
7410    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
7411    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
7412    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
7413    fn dual_stack_remove_listener(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
7414        // Ensure that when a socket is removed, it doesn't leave behind state
7415        // in the demultiplexing maps. Do this by binding a new socket at the
7416        // same address and asserting success.
7417        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
7418            SpecifiedAddr<IpAddr>,
7419        >(
7420            vec![
7421                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
7422                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
7423            ],
7424            vec![],
7425        ));
7426        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7427
7428        let mut bind_listener = || {
7429            let listener = api.create();
7430            api.set_dual_stack_enabled(&listener, enable_dual_stack)
7431                .expect("can set dual-stack enabled");
7432            let bind_addr = SpecifiedAddr::new(bind_addr);
7433            assert_eq!(
7434                api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT)),
7435                Ok(())
7436            );
7437
7438            let _: (UdpSocketDiagnosticsSeed<Ipv6, _, _>, ()) = api.close(listener).into_removed();
7439        };
7440
7441        // The first time should succeed because the state is empty.
7442        bind_listener();
7443        // The second time should succeed because the first removal didn't
7444        // leave any state behind.
7445        bind_listener();
7446    }
7447
7448    #[test_case(V6_REMOTE_IP, true; "This stack with dualstack enabled")]
7449    #[test_case(V6_REMOTE_IP, false; "This stack with dualstack disabled")]
7450    #[test_case(V4_REMOTE_IP_MAPPED, true; "other stack with dualstack enabled")]
7451    fn dualstack_remove_connected(remote_ip: SpecifiedAddr<Ipv6Addr>, enable_dual_stack: bool) {
7452        // Ensure that when a socket is removed, it doesn't leave behind state
7453        // in the demultiplexing maps. Do this by binding a new socket at the
7454        // same address and asserting success.
7455        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7456            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
7457            remote_ip.into(),
7458            [FakeDeviceId {}],
7459            |device_configs| {
7460                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7461                    device_configs,
7462                ))
7463            },
7464        );
7465        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7466
7467        let mut bind_connected = || {
7468            let socket = api.create();
7469            api.set_dual_stack_enabled(&socket, enable_dual_stack)
7470                .expect("can set dual-stack enabled");
7471            assert_eq!(
7472                api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into(),),
7473                Ok(())
7474            );
7475
7476            let _: (UdpSocketDiagnosticsSeed<Ipv6, _, _>, ()) = api.close(socket).into_removed();
7477        };
7478
7479        // The first time should succeed because the state is empty.
7480        bind_connected();
7481        // The second time should succeed because the first removal didn't
7482        // leave any state behind.
7483        bind_connected();
7484    }
7485
7486    #[test_case(false, V6_REMOTE_IP, Ok(());
7487        "connect to this stack with dualstack disabled")]
7488    #[test_case(true, V6_REMOTE_IP, Ok(());
7489        "connect to this stack with dualstack enabled")]
7490    #[test_case(false, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
7491        "connect to other stack with dualstack disabled")]
7492    #[test_case(true, V4_REMOTE_IP_MAPPED, Ok(());
7493        "connect to other stack with dualstack enabled")]
7494    fn dualstack_connect_unbound(
7495        enable_dual_stack: bool,
7496        remote_ip: SpecifiedAddr<Ipv6Addr>,
7497        expected_outcome: Result<(), ConnectError>,
7498    ) {
7499        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7500            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
7501            remote_ip.into(),
7502            [FakeDeviceId {}],
7503            |device_configs| {
7504                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7505                    device_configs,
7506                ))
7507            },
7508        );
7509        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7510
7511        let socket = api.create();
7512
7513        api.set_dual_stack_enabled(&socket, enable_dual_stack).expect("can set dual-stack enabled");
7514
7515        assert_eq!(
7516            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
7517            expected_outcome
7518        );
7519
7520        if expected_outcome.is_ok() {
7521            assert_matches!(
7522                api.get_info(&socket),
7523                SocketInfo::Connected(datagram::ConnInfo{
7524                    local_ip: _,
7525                    local_identifier: _,
7526                    remote_ip: found_remote_ip,
7527                    remote_identifier: found_remote_port,
7528                }) if found_remote_ip.addr() == remote_ip &&
7529                    found_remote_port == u16::from(REMOTE_PORT)
7530            );
7531            // Disconnect the socket, returning it to the original state.
7532            assert_eq!(api.disconnect(&socket), Ok(()));
7533        }
7534
7535        // Verify the original state is preserved.
7536        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
7537    }
7538
7539    #[test_case(V6_LOCAL_IP, V6_REMOTE_IP, Ok(());
7540        "listener in this stack connected in this stack")]
7541    #[test_case(V6_LOCAL_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
7542        "listener in this stack connected in other stack")]
7543    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V6_REMOTE_IP, Ok(());
7544        "listener in both stacks connected in this stack")]
7545    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V4_REMOTE_IP_MAPPED, Ok(());
7546        "listener in both stacks connected in other stack")]
7547    #[test_case(V4_LOCAL_IP_MAPPED, V6_REMOTE_IP,
7548        Err(ConnectError::RemoteUnexpectedlyNonMapped);
7549        "listener in other stack connected in this stack")]
7550    #[test_case(V4_LOCAL_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
7551        "listener in other stack connected in other stack")]
7552    fn dualstack_connect_listener(
7553        local_ip: Ipv6Addr,
7554        remote_ip: SpecifiedAddr<Ipv6Addr>,
7555        expected_outcome: Result<(), ConnectError>,
7556    ) {
7557        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7558            local_ip.to_ip_addr(),
7559            remote_ip.into(),
7560            [FakeDeviceId {}],
7561            |device_configs| {
7562                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7563                    device_configs,
7564                ))
7565            },
7566        );
7567        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7568        let socket = api.create();
7569
7570        assert_eq!(
7571            api.listen(
7572                &socket,
7573                SpecifiedAddr::new(local_ip).map(|local_ip| ZonedAddr::Unzoned(local_ip)),
7574                Some(LOCAL_PORT),
7575            ),
7576            Ok(())
7577        );
7578
7579        assert_eq!(
7580            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
7581            expected_outcome
7582        );
7583
7584        if expected_outcome.is_ok() {
7585            assert_matches!(
7586                api.get_info(&socket),
7587                SocketInfo::Connected(datagram::ConnInfo{
7588                    local_ip: _,
7589                    local_identifier: _,
7590                    remote_ip: found_remote_ip,
7591                    remote_identifier: found_remote_port,
7592                }) if found_remote_ip.addr() == remote_ip &&
7593                    found_remote_port == u16::from(REMOTE_PORT)
7594            );
7595            // Disconnect the socket, returning it to the original state.
7596            assert_eq!(api.disconnect(&socket), Ok(()));
7597        }
7598
7599        // Verify the original state is preserved.
7600        assert_matches!(
7601            api.get_info(&socket),
7602            SocketInfo::Listener(datagram::ListenerInfo {
7603                local_ip: found_local_ip,
7604                local_identifier: found_local_port,
7605            }) if found_local_port == LOCAL_PORT &&
7606                local_ip == found_local_ip.map(
7607                    |a| a.addr().get()
7608                ).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS)
7609        );
7610    }
7611
7612    #[test_case(V6_REMOTE_IP, V6_REMOTE_IP, Ok(());
7613        "connected in this stack reconnected in this stack")]
7614    #[test_case(V6_REMOTE_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
7615        "connected in this stack reconnected in other stack")]
7616    #[test_case(V4_REMOTE_IP_MAPPED, V6_REMOTE_IP,
7617        Err(ConnectError::RemoteUnexpectedlyNonMapped);
7618        "connected in other stack reconnected in this stack")]
7619    #[test_case(V4_REMOTE_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
7620        "connected in other stack reconnected in other stack")]
7621    fn dualstack_connect_connected(
7622        original_remote_ip: SpecifiedAddr<Ipv6Addr>,
7623        new_remote_ip: SpecifiedAddr<Ipv6Addr>,
7624        expected_outcome: Result<(), ConnectError>,
7625    ) {
7626        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7627            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
7628            original_remote_ip.into(),
7629            [FakeDeviceId {}],
7630            |device_configs| {
7631                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7632                    device_configs,
7633                ))
7634            },
7635        );
7636
7637        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7638        let socket = api.create();
7639
7640        assert_eq!(
7641            api.connect(&socket, Some(ZonedAddr::Unzoned(original_remote_ip)), REMOTE_PORT.into(),),
7642            Ok(())
7643        );
7644
7645        assert_eq!(
7646            api.connect(
7647                &socket,
7648                Some(ZonedAddr::Unzoned(new_remote_ip)),
7649                OTHER_REMOTE_PORT.into(),
7650            ),
7651            expected_outcome
7652        );
7653
7654        let (expected_remote_ip, expected_remote_port) = if expected_outcome.is_ok() {
7655            (new_remote_ip, OTHER_REMOTE_PORT)
7656        } else {
7657            // Verify the original state is preserved.
7658            (original_remote_ip, REMOTE_PORT)
7659        };
7660        assert_matches!(
7661            api.get_info(&socket),
7662            SocketInfo::Connected(datagram::ConnInfo{
7663                local_ip: _,
7664                local_identifier: _,
7665                remote_ip: found_remote_ip,
7666                remote_identifier: found_remote_port,
7667            }) if found_remote_ip.addr() == expected_remote_ip &&
7668                found_remote_port == u16::from(expected_remote_port)
7669        );
7670
7671        // Disconnect the socket and verify it returns to unbound state.
7672        assert_eq!(api.disconnect(&socket), Ok(()));
7673        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
7674    }
7675
7676    type FakeBoundSocketMap<I> =
7677        UdpBoundSocketMap<I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
7678    type FakePortAlloc<'a, I> =
7679        UdpPortAlloc<'a, I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
7680
7681    fn listen<I: IpExt>(
7682        ip: I::Addr,
7683        port: u16,
7684    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
7685        let addr = SpecifiedAddr::new(ip).map(|a| SocketIpAddr::try_from(a).unwrap());
7686        let port = NonZeroU16::new(port).expect("port must be nonzero");
7687        AddrVec::Listen(ListenerAddr {
7688            ip: ListenerIpAddr { addr, identifier: port },
7689            device: None,
7690        })
7691    }
7692
7693    fn listen_device<I: IpExt>(
7694        ip: I::Addr,
7695        port: u16,
7696        device: FakeWeakDeviceId<FakeDeviceId>,
7697    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
7698        let addr = SpecifiedAddr::new(ip).map(|a| SocketIpAddr::try_from(a).unwrap());
7699        let port = NonZeroU16::new(port).expect("port must be nonzero");
7700        AddrVec::Listen(ListenerAddr {
7701            ip: ListenerIpAddr { addr, identifier: port },
7702            device: Some(device),
7703        })
7704    }
7705
7706    fn conn<I: IpExt>(
7707        local_ip: I::Addr,
7708        local_port: u16,
7709        remote_ip: I::Addr,
7710        remote_port: u16,
7711    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
7712        let local_ip = SocketIpAddr::new(local_ip).expect("addr must be specified & non-mapped");
7713        let local_port = NonZeroU16::new(local_port).expect("port must be nonzero");
7714        let remote_ip = SocketIpAddr::new(remote_ip).expect("addr must be specified & non-mapped");
7715        let remote_port = NonZeroU16::new(remote_port).expect("port must be nonzero").into();
7716        AddrVec::Conn(ConnAddr {
7717            ip: ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) },
7718            device: None,
7719        })
7720    }
7721
7722    const SHARING_DOMAIN1: SharingDomain = SharingDomain::new(1);
7723    const SHARING_DOMAIN2: SharingDomain = SharingDomain::new(42);
7724    const EXCLUSIVE: Sharing = Sharing { reuse_addr: false, reuse_port: ReusePortOption::Disabled };
7725    const REUSE_ADDR: Sharing = Sharing { reuse_addr: true, reuse_port: ReusePortOption::Disabled };
7726    const REUSE_PORT: Sharing =
7727        Sharing { reuse_addr: false, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN1) };
7728    const REUSE_ADDR_PORT: Sharing =
7729        Sharing { reuse_addr: true, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN1) };
7730    const REUSE_PORT2: Sharing =
7731        Sharing { reuse_addr: false, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN2) };
7732    const REUSE_ADDR_PORT2: Sharing =
7733        Sharing { reuse_addr: true, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN2) };
7734
7735    #[test_case([
7736        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
7737        (listen(ip_v4!("0.0.0.0"), 2), EXCLUSIVE)],
7738            Ok(()); "listen_any_ip_different_port")]
7739    #[test_case([
7740        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
7741        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE)],
7742            Err(InsertError::Exists); "any_ip_same_port")]
7743    #[test_case([
7744        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7745        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7746            Err(InsertError::Exists); "listen_same_specific_ip")]
7747    #[test_case([
7748        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7749        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7750            Ok(()); "listen_same_specific_ip_reuse_addr")]
7751    #[test_case([
7752        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7753        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7754            Ok(()); "listen_same_specific_ip_reuse_port")]
7755    #[test_case([
7756        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7757        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7758            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr")]
7759    #[test_case([
7760        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7761        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7762            Ok(()); "listen_same_specific_ip_reuse_addr_and_reuse_addr_port")]
7763    #[test_case([
7764        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7765        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7766            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_port")]
7767    #[test_case([
7768        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7769        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7770            Ok(()); "listen_same_specific_ip_reuse_port_and_reuse_addr_port")]
7771    #[test_case([
7772        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7773        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7774            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port")]
7775    #[test_case([
7776        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7777        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7778            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr")]
7779    #[test_case([
7780        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7781        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7782            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_exclusive")]
7783    #[test_case([
7784        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7785        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7786            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_port")]
7787    #[test_case([
7788        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7789        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7790            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_exclusive")]
7791    #[test_case([
7792        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7793        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7794            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr_port")]
7795    #[test_case([
7796        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7797        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7798            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_exclusive")]
7799    #[test_case([
7800        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7801        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7802            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_reuse_addr")]
7803    #[test_case([
7804        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7805        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7806            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_reuse_port")]
7807    #[test_case([
7808        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7809        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7810        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),],
7811            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_port_and_reuse_addr")]
7812    #[test_case([
7813        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7814        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7815        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),],
7816            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_and_reuse_port")]
7817    #[test_case([
7818        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7819        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT2)],
7820            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_and_reuse_port2")]
7821    #[test_case([
7822        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7823        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT2)],
7824            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port2")]
7825    #[test_case([
7826        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7827        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT2),
7828        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7829            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port2_and_reuse_port")]
7830    #[test_case([
7831        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7832        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7833            Ok(()); "conn_shadows_listener_reuse_port")]
7834    #[test_case([
7835        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7836        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7837            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive")]
7838    #[test_case([
7839        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7840        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7841            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive_reuse_port")]
7842    #[test_case([
7843        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7844        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7845            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_reuse_port_exclusive")]
7846    #[test_case([
7847        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7848        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7849            Err(InsertError::IndirectConflict); "conn_indirect_conflict_specific_listener")]
7850    #[test_case([
7851        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7852        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7853            Err(InsertError::IndirectConflict); "conn_indirect_conflict_any_listener")]
7854    #[test_case([
7855        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7856        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7857            Err(InsertError::IndirectConflict); "specific_listener_indirect_conflict_conn")]
7858    #[test_case([
7859        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7860        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7861            Err(InsertError::IndirectConflict); "any_listener_indirect_conflict_conn")]
7862    fn bind_sequence<
7863        C: IntoIterator<Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing)>,
7864    >(
7865        spec: C,
7866        expected: Result<(), InsertError>,
7867    ) {
7868        let mut primary_ids = Vec::new();
7869
7870        let mut create_socket = || {
7871            let primary = datagram::testutil::create_primary_id(());
7872            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7873            primary_ids.push(primary);
7874            id
7875        };
7876
7877        let mut map = FakeBoundSocketMap::<Ipv4>::default();
7878        let mut spec = spec.into_iter().peekable();
7879        let mut try_insert = |(addr, options)| match addr {
7880            AddrVec::Conn(c) => map
7881                .conns_mut()
7882                .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7883                .map(|_| ()),
7884            AddrVec::Listen(l) => map
7885                .listeners_mut()
7886                .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7887                .map(|_| ()),
7888        };
7889        let last = loop {
7890            let one_spec = spec.next().expect("empty list of test cases");
7891            if spec.peek().is_none() {
7892                break one_spec;
7893            } else {
7894                try_insert(one_spec).expect("intermediate bind failed")
7895            }
7896        };
7897
7898        let result = try_insert(last);
7899        assert_eq!(result, expected);
7900    }
7901
7902    #[test_case([
7903            (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7904            (listen(ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7905        ]; "distinct")]
7906    #[test_case([
7907            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7908            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7909        ]; "listen_reuse_port")]
7910    #[test_case([
7911            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7912            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7913        ]; "conn_reuse_port")]
7914    fn remove_sequence<I>(spec: I)
7915    where
7916        I: IntoIterator<
7917            Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing),
7918        >,
7919        I::IntoIter: ExactSizeIterator,
7920    {
7921        enum Socket<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes, LI, RI> {
7922            Listener(UdpSocketId<I, D, BT>, ListenerAddr<ListenerIpAddr<I::Addr, LI>, D>),
7923            Conn(UdpSocketId<I, D, BT>, ConnAddr<ConnIpAddr<I::Addr, LI, RI>, D>),
7924        }
7925        let spec = spec.into_iter();
7926        let spec_len = spec.len();
7927
7928        let mut primary_ids = Vec::new();
7929
7930        let mut create_socket = || {
7931            let primary = datagram::testutil::create_primary_id(());
7932            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7933            primary_ids.push(primary);
7934            id
7935        };
7936
7937        for spec in spec.permutations(spec_len) {
7938            let mut map = FakeBoundSocketMap::<Ipv4>::default();
7939            let sockets = spec
7940                .into_iter()
7941                .map(|(addr, options)| match addr {
7942                    AddrVec::Conn(c) => map
7943                        .conns_mut()
7944                        .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7945                        .map(|entry| {
7946                            Socket::Conn(
7947                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7948                                entry.get_addr().clone(),
7949                            )
7950                        })
7951                        .expect("insert_failed"),
7952                    AddrVec::Listen(l) => map
7953                        .listeners_mut()
7954                        .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7955                        .map(|entry| {
7956                            Socket::Listener(
7957                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7958                                entry.get_addr().clone(),
7959                            )
7960                        })
7961                        .expect("insert_failed"),
7962                })
7963                .collect::<Vec<_>>();
7964
7965            for socket in sockets {
7966                match socket {
7967                    Socket::Listener(l, addr) => {
7968                        assert_matches!(
7969                            map.listeners_mut().remove(&EitherIpSocket::V4(l), &addr),
7970                            Ok(())
7971                        );
7972                    }
7973                    Socket::Conn(c, addr) => {
7974                        assert_matches!(
7975                            map.conns_mut().remove(&EitherIpSocket::V4(c), &addr),
7976                            Ok(())
7977                        );
7978                    }
7979                }
7980            }
7981        }
7982    }
7983
7984    enum OriginalSocketState {
7985        Unbound,
7986        Listener,
7987        Connected,
7988    }
7989
7990    impl OriginalSocketState {
7991        fn create_socket<I, C>(&self, api: &mut UdpApi<I, C>) -> UdpApiSocketId<I, C>
7992        where
7993            I: TestIpExt,
7994            C: ContextPair,
7995            C::CoreContext: StateContext<I, C::BindingsContext>
7996                + UdpCounterContext<
7997                    I,
7998                    <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
7999                    C::BindingsContext,
8000                >,
8001            C::BindingsContext:
8002                UdpBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
8003            <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>: Default,
8004            <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId:
8005                netstack3_base::InterfaceProperties<
8006                        <C::BindingsContext as MatcherBindingsTypes>::DeviceClass,
8007                    >,
8008        {
8009            let socket = api.create();
8010            match self {
8011                OriginalSocketState::Unbound => {}
8012                OriginalSocketState::Listener => {
8013                    api.listen(
8014                        &socket,
8015                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)),
8016                        Some(LOCAL_PORT),
8017                    )
8018                    .expect("listen should succeed");
8019                }
8020                OriginalSocketState::Connected => {
8021                    api.connect(
8022                        &socket,
8023                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
8024                        UdpRemotePort::Set(REMOTE_PORT),
8025                    )
8026                    .expect("connect should succeed");
8027                }
8028            }
8029            socket
8030        }
8031    }
8032
8033    #[test_case(OriginalSocketState::Unbound; "unbound")]
8034    #[test_case(OriginalSocketState::Listener; "listener")]
8035    #[test_case(OriginalSocketState::Connected; "connected")]
8036    fn set_get_dual_stack_enabled_v4(original_state: OriginalSocketState) {
8037        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
8038            vec![Ipv4::TEST_ADDRS.local_ip],
8039            vec![Ipv4::TEST_ADDRS.remote_ip],
8040        ));
8041        let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
8042        let socket = original_state.create_socket(&mut api);
8043
8044        for enabled in [true, false] {
8045            assert_eq!(
8046                api.set_dual_stack_enabled(&socket, enabled),
8047                Err(NotDualStackCapableError.into())
8048            );
8049            assert_eq!(api.get_dual_stack_enabled(&socket), Err(NotDualStackCapableError));
8050        }
8051    }
8052
8053    #[test_case(OriginalSocketState::Unbound, Ok(()); "unbound")]
8054    #[test_case(OriginalSocketState::Listener, Err(SetDualStackEnabledError::SocketIsBound);
8055        "listener")]
8056    #[test_case(OriginalSocketState::Connected, Err(SetDualStackEnabledError::SocketIsBound);
8057        "connected")]
8058    fn set_get_dual_stack_enabled_v6(
8059        original_state: OriginalSocketState,
8060        expected_result: Result<(), SetDualStackEnabledError>,
8061    ) {
8062        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
8063            vec![Ipv6::TEST_ADDRS.local_ip],
8064            vec![Ipv6::TEST_ADDRS.remote_ip],
8065        ));
8066        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
8067        let socket = original_state.create_socket(&mut api);
8068
8069        // Expect dual stack to be enabled by default.
8070        const ORIGINALLY_ENABLED: bool = true;
8071        assert_eq!(api.get_dual_stack_enabled(&socket), Ok(ORIGINALLY_ENABLED));
8072
8073        for enabled in [false, true] {
8074            assert_eq!(api.set_dual_stack_enabled(&socket, enabled), expected_result);
8075            let expect_enabled = match expected_result {
8076                Ok(_) => enabled,
8077                // If the set was unsuccessful expect the state to be unchanged.
8078                Err(_) => ORIGINALLY_ENABLED,
8079            };
8080            assert_eq!(api.get_dual_stack_enabled(&socket), Ok(expect_enabled));
8081        }
8082    }
8083
8084    #[ip_test(I, test = false)]
8085    #[test_case::test_matrix(
8086        [MarkDomain::Mark1, MarkDomain::Mark2],
8087        [None, Some(0), Some(1)]
8088    )]
8089    fn udp_socket_marks<I: TestIpExt>(domain: MarkDomain, mark: Option<u32>) {
8090        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
8091            vec![I::TEST_ADDRS.local_ip],
8092            vec![I::TEST_ADDRS.remote_ip],
8093        ));
8094        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
8095        let socket = api.create();
8096
8097        // Doesn't have a mark by default.
8098        assert_eq!(api.get_mark(&socket, domain), Mark(None));
8099
8100        let mark = Mark(mark);
8101        // We can set and get back the mark.
8102        api.set_mark(&socket, domain, mark);
8103        assert_eq!(api.get_mark(&socket, domain), mark);
8104    }
8105
8106    #[ip_test(I)]
8107    fn udp_early_demux<I: TestIpExt>() {
8108        set_logger_for_test();
8109        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
8110        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
8111
8112        let local_ip = local_ip::<I>();
8113        let remote_ip = remote_ip::<I>();
8114        let socket = api.create();
8115
8116        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
8117            .expect("Initial call to listen_udp was expected to succeed");
8118
8119        let builder =
8120            UdpPacketBuilder::new(remote_ip.get(), local_ip.get(), Some(REMOTE_PORT), LOCAL_PORT);
8121
8122        let buffer = builder
8123            .wrap_body(Buf::new(vec![1, 2, 3, 4], ..))
8124            .serialize_vec_outer(&mut NetworkSerializationContext::default())
8125            .unwrap()
8126            .into_inner();
8127
8128        // Early demux should find only connected sockets.
8129        let early_demux_socket =
8130            <UdpIpTransportContext as IpTransportContext<I, _, _>>::early_demux(
8131                api.core_ctx(),
8132                &FakeDeviceId,
8133                remote_ip.get(),
8134                local_ip.get(),
8135                buffer.as_ref(),
8136            );
8137        assert_eq!(early_demux_socket, None);
8138
8139        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
8140            .expect("connect should succeed");
8141
8142        let early_demux_socket =
8143            <UdpIpTransportContext as IpTransportContext<I, _, _>>::early_demux(
8144                api.core_ctx(),
8145                &FakeDeviceId,
8146                remote_ip.get(),
8147                local_ip.get(),
8148                buffer.as_ref(),
8149            );
8150        assert_matches!(early_demux_socket, Some(_));
8151    }
8152
8153    fn so_error_inner<I: TestIpExt>(
8154        icmp_err: I::ErrorCode,
8155        expected_err: Option<PendingDatagramSocketError>,
8156    ) {
8157        set_logger_for_test();
8158
8159        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
8160        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
8161
8162        let local_ip = local_ip::<I>();
8163        let remote_ip = remote_ip::<I>();
8164        let socket = api.create();
8165
8166        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
8167            .expect("listen should succeed");
8168        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
8169            .expect("connect should succeed");
8170
8171        let (_, bindings_ctx) = api.contexts();
8172        assert_eq!(bindings_ctx.state.take_pending_error::<I>(&socket.downgrade()), None);
8173
8174        // Inject the ICMP error.
8175        let mut original_body = vec![0u8; 8];
8176        original_body[0..2].copy_from_slice(&LOCAL_PORT.get().to_be_bytes());
8177        original_body[2..4].copy_from_slice(&REMOTE_PORT.get().to_be_bytes());
8178        original_body[4..6].copy_from_slice(&8u16.to_be_bytes());
8179
8180        let (core_ctx, bindings_ctx) = api.contexts();
8181
8182        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
8183            core_ctx,
8184            bindings_ctx,
8185            &FakeDeviceId,
8186            Some(local_ip),
8187            remote_ip,
8188            &original_body,
8189            icmp_err,
8190        );
8191
8192        let (_, bindings_ctx) = api.contexts();
8193        assert_eq!(bindings_ctx.state.take_pending_error::<I>(&socket.downgrade()), expected_err);
8194        assert_eq!(bindings_ctx.state.take_pending_error::<I>(&socket.downgrade()), None);
8195
8196        let (core_ctx, _) = api.contexts();
8197        let (with_socket_expects, without_socket_expects) = match expected_err {
8198            Some(_) => (
8199                CounterExpectationsWithSocket {
8200                    rx_icmp_error_hard_delivered: 1,
8201                    ..Default::default()
8202                },
8203                CounterExpectationsWithoutSocket {
8204                    rx_icmp_error: 1,
8205                    rx_icmp_error_hard: 1,
8206                    ..Default::default()
8207                },
8208            ),
8209            None => (
8210                CounterExpectationsWithSocket::default(),
8211                CounterExpectationsWithoutSocket {
8212                    rx_icmp_error: 1,
8213                    rx_icmp_error_soft: 1,
8214                    ..Default::default()
8215                },
8216            ),
8217        };
8218        let per_socket_expects = match expected_err {
8219            Some(_) => CounterExpectationsWithSocket {
8220                rx_icmp_error_hard_delivered: 1,
8221                ..Default::default()
8222            },
8223            None => CounterExpectationsWithSocket::default(),
8224        };
8225        assert_counters(
8226            core_ctx,
8227            with_socket_expects,
8228            without_socket_expects,
8229            [(&socket, per_socket_expects)],
8230        );
8231    }
8232
8233    #[test_case(
8234        Icmpv4ErrorCode::DestUnreachable(
8235            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
8236            Default::default()
8237        ),
8238        None;
8239        "v4 network unreachable"
8240    )]
8241    #[test_case(
8242        Icmpv4ErrorCode::DestUnreachable(
8243            Icmpv4DestUnreachableCode::DestHostUnreachable,
8244            Default::default()
8245        ),
8246        None;
8247        "v4 host unreachable"
8248    )]
8249    #[test_case(
8250        Icmpv4ErrorCode::DestUnreachable(
8251            Icmpv4DestUnreachableCode::DestProtocolUnreachable,
8252            Default::default()
8253        ),
8254        Some(PendingDatagramSocketError::ProtocolUnreachable);
8255        "v4 protocol unreachable"
8256    )]
8257    #[test_case(
8258        Icmpv4ErrorCode::DestUnreachable(
8259            Icmpv4DestUnreachableCode::DestPortUnreachable,
8260            Default::default()
8261        ),
8262        Some(PendingDatagramSocketError::PortUnreachable);
8263        "v4 connection refused"
8264    )]
8265    fn so_error_v4(icmp_err: Icmpv4ErrorCode, expected_err: Option<PendingDatagramSocketError>) {
8266        so_error_inner::<Ipv4>(icmp_err, expected_err)
8267    }
8268
8269    #[test_case(
8270        Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute),
8271        None;
8272        "v6 no route"
8273    )]
8274    #[test_case(
8275        Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::AddrUnreachable),
8276        None;
8277        "v6 addr unreachable"
8278    )]
8279    #[test_case(
8280        Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::PortUnreachable),
8281        Some(PendingDatagramSocketError::PortUnreachable);
8282        "v6 connection refused"
8283    )]
8284    #[test_case(
8285        Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::CommAdministrativelyProhibited),
8286        Some(PendingDatagramSocketError::PermissionDenied);
8287        "v6 permission denied"
8288    )]
8289    fn so_error_v6(icmp_err: Icmpv6ErrorCode, expected_err: Option<PendingDatagramSocketError>) {
8290        so_error_inner::<Ipv6>(icmp_err, expected_err)
8291    }
8292
8293    #[test]
8294    fn so_error_dual_stack() {
8295        set_logger_for_test();
8296
8297        const REMOTE_IP: Ipv4Addr = ip_v4!("8.8.8.8");
8298        const REMOTE_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
8299
8300        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
8301            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
8302            vec![SpecifiedAddr::new(REMOTE_IP).unwrap()],
8303        ));
8304        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
8305        let socket = api.create();
8306
8307        api.listen(
8308            &socket,
8309            Some(ZonedAddr::Unzoned(SpecifiedAddr::new(V4_LOCAL_IP_MAPPED).unwrap())),
8310            Some(LOCAL_PORT),
8311        )
8312        .expect("listen should succeed");
8313
8314        api.connect(
8315            &socket,
8316            Some(ZonedAddr::Unzoned(SpecifiedAddr::new(REMOTE_IP_MAPPED).unwrap())),
8317            REMOTE_PORT.into(),
8318        )
8319        .expect("connect should succeed");
8320
8321        let (_, bindings_ctx) = api.contexts();
8322        assert_eq!(bindings_ctx.state.take_pending_error::<Ipv6>(&socket.downgrade()), None);
8323
8324        let mut original_body = vec![0u8; 8];
8325        original_body[0..2].copy_from_slice(&LOCAL_PORT.get().to_be_bytes());
8326        original_body[2..4].copy_from_slice(&REMOTE_PORT.get().to_be_bytes());
8327        original_body[4..6].copy_from_slice(&8u16.to_be_bytes());
8328
8329        let (core_ctx, bindings_ctx) = api.contexts();
8330
8331        let err = Icmpv4ErrorCode::DestUnreachable(
8332            Icmpv4DestUnreachableCode::DestPortUnreachable,
8333            Default::default(),
8334        );
8335
8336        <UdpIpTransportContext as IpTransportContext<Ipv4, _, _>>::receive_icmp_error(
8337            core_ctx,
8338            bindings_ctx,
8339            &FakeDeviceId,
8340            Some(SpecifiedAddr::new(V4_LOCAL_IP).unwrap()),
8341            SpecifiedAddr::new(REMOTE_IP).unwrap(),
8342            &original_body,
8343            err,
8344        );
8345
8346        let (_, bindings_ctx) = api.contexts();
8347        assert_eq!(
8348            bindings_ctx.state.take_pending_error::<Ipv6>(&socket.downgrade()),
8349            Some(PendingDatagramSocketError::PortUnreachable)
8350        );
8351
8352        let (core_ctx, _) = api.contexts();
8353        assert_counters(
8354            core_ctx,
8355            CounterExpectationsWithSocket { rx_icmp_error_hard_delivered: 1, ..Default::default() },
8356            CounterExpectationsWithoutSocket::default(),
8357            [(
8358                &socket,
8359                CounterExpectationsWithSocket {
8360                    rx_icmp_error_hard_delivered: 1,
8361                    ..Default::default()
8362                },
8363            )],
8364        );
8365        assert_eq!(
8366            CounterContext::<UdpCountersWithoutSocket<Ipv4>>::counters(core_ctx).cast(),
8367            CounterExpectationsWithoutSocket {
8368                rx_icmp_error: 1,
8369                rx_icmp_error_hard: 1,
8370                ..Default::default()
8371            }
8372        );
8373    }
8374
8375    fn icmp_error_failed_counters_inner<I: TestIpExt>(icmp_err: I::ErrorCode) {
8376        set_logger_for_test();
8377
8378        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
8379        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
8380
8381        let local_ip = local_ip::<I>();
8382        let remote_ip = remote_ip::<I>();
8383
8384        // ICMP body too short
8385
8386        let malformed_body = vec![0u8; 4];
8387        let (core_ctx, bindings_ctx) = api.contexts();
8388
8389        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
8390            core_ctx,
8391            bindings_ctx,
8392            &FakeDeviceId,
8393            Some(local_ip),
8394            remote_ip,
8395            &malformed_body,
8396            icmp_err.clone(),
8397        );
8398
8399        let (core_ctx, _) = api.contexts();
8400        assert_eq!(
8401            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).cast(),
8402            CounterExpectationsWithoutSocket {
8403                rx_icmp_error: 1,
8404                rx_icmp_error_hard: 1,
8405                rx_icmp_error_hard_malformed: 1,
8406                ..Default::default()
8407            }
8408        );
8409
8410        // No matching socket
8411
8412        let mut no_socket_body = vec![0u8; 8];
8413        no_socket_body[0..2].copy_from_slice(&LOCAL_PORT.get().to_be_bytes());
8414        no_socket_body[2..4].copy_from_slice(&REMOTE_PORT.get().to_be_bytes());
8415        no_socket_body[4..6].copy_from_slice(&8u16.to_be_bytes());
8416
8417        let (core_ctx, bindings_ctx) = api.contexts();
8418        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
8419            core_ctx,
8420            bindings_ctx,
8421            &FakeDeviceId,
8422            Some(local_ip),
8423            remote_ip,
8424            &no_socket_body,
8425            icmp_err,
8426        );
8427
8428        let (core_ctx, _) = api.contexts();
8429        assert_eq!(
8430            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).cast(),
8431            CounterExpectationsWithoutSocket {
8432                rx_icmp_error: 2,
8433                rx_icmp_error_hard: 2,
8434                rx_icmp_error_hard_malformed: 1,
8435                rx_icmp_error_hard_no_socket: 1,
8436                ..Default::default()
8437            }
8438        );
8439    }
8440
8441    #[test_case(
8442        Icmpv4ErrorCode::DestUnreachable(
8443            Icmpv4DestUnreachableCode::DestPortUnreachable,
8444            Default::default()
8445        );
8446        "v4 failed icmp counters"
8447    )]
8448    fn icmp_error_failed_counters_v4(icmp_err: Icmpv4ErrorCode) {
8449        icmp_error_failed_counters_inner::<Ipv4>(icmp_err);
8450    }
8451
8452    #[test_case(
8453        Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::PortUnreachable);
8454        "v6 failed icmp counters"
8455    )]
8456    fn icmp_error_failed_counters_v6(icmp_err: Icmpv6ErrorCode) {
8457        icmp_error_failed_counters_inner::<Ipv6>(icmp_err);
8458    }
8459}