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