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