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