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