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