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