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    /// Disconnects all bound sockets matching the provided matcher.
1856    ///
1857    ///
1858    /// Returns the number of sockets that were disconnected.
1859    pub fn disconnect_bound<M>(&mut self, _matcher: &M) -> usize
1860    where
1861        M: IpSocketPropertiesMatcher<<C::BindingsContext as MatcherBindingsTypes>::DeviceClass>
1862            + ?Sized,
1863    {
1864        // TODO(https://fxbug.dev/459457112): Implement disconnect for UDP.
1865        0
1866    }
1867
1868    fn datagram(&mut self) -> &mut DatagramApi<I, C, Udp<C::BindingsContext>> {
1869        let Self(pair, IpVersionMarker { .. }) = self;
1870        DatagramApi::wrap(pair)
1871    }
1872
1873    /// Creates a new unbound UDP socket with default external data.
1874    pub fn create(&mut self) -> UdpApiSocketId<I, C>
1875    where
1876        <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>: Default,
1877        <C::BindingsContext as UdpBindingsTypes>::SocketWritableListener: Default,
1878    {
1879        self.create_with(Default::default(), Default::default())
1880    }
1881
1882    /// Creates a new unbound UDP socket with provided external data.
1883    pub fn create_with(
1884        &mut self,
1885        external_data: <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>,
1886        writable_listener: <C::BindingsContext as UdpBindingsTypes>::SocketWritableListener,
1887    ) -> UdpApiSocketId<I, C> {
1888        self.datagram().create(external_data, writable_listener)
1889    }
1890
1891    /// Connect a UDP socket
1892    ///
1893    /// `connect` binds `id` as a connection to the remote address and port. It
1894    /// is also bound to a local address and port, meaning that packets sent on
1895    /// this connection will always come from that address and port. The local
1896    /// address will be chosen based on the route to the remote address, and the
1897    /// local port will be chosen from the available ones.
1898    ///
1899    /// # Errors
1900    ///
1901    /// `connect` will fail in the following cases:
1902    /// - If both `local_ip` and `local_port` are specified but conflict with an
1903    ///   existing connection or listener
1904    /// - If one or both are left unspecified but there is still no way to
1905    ///   satisfy the request (e.g., `local_ip` is specified but there are no
1906    ///   available local ports for that address)
1907    /// - If there is no route to `remote_ip`
1908    /// - If `id` belongs to an already-connected socket
1909    pub fn connect(
1910        &mut self,
1911        id: &UdpApiSocketId<I, C>,
1912        remote_ip: Option<
1913            ZonedAddr<
1914                SpecifiedAddr<I::Addr>,
1915                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
1916            >,
1917        >,
1918        remote_port: UdpRemotePort,
1919    ) -> Result<(), ConnectError> {
1920        debug!("connect on {id:?} to {remote_ip:?}:{remote_port:?}");
1921        self.datagram().connect(id, remote_ip, remote_port, ())
1922    }
1923
1924    /// Sets the bound device for a socket.
1925    ///
1926    /// Sets the device to be used for sending and receiving packets for a socket.
1927    /// If the socket is not currently bound to a local address and port, the device
1928    /// will be used when binding.
1929    pub fn set_device(
1930        &mut self,
1931        id: &UdpApiSocketId<I, C>,
1932        device_id: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
1933    ) -> Result<(), SocketError> {
1934        debug!("set device on {id:?} to {device_id:?}");
1935        self.datagram().set_device(id, device_id)
1936    }
1937
1938    /// Gets the device the specified socket is bound to.
1939    pub fn get_bound_device(
1940        &mut self,
1941        id: &UdpApiSocketId<I, C>,
1942    ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
1943        self.datagram().get_bound_device(id)
1944    }
1945
1946    /// Enable or disable dual stack operations on the given socket.
1947    ///
1948    /// This is notionally the inverse of the `IPV6_V6ONLY` socket option.
1949    ///
1950    /// # Errors
1951    ///
1952    /// Returns an error if the socket does not support the `IPV6_V6ONLY` socket
1953    /// option (e.g. an IPv4 socket).
1954    pub fn set_dual_stack_enabled(
1955        &mut self,
1956        id: &UdpApiSocketId<I, C>,
1957        enabled: bool,
1958    ) -> Result<(), SetDualStackEnabledError> {
1959        self.datagram()
1960            .with_other_stack_ip_options_mut_if_unbound(id, |other_stack| {
1961                I::map_ip(
1962                    (enabled, WrapOtherStackIpOptionsMut(other_stack)),
1963                    |(_enabled, _v4)| Err(NotDualStackCapableError.into()),
1964                    |(enabled, WrapOtherStackIpOptionsMut(other_stack))| {
1965                        let DualStackSocketState { dual_stack_enabled, .. } = other_stack;
1966                        *dual_stack_enabled = enabled;
1967                        Ok(())
1968                    },
1969                )
1970            })
1971            .map_err(|ExpectedUnboundError| {
1972                // NB: Match Linux and prefer to return `NotCapable` errors over
1973                // `SocketIsBound` errors, for IPv4 sockets.
1974                match I::VERSION {
1975                    IpVersion::V4 => NotDualStackCapableError.into(),
1976                    IpVersion::V6 => SetDualStackEnabledError::SocketIsBound,
1977                }
1978            })?
1979    }
1980
1981    /// Get the enabled state of dual stack operations on the given socket.
1982    ///
1983    /// This is notionally the inverse of the `IPV6_V6ONLY` socket option.
1984    ///
1985    /// # Errors
1986    ///
1987    /// Returns an error if the socket does not support the `IPV6_V6ONLY` socket
1988    /// option (e.g. an IPv4 socket).
1989    pub fn get_dual_stack_enabled(
1990        &mut self,
1991        id: &UdpApiSocketId<I, C>,
1992    ) -> Result<bool, NotDualStackCapableError> {
1993        self.datagram().with_other_stack_ip_options(id, |other_stack| {
1994            I::map_ip(
1995                WrapOtherStackIpOptions(other_stack),
1996                |_v4| Err(NotDualStackCapableError),
1997                |WrapOtherStackIpOptions(other_stack)| {
1998                    let DualStackSocketState { dual_stack_enabled, .. } = other_stack;
1999                    Ok(*dual_stack_enabled)
2000                },
2001            )
2002        })
2003    }
2004
2005    /// Sets the POSIX `SO_REUSEADDR` option for the specified socket.
2006    ///
2007    /// # Errors
2008    ///
2009    /// Returns an error if the socket is already bound.
2010    pub fn set_posix_reuse_addr(
2011        &mut self,
2012        id: &UdpApiSocketId<I, C>,
2013        reuse_addr: bool,
2014    ) -> Result<(), ExpectedUnboundError> {
2015        self.datagram().update_sharing(id, |sharing| {
2016            sharing.reuse_addr = reuse_addr;
2017        })
2018    }
2019
2020    /// Gets the POSIX `SO_REUSEADDR` option for the specified socket.
2021    pub fn get_posix_reuse_addr(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2022        self.datagram().get_sharing(id).reuse_addr
2023    }
2024
2025    /// Sets the POSIX `SO_REUSEPORT` option for the specified socket.
2026    ///
2027    /// # Errors
2028    ///
2029    /// Returns an error if the socket is already bound.
2030    pub fn set_posix_reuse_port(
2031        &mut self,
2032        id: &UdpApiSocketId<I, C>,
2033        reuse_port: ReusePortOption,
2034    ) -> Result<(), ExpectedUnboundError> {
2035        self.datagram().update_sharing(id, |sharing| {
2036            sharing.reuse_port = reuse_port;
2037        })
2038    }
2039
2040    /// Gets the POSIX `SO_REUSEPORT` option for the specified socket.
2041    pub fn get_posix_reuse_port(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2042        self.datagram().get_sharing(id).reuse_port.is_enabled()
2043    }
2044
2045    /// Sets the specified socket's membership status for the given group.
2046    ///
2047    /// An error is returned if the membership change request is invalid
2048    /// (e.g. leaving a group that was not joined, or joining a group multiple
2049    /// times) or if the device to use to join is unspecified or conflicts with
2050    /// the existing socket state.
2051    pub fn set_multicast_membership(
2052        &mut self,
2053        id: &UdpApiSocketId<I, C>,
2054        multicast_group: MulticastAddr<I::Addr>,
2055        interface: MulticastMembershipInterfaceSelector<
2056            I::Addr,
2057            <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2058        >,
2059        want_membership: bool,
2060    ) -> Result<(), SetMulticastMembershipError> {
2061        debug!(
2062            "set multicast membership on {id:?} for group {multicast_group:?} with interface \
2063            selector: {interface:?}: want_membership={want_membership}"
2064        );
2065        self.datagram().set_multicast_membership(id, multicast_group, interface, want_membership)
2066    }
2067
2068    /// Sets the hop limit for packets sent by the socket to a unicast
2069    /// destination.
2070    ///
2071    /// Sets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2072    /// hop limits when `ip_version` is [`IpVersion::V6`].
2073    ///
2074    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2075    /// `ip_version` of [`IpVersion::V6`].
2076    pub fn set_unicast_hop_limit(
2077        &mut self,
2078        id: &UdpApiSocketId<I, C>,
2079        unicast_hop_limit: Option<NonZeroU8>,
2080        ip_version: IpVersion,
2081    ) -> Result<(), NotDualStackCapableError> {
2082        if ip_version == I::VERSION {
2083            return Ok(self
2084                .datagram()
2085                .update_ip_hop_limit(id, SocketHopLimits::set_unicast(unicast_hop_limit)));
2086        }
2087        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2088            I::map_ip(
2089                (IpInvariant(unicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack)),
2090                |(IpInvariant(_unicast_hop_limit), _v4)| Err(NotDualStackCapableError),
2091                |(IpInvariant(unicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack))| {
2092                    let DualStackSocketState {
2093                        socket_options:
2094                            DatagramIpSpecificSocketOptions {
2095                                hop_limits: SocketHopLimits { unicast, multicast: _, version: _ },
2096                                ..
2097                            },
2098                        ..
2099                    } = other_stack;
2100                    *unicast = unicast_hop_limit;
2101                    Ok(())
2102                },
2103            )
2104        })
2105    }
2106
2107    /// Sets the hop limit for packets sent by the socket to a multicast
2108    /// destination.
2109    ///
2110    /// Sets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2111    /// hop limits when `ip_version` is [`IpVersion::V6`].
2112    ///
2113    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2114    /// `ip_version` of [`IpVersion::V6`].
2115    pub fn set_multicast_hop_limit(
2116        &mut self,
2117        id: &UdpApiSocketId<I, C>,
2118        multicast_hop_limit: Option<NonZeroU8>,
2119        ip_version: IpVersion,
2120    ) -> Result<(), NotDualStackCapableError> {
2121        if ip_version == I::VERSION {
2122            return Ok(self
2123                .datagram()
2124                .update_ip_hop_limit(id, SocketHopLimits::set_multicast(multicast_hop_limit)));
2125        }
2126        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2127            I::map_ip(
2128                (IpInvariant(multicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack)),
2129                |(IpInvariant(_multicast_hop_limit), _v4)| Err(NotDualStackCapableError),
2130                |(IpInvariant(multicast_hop_limit), WrapOtherStackIpOptionsMut(other_stack))| {
2131                    let DualStackSocketState {
2132                        socket_options:
2133                            DatagramIpSpecificSocketOptions {
2134                                hop_limits: SocketHopLimits { unicast: _, multicast, version: _ },
2135                                ..
2136                            },
2137                        ..
2138                    } = other_stack;
2139                    *multicast = multicast_hop_limit;
2140                    Ok(())
2141                },
2142            )
2143        })
2144    }
2145
2146    /// Gets the hop limit for packets sent by the socket to a unicast
2147    /// destination.
2148    ///
2149    /// Gets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2150    /// hop limits when `ip_version` is [`IpVersion::V6`].
2151    ///
2152    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2153    /// `ip_version` of [`IpVersion::V6`].
2154    pub fn get_unicast_hop_limit(
2155        &mut self,
2156        id: &UdpApiSocketId<I, C>,
2157        ip_version: IpVersion,
2158    ) -> Result<NonZeroU8, NotDualStackCapableError> {
2159        if ip_version == I::VERSION {
2160            return Ok(self.datagram().get_ip_hop_limits(id).unicast);
2161        }
2162        self.datagram().with_other_stack_ip_options_and_default_hop_limits(
2163            id,
2164            |other_stack, default_hop_limits| {
2165                I::map_ip_in(
2166                    (WrapOtherStackIpOptions(other_stack), IpInvariant(default_hop_limits)),
2167                    |_v4| Err(NotDualStackCapableError),
2168                    |(
2169                        WrapOtherStackIpOptions(other_stack),
2170                        IpInvariant(HopLimits { unicast: default_unicast, multicast: _ }),
2171                    )| {
2172                        let DualStackSocketState {
2173                            socket_options:
2174                                DatagramIpSpecificSocketOptions {
2175                                    hop_limits:
2176                                        SocketHopLimits { unicast, multicast: _, version: _ },
2177                                    ..
2178                                },
2179                            ..
2180                        } = other_stack;
2181                        Ok(unicast.unwrap_or(default_unicast))
2182                    },
2183                )
2184            },
2185        )?
2186    }
2187
2188    /// Gets the hop limit for packets sent by the socket to a multicast
2189    /// destination.
2190    ///
2191    /// Gets the IPv4 TTL when `ip_version` is [`IpVersion::V4`], and the IPv6
2192    /// hop limits when `ip_version` is [`IpVersion::V6`].
2193    ///
2194    /// Returns [`NotDualStackCapableError`] if called on an IPv4 Socket with an
2195    /// `ip_version` of [`IpVersion::V6`].
2196    pub fn get_multicast_hop_limit(
2197        &mut self,
2198        id: &UdpApiSocketId<I, C>,
2199        ip_version: IpVersion,
2200    ) -> Result<NonZeroU8, NotDualStackCapableError> {
2201        if ip_version == I::VERSION {
2202            return Ok(self.datagram().get_ip_hop_limits(id).multicast);
2203        }
2204        self.datagram().with_other_stack_ip_options_and_default_hop_limits(
2205            id,
2206            |other_stack, default_hop_limits| {
2207                I::map_ip_in(
2208                    (WrapOtherStackIpOptions(other_stack), IpInvariant(default_hop_limits)),
2209                    |_v4| Err(NotDualStackCapableError),
2210                    |(
2211                        WrapOtherStackIpOptions(other_stack),
2212                        IpInvariant(HopLimits { unicast: _, multicast: default_multicast }),
2213                    )| {
2214                        let DualStackSocketState {
2215                            socket_options:
2216                                DatagramIpSpecificSocketOptions {
2217                                    hop_limits:
2218                                        SocketHopLimits { unicast: _, multicast, version: _ },
2219                                    ..
2220                                },
2221                            ..
2222                        } = other_stack;
2223                        Ok(multicast.unwrap_or(default_multicast))
2224                    },
2225                )
2226            },
2227        )?
2228    }
2229
2230    /// Returns the configured multicast interface for the socket.
2231    pub fn get_multicast_interface(
2232        &mut self,
2233        id: &UdpApiSocketId<I, C>,
2234        ip_version: IpVersion,
2235    ) -> Result<
2236        Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2237        NotDualStackCapableError,
2238    > {
2239        if ip_version == I::VERSION {
2240            return Ok(self.datagram().get_multicast_interface(id));
2241        };
2242
2243        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2244            I::map_ip_in(
2245                WrapOtherStackIpOptions(other_stack),
2246                |_v4| Err(NotDualStackCapableError),
2247                |WrapOtherStackIpOptions(other_stack)| {
2248                    Ok(other_stack.socket_options.multicast_interface.clone())
2249                },
2250            )
2251        })
2252    }
2253
2254    /// Sets the multicast interface to `interface` for a socket.
2255    pub fn set_multicast_interface(
2256        &mut self,
2257        id: &UdpApiSocketId<I, C>,
2258        interface: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
2259        ip_version: IpVersion,
2260    ) -> Result<(), NotDualStackCapableError> {
2261        if ip_version == I::VERSION {
2262            self.datagram().set_multicast_interface(id, interface);
2263            return Ok(());
2264        };
2265
2266        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2267            I::map_ip(
2268                (IpInvariant(interface), WrapOtherStackIpOptionsMut(other_stack)),
2269                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2270                |(IpInvariant(interface), WrapOtherStackIpOptionsMut(other_stack))| {
2271                    other_stack.socket_options.multicast_interface =
2272                        interface.map(|device| device.downgrade());
2273                    Ok(())
2274                },
2275            )
2276        })
2277    }
2278
2279    /// Gets the transparent option.
2280    pub fn get_transparent(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2281        self.datagram().get_ip_transparent(id)
2282    }
2283
2284    /// Sets the transparent option.
2285    pub fn set_transparent(&mut self, id: &UdpApiSocketId<I, C>, value: bool) {
2286        self.datagram().set_ip_transparent(id, value)
2287    }
2288
2289    /// Gets the socket mark at the mark domain.
2290    pub fn get_mark(&mut self, id: &UdpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
2291        self.datagram().get_mark(id, domain)
2292    }
2293
2294    /// Sets the socket mark at the mark domain.
2295    pub fn set_mark(&mut self, id: &UdpApiSocketId<I, C>, domain: MarkDomain, mark: Mark) {
2296        self.datagram().set_mark(id, domain, mark)
2297    }
2298
2299    /// Gets the broadcast option.
2300    pub fn get_broadcast(&mut self, id: &UdpApiSocketId<I, C>) -> bool {
2301        self.datagram().with_both_stacks_ip_options(id, |this_stack, other_stack| {
2302            I::map_ip_in(
2303                (this_stack, WrapOtherStackIpOptions(other_stack)),
2304                |(this_stack, _)| this_stack.allow_broadcast.is_some(),
2305                |(_, WrapOtherStackIpOptions(other_stack))| {
2306                    other_stack.socket_options.allow_broadcast.is_some()
2307                },
2308            )
2309        })
2310    }
2311
2312    /// Sets the broadcast option.
2313    pub fn set_broadcast(&mut self, id: &UdpApiSocketId<I, C>, value: bool) {
2314        self.datagram().with_both_stacks_ip_options_mut(id, |this_stack, other_stack| {
2315            let value = value.then_some(());
2316            I::map_ip_in(
2317                (this_stack, WrapOtherStackIpOptionsMut(other_stack)),
2318                |(this_stack, _)| this_stack.allow_broadcast = value,
2319                |(_, WrapOtherStackIpOptionsMut(other_stack))| {
2320                    other_stack.socket_options.allow_broadcast = value;
2321                },
2322            )
2323        })
2324    }
2325
2326    /// Gets the loopback multicast option.
2327    pub fn get_multicast_loop(
2328        &mut self,
2329        id: &UdpApiSocketId<I, C>,
2330        ip_version: IpVersion,
2331    ) -> Result<bool, NotDualStackCapableError> {
2332        if ip_version == I::VERSION {
2333            return Ok(self.datagram().get_multicast_loop(id));
2334        };
2335
2336        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2337            I::map_ip_in(
2338                WrapOtherStackIpOptions(other_stack),
2339                |_v4| Err(NotDualStackCapableError),
2340                |WrapOtherStackIpOptions(other_stack)| {
2341                    Ok(other_stack.socket_options.multicast_loop)
2342                },
2343            )
2344        })
2345    }
2346
2347    /// Sets the loopback multicast option.
2348    pub fn set_multicast_loop(
2349        &mut self,
2350        id: &UdpApiSocketId<I, C>,
2351        value: bool,
2352        ip_version: IpVersion,
2353    ) -> Result<(), NotDualStackCapableError> {
2354        if ip_version == I::VERSION {
2355            self.datagram().set_multicast_loop(id, value);
2356            return Ok(());
2357        };
2358
2359        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2360            I::map_ip(
2361                (IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack)),
2362                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2363                |(IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack))| {
2364                    other_stack.socket_options.multicast_loop = value;
2365                    Ok(())
2366                },
2367            )
2368        })
2369    }
2370
2371    /// Gets the TCLASS/TOS option.
2372    pub fn get_dscp_and_ecn(
2373        &mut self,
2374        id: &UdpApiSocketId<I, C>,
2375        ip_version: IpVersion,
2376    ) -> Result<DscpAndEcn, NotDualStackCapableError> {
2377        if ip_version == I::VERSION {
2378            return Ok(self.datagram().get_dscp_and_ecn(id));
2379        };
2380
2381        self.datagram().with_other_stack_ip_options(id, |other_stack| {
2382            I::map_ip_in(
2383                WrapOtherStackIpOptions(other_stack),
2384                |_v4| Err(NotDualStackCapableError),
2385                |WrapOtherStackIpOptions(other_stack)| Ok(other_stack.socket_options.dscp_and_ecn),
2386            )
2387        })
2388    }
2389
2390    /// Sets the TCLASS/TOS option.
2391    pub fn set_dscp_and_ecn(
2392        &mut self,
2393        id: &UdpApiSocketId<I, C>,
2394        value: DscpAndEcn,
2395        ip_version: IpVersion,
2396    ) -> Result<(), NotDualStackCapableError> {
2397        if ip_version == I::VERSION {
2398            self.datagram().set_dscp_and_ecn(id, value);
2399            return Ok(());
2400        };
2401
2402        self.datagram().with_other_stack_ip_options_mut(id, |other_stack| {
2403            I::map_ip(
2404                (IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack)),
2405                |(IpInvariant(_interface), _v4)| Err(NotDualStackCapableError),
2406                |(IpInvariant(value), WrapOtherStackIpOptionsMut(other_stack))| {
2407                    other_stack.socket_options.dscp_and_ecn = value;
2408                    Ok(())
2409                },
2410            )
2411        })
2412    }
2413
2414    /// Sets the send buffer maximum size to `size`.
2415    pub fn set_send_buffer(&mut self, id: &UdpApiSocketId<I, C>, size: usize) {
2416        self.datagram().set_send_buffer(id, size)
2417    }
2418
2419    /// Returns the current maximum send buffer size.
2420    pub fn send_buffer(&mut self, id: &UdpApiSocketId<I, C>) -> usize {
2421        self.datagram().send_buffer(id)
2422    }
2423
2424    /// Returns the currently available send buffer space on the socket.
2425    #[cfg(any(test, feature = "testutils"))]
2426    pub fn send_buffer_available(&mut self, id: &UdpApiSocketId<I, C>) -> usize {
2427        self.datagram().send_buffer_available(id)
2428    }
2429
2430    /// Disconnects a connected UDP socket.
2431    ///
2432    /// `disconnect` removes an existing connected socket and replaces it with a
2433    /// listening socket bound to the same local address and port.
2434    ///
2435    /// # Errors
2436    ///
2437    /// Returns an error if the socket is not connected.
2438    pub fn disconnect(&mut self, id: &UdpApiSocketId<I, C>) -> Result<(), ExpectedConnError> {
2439        debug!("disconnect {id:?}");
2440        self.datagram().disconnect_connected(id)
2441    }
2442
2443    /// Shuts down a socket for reading and/or writing.
2444    ///
2445    /// # Errors
2446    ///
2447    /// Returns an error if the socket is not connected.
2448    pub fn shutdown(
2449        &mut self,
2450        id: &UdpApiSocketId<I, C>,
2451        which: ShutdownType,
2452    ) -> Result<(), ExpectedConnError> {
2453        debug!("shutdown {id:?} {which:?}");
2454        self.datagram().shutdown_connected(id, which)
2455    }
2456
2457    /// Get the shutdown state for a socket.
2458    ///
2459    /// If the socket is not connected, or if `shutdown` was not called on it,
2460    /// returns `None`.
2461    pub fn get_shutdown(&mut self, id: &UdpApiSocketId<I, C>) -> Option<ShutdownType> {
2462        self.datagram().get_shutdown_connected(id)
2463    }
2464
2465    /// Removes a socket that was previously created.
2466    pub fn close(
2467        &mut self,
2468        id: UdpApiSocketId<I, C>,
2469    ) -> RemoveResourceResultWithContext<
2470        <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>,
2471        C::BindingsContext,
2472    > {
2473        debug!("close {id:?}");
2474        self.datagram().close(id)
2475    }
2476
2477    /// Gets the [`SocketInfo`] associated with the UDP socket referenced by
2478    /// `id`.
2479    pub fn get_info(
2480        &mut self,
2481        id: &UdpApiSocketId<I, C>,
2482    ) -> SocketInfo<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
2483        self.datagram().get_info(id)
2484    }
2485
2486    /// Use an existing socket to listen for incoming UDP packets.
2487    ///
2488    /// `listen_udp` converts `id` into a listening socket and registers the new
2489    /// socket as a listener for incoming UDP packets on the given `port`. If
2490    /// `addr` is `None`, the listener is a "wildcard listener", and is bound to
2491    /// all local addresses. See the [`crate::transport`] module documentation
2492    /// for more details.
2493    ///
2494    /// If `addr` is `Some``, and `addr` is already bound on the given port
2495    /// (either by a listener or a connection), `listen_udp` will fail. If
2496    /// `addr` is `None`, and a wildcard listener is already bound to the given
2497    /// port, `listen_udp` will fail.
2498    ///
2499    /// # Errors
2500    ///
2501    /// Returns an error if the socket is not currently unbound.
2502    pub fn listen(
2503        &mut self,
2504        id: &UdpApiSocketId<I, C>,
2505        addr: Option<
2506            ZonedAddr<
2507                SpecifiedAddr<I::Addr>,
2508                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2509            >,
2510        >,
2511        port: Option<NonZeroU16>,
2512    ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
2513        debug!("listen on {id:?} on {addr:?}:{port:?}");
2514        self.datagram().listen(id, addr, port)
2515    }
2516
2517    /// Sends a UDP packet on an existing socket.
2518    ///
2519    /// # Errors
2520    ///
2521    /// Returns an error if the socket is not connected or the packet cannot be
2522    /// sent. On error, the original `body` is returned unmodified so that it
2523    /// can be reused by the caller.
2524    pub fn send<B: BufferMut>(
2525        &mut self,
2526        id: &UdpApiSocketId<I, C>,
2527        body: B,
2528    ) -> Result<(), Either<SendError, ExpectedConnError>> {
2529        self.core_ctx().increment_both(id, |c| &c.tx);
2530        self.datagram().send_conn(id, body).map_err(|err| {
2531            self.core_ctx().increment_both(id, |c| &c.tx_error);
2532            match err {
2533                DatagramSendError::NotConnected => Either::Right(ExpectedConnError),
2534                DatagramSendError::NotWriteable => Either::Left(SendError::NotWriteable),
2535                DatagramSendError::SendBufferFull => Either::Left(SendError::SendBufferFull),
2536                DatagramSendError::InvalidLength => Either::Left(SendError::InvalidLength),
2537                DatagramSendError::IpSock(err) => Either::Left(SendError::IpSock(err)),
2538                DatagramSendError::SerializeError(err) => match err {
2539                    UdpSerializeError::RemotePortUnset => Either::Left(SendError::RemotePortUnset),
2540                },
2541            }
2542        })
2543    }
2544
2545    /// Sends a UDP packet to the provided destination address.
2546    ///
2547    /// If this is called with an unbound socket, the socket will be implicitly
2548    /// bound. If that succeeds, the ID for the new socket is returned.
2549    ///
2550    /// # Errors
2551    ///
2552    /// Returns an error if the socket is unbound and connecting fails, or if the
2553    /// packet could not be sent. If the socket is unbound and connecting succeeds
2554    /// but sending fails, the socket remains connected.
2555    pub fn send_to<B: BufferMut>(
2556        &mut self,
2557        id: &UdpApiSocketId<I, C>,
2558        remote_ip: Option<
2559            ZonedAddr<
2560                SpecifiedAddr<I::Addr>,
2561                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
2562            >,
2563        >,
2564        remote_port: UdpRemotePort,
2565        body: B,
2566    ) -> Result<(), Either<LocalAddressError, SendToError>> {
2567        // Match Linux's behavior and verify the remote port is set.
2568        match remote_port {
2569            UdpRemotePort::Unset => return Err(Either::Right(SendToError::RemotePortUnset)),
2570            UdpRemotePort::Set(_) => {}
2571        }
2572
2573        self.core_ctx().increment_both(id, |c| &c.tx);
2574        self.datagram().send_to(id, remote_ip, remote_port, body).map_err(|e| {
2575            self.core_ctx().increment_both(id, |c| &c.tx_error);
2576            match e {
2577                Either::Left(e) => Either::Left(e),
2578                Either::Right(e) => {
2579                    let err = match e {
2580                        datagram::SendToError::SerializeError(err) => match err {
2581                            UdpSerializeError::RemotePortUnset => SendToError::RemotePortUnset,
2582                        },
2583                        datagram::SendToError::NotWriteable => SendToError::NotWriteable,
2584                        datagram::SendToError::SendBufferFull => SendToError::SendBufferFull,
2585                        datagram::SendToError::InvalidLength => SendToError::InvalidLength,
2586                        datagram::SendToError::Zone(e) => SendToError::Zone(e),
2587                        datagram::SendToError::CreateAndSend(e) => match e {
2588                            IpSockCreateAndSendError::Send(e) => SendToError::Send(e),
2589                            IpSockCreateAndSendError::Create(e) => SendToError::CreateSock(e),
2590                        },
2591                        datagram::SendToError::RemoteUnexpectedlyMapped => {
2592                            SendToError::RemoteUnexpectedlyMapped
2593                        }
2594                        datagram::SendToError::RemoteUnexpectedlyNonMapped => {
2595                            SendToError::RemoteUnexpectedlyNonMapped
2596                        }
2597                    };
2598                    Either::Right(err)
2599                }
2600            }
2601        })
2602    }
2603
2604    /// Collects all currently opened sockets, returning a cloned reference for
2605    /// each one.
2606    pub fn collect_all_sockets(&mut self) -> Vec<UdpApiSocketId<I, C>> {
2607        self.datagram().collect_all_sockets()
2608    }
2609
2610    /// Provides inspect data for UDP sockets.
2611    pub fn inspect<N>(&mut self, inspector: &mut N)
2612    where
2613        N: Inspector
2614            + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2615        for<'a> N::ChildInspector<'a>:
2616            InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
2617    {
2618        DatagramStateContext::for_each_socket(self.core_ctx(), |_ctx, socket_id, socket_state| {
2619            inspector.record_debug_child(socket_id, |inspector| {
2620                socket_state.record_common_info(inspector);
2621                inspector.record_child("Counters", |inspector| {
2622                    inspector.delegate_inspectable(&CombinedUdpCounters {
2623                        with_socket: socket_id.counters(),
2624                        without_socket: None,
2625                    });
2626                });
2627            });
2628        });
2629    }
2630}
2631
2632/// Error when sending a packet on a socket.
2633#[derive(Copy, Clone, Debug, Eq, PartialEq, GenericOverIp, Error)]
2634#[generic_over_ip()]
2635pub enum SendError {
2636    /// The socket is not writeable.
2637    #[error("socket not writable")]
2638    NotWriteable,
2639    /// The packet couldn't be sent.
2640    #[error("packet couldn't be sent: {0}")]
2641    IpSock(#[from] IpSockSendError),
2642    /// Disallow sending packets with a remote port of 0. See
2643    /// [`UdpRemotePort::Unset`] for the rationale.
2644    #[error("remote port unset")]
2645    RemotePortUnset,
2646    /// The socket's send buffer is full.
2647    #[error("send buffer is full")]
2648    SendBufferFull,
2649    /// Invalid message length.
2650    #[error("invalid message length")]
2651    InvalidLength,
2652}
2653
2654impl<I: IpExt, BC: UdpBindingsContext<I, CC::DeviceId>, CC: StateContext<I, BC>>
2655    DatagramSpecStateContext<I, CC, BC> for Udp<BC>
2656{
2657    type SocketsStateCtx<'a> = CC::SocketStateCtx<'a>;
2658
2659    fn with_all_sockets_mut<O, F: FnOnce(&mut UdpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
2660        core_ctx: &mut CC,
2661        cb: F,
2662    ) -> O {
2663        StateContext::with_all_sockets_mut(core_ctx, cb)
2664    }
2665
2666    fn with_all_sockets<O, F: FnOnce(&UdpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
2667        core_ctx: &mut CC,
2668        cb: F,
2669    ) -> O {
2670        StateContext::with_all_sockets(core_ctx, cb)
2671    }
2672
2673    fn with_socket_state<
2674        O,
2675        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &UdpSocketState<I, CC::WeakDeviceId, BC>) -> O,
2676    >(
2677        core_ctx: &mut CC,
2678        id: &UdpSocketId<I, CC::WeakDeviceId, BC>,
2679        cb: F,
2680    ) -> O {
2681        StateContext::with_socket_state(core_ctx, id, cb)
2682    }
2683
2684    fn with_socket_state_mut<
2685        O,
2686        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut UdpSocketState<I, CC::WeakDeviceId, BC>) -> O,
2687    >(
2688        core_ctx: &mut CC,
2689        id: &UdpSocketId<I, CC::WeakDeviceId, BC>,
2690        cb: F,
2691    ) -> O {
2692        StateContext::with_socket_state_mut(core_ctx, id, cb)
2693    }
2694
2695    fn for_each_socket<
2696        F: FnMut(
2697            &mut Self::SocketsStateCtx<'_>,
2698            &UdpSocketId<I, CC::WeakDeviceId, BC>,
2699            &UdpSocketState<I, CC::WeakDeviceId, BC>,
2700        ),
2701    >(
2702        core_ctx: &mut CC,
2703        cb: F,
2704    ) {
2705        StateContext::for_each_socket(core_ctx, cb)
2706    }
2707}
2708
2709impl<
2710    I: IpExt,
2711    BC: UdpBindingsContext<I, CC::DeviceId>,
2712    CC: BoundStateContext<I, BC> + UdpStateContext,
2713> DatagramSpecBoundStateContext<I, CC, BC> for Udp<BC>
2714{
2715    type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
2716
2717    fn with_bound_sockets<
2718        O,
2719        F: FnOnce(
2720            &mut Self::IpSocketsCtx<'_>,
2721            &DatagramBoundSockets<
2722                I,
2723                CC::WeakDeviceId,
2724                UdpAddrSpec,
2725                UdpSocketMapStateSpec<I, CC::WeakDeviceId, BC>,
2726            >,
2727        ) -> O,
2728    >(
2729        core_ctx: &mut CC,
2730        cb: F,
2731    ) -> O {
2732        core_ctx.with_bound_sockets(|core_ctx, BoundSockets { bound_sockets }| {
2733            cb(core_ctx, bound_sockets)
2734        })
2735    }
2736
2737    fn with_bound_sockets_mut<
2738        O,
2739        F: FnOnce(
2740            &mut Self::IpSocketsCtx<'_>,
2741            &mut DatagramBoundSockets<
2742                I,
2743                CC::WeakDeviceId,
2744                UdpAddrSpec,
2745                UdpSocketMapStateSpec<I, CC::WeakDeviceId, BC>,
2746            >,
2747        ) -> O,
2748    >(
2749        core_ctx: &mut CC,
2750        cb: F,
2751    ) -> O {
2752        core_ctx.with_bound_sockets_mut(|core_ctx, BoundSockets { bound_sockets }| {
2753            cb(core_ctx, bound_sockets)
2754        })
2755    }
2756
2757    type DualStackContext = CC::DualStackContext;
2758    type NonDualStackContext = CC::NonDualStackContext;
2759    fn dual_stack_context_mut(
2760        core_ctx: &mut CC,
2761    ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
2762        BoundStateContext::dual_stack_context_mut(core_ctx)
2763    }
2764
2765    fn dual_stack_context(
2766        core_ctx: &CC,
2767    ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
2768        BoundStateContext::dual_stack_context(core_ctx)
2769    }
2770
2771    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
2772        core_ctx: &mut CC,
2773        cb: F,
2774    ) -> O {
2775        core_ctx.with_transport_context(cb)
2776    }
2777}
2778
2779impl<
2780    BC: UdpBindingsContext<Ipv6, CC::DeviceId> + UdpBindingsContext<Ipv4, CC::DeviceId>,
2781    CC: DualStackBoundStateContext<Ipv6, BC> + UdpStateContext,
2782> DualStackDatagramSpecBoundStateContext<Ipv6, CC, BC> for Udp<BC>
2783{
2784    type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
2785    fn dual_stack_enabled(
2786        _core_ctx: &CC,
2787        state: &impl AsRef<IpOptions<Ipv6, CC::WeakDeviceId, Udp<BC>>>,
2788    ) -> bool {
2789        let DualStackSocketState { dual_stack_enabled, .. } = state.as_ref().other_stack();
2790        *dual_stack_enabled
2791    }
2792
2793    fn to_other_socket_options<'a>(
2794        _core_ctx: &CC,
2795        state: &'a IpOptions<Ipv6, CC::WeakDeviceId, Udp<BC>>,
2796    ) -> &'a DatagramIpSpecificSocketOptions<Ipv4, CC::WeakDeviceId> {
2797        &state.other_stack().socket_options
2798    }
2799
2800    fn ds_converter(_core_ctx: &CC) -> impl DualStackConverter<Ipv6, CC::WeakDeviceId, Self> {
2801        ()
2802    }
2803
2804    fn to_other_bound_socket_id(
2805        _core_ctx: &CC,
2806        id: &UdpSocketId<Ipv6, CC::WeakDeviceId, BC>,
2807    ) -> EitherIpSocket<CC::WeakDeviceId, Udp<BC>> {
2808        EitherIpSocket::V6(id.clone())
2809    }
2810
2811    fn with_both_bound_sockets_mut<
2812        O,
2813        F: FnOnce(
2814            &mut Self::IpSocketsCtx<'_>,
2815            &mut UdpBoundSocketMap<Ipv6, CC::WeakDeviceId, BC>,
2816            &mut UdpBoundSocketMap<Ipv4, CC::WeakDeviceId, BC>,
2817        ) -> O,
2818    >(
2819        core_ctx: &mut CC,
2820        cb: F,
2821    ) -> O {
2822        core_ctx.with_both_bound_sockets_mut(
2823            |core_ctx,
2824             BoundSockets { bound_sockets: bound_first },
2825             BoundSockets { bound_sockets: bound_second }| {
2826                cb(core_ctx, bound_first, bound_second)
2827            },
2828        )
2829    }
2830
2831    fn with_other_bound_sockets_mut<
2832        O,
2833        F: FnOnce(
2834            &mut Self::IpSocketsCtx<'_>,
2835            &mut UdpBoundSocketMap<Ipv4, CC::WeakDeviceId, BC>,
2836        ) -> O,
2837    >(
2838        core_ctx: &mut CC,
2839        cb: F,
2840    ) -> O {
2841        core_ctx.with_other_bound_sockets_mut(|core_ctx, BoundSockets { bound_sockets }| {
2842            cb(core_ctx, bound_sockets)
2843        })
2844    }
2845
2846    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
2847        core_ctx: &mut CC,
2848        cb: F,
2849    ) -> O {
2850        core_ctx.with_transport_context(|core_ctx| cb(core_ctx))
2851    }
2852}
2853
2854impl<
2855    BC: UdpBindingsContext<Ipv4, CC::DeviceId>,
2856    CC: BoundStateContext<Ipv4, BC> + NonDualStackBoundStateContext<Ipv4, BC> + UdpStateContext,
2857> NonDualStackDatagramSpecBoundStateContext<Ipv4, CC, BC> for Udp<BC>
2858{
2859    fn nds_converter(_core_ctx: &CC) -> impl NonDualStackConverter<Ipv4, CC::WeakDeviceId, Self> {
2860        ()
2861    }
2862}
2863
2864#[cfg(test)]
2865pub(crate) mod testutils {
2866    use alloc::borrow::ToOwned;
2867    use alloc::vec;
2868    use core::ops::{Deref, DerefMut};
2869
2870    use net_types::ip::{IpAddr, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr, Ipv6SourceAddr};
2871    use netstack3_base::testutil::{
2872        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSocketWritableListener, FakeStrongDeviceId,
2873        FakeWeakDeviceId,
2874    };
2875    use netstack3_base::{CtxPair, ResourceCounterContext, UninstantiableWrapper};
2876    use netstack3_hashmap::HashMap;
2877    use netstack3_ip::device::IpDeviceStateIpExt;
2878    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeDualStackIpSocketCtx};
2879    use netstack3_ip::testutil::DualStackSendIpPacketMeta;
2880
2881    use super::*;
2882    /// A packet received on a socket.
2883    #[derive(Debug, Derivative, PartialEq)]
2884    #[derivative(Default(bound = ""))]
2885    pub(crate) struct SocketReceived<I: Ip> {
2886        pub(crate) packets: Vec<ReceivedPacket<I>>,
2887        #[derivative(Default(value = "usize::MAX"))]
2888        pub(crate) max_size: usize,
2889    }
2890
2891    #[derive(Debug, PartialEq)]
2892    pub(crate) struct ReceivedPacket<I: Ip> {
2893        pub(crate) meta: UdpPacketMeta<I>,
2894        pub(crate) body: Vec<u8>,
2895    }
2896
2897    impl<D: FakeStrongDeviceId> FakeUdpCoreCtx<D> {
2898        pub(crate) fn new_with_device<I: TestIpExt>(device: D) -> Self {
2899            Self::with_local_remote_ip_addrs_and_device(
2900                vec![local_ip::<I>()],
2901                vec![remote_ip::<I>()],
2902                device,
2903            )
2904        }
2905
2906        fn with_local_remote_ip_addrs_and_device<A: Into<SpecifiedAddr<IpAddr>>>(
2907            local_ips: Vec<A>,
2908            remote_ips: Vec<A>,
2909            device: D,
2910        ) -> Self {
2911            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
2912                device,
2913                local_ips,
2914                remote_ips,
2915            }]))
2916        }
2917
2918        pub(crate) fn with_ip_socket_ctx_state(state: FakeDualStackIpSocketCtx<D>) -> Self {
2919            Self {
2920                all_sockets: Default::default(),
2921                bound_sockets: FakeUdpBoundSocketsCtx {
2922                    bound_sockets: Default::default(),
2923                    ip_socket_ctx: InnerIpSocketCtx::with_state(state),
2924                },
2925            }
2926        }
2927    }
2928
2929    impl FakeUdpCoreCtx<FakeDeviceId> {
2930        pub(crate) fn new_fake_device<I: TestIpExt>() -> Self {
2931            Self::new_with_device::<I>(FakeDeviceId)
2932        }
2933
2934        pub(crate) fn with_local_remote_ip_addrs<A: Into<SpecifiedAddr<IpAddr>>>(
2935            local_ips: Vec<A>,
2936            remote_ips: Vec<A>,
2937        ) -> Self {
2938            Self::with_local_remote_ip_addrs_and_device(local_ips, remote_ips, FakeDeviceId)
2939        }
2940    }
2941
2942    /// UDP tests context pair.
2943    pub(crate) type FakeUdpCtx<D> = CtxPair<FakeUdpCoreCtx<D>, FakeUdpBindingsCtx<D>>;
2944
2945    #[derive(Derivative)]
2946    #[derivative(Default(bound = ""))]
2947    pub(crate) struct FakeBoundSockets<D: StrongDeviceIdentifier> {
2948        v4: BoundSockets<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
2949        v6: BoundSockets<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
2950    }
2951
2952    impl<D: StrongDeviceIdentifier> FakeBoundSockets<D> {
2953        fn bound_sockets<I: IpExt>(&self) -> &BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
2954            I::map_ip_out(self, |state| &state.v4, |state| &state.v6)
2955        }
2956
2957        fn bound_sockets_mut<I: IpExt>(
2958            &mut self,
2959        ) -> &mut BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
2960            I::map_ip_out(self, |state| &mut state.v4, |state| &mut state.v6)
2961        }
2962    }
2963
2964    pub(crate) struct FakeUdpBoundSocketsCtx<D: FakeStrongDeviceId> {
2965        pub(crate) bound_sockets: FakeBoundSockets<D>,
2966        pub(crate) ip_socket_ctx: InnerIpSocketCtx<D>,
2967    }
2968
2969    /// `FakeBindingsCtx` specialized for UDP.
2970    pub(crate) type FakeUdpBindingsCtx<D> = FakeBindingsCtx<(), (), FakeBindingsCtxState<D>, ()>;
2971
2972    /// The inner context providing a fake IP socket context to
2973    /// [`FakeUdpBoundSocketsCtx`].
2974    type InnerIpSocketCtx<D> =
2975        FakeCoreCtx<FakeDualStackIpSocketCtx<D>, DualStackSendIpPacketMeta<D>, D>;
2976
2977    pub(crate) type UdpFakeDeviceCtx = FakeUdpCtx<FakeDeviceId>;
2978    pub(crate) type UdpFakeDeviceCoreCtx = FakeUdpCoreCtx<FakeDeviceId>;
2979
2980    #[derive(Derivative)]
2981    #[derivative(Default(bound = ""))]
2982    pub(crate) struct FakeBindingsCtxState<D: StrongDeviceIdentifier> {
2983        received_v4:
2984            HashMap<WeakUdpSocketId<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv4>>,
2985        received_v6:
2986            HashMap<WeakUdpSocketId<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv6>>,
2987    }
2988
2989    impl<D: StrongDeviceIdentifier> FakeBindingsCtxState<D> {
2990        pub(crate) fn received<I: TestIpExt>(
2991            &self,
2992        ) -> &HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
2993        {
2994            #[derive(GenericOverIp)]
2995            #[generic_over_ip(I, Ip)]
2996            struct Wrap<'a, I: TestIpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
2997                &'a HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
2998            );
2999            let Wrap(map) = I::map_ip_out(
3000                self,
3001                |state| Wrap(&state.received_v4),
3002                |state| Wrap(&state.received_v6),
3003            );
3004            map
3005        }
3006
3007        pub(crate) fn received_mut<I: IpExt>(
3008            &mut self,
3009        ) -> &mut HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
3010        {
3011            #[derive(GenericOverIp)]
3012            #[generic_over_ip(I, Ip)]
3013            struct Wrap<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
3014                &'a mut HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
3015            );
3016            let Wrap(map) = I::map_ip_out(
3017                self,
3018                |state| Wrap(&mut state.received_v4),
3019                |state| Wrap(&mut state.received_v6),
3020            );
3021            map
3022        }
3023
3024        pub(crate) fn socket_data<I: TestIpExt>(
3025            &self,
3026        ) -> HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, Vec<&'_ [u8]>> {
3027            self.received::<I>()
3028                .iter()
3029                .map(|(id, SocketReceived { packets, .. })| {
3030                    (
3031                        id.clone(),
3032                        packets.iter().map(|ReceivedPacket { meta: _, body }| &body[..]).collect(),
3033                    )
3034                })
3035                .collect()
3036        }
3037    }
3038
3039    impl<I: IpExt, D: StrongDeviceIdentifier> UdpReceiveBindingsContext<I, D>
3040        for FakeUdpBindingsCtx<D>
3041    {
3042        fn receive_udp(
3043            &mut self,
3044            id: &UdpSocketId<I, D::Weak, Self>,
3045            _device_id: &D,
3046            meta: UdpPacketMeta<I>,
3047            body: &[u8],
3048        ) -> Result<(), ReceiveUdpError> {
3049            let SocketReceived { packets, max_size } =
3050                self.state.received_mut::<I>().entry(id.downgrade()).or_default();
3051            if packets.len() < *max_size {
3052                packets.push(ReceivedPacket { meta, body: body.to_owned() });
3053                Ok(())
3054            } else {
3055                Err(ReceiveUdpError::QueueFull)
3056            }
3057        }
3058    }
3059
3060    impl<D: StrongDeviceIdentifier> UdpBindingsTypes for FakeUdpBindingsCtx<D> {
3061        type ExternalData<I: Ip> = ();
3062        type SocketWritableListener = FakeSocketWritableListener;
3063    }
3064
3065    /// Utilities for accessing locked internal state in tests.
3066    impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> UdpSocketId<I, D, BT> {
3067        fn get(&self) -> impl Deref<Target = UdpSocketState<I, D, BT>> + '_ {
3068            self.state().read()
3069        }
3070
3071        fn get_mut(&self) -> impl DerefMut<Target = UdpSocketState<I, D, BT>> + '_ {
3072            self.state().write()
3073        }
3074    }
3075
3076    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpCoreCtx<D> {
3077        type DeviceId = D;
3078        type WeakDeviceId = FakeWeakDeviceId<D>;
3079    }
3080
3081    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpBoundSocketsCtx<D> {
3082        type DeviceId = D;
3083        type WeakDeviceId = FakeWeakDeviceId<D>;
3084    }
3085
3086    impl<I: TestIpExt, D: FakeStrongDeviceId> StateContext<I, FakeUdpBindingsCtx<D>>
3087        for FakeUdpCoreCtx<D>
3088    {
3089        type SocketStateCtx<'a> = FakeUdpBoundSocketsCtx<D>;
3090
3091        fn with_all_sockets_mut<
3092            O,
3093            F: FnOnce(&mut UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
3094        >(
3095            &mut self,
3096            cb: F,
3097        ) -> O {
3098            cb(self.all_sockets.socket_set_mut())
3099        }
3100
3101        fn with_all_sockets<
3102            O,
3103            F: FnOnce(&UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
3104        >(
3105            &mut self,
3106            cb: F,
3107        ) -> O {
3108            cb(self.all_sockets.socket_set())
3109        }
3110
3111        fn with_socket_state<
3112            O,
3113            F: FnOnce(
3114                &mut Self::SocketStateCtx<'_>,
3115                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3116            ) -> O,
3117        >(
3118            &mut self,
3119            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3120            cb: F,
3121        ) -> O {
3122            cb(&mut self.bound_sockets, &id.get())
3123        }
3124
3125        fn with_socket_state_mut<
3126            O,
3127            F: FnOnce(
3128                &mut Self::SocketStateCtx<'_>,
3129                &mut UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3130            ) -> O,
3131        >(
3132            &mut self,
3133            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3134            cb: F,
3135        ) -> O {
3136            cb(&mut self.bound_sockets, &mut id.get_mut())
3137        }
3138
3139        fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
3140            &mut self,
3141            cb: F,
3142        ) -> O {
3143            cb(&mut self.bound_sockets)
3144        }
3145
3146        fn for_each_socket<
3147            F: FnMut(
3148                &mut Self::SocketStateCtx<'_>,
3149                &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3150                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3151            ),
3152        >(
3153            &mut self,
3154            mut cb: F,
3155        ) {
3156            self.all_sockets.socket_set().keys().for_each(|id| {
3157                let id = UdpSocketId::from(id.clone());
3158                cb(&mut self.bound_sockets, &id, &id.get());
3159            })
3160        }
3161    }
3162
3163    impl<I: TestIpExt, D: FakeStrongDeviceId> BoundStateContext<I, FakeUdpBindingsCtx<D>>
3164        for FakeUdpBoundSocketsCtx<D>
3165    {
3166        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3167        type DualStackContext = I::UdpDualStackBoundStateContext<D>;
3168        type NonDualStackContext = I::UdpNonDualStackBoundStateContext<D>;
3169
3170        fn with_bound_sockets<
3171            O,
3172            F: FnOnce(
3173                &mut Self::IpSocketsCtx<'_>,
3174                &BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3175            ) -> O,
3176        >(
3177            &mut self,
3178            cb: F,
3179        ) -> O {
3180            let Self { bound_sockets, ip_socket_ctx } = self;
3181            cb(ip_socket_ctx, bound_sockets.bound_sockets())
3182        }
3183
3184        fn with_bound_sockets_mut<
3185            O,
3186            F: FnOnce(
3187                &mut Self::IpSocketsCtx<'_>,
3188                &mut BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3189            ) -> O,
3190        >(
3191            &mut self,
3192            cb: F,
3193        ) -> O {
3194            let Self { bound_sockets, ip_socket_ctx } = self;
3195            cb(ip_socket_ctx, bound_sockets.bound_sockets_mut())
3196        }
3197
3198        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3199            &mut self,
3200            cb: F,
3201        ) -> O {
3202            cb(&mut self.ip_socket_ctx)
3203        }
3204
3205        fn dual_stack_context(
3206            &self,
3207        ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
3208            struct Wrap<'a, I: TestIpExt, D: FakeStrongDeviceId + 'static>(
3209                MaybeDualStack<
3210                    &'a I::UdpDualStackBoundStateContext<D>,
3211                    &'a I::UdpNonDualStackBoundStateContext<D>,
3212                >,
3213            );
3214            // TODO(https://fxbug.dev/42082123): Replace this with a derived impl.
3215            impl<'a, I: TestIpExt, NewIp: TestIpExt, D: FakeStrongDeviceId + 'static>
3216                GenericOverIp<NewIp> for Wrap<'a, I, D>
3217            {
3218                type Type = Wrap<'a, NewIp, D>;
3219            }
3220
3221            let Wrap(context) = I::map_ip_out(
3222                self,
3223                |this| Wrap(MaybeDualStack::NotDualStack(this)),
3224                |this| Wrap(MaybeDualStack::DualStack(this)),
3225            );
3226            context
3227        }
3228
3229        fn dual_stack_context_mut(
3230            &mut self,
3231        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
3232            struct Wrap<'a, I: TestIpExt, D: FakeStrongDeviceId + 'static>(
3233                MaybeDualStack<
3234                    &'a mut I::UdpDualStackBoundStateContext<D>,
3235                    &'a mut I::UdpNonDualStackBoundStateContext<D>,
3236                >,
3237            );
3238            // TODO(https://fxbug.dev/42082123): Replace this with a derived impl.
3239            impl<'a, I: TestIpExt, NewIp: TestIpExt, D: FakeStrongDeviceId + 'static>
3240                GenericOverIp<NewIp> for Wrap<'a, I, D>
3241            {
3242                type Type = Wrap<'a, NewIp, D>;
3243            }
3244
3245            let Wrap(context) = I::map_ip_out(
3246                self,
3247                |this| Wrap(MaybeDualStack::NotDualStack(this)),
3248                |this| Wrap(MaybeDualStack::DualStack(this)),
3249            );
3250            context
3251        }
3252    }
3253
3254    impl<D: FakeStrongDeviceId + 'static> UdpStateContext for FakeUdpBoundSocketsCtx<D> {}
3255
3256    impl<D: FakeStrongDeviceId> NonDualStackBoundStateContext<Ipv4, FakeUdpBindingsCtx<D>>
3257        for FakeUdpBoundSocketsCtx<D>
3258    {
3259    }
3260
3261    impl<D: FakeStrongDeviceId> DualStackBoundStateContext<Ipv6, FakeUdpBindingsCtx<D>>
3262        for FakeUdpBoundSocketsCtx<D>
3263    {
3264        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3265
3266        fn with_both_bound_sockets_mut<
3267            O,
3268            F: FnOnce(
3269                &mut Self::IpSocketsCtx<'_>,
3270                &mut BoundSockets<Ipv6, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3271                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3272            ) -> O,
3273        >(
3274            &mut self,
3275            cb: F,
3276        ) -> O {
3277            let Self { ip_socket_ctx, bound_sockets: FakeBoundSockets { v4, v6 } } = self;
3278            cb(ip_socket_ctx, v6, v4)
3279        }
3280
3281        fn with_other_bound_sockets_mut<
3282            O,
3283            F: FnOnce(
3284                &mut Self::IpSocketsCtx<'_>,
3285                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3286            ) -> O,
3287        >(
3288            &mut self,
3289            cb: F,
3290        ) -> O {
3291            DualStackBoundStateContext::with_both_bound_sockets_mut(
3292                self,
3293                |core_ctx, _bound, other_bound| cb(core_ctx, other_bound),
3294            )
3295        }
3296
3297        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3298            &mut self,
3299            cb: F,
3300        ) -> O {
3301            cb(&mut self.ip_socket_ctx)
3302        }
3303    }
3304
3305    /// Ip packet delivery for the [`FakeUdpCoreCtx`].
3306    impl<I: IpExt + IpDeviceStateIpExt + TestIpExt, D: FakeStrongDeviceId>
3307        IpTransportContext<I, FakeUdpBindingsCtx<D>, FakeUdpCoreCtx<D>> for UdpIpTransportContext
3308    {
3309        fn receive_icmp_error(
3310            _core_ctx: &mut FakeUdpCoreCtx<D>,
3311            _bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3312            _device: &D,
3313            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3314            _original_dst_ip: SpecifiedAddr<I::Addr>,
3315            _original_udp_packet: &[u8],
3316            _err: I::ErrorCode,
3317        ) {
3318            unimplemented!()
3319        }
3320
3321        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3322            core_ctx: &mut FakeUdpCoreCtx<D>,
3323            bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3324            device: &D,
3325            src_ip: I::RecvSrcAddr,
3326            dst_ip: SpecifiedAddr<I::Addr>,
3327            buffer: B,
3328            info: &LocalDeliveryPacketInfo<I, H>,
3329        ) -> Result<(), (B, TransportReceiveError)> {
3330            receive_ip_packet::<I, _, _, _, _>(
3331                core_ctx,
3332                bindings_ctx,
3333                device,
3334                src_ip,
3335                dst_ip,
3336                buffer,
3337                info,
3338            )
3339        }
3340    }
3341
3342    #[derive(Derivative)]
3343    #[derivative(Default(bound = ""))]
3344    pub(crate) struct FakeDualStackSocketState<D: StrongDeviceIdentifier> {
3345        v4: UdpSocketSet<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
3346        v6: UdpSocketSet<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
3347        udpv4_counters_with_socket: UdpCountersWithSocket<Ipv4>,
3348        udpv6_counters_with_socket: UdpCountersWithSocket<Ipv6>,
3349        udpv4_counters_without_socket: UdpCountersWithoutSocket<Ipv4>,
3350        udpv6_counters_without_socket: UdpCountersWithoutSocket<Ipv6>,
3351    }
3352
3353    impl<D: StrongDeviceIdentifier> FakeDualStackSocketState<D> {
3354        fn socket_set<I: IpExt>(&self) -> &UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3355            I::map_ip_out(self, |dual| &dual.v4, |dual| &dual.v6)
3356        }
3357
3358        fn socket_set_mut<I: IpExt>(
3359            &mut self,
3360        ) -> &mut UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3361            I::map_ip_out(self, |dual| &mut dual.v4, |dual| &mut dual.v6)
3362        }
3363
3364        fn udp_counters_with_socket<I: Ip>(&self) -> &UdpCountersWithSocket<I> {
3365            I::map_ip_out(
3366                self,
3367                |dual| &dual.udpv4_counters_with_socket,
3368                |dual| &dual.udpv6_counters_with_socket,
3369            )
3370        }
3371        fn udp_counters_without_socket<I: Ip>(&self) -> &UdpCountersWithoutSocket<I> {
3372            I::map_ip_out(
3373                self,
3374                |dual| &dual.udpv4_counters_without_socket,
3375                |dual| &dual.udpv6_counters_without_socket,
3376            )
3377        }
3378    }
3379    pub(crate) struct FakeUdpCoreCtx<D: FakeStrongDeviceId> {
3380        pub(crate) bound_sockets: FakeUdpBoundSocketsCtx<D>,
3381        // NB: socket sets are last in the struct so all the strong refs are
3382        // dropped before the primary refs contained herein.
3383        pub(crate) all_sockets: FakeDualStackSocketState<D>,
3384    }
3385
3386    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithSocket<I>> for FakeUdpCoreCtx<D> {
3387        fn counters(&self) -> &UdpCountersWithSocket<I> {
3388            &self.all_sockets.udp_counters_with_socket()
3389        }
3390    }
3391
3392    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithoutSocket<I>>
3393        for FakeUdpCoreCtx<D>
3394    {
3395        fn counters(&self) -> &UdpCountersWithoutSocket<I> {
3396            &self.all_sockets.udp_counters_without_socket()
3397        }
3398    }
3399
3400    impl<I: DualStackIpExt, D: FakeStrongDeviceId>
3401        ResourceCounterContext<
3402            UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3403            UdpCountersWithSocket<I>,
3404        > for FakeUdpCoreCtx<D>
3405    {
3406        fn per_resource_counters<'a>(
3407            &'a self,
3408            resource: &'a UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3409        ) -> &'a UdpCountersWithSocket<I> {
3410            resource.counters()
3411        }
3412    }
3413
3414    pub(crate) fn local_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3415        I::get_other_ip_address(1)
3416    }
3417
3418    pub(crate) fn remote_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3419        I::get_other_ip_address(2)
3420    }
3421
3422    pub(crate) trait BaseTestIpExt:
3423        netstack3_base::testutil::TestIpExt + IpExt + IpDeviceStateIpExt
3424    {
3425        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3426            DualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3427        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3428            NonDualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3429        fn into_recv_src_addr(addr: Self::Addr) -> Self::RecvSrcAddr;
3430    }
3431
3432    impl BaseTestIpExt for Ipv4 {
3433        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3434            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3435
3436        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3437            FakeUdpBoundSocketsCtx<D>;
3438
3439        fn into_recv_src_addr(addr: Ipv4Addr) -> Ipv4SourceAddr {
3440            Ipv4SourceAddr::new(addr).unwrap_or_else(|| panic!("{addr} is not a valid source addr"))
3441        }
3442    }
3443
3444    impl BaseTestIpExt for Ipv6 {
3445        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3446            FakeUdpBoundSocketsCtx<D>;
3447        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3448            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3449
3450        fn into_recv_src_addr(addr: Ipv6Addr) -> Ipv6SourceAddr {
3451            Ipv6SourceAddr::new(addr).unwrap_or_else(|| panic!("{addr} is not a valid source addr"))
3452        }
3453    }
3454
3455    pub(crate) trait TestIpExt: BaseTestIpExt<OtherVersion: BaseTestIpExt> {}
3456    impl<I: BaseTestIpExt<OtherVersion: BaseTestIpExt>> TestIpExt for I {}
3457}
3458
3459#[cfg(test)]
3460mod tests {
3461    use alloc::borrow::ToOwned;
3462    use alloc::vec;
3463    use core::convert::TryInto as _;
3464    use core::num::NonZeroU16;
3465
3466    use assert_matches::assert_matches;
3467    use ip_test_macro::ip_test;
3468    use itertools::Itertools as _;
3469    use net_declare::{net_ip_v4 as ip_v4, net_ip_v6};
3470    use net_types::ip::{IpAddr, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
3471    use net_types::{
3472        AddrAndZone, LinkLocalAddr, MulticastAddr, Scope as _, ScopeableAddress as _, ZonedAddr,
3473    };
3474    use netstack3_base::socket::{SocketIpAddrExt as _, StrictlyZonedAddr};
3475    use netstack3_base::sync::PrimaryRc;
3476    use netstack3_base::testutil::{
3477        FakeDeviceId, FakeReferencyDeviceId, FakeStrongDeviceId, FakeWeakDeviceId,
3478        MultipleDevicesId, TestIpExt as _, set_logger_for_test,
3479    };
3480    use netstack3_base::{
3481        CounterCollection, Mark, MarkDomain, RemoteAddressError, SendFrameErrorReason,
3482    };
3483    use netstack3_datagram::MulticastInterfaceSelector;
3484    use netstack3_hashmap::{HashMap, HashSet};
3485    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeDualStackIpSocketCtx};
3486    use netstack3_ip::testutil::{DualStackSendIpPacketMeta, FakeIpHeaderInfo};
3487    use netstack3_ip::{IpPacketDestination, ResolveRouteError, SendIpPacketMeta};
3488    use packet::{Buf, Serializer};
3489    use test_case::test_case;
3490
3491    use crate::internal::counters::testutil::{
3492        CounterExpectationsWithSocket, CounterExpectationsWithoutSocket,
3493    };
3494
3495    use super::testutils::{
3496        FakeUdpBindingsCtx, FakeUdpCoreCtx, FakeUdpCtx, ReceivedPacket, SocketReceived, TestIpExt,
3497        UdpFakeDeviceCoreCtx, UdpFakeDeviceCtx, local_ip, remote_ip,
3498    };
3499    use super::*;
3500
3501    /// Helper function to inject an UDP packet with the provided parameters.
3502    fn receive_udp_packet<
3503        I: TestIpExt,
3504        D: FakeStrongDeviceId,
3505        CC: DeviceIdContext<AnyDevice, DeviceId = D>,
3506    >(
3507        core_ctx: &mut CC,
3508        bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3509        device: D,
3510        meta: UdpPacketMeta<I>,
3511        body: &[u8],
3512    ) -> Result<(), TransportReceiveError>
3513    where
3514        UdpIpTransportContext: IpTransportContext<I, FakeUdpBindingsCtx<D>, CC>,
3515    {
3516        let UdpPacketMeta { src_ip, src_port, dst_ip, dst_port, dscp_and_ecn } = meta;
3517        let builder = UdpPacketBuilder::new(src_ip, dst_ip, src_port, dst_port);
3518
3519        let buffer = builder
3520            .wrap_body(Buf::new(body.to_owned(), ..))
3521            .serialize_vec_outer()
3522            .unwrap()
3523            .into_inner();
3524        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
3525            core_ctx,
3526            bindings_ctx,
3527            &device,
3528            I::into_recv_src_addr(src_ip),
3529            SpecifiedAddr::new(dst_ip).unwrap(),
3530            buffer,
3531            &LocalDeliveryPacketInfo {
3532                header_info: FakeIpHeaderInfo { dscp_and_ecn, ..Default::default() },
3533                ..Default::default()
3534            },
3535        )
3536        .map_err(|(_buffer, e)| e)
3537    }
3538
3539    const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(100).unwrap();
3540    const OTHER_LOCAL_PORT: NonZeroU16 = LOCAL_PORT.checked_add(1).unwrap();
3541    const REMOTE_PORT: NonZeroU16 = NonZeroU16::new(200).unwrap();
3542    const OTHER_REMOTE_PORT: NonZeroU16 = REMOTE_PORT.checked_add(1).unwrap();
3543
3544    fn conn_addr<I>(
3545        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3546    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3547    where
3548        I: TestIpExt,
3549    {
3550        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3551        let remote_ip = SocketIpAddr::try_from(remote_ip::<I>()).unwrap();
3552        ConnAddr {
3553            ip: ConnIpAddr {
3554                local: (local_ip, LOCAL_PORT),
3555                remote: (remote_ip, REMOTE_PORT.into()),
3556            },
3557            device,
3558        }
3559        .into()
3560    }
3561
3562    fn local_listener<I>(
3563        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3564    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3565    where
3566        I: TestIpExt,
3567    {
3568        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3569        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: Some(local_ip) }, device }
3570            .into()
3571    }
3572
3573    fn wildcard_listener<I>(
3574        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3575    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3576    where
3577        I: TestIpExt,
3578    {
3579        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: None }, device }.into()
3580    }
3581
3582    #[track_caller]
3583    fn assert_counters<
3584        'a,
3585        I: IpExt,
3586        D: WeakDeviceIdentifier,
3587        BT: UdpBindingsTypes,
3588        CC: UdpCounterContext<I, D, BT>,
3589    >(
3590        core_ctx: &CC,
3591        with_socket_expects: CounterExpectationsWithSocket,
3592        without_socket_expects: CounterExpectationsWithoutSocket,
3593        per_socket_expects: impl IntoIterator<
3594            Item = (&'a UdpSocketId<I, D, BT>, CounterExpectationsWithSocket),
3595        >,
3596    ) {
3597        assert_eq!(
3598            CounterContext::<UdpCountersWithSocket<I>>::counters(core_ctx).cast(),
3599            with_socket_expects
3600        );
3601        assert_eq!(
3602            CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).cast(),
3603            without_socket_expects
3604        );
3605        for (id, expects) in per_socket_expects.into_iter() {
3606            assert_eq!(core_ctx.per_resource_counters(id).cast(), expects);
3607        }
3608    }
3609
3610    #[ip_test(I)]
3611    #[test_case(conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))), [
3612            conn_addr(None), local_listener(Some(FakeWeakDeviceId(FakeDeviceId))), local_listener(None),
3613            wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)
3614        ]; "conn with device")]
3615    #[test_case(local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3616        [local_listener(None), wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)];
3617        "local listener with device")]
3618    #[test_case(wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), [wildcard_listener(None)];
3619        "wildcard listener with device")]
3620    #[test_case(conn_addr(None), [local_listener(None), wildcard_listener(None)]; "conn no device")]
3621    #[test_case(local_listener(None), [wildcard_listener(None)]; "local listener no device")]
3622    #[test_case(wildcard_listener(None), []; "wildcard listener no device")]
3623    fn test_udp_addr_vec_iter_shadows_conn<I: IpExt, D: WeakDeviceIdentifier, const N: usize>(
3624        addr: AddrVec<I, D, UdpAddrSpec>,
3625        expected_shadows: [AddrVec<I, D, UdpAddrSpec>; N],
3626    ) {
3627        assert_eq!(addr.iter_shadows().collect::<HashSet<_>>(), HashSet::from(expected_shadows));
3628    }
3629
3630    #[ip_test(I)]
3631    fn test_iter_receiving_addrs<I: TestIpExt>() {
3632        let addr = ConnIpAddr {
3633            local: (SocketIpAddr::try_from(local_ip::<I>()).unwrap(), LOCAL_PORT),
3634            remote: (SocketIpAddr::try_from(remote_ip::<I>()).unwrap(), REMOTE_PORT.into()),
3635        };
3636        assert_eq!(
3637            iter_receiving_addrs::<I, _>(addr, FakeWeakDeviceId(FakeDeviceId)).collect::<Vec<_>>(),
3638            vec![
3639                // A socket connected on exactly the receiving vector has precedence.
3640                conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))),
3641                // Connected takes precedence over listening with device match.
3642                conn_addr(None),
3643                local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3644                // Specific IP takes precedence over device match.
3645                local_listener(None),
3646                wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3647                // Fallback to least specific
3648                wildcard_listener(None)
3649            ]
3650        );
3651    }
3652
3653    /// Tests UDP listeners over different IP versions.
3654    ///
3655    /// Tests that a listener can be created, that the context receives packet
3656    /// notifications for that listener, and that we can send data using that
3657    /// listener.
3658    #[ip_test(I)]
3659    fn test_listen_udp<I: TestIpExt>() {
3660        set_logger_for_test();
3661        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3662        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3663        let local_ip = local_ip::<I>();
3664        let remote_ip = remote_ip::<I>();
3665        let socket = api.create();
3666        // Create a listener on the local port, bound to the local IP:
3667        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3668            .expect("listen_udp failed");
3669
3670        // Inject a packet and check that the context receives it:
3671        let body = [1, 2, 3, 4, 5];
3672        let (core_ctx, bindings_ctx) = api.contexts();
3673        let meta = UdpPacketMeta::<I> {
3674            src_ip: remote_ip.get(),
3675            src_port: Some(REMOTE_PORT),
3676            dst_ip: local_ip.get(),
3677            dst_port: LOCAL_PORT,
3678            dscp_and_ecn: DscpAndEcn::default(),
3679        };
3680        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body[..])
3681            .expect("receive udp packet should succeed");
3682
3683        assert_eq!(
3684            bindings_ctx.state.received::<I>(),
3685            &HashMap::from([(
3686                socket.downgrade(),
3687                SocketReceived {
3688                    packets: vec![ReceivedPacket { meta, body: body.into() }],
3689                    max_size: usize::MAX
3690                }
3691            )])
3692        );
3693
3694        // Send a packet providing a local ip:
3695        api.send_to(
3696            &socket,
3697            Some(ZonedAddr::Unzoned(remote_ip)),
3698            REMOTE_PORT.into(),
3699            Buf::new(body.to_vec(), ..),
3700        )
3701        .expect("send_to suceeded");
3702
3703        // And send a packet that doesn't:
3704        api.send_to(
3705            &socket,
3706            Some(ZonedAddr::Unzoned(remote_ip)),
3707            REMOTE_PORT.into(),
3708            Buf::new(body.to_vec(), ..),
3709        )
3710        .expect("send_to succeeded");
3711        let frames = api.core_ctx().bound_sockets.ip_socket_ctx.frames();
3712        assert_eq!(frames.len(), 2);
3713        let check_frame =
3714            |(meta, frame_body): &(DualStackSendIpPacketMeta<FakeDeviceId>, Vec<u8>)| {
3715                let SendIpPacketMeta {
3716                    device: _,
3717                    src_ip,
3718                    dst_ip,
3719                    destination,
3720                    proto,
3721                    ttl: _,
3722                    mtu: _,
3723                    dscp_and_ecn: _,
3724                } = meta.try_as::<I>().unwrap();
3725                assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
3726                assert_eq!(src_ip, &local_ip);
3727                assert_eq!(dst_ip, &remote_ip);
3728                assert_eq!(proto, &IpProto::Udp.into());
3729                let mut buf = &frame_body[..];
3730                let udp_packet =
3731                    UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
3732                        .expect("Parsed sent UDP packet");
3733                assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
3734                assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
3735                assert_eq!(udp_packet.body(), &body[..]);
3736            };
3737        check_frame(&frames[0]);
3738        check_frame(&frames[1]);
3739    }
3740
3741    #[ip_test(I)]
3742    fn test_receive_udp_queue_full<I: TestIpExt>() {
3743        set_logger_for_test();
3744        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3745        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3746        let local_ip = local_ip::<I>();
3747        let remote_ip = remote_ip::<I>();
3748        let socket = api.create();
3749
3750        // Create a listener on the local port, bound to the local IP:
3751        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3752            .expect("listen_udp failed");
3753
3754        let (core_ctx, bindings_ctx) = api.contexts();
3755        // Simulate a full RX queue.
3756        {
3757            let received =
3758                bindings_ctx.state.received_mut::<I>().entry(socket.downgrade()).or_default();
3759            received.max_size = 0;
3760        }
3761
3762        // Inject a packet.
3763        let body = [1, 2, 3, 4, 5];
3764        let meta = UdpPacketMeta::<I> {
3765            src_ip: remote_ip.get(),
3766            src_port: Some(REMOTE_PORT),
3767            dst_ip: local_ip.get(),
3768            dst_port: LOCAL_PORT,
3769            dscp_and_ecn: DscpAndEcn::default(),
3770        };
3771        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..])
3772            .expect("receive udp packet should succeed");
3773
3774        assert_counters(
3775            api.core_ctx(),
3776            CounterExpectationsWithSocket {
3777                rx_delivered: 1,
3778                rx_queue_full: 1,
3779                ..Default::default()
3780            },
3781            CounterExpectationsWithoutSocket { rx: 1, ..Default::default() },
3782            [(
3783                &socket,
3784                CounterExpectationsWithSocket {
3785                    rx_delivered: 1,
3786                    rx_queue_full: 1,
3787                    ..Default::default()
3788                },
3789            )],
3790        )
3791    }
3792
3793    /// Tests that UDP packets without a connection are dropped.
3794    ///
3795    /// Tests that receiving a UDP packet on a port over which there isn't a
3796    /// listener causes the packet to be dropped correctly.
3797    #[ip_test(I)]
3798    fn test_udp_drop<I: TestIpExt>() {
3799        set_logger_for_test();
3800        let UdpFakeDeviceCtx { mut core_ctx, mut bindings_ctx } =
3801            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3802        let local_ip = local_ip::<I>();
3803        let remote_ip = remote_ip::<I>();
3804
3805        let meta = UdpPacketMeta::<I> {
3806            src_ip: remote_ip.get(),
3807            src_port: Some(REMOTE_PORT),
3808            dst_ip: local_ip.get(),
3809            dst_port: LOCAL_PORT,
3810            dscp_and_ecn: DscpAndEcn::default(),
3811        };
3812        let body = [1, 2, 3, 4, 5];
3813        assert_matches!(
3814            receive_udp_packet(&mut core_ctx, &mut bindings_ctx, FakeDeviceId, meta, &body[..]),
3815            Err(TransportReceiveError::PortUnreachable)
3816        );
3817        assert_eq!(&bindings_ctx.state.socket_data::<I>(), &HashMap::new());
3818    }
3819
3820    /// Tests that UDP connections can be created and data can be transmitted
3821    /// over it.
3822    ///
3823    /// Only tests with specified local port and address bounds.
3824    #[ip_test(I)]
3825    fn test_udp_conn_basic<I: TestIpExt>() {
3826        set_logger_for_test();
3827        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3828        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3829        let local_ip = local_ip::<I>();
3830        let remote_ip = remote_ip::<I>();
3831        let socket = api.create();
3832        // Create a UDP connection with a specified local port and local IP.
3833        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3834            .expect("listen_udp failed");
3835        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3836            .expect("connect failed");
3837
3838        // Inject a UDP packet and see if we receive it on the context.
3839        let meta = UdpPacketMeta::<I> {
3840            src_ip: remote_ip.get(),
3841            src_port: Some(REMOTE_PORT),
3842            dst_ip: local_ip.get(),
3843            dst_port: LOCAL_PORT,
3844            dscp_and_ecn: DscpAndEcn::default(),
3845        };
3846        let body = [1, 2, 3, 4, 5];
3847        let (core_ctx, bindings_ctx) = api.contexts();
3848        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..])
3849            .expect("receive udp packet should succeed");
3850
3851        assert_eq!(
3852            bindings_ctx.state.socket_data(),
3853            HashMap::from([(socket.downgrade(), vec![&body[..]])])
3854        );
3855
3856        // Now try to send something over this new connection.
3857        api.send(&socket, Buf::new(body.to_vec(), ..)).expect("send_udp_conn returned an error");
3858
3859        let (meta, frame_body) =
3860            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
3861        // Check first frame.
3862        let SendIpPacketMeta {
3863            device: _,
3864            src_ip,
3865            dst_ip,
3866            destination,
3867            proto,
3868            ttl: _,
3869            mtu: _,
3870            dscp_and_ecn: _,
3871        } = meta.try_as::<I>().unwrap();
3872        assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
3873        assert_eq!(src_ip, &local_ip);
3874        assert_eq!(dst_ip, &remote_ip);
3875        assert_eq!(proto, &IpProto::Udp.into());
3876        let mut buf = &frame_body[..];
3877        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
3878            .expect("Parsed sent UDP packet");
3879        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
3880        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
3881        assert_eq!(udp_packet.body(), &body[..]);
3882
3883        let expects_with_socket =
3884            || CounterExpectationsWithSocket { rx_delivered: 1, tx: 1, ..Default::default() };
3885        assert_counters(
3886            api.core_ctx(),
3887            expects_with_socket(),
3888            CounterExpectationsWithoutSocket { rx: 1, ..Default::default() },
3889            [(&socket, expects_with_socket())],
3890        )
3891    }
3892
3893    /// Tests that UDP connections fail with an appropriate error for
3894    /// non-routable remote addresses.
3895    #[ip_test(I)]
3896    fn test_udp_conn_unroutable<I: TestIpExt>() {
3897        set_logger_for_test();
3898        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3899        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3900        // Set fake context callback to treat all addresses as unroutable.
3901        let remote_ip = I::get_other_ip_address(127);
3902        // Create a UDP connection with a specified local port and local IP.
3903        let unbound = api.create();
3904        let conn_err = api
3905            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3906            .unwrap_err();
3907
3908        assert_eq!(conn_err, ConnectError::Ip(ResolveRouteError::Unreachable.into()));
3909    }
3910
3911    /// Tests that UDP listener creation fails with an appropriate error when
3912    /// local address is non-local.
3913    #[ip_test(I)]
3914    fn test_udp_conn_cannot_bind<I: TestIpExt>() {
3915        set_logger_for_test();
3916        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3917        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3918
3919        // Use remote address to trigger IpSockCreationError::LocalAddrNotAssigned.
3920        let remote_ip = remote_ip::<I>();
3921        // Create a UDP listener with a specified local port and local ip:
3922        let unbound = api.create();
3923        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT));
3924
3925        assert_eq!(result, Err(Either::Right(LocalAddressError::CannotBindToAddress)));
3926    }
3927
3928    #[test]
3929    fn test_udp_conn_picks_link_local_source_address() {
3930        set_logger_for_test();
3931        // When the remote address has global scope but the source address
3932        // is link-local, make sure that the socket implicitly has its bound
3933        // device set.
3934        set_logger_for_test();
3935        let local_ip = SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap();
3936        let remote_ip = SpecifiedAddr::new(net_ip_v6!("1:2:3:4::")).unwrap();
3937        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
3938            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
3939        );
3940        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
3941        let socket = api.create();
3942        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3943            .expect("can connect");
3944
3945        let info = api.get_info(&socket);
3946        let (conn_local_ip, conn_remote_ip) = assert_matches!(
3947            info,
3948            SocketInfo::Connected(datagram::ConnInfo {
3949                local_ip: conn_local_ip,
3950                remote_ip: conn_remote_ip,
3951                local_identifier: _,
3952                remote_identifier: _,
3953            }) => (conn_local_ip, conn_remote_ip)
3954        );
3955        assert_eq!(
3956            conn_local_ip,
3957            StrictlyZonedAddr::new_with_zone(local_ip, || FakeWeakDeviceId(FakeDeviceId)),
3958        );
3959        assert_eq!(conn_remote_ip, StrictlyZonedAddr::new_unzoned_or_panic(remote_ip));
3960
3961        // Double-check that the bound device can't be changed after being set
3962        // implicitly.
3963        assert_eq!(
3964            api.set_device(&socket, None),
3965            Err(SocketError::Local(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)))
3966        );
3967    }
3968
3969    #[ip_test(I)]
3970    #[test_case(
3971        true,
3972        Err(IpSockCreationError::Route(ResolveRouteError::Unreachable).into()); "remove device")]
3973    #[test_case(false, Ok(()); "dont remove device")]
3974    fn test_udp_conn_device_removed<I: TestIpExt>(
3975        remove_device: bool,
3976        expected: Result<(), ConnectError>,
3977    ) {
3978        set_logger_for_test();
3979        let device = FakeReferencyDeviceId::default();
3980        let mut ctx =
3981            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
3982        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3983
3984        let unbound = api.create();
3985        api.set_device(&unbound, Some(&device)).unwrap();
3986
3987        if remove_device {
3988            device.mark_removed();
3989        }
3990
3991        let remote_ip = remote_ip::<I>();
3992        assert_eq!(
3993            api.connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
3994            expected,
3995        );
3996    }
3997
3998    /// Tests that UDP connections fail with an appropriate error when local
3999    /// ports are exhausted.
4000    #[ip_test(I)]
4001    fn test_udp_conn_exhausted<I: TestIpExt>() {
4002        // NB: We don't enable logging for this test because it's very spammy.
4003        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4004        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4005
4006        let local_ip = local_ip::<I>();
4007        // Exhaust local ports to trigger FailedToAllocateLocalPort error.
4008        for port_num in FakePortAlloc::<I>::EPHEMERAL_RANGE {
4009            let socket = api.create();
4010            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), NonZeroU16::new(port_num))
4011                .unwrap();
4012        }
4013
4014        let remote_ip = remote_ip::<I>();
4015        let unbound = api.create();
4016        let conn_err = api
4017            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4018            .unwrap_err();
4019
4020        assert_eq!(conn_err, ConnectError::CouldNotAllocateLocalPort);
4021    }
4022
4023    #[ip_test(I)]
4024    fn test_connect_success<I: TestIpExt>() {
4025        set_logger_for_test();
4026        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4027        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4028
4029        let local_ip = local_ip::<I>();
4030        let remote_ip = remote_ip::<I>();
4031        let multicast_addr = I::get_multicast_addr(3);
4032        let socket = api.create();
4033        let sharing_domain = SharingDomain::new(1);
4034
4035        // Set some properties on the socket that should be preserved.
4036        api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4037            .expect("is unbound");
4038        api.set_multicast_membership(
4039            &socket,
4040            multicast_addr,
4041            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4042            true,
4043        )
4044        .expect("join multicast group should succeed");
4045
4046        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4047            .expect("Initial call to listen_udp was expected to succeed");
4048
4049        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4050            .expect("connect should succeed");
4051
4052        // Check that socket options set on the listener are propagated to the
4053        // connected socket.
4054        assert!(api.get_posix_reuse_port(&socket));
4055        assert_eq!(
4056            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
4057            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
4058        );
4059        assert_eq!(
4060            api.set_multicast_membership(
4061                &socket,
4062                multicast_addr,
4063                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4064                true
4065            ),
4066            Err(SetMulticastMembershipError::GroupAlreadyJoined)
4067        );
4068    }
4069
4070    #[ip_test(I)]
4071    fn test_connect_fails<I: TestIpExt>() {
4072        set_logger_for_test();
4073        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4074        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4075        let local_ip = local_ip::<I>();
4076        let remote_ip = I::get_other_ip_address(127);
4077        let multicast_addr = I::get_multicast_addr(3);
4078        let socket = api.create();
4079
4080        // Set some properties on the socket that should be preserved.
4081        let sharing_domain = SharingDomain::new(1);
4082        api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4083            .expect("is unbound");
4084        api.set_multicast_membership(
4085            &socket,
4086            multicast_addr,
4087            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4088            true,
4089        )
4090        .expect("join multicast group should succeed");
4091
4092        // Create a UDP connection with a specified local port and local IP.
4093        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4094            .expect("Initial call to listen_udp was expected to succeed");
4095
4096        assert_matches!(
4097            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
4098            Err(ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable)))
4099        );
4100
4101        // Check that the listener was unchanged by the failed connection.
4102        assert!(api.get_posix_reuse_port(&socket));
4103        assert_eq!(
4104            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
4105            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
4106        );
4107        assert_eq!(
4108            api.set_multicast_membership(
4109                &socket,
4110                multicast_addr,
4111                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
4112                true
4113            ),
4114            Err(SetMulticastMembershipError::GroupAlreadyJoined)
4115        );
4116    }
4117
4118    #[ip_test(I)]
4119    fn test_reconnect_udp_conn_success<I: TestIpExt>() {
4120        set_logger_for_test();
4121
4122        let local_ip = local_ip::<I>();
4123        let remote_ip = remote_ip::<I>();
4124        let other_remote_ip = I::get_other_ip_address(3);
4125
4126        let mut ctx =
4127            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4128                vec![local_ip],
4129                vec![remote_ip, other_remote_ip],
4130            ));
4131        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4132
4133        let socket = api.create();
4134        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4135            .expect("listen should succeed");
4136
4137        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4138            .expect("connect was expected to succeed");
4139
4140        api.connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
4141            .expect("connect should succeed");
4142        assert_eq!(
4143            api.get_info(&socket),
4144            SocketInfo::Connected(datagram::ConnInfo {
4145                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
4146                local_identifier: LOCAL_PORT,
4147                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(other_remote_ip),
4148                remote_identifier: OTHER_REMOTE_PORT.into(),
4149            })
4150        );
4151    }
4152
4153    #[ip_test(I)]
4154    fn test_reconnect_udp_conn_fails<I: TestIpExt>() {
4155        set_logger_for_test();
4156        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4157        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4158        let local_ip = local_ip::<I>();
4159        let remote_ip = remote_ip::<I>();
4160        let other_remote_ip = I::get_other_ip_address(3);
4161
4162        let socket = api.create();
4163        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4164            .expect("listen should succeed");
4165
4166        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4167            .expect("connect was expected to succeed");
4168        let error = api
4169            .connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
4170            .expect_err("connect should fail");
4171        assert_matches!(
4172            error,
4173            ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable))
4174        );
4175
4176        assert_eq!(
4177            api.get_info(&socket),
4178            SocketInfo::Connected(datagram::ConnInfo {
4179                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
4180                local_identifier: LOCAL_PORT,
4181                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip),
4182                remote_identifier: REMOTE_PORT.into()
4183            })
4184        );
4185    }
4186
4187    #[ip_test(I)]
4188    fn test_send_to<I: TestIpExt>() {
4189        set_logger_for_test();
4190
4191        let local_ip = local_ip::<I>();
4192        let remote_ip = remote_ip::<I>();
4193        let other_remote_ip = I::get_other_ip_address(3);
4194
4195        let mut ctx =
4196            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4197                vec![local_ip],
4198                vec![remote_ip, other_remote_ip],
4199            ));
4200        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4201
4202        let socket = api.create();
4203        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
4204            .expect("listen should succeed");
4205        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4206            .expect("connect should succeed");
4207
4208        let body = [1, 2, 3, 4, 5];
4209        // Try to send something with send_to
4210        api.send_to(
4211            &socket,
4212            Some(ZonedAddr::Unzoned(other_remote_ip)),
4213            REMOTE_PORT.into(),
4214            Buf::new(body.to_vec(), ..),
4215        )
4216        .expect("send_to failed");
4217
4218        // The socket should not have been affected.
4219        let info = api.get_info(&socket);
4220        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
4221        assert_eq!(info.local_ip.into_inner(), ZonedAddr::Unzoned(local_ip));
4222        assert_eq!(info.remote_ip.into_inner(), ZonedAddr::Unzoned(remote_ip));
4223        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
4224
4225        // Check first frame.
4226        let (meta, frame_body) =
4227            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
4228        let SendIpPacketMeta {
4229            device: _,
4230            src_ip,
4231            dst_ip,
4232            destination,
4233            proto,
4234            ttl: _,
4235            mtu: _,
4236            dscp_and_ecn: _,
4237        } = meta.try_as::<I>().unwrap();
4238
4239        assert_eq!(destination, &IpPacketDestination::Neighbor(other_remote_ip));
4240        assert_eq!(src_ip, &local_ip);
4241        assert_eq!(dst_ip, &other_remote_ip);
4242        assert_eq!(proto, &I::Proto::from(IpProto::Udp));
4243        let mut buf = &frame_body[..];
4244        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
4245            .expect("Parsed sent UDP packet");
4246        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
4247        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
4248        assert_eq!(udp_packet.body(), &body[..]);
4249    }
4250
4251    /// Tests that UDP send failures are propagated as errors.
4252    ///
4253    /// Only tests with specified local port and address bounds.
4254    #[ip_test(I)]
4255    fn test_send_udp_conn_failure<I: TestIpExt>() {
4256        set_logger_for_test();
4257        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4258        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4259        let remote_ip = remote_ip::<I>();
4260        // Create a UDP connection with a specified local port and local IP.
4261        let socket = api.create();
4262        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4263            .expect("connect failed");
4264
4265        // Instruct the fake frame context to throw errors.
4266        api.core_ctx().bound_sockets.ip_socket_ctx.frames.set_should_error_for_frame(
4267            |_frame_meta| Some(SendFrameErrorReason::SizeConstraintsViolation),
4268        );
4269
4270        // Now try to send something over this new connection:
4271        let send_err = api.send(&socket, Buf::new(Vec::new(), ..)).unwrap_err();
4272        assert_eq!(send_err, Either::Left(SendError::IpSock(IpSockSendError::Mtu)));
4273
4274        let expects_with_socket =
4275            || CounterExpectationsWithSocket { tx: 1, tx_error: 1, ..Default::default() };
4276        assert_counters(
4277            api.core_ctx(),
4278            expects_with_socket(),
4279            Default::default(),
4280            [(&socket, expects_with_socket())],
4281        )
4282    }
4283
4284    #[ip_test(I)]
4285    fn test_send_udp_conn_device_removed<I: TestIpExt>() {
4286        set_logger_for_test();
4287        let device = FakeReferencyDeviceId::default();
4288        let mut ctx =
4289            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
4290        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4291        let remote_ip = remote_ip::<I>();
4292        let socket = api.create();
4293        api.set_device(&socket, Some(&device)).unwrap();
4294        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4295            .expect("connect failed");
4296
4297        for (device_removed, expected_res) in [
4298            (false, Ok(())),
4299            (
4300                true,
4301                Err(Either::Left(SendError::IpSock(IpSockSendError::Unroutable(
4302                    ResolveRouteError::Unreachable,
4303                )))),
4304            ),
4305        ] {
4306            if device_removed {
4307                device.mark_removed();
4308            }
4309
4310            assert_eq!(api.send(&socket, Buf::new(Vec::new(), ..)), expected_res)
4311        }
4312    }
4313
4314    #[ip_test(I)]
4315    #[test_case(false, ShutdownType::Send; "shutdown send then send")]
4316    #[test_case(false, ShutdownType::SendAndReceive; "shutdown both then send")]
4317    #[test_case(true, ShutdownType::Send; "shutdown send then sendto")]
4318    #[test_case(true, ShutdownType::SendAndReceive; "shutdown both then sendto")]
4319    fn test_send_udp_after_shutdown<I: TestIpExt>(send_to: bool, shutdown: ShutdownType) {
4320        set_logger_for_test();
4321
4322        #[derive(Debug)]
4323        struct NotWriteableError;
4324
4325        let send = |remote_ip, api: &mut UdpApi<_, _>, id| -> Result<(), NotWriteableError> {
4326            match remote_ip {
4327                Some(remote_ip) => api.send_to(
4328                    id,
4329                    Some(remote_ip),
4330                    REMOTE_PORT.into(),
4331                    Buf::new(Vec::new(), ..),
4332                )
4333                .map_err(
4334                    |e| assert_matches!(e, Either::Right(SendToError::NotWriteable) => NotWriteableError)
4335                ),
4336                None => api.send(
4337                    id,
4338                    Buf::new(Vec::new(), ..),
4339                )
4340                .map_err(|e| assert_matches!(e, Either::Left(SendError::NotWriteable) => NotWriteableError)),
4341            }
4342        };
4343
4344        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4345        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4346
4347        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
4348        let send_to_ip = send_to.then_some(remote_ip);
4349
4350        let socket = api.create();
4351        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
4352
4353        send(send_to_ip, &mut api, &socket).expect("can send");
4354        api.shutdown(&socket, shutdown).expect("is connected");
4355
4356        assert_matches!(send(send_to_ip, &mut api, &socket), Err(NotWriteableError));
4357    }
4358
4359    #[ip_test(I)]
4360    #[test_case(ShutdownType::Receive; "receive")]
4361    #[test_case(ShutdownType::SendAndReceive; "both")]
4362    fn test_marked_for_receive_shutdown<I: TestIpExt>(which: ShutdownType) {
4363        set_logger_for_test();
4364
4365        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4366        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4367
4368        let socket = api.create();
4369        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
4370            .expect("can bind");
4371        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
4372            .expect("can connect");
4373
4374        // Receive once, then set the shutdown flag, then receive again and
4375        // check that it doesn't get to the socket.
4376
4377        let meta = UdpPacketMeta::<I> {
4378            src_ip: remote_ip::<I>().get(),
4379            src_port: Some(REMOTE_PORT),
4380            dst_ip: local_ip::<I>().get(),
4381            dst_port: LOCAL_PORT,
4382            dscp_and_ecn: DscpAndEcn::default(),
4383        };
4384        let packet = [1, 1, 1, 1];
4385        let (core_ctx, bindings_ctx) = api.contexts();
4386
4387        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &packet[..])
4388            .expect("receive udp packet should succeed");
4389
4390        assert_eq!(
4391            bindings_ctx.state.socket_data(),
4392            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4393        );
4394        api.shutdown(&socket, which).expect("is connected");
4395        let (core_ctx, bindings_ctx) = api.contexts();
4396        assert_matches!(
4397            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &packet[..]),
4398            Err(TransportReceiveError::PortUnreachable)
4399        );
4400        assert_eq!(
4401            bindings_ctx.state.socket_data(),
4402            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4403        );
4404
4405        // Calling shutdown for the send direction doesn't change anything.
4406        api.shutdown(&socket, ShutdownType::Send).expect("is connected");
4407        let (core_ctx, bindings_ctx) = api.contexts();
4408        assert_matches!(
4409            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &packet[..]),
4410            Err(TransportReceiveError::PortUnreachable)
4411        );
4412        assert_eq!(
4413            bindings_ctx.state.socket_data(),
4414            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4415        );
4416    }
4417
4418    /// Tests that if we have multiple listeners and connections, demuxing the
4419    /// flows is performed correctly.
4420    #[ip_test(I)]
4421    fn test_udp_demux<I: TestIpExt>() {
4422        set_logger_for_test();
4423        let local_ip = local_ip::<I>();
4424        let remote_ip_a = I::get_other_ip_address(70);
4425        let remote_ip_b = I::get_other_ip_address(72);
4426        let local_port_a = NonZeroU16::new(100).unwrap();
4427        let local_port_b = NonZeroU16::new(101).unwrap();
4428        let local_port_c = NonZeroU16::new(102).unwrap();
4429        let local_port_d = NonZeroU16::new(103).unwrap();
4430
4431        let mut ctx =
4432            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4433                vec![local_ip],
4434                vec![remote_ip_a, remote_ip_b],
4435            ));
4436        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4437
4438        let sharing_domain = SharingDomain::new(1);
4439
4440        // Create some UDP connections and listeners:
4441        // conn2 has just a remote addr different than conn1, which requires
4442        // allowing them to share the local port.
4443        let [conn1, conn2] = [remote_ip_a, remote_ip_b].map(|remote_ip| {
4444            let socket = api.create();
4445            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4446                .expect("is unbound");
4447            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_d))
4448                .expect("listen_udp failed");
4449            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4450                .expect("connect failed");
4451            socket
4452        });
4453        let list1 = api.create();
4454        api.listen(&list1, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_a))
4455            .expect("listen_udp failed");
4456        let list2 = api.create();
4457        api.listen(&list2, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_b))
4458            .expect("listen_udp failed");
4459        let wildcard_list = api.create();
4460        api.listen(&wildcard_list, None, Some(local_port_c)).expect("listen_udp failed");
4461
4462        let mut expectations = HashMap::<WeakUdpSocketId<I, _, _>, SocketReceived<I>>::new();
4463        // Now inject UDP packets that each of the created connections should
4464        // receive.
4465        let meta = UdpPacketMeta {
4466            src_ip: remote_ip_a.get(),
4467            src_port: Some(REMOTE_PORT),
4468            dst_ip: local_ip.get(),
4469            dst_port: local_port_d,
4470            dscp_and_ecn: DscpAndEcn::default(),
4471        };
4472        let body_conn1 = [1, 1, 1, 1];
4473        let (core_ctx, bindings_ctx) = api.contexts();
4474        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_conn1[..])
4475            .expect("receive udp packet should succeed");
4476        expectations
4477            .entry(conn1.downgrade())
4478            .or_default()
4479            .packets
4480            .push(ReceivedPacket { meta: meta, body: body_conn1.into() });
4481        assert_eq!(bindings_ctx.state.received(), &expectations);
4482
4483        let meta = UdpPacketMeta {
4484            src_ip: remote_ip_b.get(),
4485            src_port: Some(REMOTE_PORT),
4486            dst_ip: local_ip.get(),
4487            dst_port: local_port_d,
4488            dscp_and_ecn: DscpAndEcn::default(),
4489        };
4490        let body_conn2 = [2, 2, 2, 2];
4491        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_conn2[..])
4492            .expect("receive udp packet should succeed");
4493        expectations
4494            .entry(conn2.downgrade())
4495            .or_default()
4496            .packets
4497            .push(ReceivedPacket { meta: meta, body: body_conn2.into() });
4498        assert_eq!(bindings_ctx.state.received(), &expectations);
4499
4500        let meta = UdpPacketMeta {
4501            src_ip: remote_ip_a.get(),
4502            src_port: Some(REMOTE_PORT),
4503            dst_ip: local_ip.get(),
4504            dst_port: local_port_a,
4505            dscp_and_ecn: DscpAndEcn::default(),
4506        };
4507        let body_list1 = [3, 3, 3, 3];
4508        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_list1[..])
4509            .expect("receive udp packet should succeed");
4510        expectations
4511            .entry(list1.downgrade())
4512            .or_default()
4513            .packets
4514            .push(ReceivedPacket { meta: meta, body: body_list1.into() });
4515        assert_eq!(bindings_ctx.state.received(), &expectations);
4516
4517        let meta = UdpPacketMeta {
4518            src_ip: remote_ip_a.get(),
4519            src_port: Some(REMOTE_PORT),
4520            dst_ip: local_ip.get(),
4521            dst_port: local_port_b,
4522            dscp_and_ecn: DscpAndEcn::default(),
4523        };
4524        let body_list2 = [4, 4, 4, 4];
4525        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_list2[..])
4526            .expect("receive udp packet should succeed");
4527        expectations
4528            .entry(list2.downgrade())
4529            .or_default()
4530            .packets
4531            .push(ReceivedPacket { meta: meta, body: body_list2.into() });
4532        assert_eq!(bindings_ctx.state.received(), &expectations);
4533
4534        let meta = UdpPacketMeta {
4535            src_ip: remote_ip_a.get(),
4536            src_port: Some(REMOTE_PORT),
4537            dst_ip: local_ip.get(),
4538            dst_port: local_port_c,
4539            dscp_and_ecn: DscpAndEcn::default(),
4540        };
4541        let body_wildcard_list = [5, 5, 5, 5];
4542        receive_udp_packet(
4543            core_ctx,
4544            bindings_ctx,
4545            FakeDeviceId,
4546            meta.clone(),
4547            &body_wildcard_list[..],
4548        )
4549        .expect("receive udp packet should succeed");
4550        expectations
4551            .entry(wildcard_list.downgrade())
4552            .or_default()
4553            .packets
4554            .push(ReceivedPacket { meta: meta, body: body_wildcard_list.into() });
4555        assert_eq!(bindings_ctx.state.received(), &expectations);
4556    }
4557
4558    /// Tests UDP wildcard listeners for different IP versions.
4559    #[ip_test(I)]
4560    fn test_wildcard_listeners<I: TestIpExt>() {
4561        set_logger_for_test();
4562        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4563        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4564        let local_ip_a = I::get_other_ip_address(1);
4565        let local_ip_b = I::get_other_ip_address(2);
4566        let remote_ip_a = I::get_other_ip_address(70);
4567        let remote_ip_b = I::get_other_ip_address(72);
4568        let listener = api.create();
4569        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4570
4571        let body = [1, 2, 3, 4, 5];
4572        let (core_ctx, bindings_ctx) = api.contexts();
4573        let meta_1 = UdpPacketMeta {
4574            src_ip: remote_ip_a.get(),
4575            src_port: Some(REMOTE_PORT),
4576            dst_ip: local_ip_a.get(),
4577            dst_port: LOCAL_PORT,
4578            dscp_and_ecn: DscpAndEcn::default(),
4579        };
4580        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta_1.clone(), &body[..])
4581            .expect("receive udp packet should succeed");
4582
4583        // Receive into a different local IP.
4584        let meta_2 = UdpPacketMeta {
4585            src_ip: remote_ip_b.get(),
4586            src_port: Some(REMOTE_PORT),
4587            dst_ip: local_ip_b.get(),
4588            dst_port: LOCAL_PORT,
4589            dscp_and_ecn: DscpAndEcn::default(),
4590        };
4591        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta_2.clone(), &body[..])
4592            .expect("receive udp packet should succeed");
4593
4594        // Check that we received both packets for the listener.
4595        assert_eq!(
4596            bindings_ctx.state.received::<I>(),
4597            &HashMap::from([(
4598                listener.downgrade(),
4599                SocketReceived {
4600                    packets: vec![
4601                        ReceivedPacket { meta: meta_1, body: body.into() },
4602                        ReceivedPacket { meta: meta_2, body: body.into() }
4603                    ],
4604                    max_size: usize::MAX,
4605                }
4606            )])
4607        );
4608    }
4609
4610    #[ip_test(I)]
4611    fn test_receive_source_port_zero_on_listener<I: TestIpExt>() {
4612        set_logger_for_test();
4613        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4614        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4615        let listener = api.create();
4616        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4617
4618        let body = [];
4619        let meta = UdpPacketMeta::<I> {
4620            src_ip: I::TEST_ADDRS.remote_ip.get(),
4621            src_port: None,
4622            dst_ip: I::TEST_ADDRS.local_ip.get(),
4623            dst_port: LOCAL_PORT,
4624            dscp_and_ecn: DscpAndEcn::default(),
4625        };
4626
4627        let (core_ctx, bindings_ctx) = api.contexts();
4628        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body[..])
4629            .expect("receive udp packet should succeed");
4630        // Check that we received both packets for the listener.
4631        assert_eq!(
4632            bindings_ctx.state.received(),
4633            &HashMap::from([(
4634                listener.downgrade(),
4635                SocketReceived {
4636                    packets: vec![ReceivedPacket { meta, body: vec![] }],
4637                    max_size: usize::MAX
4638                }
4639            )])
4640        );
4641    }
4642
4643    #[ip_test(I)]
4644    fn test_receive_source_addr_unspecified_on_listener<I: TestIpExt>() {
4645        set_logger_for_test();
4646        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4647        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4648        let listener = api.create();
4649        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4650
4651        let meta = UdpPacketMeta::<I> {
4652            src_ip: I::UNSPECIFIED_ADDRESS,
4653            src_port: Some(REMOTE_PORT),
4654            dst_ip: I::TEST_ADDRS.local_ip.get(),
4655            dst_port: LOCAL_PORT,
4656            dscp_and_ecn: DscpAndEcn::default(),
4657        };
4658        let body = [];
4659        let (core_ctx, bindings_ctx) = api.contexts();
4660        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..])
4661            .expect("receive udp packet should succeed");
4662        // Check that we received the packet on the listener.
4663        assert_eq!(
4664            bindings_ctx.state.socket_data(),
4665            HashMap::from([(listener.downgrade(), vec![&body[..]])])
4666        );
4667    }
4668
4669    #[ip_test(I)]
4670    #[test_case(NonZeroU16::new(u16::MAX).unwrap(), Ok(NonZeroU16::new(u16::MAX).unwrap()); "ephemeral available")]
4671    #[test_case(NonZeroU16::new(100).unwrap(), Err(LocalAddressError::FailedToAllocateLocalPort);
4672        "no ephemeral available")]
4673    fn test_bind_picked_port_all_others_taken<I: TestIpExt>(
4674        available_port: NonZeroU16,
4675        expected_result: Result<NonZeroU16, LocalAddressError>,
4676    ) {
4677        // NB: We don't enable logging for this test because it's very spammy.
4678        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4679        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4680
4681        for port in 1..=u16::MAX {
4682            let port = NonZeroU16::new(port).unwrap();
4683            if port == available_port {
4684                continue;
4685            }
4686            let unbound = api.create();
4687            api.listen(&unbound, None, Some(port)).expect("uncontested bind");
4688        }
4689
4690        // Now that all but the LOCAL_PORT are occupied, ask the stack to
4691        // select a port.
4692        let socket = api.create();
4693        let result = api
4694            .listen(&socket, None, None)
4695            .map(|()| {
4696                let info = api.get_info(&socket);
4697                assert_matches!(info, SocketInfo::Listener(info) => info.local_identifier)
4698            })
4699            .map_err(Either::unwrap_right);
4700        assert_eq!(result, expected_result);
4701    }
4702
4703    #[ip_test(I)]
4704    fn test_receive_multicast_packet<I: TestIpExt>() {
4705        set_logger_for_test();
4706        let local_ip = local_ip::<I>();
4707        let remote_ip = I::get_other_ip_address(70);
4708        let multicast_addr = I::get_multicast_addr(0);
4709        let multicast_addr_other = I::get_multicast_addr(1);
4710
4711        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
4712            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
4713        );
4714        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4715
4716        let sharing_domain = SharingDomain::new(1);
4717
4718        // Create 3 sockets: one listener for all IPs, two listeners on the same
4719        // local address.
4720        let any_listener = {
4721            let socket = api.create();
4722            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4723                .expect("is unbound");
4724            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4725            socket
4726        };
4727
4728        let specific_listeners = [(); 2].map(|()| {
4729            let socket = api.create();
4730            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
4731                .expect("is unbound");
4732            api.listen(
4733                &socket,
4734                Some(ZonedAddr::Unzoned(multicast_addr.into_specified())),
4735                Some(LOCAL_PORT),
4736            )
4737            .expect("listen_udp failed");
4738            socket
4739        });
4740
4741        let (core_ctx, bindings_ctx) = api.contexts();
4742        let mut receive_packet = |body, local_ip: MulticastAddr<I::Addr>| {
4743            let meta = UdpPacketMeta::<I> {
4744                src_ip: remote_ip.get(),
4745                src_port: Some(REMOTE_PORT),
4746                dst_ip: local_ip.get(),
4747                dst_port: LOCAL_PORT,
4748                dscp_and_ecn: DscpAndEcn::default(),
4749            };
4750            let body = [body];
4751            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body)
4752                .expect("receive udp packet should succeed")
4753        };
4754
4755        // These packets should be received by all listeners.
4756        receive_packet(1, multicast_addr);
4757        receive_packet(2, multicast_addr);
4758
4759        // This packet should be received only by the all-IPs listener.
4760        receive_packet(3, multicast_addr_other);
4761
4762        assert_eq!(
4763            bindings_ctx.state.socket_data(),
4764            HashMap::from([
4765                (specific_listeners[0].downgrade(), vec![[1].as_slice(), &[2]]),
4766                (specific_listeners[1].downgrade(), vec![&[1], &[2]]),
4767                (any_listener.downgrade(), vec![&[1], &[2], &[3]]),
4768            ]),
4769        );
4770
4771        assert_counters(
4772            api.core_ctx(),
4773            CounterExpectationsWithSocket { rx_delivered: 7, ..Default::default() },
4774            CounterExpectationsWithoutSocket { rx: 3, ..Default::default() },
4775            [
4776                (
4777                    &any_listener,
4778                    CounterExpectationsWithSocket { rx_delivered: 3, ..Default::default() },
4779                ),
4780                (
4781                    &specific_listeners[0],
4782                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
4783                ),
4784                (
4785                    &specific_listeners[1],
4786                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
4787                ),
4788            ],
4789        )
4790    }
4791
4792    type UdpMultipleDevicesCtx = FakeUdpCtx<MultipleDevicesId>;
4793    type UdpMultipleDevicesCoreCtx = FakeUdpCoreCtx<MultipleDevicesId>;
4794    type UdpMultipleDevicesBindingsCtx = FakeUdpBindingsCtx<MultipleDevicesId>;
4795
4796    impl FakeUdpCoreCtx<MultipleDevicesId> {
4797        fn new_multiple_devices<I: TestIpExt>() -> Self {
4798            let remote_ips = vec![I::get_other_remote_ip_address(1)];
4799            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
4800                MultipleDevicesId::all().into_iter().enumerate().map(|(i, device)| {
4801                    FakeDeviceConfig {
4802                        device,
4803                        local_ips: vec![Self::local_ip(i)],
4804                        remote_ips: remote_ips.clone(),
4805                    }
4806                }),
4807            ))
4808        }
4809
4810        fn local_ip<A: IpAddress>(index: usize) -> SpecifiedAddr<A>
4811        where
4812            A::Version: TestIpExt,
4813        {
4814            A::Version::get_other_ip_address((index + 1).try_into().unwrap())
4815        }
4816    }
4817
4818    /// Tests that if sockets are bound to devices, they will only receive
4819    /// packets that are received on those devices.
4820    #[ip_test(I)]
4821    fn test_bound_to_device_receive<I: TestIpExt>() {
4822        set_logger_for_test();
4823        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4824            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4825        );
4826        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4827        let bound_first_device = api.create();
4828        api.listen(
4829            &bound_first_device,
4830            Some(ZonedAddr::Unzoned(local_ip::<I>())),
4831            Some(LOCAL_PORT),
4832        )
4833        .expect("listen should succeed");
4834        api.connect(
4835            &bound_first_device,
4836            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
4837            REMOTE_PORT.into(),
4838        )
4839        .expect("connect should succeed");
4840        api.set_device(&bound_first_device, Some(&MultipleDevicesId::A))
4841            .expect("bind should succeed");
4842
4843        let bound_second_device = api.create();
4844        api.set_device(&bound_second_device, Some(&MultipleDevicesId::B)).unwrap();
4845        api.listen(&bound_second_device, None, Some(LOCAL_PORT)).expect("listen should succeed");
4846
4847        // Inject a packet received on `MultipleDevicesId::A` from the specified
4848        // remote; this should go to the first socket.
4849        let meta = UdpPacketMeta::<I> {
4850            src_ip: I::get_other_remote_ip_address(1).get(),
4851            src_port: Some(REMOTE_PORT),
4852            dst_ip: local_ip::<I>().get(),
4853            dst_port: LOCAL_PORT,
4854            dscp_and_ecn: DscpAndEcn::default(),
4855        };
4856        let body = [1, 2, 3, 4, 5];
4857        let (core_ctx, bindings_ctx) = api.contexts();
4858        receive_udp_packet(core_ctx, bindings_ctx, MultipleDevicesId::A, meta.clone(), &body[..])
4859            .expect("receive udp packet should succeed");
4860
4861        // A second packet received on `MultipleDevicesId::B` will go to the
4862        // second socket.
4863        receive_udp_packet(core_ctx, bindings_ctx, MultipleDevicesId::B, meta, &body[..])
4864            .expect("receive udp packet should succeed");
4865        assert_eq!(
4866            bindings_ctx.state.socket_data(),
4867            HashMap::from([
4868                (bound_first_device.downgrade(), vec![&body[..]]),
4869                (bound_second_device.downgrade(), vec![&body[..]])
4870            ])
4871        );
4872    }
4873
4874    /// Tests that if sockets are bound to devices, they will send packets out
4875    /// of those devices.
4876    #[ip_test(I)]
4877    fn test_bound_to_device_send<I: TestIpExt>() {
4878        set_logger_for_test();
4879        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4880            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4881        );
4882        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4883        let bound_on_devices = MultipleDevicesId::all().map(|device| {
4884            let socket = api.create();
4885            api.set_device(&socket, Some(&device)).unwrap();
4886            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
4887            socket
4888        });
4889
4890        // Send a packet from each socket.
4891        let body = [1, 2, 3, 4, 5];
4892        for socket in bound_on_devices {
4893            api.send_to(
4894                &socket,
4895                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
4896                REMOTE_PORT.into(),
4897                Buf::new(body.to_vec(), ..),
4898            )
4899            .expect("send should succeed");
4900        }
4901
4902        let mut received_devices = api
4903            .core_ctx()
4904            .bound_sockets
4905            .ip_socket_ctx
4906            .frames()
4907            .iter()
4908            .map(|(meta, _body)| {
4909                let SendIpPacketMeta {
4910                    device,
4911                    src_ip: _,
4912                    dst_ip,
4913                    destination: _,
4914                    proto,
4915                    ttl: _,
4916                    mtu: _,
4917                    dscp_and_ecn: _,
4918                } = meta.try_as::<I>().unwrap();
4919                assert_eq!(proto, &IpProto::Udp.into());
4920                assert_eq!(dst_ip, &I::get_other_remote_ip_address(1));
4921                *device
4922            })
4923            .collect::<Vec<_>>();
4924        received_devices.sort();
4925        assert_eq!(received_devices, &MultipleDevicesId::all());
4926    }
4927
4928    fn receive_packet_on<I: TestIpExt>(
4929        core_ctx: &mut UdpMultipleDevicesCoreCtx,
4930        bindings_ctx: &mut UdpMultipleDevicesBindingsCtx,
4931        device: MultipleDevicesId,
4932    ) -> Result<(), TransportReceiveError> {
4933        let meta = UdpPacketMeta::<I> {
4934            src_ip: I::get_other_remote_ip_address(1).get(),
4935            src_port: Some(REMOTE_PORT),
4936            dst_ip: local_ip::<I>().get(),
4937            dst_port: LOCAL_PORT,
4938            dscp_and_ecn: DscpAndEcn::default(),
4939        };
4940        const BODY: [u8; 5] = [1, 2, 3, 4, 5];
4941        receive_udp_packet(core_ctx, bindings_ctx, device, meta, &BODY[..])
4942    }
4943
4944    /// Check that sockets can be bound to and unbound from devices.
4945    #[ip_test(I)]
4946    fn test_bind_unbind_device<I: TestIpExt>() {
4947        set_logger_for_test();
4948        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4949            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4950        );
4951        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4952
4953        // Start with `socket` bound to a device.
4954        let socket = api.create();
4955        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
4956        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen failed");
4957
4958        // Since it is bound, it does not receive a packet from another device.
4959        let (core_ctx, bindings_ctx) = api.contexts();
4960        assert_matches!(
4961            receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B),
4962            Err(TransportReceiveError::PortUnreachable)
4963        );
4964        let received = &bindings_ctx.state.socket_data::<I>();
4965        assert_eq!(received, &HashMap::new());
4966
4967        // When unbound, the socket can receive packets on the other device.
4968        api.set_device(&socket, None).expect("clearing bound device failed");
4969        let (core_ctx, bindings_ctx) = api.contexts();
4970        receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B)
4971            .expect("receive udp packet should succeed");
4972        let received = bindings_ctx.state.received::<I>().iter().collect::<Vec<_>>();
4973        let (rx_socket, socket_received) =
4974            assert_matches!(received[..], [(rx_socket, packets)] => (rx_socket, packets));
4975        assert_eq!(rx_socket, &socket);
4976        assert_matches!(socket_received.packets[..], [_]);
4977    }
4978
4979    /// Check that bind fails as expected when it would cause illegal shadowing.
4980    #[ip_test(I)]
4981    fn test_unbind_device_fails<I: TestIpExt>() {
4982        set_logger_for_test();
4983        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4984            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4985        );
4986        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4987
4988        let bound_on_devices = MultipleDevicesId::all().map(|device| {
4989            let socket = api.create();
4990            api.set_device(&socket, Some(&device)).unwrap();
4991            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
4992            socket
4993        });
4994
4995        // Clearing the bound device is not allowed for either socket since it
4996        // would then be shadowed by the other socket.
4997        for socket in bound_on_devices {
4998            assert_matches!(
4999                api.set_device(&socket, None),
5000                Err(SocketError::Local(LocalAddressError::AddressInUse))
5001            );
5002        }
5003    }
5004
5005    /// Check that binding a device fails if it would make a connected socket
5006    /// unroutable.
5007    #[ip_test(I)]
5008    fn test_bind_conn_socket_device_fails<I: TestIpExt>() {
5009        set_logger_for_test();
5010        let device_configs = HashMap::from(
5011            [(MultipleDevicesId::A, 1), (MultipleDevicesId::B, 2)].map(|(device, i)| {
5012                (
5013                    device,
5014                    FakeDeviceConfig {
5015                        device,
5016                        local_ips: vec![I::get_other_ip_address(i)],
5017                        remote_ips: vec![I::get_other_remote_ip_address(i)],
5018                    },
5019                )
5020            }),
5021        );
5022        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5023            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5024                device_configs.iter().map(|(_, v)| v).cloned(),
5025            )),
5026        );
5027        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5028        let socket = api.create();
5029        api.connect(
5030            &socket,
5031            Some(ZonedAddr::Unzoned(device_configs[&MultipleDevicesId::A].remote_ips[0])),
5032            REMOTE_PORT.into(),
5033        )
5034        .expect("connect should succeed");
5035
5036        // `socket` is not explicitly bound to device `A` but its route must
5037        // go through it because of the destination address. Therefore binding
5038        // to device `B` wil not work.
5039        assert_matches!(
5040            api.set_device(&socket, Some(&MultipleDevicesId::B)),
5041            Err(SocketError::Remote(RemoteAddressError::NoRoute))
5042        );
5043
5044        // Binding to device `A` should be fine.
5045        api.set_device(&socket, Some(&MultipleDevicesId::A)).expect("routing picked A already");
5046    }
5047
5048    #[ip_test(I)]
5049    fn test_bound_device_receive_multicast_packet<I: TestIpExt>() {
5050        set_logger_for_test();
5051        let remote_ip = I::get_other_ip_address(1);
5052        let multicast_addr = I::get_multicast_addr(0);
5053
5054        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5055            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5056        );
5057        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5058
5059        let sharing_domain = SharingDomain::new(1);
5060
5061        // Create 3 sockets: one listener bound on each device and one not bound
5062        // to a device.
5063
5064        let bound_on_devices = MultipleDevicesId::all().map(|device| {
5065            let listener = api.create();
5066            api.set_device(&listener, Some(&device)).unwrap();
5067            api.set_posix_reuse_port(&listener, ReusePortOption::Enabled(sharing_domain))
5068                .expect("is unbound");
5069            api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
5070
5071            (device, listener)
5072        });
5073
5074        let listener = api.create();
5075        api.set_posix_reuse_port(&listener, ReusePortOption::Enabled(sharing_domain))
5076            .expect("is unbound");
5077        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
5078
5079        fn index_for_device(id: MultipleDevicesId) -> u8 {
5080            match id {
5081                MultipleDevicesId::A => 0,
5082                MultipleDevicesId::B => 1,
5083                MultipleDevicesId::C => 2,
5084            }
5085        }
5086
5087        let (core_ctx, bindings_ctx) = api.contexts();
5088        let mut receive_packet = |remote_ip: SpecifiedAddr<I::Addr>, device: MultipleDevicesId| {
5089            let meta = UdpPacketMeta::<I> {
5090                src_ip: remote_ip.get(),
5091                src_port: Some(REMOTE_PORT),
5092                dst_ip: multicast_addr.get(),
5093                dst_port: LOCAL_PORT,
5094                dscp_and_ecn: DscpAndEcn::default(),
5095            };
5096            let body = vec![index_for_device(device)];
5097            receive_udp_packet(core_ctx, bindings_ctx, device, meta, &body)
5098                .expect("receive udp packet should succeed")
5099        };
5100
5101        // Receive packets from the remote IP on each device (2 packets total).
5102        // Listeners bound on devices should receive one, and the other listener
5103        // should receive both.
5104        for device in MultipleDevicesId::all() {
5105            receive_packet(remote_ip, device);
5106        }
5107
5108        let per_socket_data = bindings_ctx.state.socket_data();
5109        for (device, listener) in bound_on_devices {
5110            assert_eq!(per_socket_data[&listener.downgrade()], vec![&[index_for_device(device)]]);
5111        }
5112        let expected_listener_data = &MultipleDevicesId::all().map(|d| vec![index_for_device(d)]);
5113        assert_eq!(&per_socket_data[&listener.downgrade()], expected_listener_data);
5114    }
5115
5116    /// Tests establishing a UDP connection without providing a local IP
5117    #[ip_test(I)]
5118    fn test_conn_unspecified_local_ip<I: TestIpExt>() {
5119        set_logger_for_test();
5120        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5121        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5122        let socket = api.create();
5123        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5124        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5125            .expect("connect failed");
5126        let info = api.get_info(&socket);
5127        assert_eq!(
5128            info,
5129            SocketInfo::Connected(datagram::ConnInfo {
5130                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip::<I>()),
5131                local_identifier: LOCAL_PORT,
5132                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<I>()),
5133                remote_identifier: REMOTE_PORT.into(),
5134            })
5135        );
5136    }
5137
5138    #[ip_test(I)]
5139    fn test_multicast_sendto<I: TestIpExt>() {
5140        set_logger_for_test();
5141
5142        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5143            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5144        );
5145
5146        // Add multicsat route for every device.
5147        for device in MultipleDevicesId::all().iter() {
5148            ctx.core_ctx
5149                .bound_sockets
5150                .ip_socket_ctx
5151                .state
5152                .add_subnet_route(*device, I::MULTICAST_SUBNET);
5153        }
5154
5155        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5156        let socket = api.create();
5157
5158        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
5159            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
5160                .expect("bind should succeed");
5161
5162            let multicast_ip = I::get_multicast_addr(i.try_into().unwrap());
5163            api.send_to(
5164                &socket,
5165                Some(ZonedAddr::Unzoned(multicast_ip.into())),
5166                REMOTE_PORT.into(),
5167                Buf::new(b"packet".to_vec(), ..),
5168            )
5169            .expect("send should succeed");
5170
5171            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
5172            assert_eq!(packets.len(), 1usize);
5173            for (meta, _body) in packets {
5174                let meta = meta.try_as::<I>().unwrap();
5175                assert_eq!(meta.device, *target_device);
5176                assert_eq!(meta.proto, IpProto::Udp.into());
5177                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
5178                assert_eq!(meta.dst_ip, multicast_ip.into());
5179                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
5180            }
5181        }
5182    }
5183
5184    #[ip_test(I)]
5185    fn test_multicast_send<I: TestIpExt>() {
5186        set_logger_for_test();
5187
5188        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5189            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5190        );
5191
5192        // Add multicsat route for every device.
5193        for device in MultipleDevicesId::all().iter() {
5194            ctx.core_ctx
5195                .bound_sockets
5196                .ip_socket_ctx
5197                .state
5198                .add_subnet_route(*device, I::MULTICAST_SUBNET);
5199        }
5200
5201        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5202        let multicast_ip = I::get_multicast_addr(42);
5203
5204        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
5205            let socket = api.create();
5206
5207            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
5208                .expect("set_multicast_interface should succeed");
5209
5210            api.connect(&socket, Some(ZonedAddr::Unzoned(multicast_ip.into())), REMOTE_PORT.into())
5211                .expect("send should succeed");
5212
5213            api.send(&socket, Buf::new(b"packet".to_vec(), ..)).expect("send should succeed");
5214
5215            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
5216            assert_eq!(packets.len(), 1usize);
5217            for (meta, _body) in packets {
5218                let meta = meta.try_as::<I>().unwrap();
5219                assert_eq!(meta.device, *target_device);
5220                assert_eq!(meta.proto, IpProto::Udp.into());
5221                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
5222                assert_eq!(meta.dst_ip, multicast_ip.into());
5223                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
5224            }
5225        }
5226    }
5227
5228    /// Tests local port allocation for [`connect`].
5229    ///
5230    /// Tests that calling [`connect`] causes a valid local port to be
5231    /// allocated.
5232    #[ip_test(I)]
5233    fn test_udp_local_port_alloc<I: TestIpExt>() {
5234        let local_ip = local_ip::<I>();
5235        let ip_a = I::get_other_ip_address(100);
5236        let ip_b = I::get_other_ip_address(200);
5237
5238        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
5239            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ip_a, ip_b]),
5240        );
5241        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5242
5243        let conn_a = api.create();
5244        api.connect(&conn_a, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
5245            .expect("connect failed");
5246        let conn_b = api.create();
5247        api.connect(&conn_b, Some(ZonedAddr::Unzoned(ip_b)), REMOTE_PORT.into())
5248            .expect("connect failed");
5249        let conn_c = api.create();
5250        api.connect(&conn_c, Some(ZonedAddr::Unzoned(ip_a)), OTHER_REMOTE_PORT.into())
5251            .expect("connect failed");
5252        let conn_d = api.create();
5253        api.connect(&conn_d, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
5254            .expect("connect failed");
5255        let valid_range = &FakePortAlloc::<I>::EPHEMERAL_RANGE;
5256        let mut get_conn_port = |id| {
5257            let info = api.get_info(&id);
5258            let info = assert_matches!(info, SocketInfo::Connected(info) => info);
5259            let datagram::ConnInfo {
5260                local_ip: _,
5261                local_identifier,
5262                remote_ip: _,
5263                remote_identifier: _,
5264            } = info;
5265            local_identifier
5266        };
5267        let port_a = get_conn_port(conn_a).get();
5268        let port_b = get_conn_port(conn_b).get();
5269        let port_c = get_conn_port(conn_c).get();
5270        let port_d = get_conn_port(conn_d).get();
5271        assert!(valid_range.contains(&port_a));
5272        assert!(valid_range.contains(&port_b));
5273        assert!(valid_range.contains(&port_c));
5274        assert!(valid_range.contains(&port_d));
5275        assert_ne!(port_a, port_b);
5276        assert_ne!(port_a, port_c);
5277        assert_ne!(port_a, port_d);
5278    }
5279
5280    /// Tests that if `listen_udp` fails, it can be retried later.
5281    #[ip_test(I)]
5282    fn test_udp_retry_listen_after_removing_conflict<I: TestIpExt>() {
5283        set_logger_for_test();
5284        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5285        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5286
5287        let listen_unbound = |api: &mut UdpApi<_, _>, socket: &UdpSocketId<_, _, _>| {
5288            api.listen(socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5289        };
5290
5291        // Tie up the address so the second call to `connect` fails.
5292        let listener = api.create();
5293        listen_unbound(&mut api, &listener)
5294            .expect("Initial call to listen_udp was expected to succeed");
5295
5296        // Trying to connect on the same address should fail.
5297        let unbound = api.create();
5298        assert_eq!(
5299            listen_unbound(&mut api, &unbound),
5300            Err(Either::Right(LocalAddressError::AddressInUse))
5301        );
5302
5303        // Once the first listener is removed, the second socket can be
5304        // connected.
5305        api.close(listener).into_removed();
5306
5307        listen_unbound(&mut api, &unbound).expect("listen should succeed");
5308    }
5309
5310    /// Tests local port allocation for [`listen_udp`].
5311    ///
5312    /// Tests that calling [`listen_udp`] causes a valid local port to be
5313    /// allocated when no local port is passed.
5314    #[ip_test(I)]
5315    fn test_udp_listen_port_alloc<I: TestIpExt>() {
5316        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5317        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5318        let local_ip = local_ip::<I>();
5319
5320        let wildcard_list = api.create();
5321        api.listen(&wildcard_list, None, None).expect("listen_udp failed");
5322        let specified_list = api.create();
5323        api.listen(&specified_list, Some(ZonedAddr::Unzoned(local_ip)), None)
5324            .expect("listen_udp failed");
5325        let mut get_listener_port = |id| {
5326            let info = api.get_info(&id);
5327            let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5328            let datagram::ListenerInfo { local_ip: _, local_identifier } = info;
5329            local_identifier
5330        };
5331        let wildcard_port = get_listener_port(wildcard_list);
5332        let specified_port = get_listener_port(specified_list);
5333        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&wildcard_port.get()));
5334        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&specified_port.get()));
5335        assert_ne!(wildcard_port, specified_port);
5336    }
5337
5338    #[ip_test(I)]
5339    fn test_bind_multiple_reuse_port<I: TestIpExt>() {
5340        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5341        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5342        let listeners = [(), ()].map(|()| {
5343            let socket = api.create();
5344            let sharing_domain = SharingDomain::new(1);
5345            api.set_posix_reuse_port(&socket, ReusePortOption::Enabled(sharing_domain))
5346                .expect("is unbound");
5347            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5348            socket
5349        });
5350
5351        for listener in listeners {
5352            assert_eq!(
5353                api.get_info(&listener),
5354                SocketInfo::Listener(datagram::ListenerInfo {
5355                    local_ip: None,
5356                    local_identifier: LOCAL_PORT
5357                })
5358            );
5359        }
5360    }
5361
5362    #[ip_test(I)]
5363    fn test_set_unset_reuse_port_unbound<I: TestIpExt>() {
5364        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5365        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5366        let unbound = api.create();
5367        let sharing_domain = SharingDomain::new(1);
5368        api.set_posix_reuse_port(&unbound, ReusePortOption::Enabled(sharing_domain))
5369            .expect("is unbound");
5370        api.set_posix_reuse_port(&unbound, ReusePortOption::Disabled).expect("is unbound");
5371        api.listen(&unbound, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5372
5373        // Because there is already a listener bound without `SO_REUSEPORT` set,
5374        // the next bind to the same address should fail.
5375        assert_eq!(
5376            {
5377                let unbound = api.create();
5378                api.listen(&unbound, None, Some(LOCAL_PORT))
5379            },
5380            Err(Either::Right(LocalAddressError::AddressInUse))
5381        );
5382    }
5383
5384    #[ip_test(I)]
5385    #[test_case(bind_as_listener)]
5386    #[test_case(bind_as_connected)]
5387    fn test_set_unset_reuse_port_bound<I: TestIpExt>(
5388        set_up_socket: impl FnOnce(
5389            &mut UdpMultipleDevicesCtx,
5390            &UdpSocketId<
5391                I,
5392                FakeWeakDeviceId<MultipleDevicesId>,
5393                FakeUdpBindingsCtx<MultipleDevicesId>,
5394            >,
5395        ),
5396    ) {
5397        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5398            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5399        );
5400        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5401        set_up_socket(&mut ctx, &socket);
5402
5403        // Per src/connectivity/network/netstack3/docs/POSIX_COMPATIBILITY.md,
5404        // Netstack3 only allows setting SO_REUSEPORT on unbound sockets.
5405        assert_matches!(
5406            UdpApi::<I, _>::new(ctx.as_mut())
5407                .set_posix_reuse_port(&socket, ReusePortOption::Disabled),
5408            Err(ExpectedUnboundError)
5409        )
5410    }
5411
5412    /// Tests [`remove_udp`]
5413    #[ip_test(I)]
5414    fn test_remove_udp_conn<I: TestIpExt>() {
5415        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5416        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5417
5418        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5419        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
5420        let socket = api.create();
5421        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).unwrap();
5422        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
5423        api.close(socket).into_removed();
5424    }
5425
5426    /// Tests [`remove_udp`]
5427    #[ip_test(I)]
5428    fn test_remove_udp_listener<I: TestIpExt>() {
5429        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5430        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5431        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5432
5433        // Test removing a specified listener.
5434        let specified = api.create();
5435        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5436        api.close(specified).into_removed();
5437
5438        // Test removing a wildcard listener.
5439        let wildcard = api.create();
5440        api.listen(&wildcard, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5441        api.close(wildcard).into_removed();
5442    }
5443
5444    fn try_join_leave_multicast<I: TestIpExt>(
5445        mcast_addr: MulticastAddr<I::Addr>,
5446        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
5447        set_up_ctx: impl FnOnce(&mut UdpMultipleDevicesCtx),
5448        set_up_socket: impl FnOnce(
5449            &mut UdpMultipleDevicesCtx,
5450            &UdpSocketId<
5451                I,
5452                FakeWeakDeviceId<MultipleDevicesId>,
5453                FakeUdpBindingsCtx<MultipleDevicesId>,
5454            >,
5455        ),
5456    ) -> (
5457        Result<(), SetMulticastMembershipError>,
5458        HashMap<(MultipleDevicesId, MulticastAddr<I::Addr>), NonZeroUsize>,
5459    ) {
5460        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5461            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5462        );
5463        set_up_ctx(&mut ctx);
5464
5465        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5466        set_up_socket(&mut ctx, &socket);
5467        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5468        let result = api.set_multicast_membership(&socket, mcast_addr, interface, true);
5469
5470        let memberships_snapshot =
5471            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>();
5472        if let Ok(()) = result {
5473            api.set_multicast_membership(&socket, mcast_addr, interface, false)
5474                .expect("leaving group failed");
5475        }
5476        assert_eq!(
5477            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5478            HashMap::default()
5479        );
5480
5481        (result, memberships_snapshot)
5482    }
5483
5484    fn leave_unbound<I: TestIpExt>(
5485        _ctx: &mut UdpMultipleDevicesCtx,
5486        _unbound: &UdpSocketId<
5487            I,
5488            FakeWeakDeviceId<MultipleDevicesId>,
5489            FakeUdpBindingsCtx<MultipleDevicesId>,
5490        >,
5491    ) {
5492    }
5493
5494    fn bind_as_listener<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            .listen(unbound, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5504            .expect("listen should succeed")
5505    }
5506
5507    fn bind_as_connected<I: TestIpExt>(
5508        ctx: &mut UdpMultipleDevicesCtx,
5509        unbound: &UdpSocketId<
5510            I,
5511            FakeWeakDeviceId<MultipleDevicesId>,
5512            FakeUdpBindingsCtx<MultipleDevicesId>,
5513        >,
5514    ) {
5515        UdpApi::<I, _>::new(ctx.as_mut())
5516            .connect(
5517                unbound,
5518                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5519                REMOTE_PORT.into(),
5520            )
5521            .expect("connect should succeed")
5522    }
5523
5524    fn iface_id<A: IpAddress>(
5525        id: MultipleDevicesId,
5526    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
5527        MulticastInterfaceSelector::Interface(id).into()
5528    }
5529    fn iface_addr<A: IpAddress>(
5530        addr: SpecifiedAddr<A>,
5531    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
5532        MulticastInterfaceSelector::LocalAddress(addr).into()
5533    }
5534
5535    #[ip_test(I)]
5536    #[test_case(iface_id(MultipleDevicesId::A), leave_unbound::<I>; "device_no_addr_unbound")]
5537    #[test_case(iface_addr(local_ip::<I>()), leave_unbound::<I>; "addr_no_device_unbound")]
5538    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, leave_unbound::<I>;
5539        "any_interface_unbound")]
5540    #[test_case(iface_id(MultipleDevicesId::A), bind_as_listener::<I>; "device_no_addr_listener")]
5541    #[test_case(iface_addr(local_ip::<I>()), bind_as_listener::<I>; "addr_no_device_listener")]
5542    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_listener::<I>;
5543        "any_interface_listener")]
5544    #[test_case(iface_id(MultipleDevicesId::A), bind_as_connected::<I>; "device_no_addr_connected")]
5545    #[test_case(iface_addr(local_ip::<I>()), bind_as_connected::<I>; "addr_no_device_connected")]
5546    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_connected::<I>;
5547        "any_interface_connected")]
5548    fn test_join_leave_multicast_succeeds<I: TestIpExt>(
5549        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
5550        set_up_socket: impl FnOnce(
5551            &mut UdpMultipleDevicesCtx,
5552            &UdpSocketId<
5553                I,
5554                FakeWeakDeviceId<MultipleDevicesId>,
5555                FakeUdpBindingsCtx<MultipleDevicesId>,
5556            >,
5557        ),
5558    ) {
5559        let mcast_addr = I::get_multicast_addr(3);
5560
5561        let set_up_ctx = |ctx: &mut UdpMultipleDevicesCtx| {
5562            // Ensure there is a route to the multicast address, if the interface
5563            // selector requires it.
5564            match interface {
5565                MulticastMembershipInterfaceSelector::Specified(_) => {}
5566                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
5567                    ctx.core_ctx
5568                        .bound_sockets
5569                        .ip_socket_ctx
5570                        .state
5571                        .add_route(MultipleDevicesId::A, mcast_addr.into_specified().into());
5572                }
5573            }
5574        };
5575
5576        let (result, ip_options) =
5577            try_join_leave_multicast(mcast_addr, interface, set_up_ctx, set_up_socket);
5578        assert_eq!(result, Ok(()));
5579        assert_eq!(
5580            ip_options,
5581            HashMap::from([((MultipleDevicesId::A, mcast_addr), NonZeroUsize::new(1).unwrap())])
5582        );
5583    }
5584
5585    #[ip_test(I)]
5586    #[test_case(leave_unbound::<I>; "unbound")]
5587    #[test_case(bind_as_listener::<I>; "listener")]
5588    #[test_case(bind_as_connected::<I>; "connected")]
5589    fn test_join_multicast_fails_without_route<I: TestIpExt>(
5590        set_up_socket: impl FnOnce(
5591            &mut UdpMultipleDevicesCtx,
5592            &UdpSocketId<
5593                I,
5594                FakeWeakDeviceId<MultipleDevicesId>,
5595                FakeUdpBindingsCtx<MultipleDevicesId>,
5596            >,
5597        ),
5598    ) {
5599        let mcast_addr = I::get_multicast_addr(3);
5600
5601        let (result, ip_options) = try_join_leave_multicast(
5602            mcast_addr,
5603            MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
5604            |_: &mut UdpMultipleDevicesCtx| { /* Don't install a route to `mcast_addr` */ },
5605            set_up_socket,
5606        );
5607        assert_eq!(result, Err(SetMulticastMembershipError::NoDeviceAvailable));
5608        assert_eq!(ip_options, HashMap::new());
5609    }
5610
5611    #[ip_test(I)]
5612    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), leave_unbound, Ok(());
5613        "with_ip_unbound")]
5614    #[test_case(MultipleDevicesId::A, None, leave_unbound, Ok(());
5615        "without_ip_unbound")]
5616    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_listener, Ok(());
5617        "with_ip_listener")]
5618    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_connected, Ok(());
5619        "with_ip_connected")]
5620    fn test_join_leave_multicast_interface_inferred_from_bound_device<I: TestIpExt>(
5621        bound_device: MultipleDevicesId,
5622        interface_addr: Option<SpecifiedAddr<I::Addr>>,
5623        set_up_socket: impl FnOnce(
5624            &mut UdpMultipleDevicesCtx,
5625            &UdpSocketId<
5626                I,
5627                FakeWeakDeviceId<MultipleDevicesId>,
5628                FakeUdpBindingsCtx<MultipleDevicesId>,
5629            >,
5630        ),
5631        expected_result: Result<(), SetMulticastMembershipError>,
5632    ) {
5633        let mcast_addr = I::get_multicast_addr(3);
5634        let (result, ip_options) = try_join_leave_multicast(
5635            mcast_addr,
5636            interface_addr
5637                .map(MulticastInterfaceSelector::LocalAddress)
5638                .map(Into::into)
5639                .unwrap_or(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute),
5640            |_: &mut UdpMultipleDevicesCtx| { /* No ctx setup required */ },
5641            |ctx, unbound| {
5642                UdpApi::<I, _>::new(ctx.as_mut())
5643                    .set_device(&unbound, Some(&bound_device))
5644                    .unwrap();
5645                set_up_socket(ctx, &unbound)
5646            },
5647        );
5648        assert_eq!(result, expected_result);
5649        assert_eq!(
5650            ip_options,
5651            expected_result.map_or_else(
5652                |_| HashMap::default(),
5653                |()| HashMap::from([((bound_device, mcast_addr), NonZeroUsize::new(1).unwrap())])
5654            )
5655        );
5656    }
5657
5658    #[ip_test(I)]
5659    fn test_multicast_membership_with_removed_device<I: TestIpExt>() {
5660        let device = FakeReferencyDeviceId::default();
5661        let mut ctx =
5662            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
5663        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5664
5665        let unbound = api.create();
5666        api.set_device(&unbound, Some(&device)).unwrap();
5667
5668        device.mark_removed();
5669
5670        let group = I::get_multicast_addr(4);
5671        assert_eq!(
5672            api.set_multicast_membership(
5673                &unbound,
5674                group,
5675                // Will use the socket's bound device.
5676                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
5677                true,
5678            ),
5679            Err(SetMulticastMembershipError::DeviceDoesNotExist),
5680        );
5681
5682        // Should not have updated the device's multicast state.
5683        //
5684        // Note that even though we mock the device being removed above, its
5685        // state still exists in the fake IP socket context so we can inspect
5686        // it here.
5687        assert_eq!(
5688            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5689            HashMap::default(),
5690        );
5691    }
5692
5693    #[ip_test(I)]
5694    fn test_remove_udp_unbound_leaves_multicast_groups<I: TestIpExt>() {
5695        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5696            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5697        );
5698        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5699
5700        let unbound = api.create();
5701        let group = I::get_multicast_addr(4);
5702        api.set_multicast_membership(
5703            &unbound,
5704            group,
5705            MulticastInterfaceSelector::LocalAddress(local_ip::<I>()).into(),
5706            true,
5707        )
5708        .expect("join group failed");
5709
5710        assert_eq!(
5711            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5712            HashMap::from([((MultipleDevicesId::A, group), NonZeroUsize::new(1).unwrap())])
5713        );
5714
5715        api.close(unbound).into_removed();
5716        assert_eq!(
5717            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5718            HashMap::default()
5719        );
5720    }
5721
5722    #[ip_test(I)]
5723    fn test_remove_udp_listener_leaves_multicast_groups<I: TestIpExt>() {
5724        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5725            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5726        );
5727        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5728        let local_ip = local_ip::<I>();
5729
5730        let socket = api.create();
5731        let first_group = I::get_multicast_addr(4);
5732        api.set_multicast_membership(
5733            &socket,
5734            first_group,
5735            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5736            true,
5737        )
5738        .expect("join group failed");
5739
5740        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
5741            .expect("listen_udp failed");
5742        let second_group = I::get_multicast_addr(5);
5743        api.set_multicast_membership(
5744            &socket,
5745            second_group,
5746            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5747            true,
5748        )
5749        .expect("join group failed");
5750
5751        assert_eq!(
5752            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5753            HashMap::from([
5754                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
5755                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
5756            ])
5757        );
5758
5759        api.close(socket).into_removed();
5760        assert_eq!(
5761            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5762            HashMap::default()
5763        );
5764    }
5765
5766    #[ip_test(I)]
5767    fn test_remove_udp_connected_leaves_multicast_groups<I: TestIpExt>() {
5768        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5769            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5770        );
5771        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5772        let local_ip = local_ip::<I>();
5773
5774        let socket = api.create();
5775        let first_group = I::get_multicast_addr(4);
5776        api.set_multicast_membership(
5777            &socket,
5778            first_group,
5779            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5780            true,
5781        )
5782        .expect("join group failed");
5783
5784        api.connect(
5785            &socket,
5786            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5787            REMOTE_PORT.into(),
5788        )
5789        .expect("connect failed");
5790
5791        let second_group = I::get_multicast_addr(5);
5792        api.set_multicast_membership(
5793            &socket,
5794            second_group,
5795            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5796            true,
5797        )
5798        .expect("join group failed");
5799
5800        assert_eq!(
5801            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5802            HashMap::from([
5803                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
5804                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
5805            ])
5806        );
5807
5808        api.close(socket).into_removed();
5809        assert_eq!(
5810            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5811            HashMap::default()
5812        );
5813    }
5814
5815    #[ip_test(I)]
5816    #[should_panic(expected = "listen again failed")]
5817    fn test_listen_udp_removes_unbound<I: TestIpExt>() {
5818        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5819        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5820        let local_ip = local_ip::<I>();
5821        let socket = api.create();
5822
5823        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
5824            .expect("listen_udp failed");
5825
5826        // Attempting to create a new listener from the same unbound ID should
5827        // panic since the unbound socket ID is now invalid.
5828        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(OTHER_LOCAL_PORT))
5829            .expect("listen again failed");
5830    }
5831
5832    #[ip_test(I)]
5833    fn test_get_conn_info<I: TestIpExt>() {
5834        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5835        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5836        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5837        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
5838        // Create a UDP connection with a specified local port and local IP.
5839        let socket = api.create();
5840        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5841        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
5842        let info = api.get_info(&socket);
5843        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
5844        assert_eq!(info.local_ip.into_inner(), local_ip.map_zone(FakeWeakDeviceId));
5845        assert_eq!(info.local_identifier, LOCAL_PORT);
5846        assert_eq!(info.remote_ip.into_inner(), remote_ip.map_zone(FakeWeakDeviceId));
5847        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
5848    }
5849
5850    #[ip_test(I)]
5851    fn test_get_listener_info<I: TestIpExt>() {
5852        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5853        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5854        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5855
5856        // Check getting info on specified listener.
5857        let specified = api.create();
5858        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5859        let info = api.get_info(&specified);
5860        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5861        assert_eq!(info.local_ip.unwrap().into_inner(), local_ip.map_zone(FakeWeakDeviceId));
5862        assert_eq!(info.local_identifier, LOCAL_PORT);
5863
5864        // Check getting info on wildcard listener.
5865        let wildcard = api.create();
5866        api.listen(&wildcard, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
5867        let info = api.get_info(&wildcard);
5868        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5869        assert_eq!(info.local_ip, None);
5870        assert_eq!(info.local_identifier, OTHER_LOCAL_PORT);
5871    }
5872
5873    #[ip_test(I)]
5874    fn test_get_reuse_port<I: TestIpExt>() {
5875        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5876        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5877        let first = api.create();
5878        assert_eq!(api.get_posix_reuse_port(&first), false);
5879
5880        let sharing_domain = SharingDomain::new(1);
5881        api.set_posix_reuse_port(&first, ReusePortOption::Enabled(sharing_domain))
5882            .expect("is unbound");
5883
5884        assert_eq!(api.get_posix_reuse_port(&first), true);
5885
5886        api.listen(&first, Some(ZonedAddr::Unzoned(local_ip::<I>())), None).expect("listen failed");
5887        assert_eq!(api.get_posix_reuse_port(&first), true);
5888        api.close(first).into_removed();
5889
5890        let second = api.create();
5891        api.set_posix_reuse_port(&second, ReusePortOption::Enabled(sharing_domain))
5892            .expect("is unbound");
5893        api.connect(&second, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5894            .expect("connect failed");
5895
5896        assert_eq!(api.get_posix_reuse_port(&second), true);
5897    }
5898
5899    #[ip_test(I)]
5900    fn test_get_bound_device_unbound<I: TestIpExt>() {
5901        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5902        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5903        let unbound = api.create();
5904
5905        assert_eq!(api.get_bound_device(&unbound), None);
5906
5907        api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
5908        assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
5909    }
5910
5911    #[ip_test(I)]
5912    fn test_get_bound_device_listener<I: TestIpExt>() {
5913        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5914        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5915        let socket = api.create();
5916
5917        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
5918        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5919            .expect("failed to listen");
5920        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
5921
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_get_bound_device_connected<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 socket = api.create();
5931        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
5932        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5933            .expect("failed to connect");
5934        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
5935        api.set_device(&socket, None).expect("failed to set device");
5936        assert_eq!(api.get_bound_device(&socket), None);
5937    }
5938
5939    #[ip_test(I)]
5940    fn test_listen_udp_forwards_errors<I: TestIpExt>() {
5941        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5942        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5943        let remote_ip = remote_ip::<I>();
5944
5945        // Check listening to a non-local IP fails.
5946        let unbound = api.create();
5947        let listen_err = api
5948            .listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT))
5949            .expect_err("listen_udp unexpectedly succeeded");
5950        assert_eq!(listen_err, Either::Right(LocalAddressError::CannotBindToAddress));
5951
5952        let unbound = api.create();
5953        let _ = api.listen(&unbound, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
5954        let unbound = api.create();
5955        let listen_err = api
5956            .listen(&unbound, None, Some(OTHER_LOCAL_PORT))
5957            .expect_err("listen_udp unexpectedly succeeded");
5958        assert_eq!(listen_err, Either::Right(LocalAddressError::AddressInUse));
5959    }
5960
5961    const IPV6_LINK_LOCAL_ADDR: Ipv6Addr = net_ip_v6!("fe80::1234");
5962    #[test_case(IPV6_LINK_LOCAL_ADDR, IPV6_LINK_LOCAL_ADDR; "unicast")]
5963    #[test_case(IPV6_LINK_LOCAL_ADDR, MulticastAddr::new(net_ip_v6!("ff02::1234")).unwrap().get(); "multicast")]
5964    fn test_listen_udp_ipv6_link_local_requires_zone(
5965        interface_addr: Ipv6Addr,
5966        bind_addr: Ipv6Addr,
5967    ) {
5968        type I = Ipv6;
5969        let interface_addr = LinkLocalAddr::new(interface_addr).unwrap().into_specified();
5970
5971        let mut ctx =
5972            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
5973                vec![interface_addr],
5974                vec![remote_ip::<I>()],
5975            ));
5976        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5977
5978        let bind_addr = LinkLocalAddr::new(bind_addr).unwrap().into_specified();
5979        assert!(bind_addr.scope().can_have_zone());
5980
5981        let unbound = api.create();
5982        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(bind_addr)), Some(LOCAL_PORT));
5983        assert_eq!(
5984            result,
5985            Err(Either::Right(LocalAddressError::Zone(ZonedAddressError::RequiredZoneNotProvided)))
5986        );
5987    }
5988
5989    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
5990    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "not matching")]
5991    fn test_listen_udp_ipv6_link_local_with_bound_device_set(
5992        zone_id: MultipleDevicesId,
5993        expected_result: Result<(), LocalAddressError>,
5994    ) {
5995        type I = Ipv6;
5996        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5997        assert!(ll_addr.scope().can_have_zone());
5998
5999        let remote_ips = vec![remote_ip::<I>()];
6000        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6001            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6002                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6003                    |(device, local_ip)| FakeDeviceConfig {
6004                        device,
6005                        local_ips: vec![local_ip],
6006                        remote_ips: remote_ips.clone(),
6007                    },
6008                ),
6009            )),
6010        );
6011        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6012
6013        let socket = api.create();
6014        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
6015
6016        let result = api
6017            .listen(
6018                &socket,
6019                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
6020                Some(LOCAL_PORT),
6021            )
6022            .map_err(Either::unwrap_right);
6023        assert_eq!(result, expected_result);
6024    }
6025
6026    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
6027    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::AddressMismatch); "not matching")]
6028    fn test_listen_udp_ipv6_link_local_with_zone_requires_addr_assigned_to_device(
6029        zone_id: MultipleDevicesId,
6030        expected_result: Result<(), LocalAddressError>,
6031    ) {
6032        type I = Ipv6;
6033        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6034        assert!(ll_addr.scope().can_have_zone());
6035
6036        let remote_ips = vec![remote_ip::<I>()];
6037        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6038            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6039                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6040                    |(device, local_ip)| FakeDeviceConfig {
6041                        device,
6042                        local_ips: vec![local_ip],
6043                        remote_ips: remote_ips.clone(),
6044                    },
6045                ),
6046            )),
6047        );
6048        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6049
6050        let socket = api.create();
6051        let result = api
6052            .listen(
6053                &socket,
6054                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
6055                Some(LOCAL_PORT),
6056            )
6057            .map_err(Either::unwrap_right);
6058        assert_eq!(result, expected_result);
6059    }
6060
6061    #[test_case(None, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "clear device")]
6062    #[test_case(Some(MultipleDevicesId::A), Ok(()); "set same device")]
6063    #[test_case(Some(MultipleDevicesId::B),
6064                Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "change device")]
6065    fn test_listen_udp_ipv6_listen_link_local_update_bound_device(
6066        new_device: Option<MultipleDevicesId>,
6067        expected_result: Result<(), LocalAddressError>,
6068    ) {
6069        type I = Ipv6;
6070        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6071        assert!(ll_addr.scope().can_have_zone());
6072
6073        let remote_ips = vec![remote_ip::<I>()];
6074        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6075            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6076                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
6077                    |(device, local_ip)| FakeDeviceConfig {
6078                        device,
6079                        local_ips: vec![local_ip],
6080                        remote_ips: remote_ips.clone(),
6081                    },
6082                ),
6083            )),
6084        );
6085        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6086
6087        let socket = api.create();
6088        api.listen(
6089            &socket,
6090            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap())),
6091            Some(LOCAL_PORT),
6092        )
6093        .expect("listen failed");
6094
6095        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(MultipleDevicesId::A)));
6096
6097        assert_eq!(
6098            api.set_device(&socket, new_device.as_ref()),
6099            expected_result.map_err(SocketError::Local),
6100        );
6101    }
6102
6103    #[test_case(None; "bind all IPs")]
6104    #[test_case(Some(ZonedAddr::Unzoned(local_ip::<Ipv6>())); "bind unzoned")]
6105    #[test_case(Some(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6106        MultipleDevicesId::A).unwrap())); "bind with same zone")]
6107    fn test_udp_ipv6_connect_with_unzoned(
6108        bound_addr: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>>,
6109    ) {
6110        let remote_ips = vec![remote_ip::<Ipv6>()];
6111
6112        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6113            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
6114                FakeDeviceConfig {
6115                    device: MultipleDevicesId::A,
6116                    local_ips: vec![
6117                        local_ip::<Ipv6>(),
6118                        SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6119                    ],
6120                    remote_ips: remote_ips.clone(),
6121                },
6122                FakeDeviceConfig {
6123                    device: MultipleDevicesId::B,
6124                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
6125                    remote_ips: remote_ips,
6126                },
6127            ])),
6128        );
6129        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6130
6131        let socket = api.create();
6132
6133        api.listen(&socket, bound_addr, Some(LOCAL_PORT)).unwrap();
6134
6135        assert_matches!(
6136            api.connect(
6137                &socket,
6138                Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())),
6139                REMOTE_PORT.into(),
6140            ),
6141            Ok(())
6142        );
6143    }
6144
6145    #[test]
6146    fn test_udp_ipv6_connect_zoned_get_info() {
6147        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6148        assert!(ll_addr.must_have_zone());
6149
6150        let remote_ips = vec![remote_ip::<Ipv6>()];
6151        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6152            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6153                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<Ipv6>())].map(
6154                    |(device, local_ip)| FakeDeviceConfig {
6155                        device,
6156                        local_ips: vec![local_ip],
6157                        remote_ips: remote_ips.clone(),
6158                    },
6159                ),
6160            )),
6161        );
6162
6163        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6164        let socket = api.create();
6165        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
6166
6167        let zoned_local_addr =
6168            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
6169        api.listen(&socket, Some(zoned_local_addr), Some(LOCAL_PORT)).unwrap();
6170
6171        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())), REMOTE_PORT.into())
6172            .expect("connect should succeed");
6173
6174        assert_eq!(
6175            api.get_info(&socket),
6176            SocketInfo::Connected(datagram::ConnInfo {
6177                local_ip: StrictlyZonedAddr::new_with_zone(ll_addr, || FakeWeakDeviceId(
6178                    MultipleDevicesId::A
6179                )),
6180                local_identifier: LOCAL_PORT,
6181                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<Ipv6>()),
6182                remote_identifier: REMOTE_PORT.into(),
6183            })
6184        );
6185    }
6186
6187    #[test_case(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap(),
6188        MultipleDevicesId::B).unwrap()),
6189        Err(ConnectError::Zone(ZonedAddressError::DeviceZoneMismatch));
6190        "connect to different zone")]
6191    #[test_case(ZonedAddr::Unzoned(SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()),
6192        Ok(FakeWeakDeviceId(MultipleDevicesId::A)); "connect implicit zone")]
6193    fn test_udp_ipv6_bind_zoned(
6194        remote_addr: ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>,
6195        expected: Result<FakeWeakDeviceId<MultipleDevicesId>, ConnectError>,
6196    ) {
6197        let remote_ips = vec![SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()];
6198
6199        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6200            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
6201                FakeDeviceConfig {
6202                    device: MultipleDevicesId::A,
6203                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()],
6204                    remote_ips: remote_ips.clone(),
6205                },
6206                FakeDeviceConfig {
6207                    device: MultipleDevicesId::B,
6208                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
6209                    remote_ips: remote_ips,
6210                },
6211            ])),
6212        );
6213
6214        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6215
6216        let socket = api.create();
6217
6218        api.listen(
6219            &socket,
6220            Some(ZonedAddr::Zoned(
6221                AddrAndZone::new(
6222                    SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
6223                    MultipleDevicesId::A,
6224                )
6225                .unwrap(),
6226            )),
6227            Some(LOCAL_PORT),
6228        )
6229        .unwrap();
6230
6231        let result = api
6232            .connect(&socket, Some(remote_addr), REMOTE_PORT.into())
6233            .map(|()| api.get_bound_device(&socket).unwrap());
6234        assert_eq!(result, expected);
6235    }
6236
6237    #[ip_test(I)]
6238    fn test_listen_udp_loopback_no_zone_is_required<I: TestIpExt>() {
6239        let loopback_addr = I::LOOPBACK_ADDRESS;
6240        let remote_ips = vec![remote_ip::<I>()];
6241
6242        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6243            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6244                [(MultipleDevicesId::A, loopback_addr), (MultipleDevicesId::B, local_ip::<I>())]
6245                    .map(|(device, local_ip)| FakeDeviceConfig {
6246                        device,
6247                        local_ips: vec![local_ip],
6248                        remote_ips: remote_ips.clone(),
6249                    }),
6250            )),
6251        );
6252        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6253
6254        let unbound = api.create();
6255        api.set_device(&unbound, Some(&MultipleDevicesId::A)).unwrap();
6256
6257        let result =
6258            api.listen(&unbound, Some(ZonedAddr::Unzoned(loopback_addr)), Some(LOCAL_PORT));
6259        assert_matches!(result, Ok(_));
6260    }
6261
6262    #[test_case(None, true, Ok(()); "connected success")]
6263    #[test_case(None, false, Ok(()); "listening success")]
6264    #[test_case(Some(MultipleDevicesId::A), true, Ok(()); "conn bind same device")]
6265    #[test_case(Some(MultipleDevicesId::A), false, Ok(()); "listen bind same device")]
6266    #[test_case(
6267        Some(MultipleDevicesId::B),
6268        true,
6269        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
6270        "conn bind different device")]
6271    #[test_case(
6272        Some(MultipleDevicesId::B),
6273        false,
6274        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
6275        "listen bind different device")]
6276    fn test_udp_ipv6_send_to_zoned(
6277        bind_device: Option<MultipleDevicesId>,
6278        connect: bool,
6279        expected: Result<(), SendToError>,
6280    ) {
6281        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6282        assert!(ll_addr.must_have_zone());
6283        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
6284
6285        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6286            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6287                [
6288                    (MultipleDevicesId::A, Ipv6::get_other_ip_address(1)),
6289                    (MultipleDevicesId::B, Ipv6::get_other_ip_address(2)),
6290                ]
6291                .map(|(device, local_ip)| FakeDeviceConfig {
6292                    device,
6293                    local_ips: vec![local_ip],
6294                    remote_ips: vec![ll_addr, conn_remote_ip],
6295                }),
6296            )),
6297        );
6298
6299        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6300        let socket = api.create();
6301
6302        if let Some(device) = bind_device {
6303            api.set_device(&socket, Some(&device)).unwrap();
6304        }
6305
6306        let send_to_remote_addr =
6307            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
6308        let result = if connect {
6309            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6310                .expect("connect should succeed");
6311            api.send_to(
6312                &socket,
6313                Some(send_to_remote_addr),
6314                REMOTE_PORT.into(),
6315                Buf::new(Vec::new(), ..),
6316            )
6317        } else {
6318            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
6319            api.send_to(
6320                &socket,
6321                Some(send_to_remote_addr),
6322                REMOTE_PORT.into(),
6323                Buf::new(Vec::new(), ..),
6324            )
6325        };
6326
6327        assert_eq!(result.map_err(|err| assert_matches!(err, Either::Right(e) => e)), expected);
6328    }
6329
6330    #[test_case(true; "connected")]
6331    #[test_case(false; "listening")]
6332    fn test_udp_ipv6_bound_zoned_send_to_zoned(connect: bool) {
6333        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::5678")).unwrap().into_specified();
6334        let device_a_local_ip = net_ip_v6!("fe80::1111");
6335        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
6336
6337        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6338            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6339                [
6340                    (MultipleDevicesId::A, device_a_local_ip),
6341                    (MultipleDevicesId::B, net_ip_v6!("fe80::2222")),
6342                ]
6343                .map(|(device, local_ip)| FakeDeviceConfig {
6344                    device,
6345                    local_ips: vec![LinkLocalAddr::new(local_ip).unwrap().into_specified()],
6346                    remote_ips: vec![ll_addr, conn_remote_ip],
6347                }),
6348            )),
6349        );
6350        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6351
6352        let socket = api.create();
6353        api.listen(
6354            &socket,
6355            Some(ZonedAddr::Zoned(
6356                AddrAndZone::new(
6357                    SpecifiedAddr::new(device_a_local_ip).unwrap(),
6358                    MultipleDevicesId::A,
6359                )
6360                .unwrap(),
6361            )),
6362            Some(LOCAL_PORT),
6363        )
6364        .expect("listen should succeed");
6365
6366        // Use a remote address on device B, while the socket is listening on
6367        // device A. This should cause a failure when sending.
6368        let send_to_remote_addr =
6369            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::B).unwrap());
6370
6371        let result = if connect {
6372            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6373                .expect("connect should succeed");
6374            api.send_to(
6375                &socket,
6376                Some(send_to_remote_addr),
6377                REMOTE_PORT.into(),
6378                Buf::new(Vec::new(), ..),
6379            )
6380        } else {
6381            api.send_to(
6382                &socket,
6383                Some(send_to_remote_addr),
6384                REMOTE_PORT.into(),
6385                Buf::new(Vec::new(), ..),
6386            )
6387        };
6388
6389        assert_matches!(
6390            result,
6391            Err(Either::Right(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch)))
6392        );
6393    }
6394
6395    #[test_case(None; "removes implicit")]
6396    #[test_case(Some(FakeDeviceId); "preserves implicit")]
6397    fn test_connect_disconnect_affects_bound_device(bind_device: Option<FakeDeviceId>) {
6398        // If a socket is bound to an unzoned address, whether or not it has a
6399        // bound device should be restored after `connect` then `disconnect`.
6400        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6401        assert!(ll_addr.must_have_zone());
6402
6403        let local_ip = local_ip::<Ipv6>();
6404        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6405            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6406        );
6407        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6408
6409        let socket = api.create();
6410        api.set_device(&socket, bind_device.as_ref()).unwrap();
6411
6412        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6413        api.connect(
6414            &socket,
6415            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6416            REMOTE_PORT.into(),
6417        )
6418        .expect("connect should succeed");
6419
6420        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6421
6422        api.disconnect(&socket).expect("was connected");
6423
6424        assert_eq!(api.get_bound_device(&socket), bind_device.map(FakeWeakDeviceId));
6425    }
6426
6427    #[test]
6428    fn test_bind_zoned_addr_connect_disconnect() {
6429        // If a socket is bound to a zoned address, the address's device should
6430        // be retained after `connect` then `disconnect`.
6431        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6432        assert!(ll_addr.must_have_zone());
6433
6434        let remote_ip = remote_ip::<Ipv6>();
6435        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6436            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![ll_addr], vec![remote_ip]),
6437        );
6438
6439        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6440
6441        let socket = api.create();
6442        api.listen(
6443            &socket,
6444            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6445            Some(LOCAL_PORT),
6446        )
6447        .unwrap();
6448        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
6449            .expect("connect should succeed");
6450
6451        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6452
6453        api.disconnect(&socket).expect("was connected");
6454        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6455    }
6456
6457    #[test]
6458    fn test_bind_device_after_connect_persists_after_disconnect() {
6459        // If a socket is bound to an unzoned address, connected to a zoned address, and then has
6460        // its device set, the device should be *retained* after `disconnect`.
6461        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6462        assert!(ll_addr.must_have_zone());
6463
6464        let local_ip = local_ip::<Ipv6>();
6465        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6466            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6467        );
6468        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6469        let socket = api.create();
6470        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6471        api.connect(
6472            &socket,
6473            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6474            REMOTE_PORT.into(),
6475        )
6476        .expect("connect should succeed");
6477
6478        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6479
6480        // This is a no-op functionally since the socket is already bound to the
6481        // device but it implies that we shouldn't unbind the device on
6482        // disconnect.
6483        api.set_device(&socket, Some(&FakeDeviceId)).expect("binding same device should succeed");
6484
6485        api.disconnect(&socket).expect("was connected");
6486        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6487    }
6488
6489    #[ip_test(I)]
6490    fn test_remove_udp_unbound<I: TestIpExt>() {
6491        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6492        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6493        let unbound = api.create();
6494        api.close(unbound).into_removed();
6495    }
6496
6497    #[ip_test(I)]
6498    fn test_hop_limits_used_for_sending_packets<I: TestIpExt>() {
6499        let some_multicast_addr: MulticastAddr<I::Addr> = I::map_ip(
6500            (),
6501            |()| Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
6502            |()| MulticastAddr::new(net_ip_v6!("ff0e::1")).unwrap(),
6503        );
6504
6505        let mut ctx =
6506            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
6507                vec![local_ip::<I>()],
6508                vec![remote_ip::<I>(), some_multicast_addr.into_specified()],
6509            ));
6510        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6511        let listener = api.create();
6512
6513        const UNICAST_HOPS: NonZeroU8 = NonZeroU8::new(23).unwrap();
6514        const MULTICAST_HOPS: NonZeroU8 = NonZeroU8::new(98).unwrap();
6515        api.set_unicast_hop_limit(&listener, Some(UNICAST_HOPS), I::VERSION).unwrap();
6516        api.set_multicast_hop_limit(&listener, Some(MULTICAST_HOPS), I::VERSION).unwrap();
6517
6518        api.listen(&listener, None, None).expect("listen failed");
6519
6520        let mut send_and_get_ttl = |remote_ip| {
6521            api.send_to(
6522                &listener,
6523                Some(ZonedAddr::Unzoned(remote_ip)),
6524                REMOTE_PORT.into(),
6525                Buf::new(vec![], ..),
6526            )
6527            .expect("send failed");
6528
6529            let (meta, _body) = api.core_ctx().bound_sockets.ip_socket_ctx.frames().last().unwrap();
6530            let SendIpPacketMeta { dst_ip, ttl, .. } = meta.try_as::<I>().unwrap();
6531            assert_eq!(*dst_ip, remote_ip);
6532            *ttl
6533        };
6534
6535        assert_eq!(send_and_get_ttl(some_multicast_addr.into_specified()), Some(MULTICAST_HOPS));
6536        assert_eq!(send_and_get_ttl(remote_ip::<I>()), Some(UNICAST_HOPS));
6537    }
6538
6539    const DUAL_STACK_ANY_ADDR: Ipv6Addr = net_ip_v6!("::");
6540    const DUAL_STACK_V4_ANY_ADDR: Ipv6Addr = net_ip_v6!("::FFFF:0.0.0.0");
6541
6542    #[derive(Copy, Clone, Debug)]
6543    enum DualStackBindAddr {
6544        Any,
6545        V4Any,
6546        V4Specific,
6547    }
6548
6549    impl DualStackBindAddr {
6550        const fn v6_addr(&self) -> Option<Ipv6Addr> {
6551            match self {
6552                Self::Any => Some(DUAL_STACK_ANY_ADDR),
6553                Self::V4Any => Some(DUAL_STACK_V4_ANY_ADDR),
6554                Self::V4Specific => None,
6555            }
6556        }
6557    }
6558    const V4_LOCAL_IP: Ipv4Addr = ip_v4!("192.168.1.10");
6559    const V4_LOCAL_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:192.168.1.10");
6560    const V6_LOCAL_IP: Ipv6Addr = net_ip_v6!("2201::1");
6561    const V6_REMOTE_IP: SpecifiedAddr<Ipv6Addr> =
6562        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("2001:db8::1")) };
6563    const V4_REMOTE_IP_MAPPED: SpecifiedAddr<Ipv6Addr> =
6564        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("::FFFF:192.0.2.1")) };
6565
6566    fn get_dual_stack_context<
6567        'a,
6568        BC: UdpBindingsTypes + 'a,
6569        CC: DatagramBoundStateContext<Ipv6, BC, Udp<BC>>,
6570    >(
6571        core_ctx: &'a mut CC,
6572    ) -> &'a mut CC::DualStackContext {
6573        match core_ctx.dual_stack_context_mut() {
6574            MaybeDualStack::NotDualStack(_) => unreachable!("UDP is a dual stack enabled protocol"),
6575            MaybeDualStack::DualStack(ds) => ds,
6576        }
6577    }
6578
6579    #[test_case(DualStackBindAddr::Any; "dual-stack")]
6580    #[test_case(DualStackBindAddr::V4Any; "v4 any")]
6581    #[test_case(DualStackBindAddr::V4Specific; "v4 specific")]
6582    fn dual_stack_delivery(bind_addr: DualStackBindAddr) {
6583        const REMOTE_IP: Ipv4Addr = ip_v4!("8.8.8.8");
6584        const REMOTE_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
6585        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
6586        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6587            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6588            vec![SpecifiedAddr::new(REMOTE_IP).unwrap()],
6589        ));
6590
6591        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6592        let listener = api.create();
6593        api.listen(
6594            &listener,
6595            SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6596            Some(LOCAL_PORT),
6597        )
6598        .expect("can bind");
6599
6600        const BODY: &[u8] = b"abcde";
6601        let (core_ctx, bindings_ctx) = api.contexts();
6602        receive_udp_packet(
6603            core_ctx,
6604            bindings_ctx,
6605            FakeDeviceId,
6606            UdpPacketMeta::<Ipv4> {
6607                src_ip: REMOTE_IP,
6608                src_port: Some(REMOTE_PORT),
6609                dst_ip: V4_LOCAL_IP,
6610                dst_port: LOCAL_PORT,
6611                dscp_and_ecn: DscpAndEcn::default(),
6612            },
6613            BODY,
6614        )
6615        .expect("receive udp packet should succeed");
6616
6617        assert_eq!(
6618            bindings_ctx.state.received::<Ipv6>(),
6619            &HashMap::from([(
6620                listener.downgrade(),
6621                SocketReceived {
6622                    packets: vec![ReceivedPacket {
6623                        body: BODY.into(),
6624                        meta: UdpPacketMeta::<Ipv6> {
6625                            src_ip: REMOTE_IP_MAPPED,
6626                            src_port: Some(REMOTE_PORT),
6627                            dst_ip: V4_LOCAL_IP_MAPPED,
6628                            dst_port: LOCAL_PORT,
6629                            dscp_and_ecn: DscpAndEcn::default(),
6630                        }
6631                    }],
6632                    max_size: usize::MAX,
6633                }
6634            )])
6635        );
6636    }
6637
6638    #[test_case(DualStackBindAddr::Any, true; "dual-stack any bind v4 first")]
6639    #[test_case(DualStackBindAddr::V4Any, true; "v4 any bind v4 first")]
6640    #[test_case(DualStackBindAddr::V4Specific, true; "v4 specific bind v4 first")]
6641    #[test_case(DualStackBindAddr::Any, false; "dual-stack any bind v4 second")]
6642    #[test_case(DualStackBindAddr::V4Any, false; "v4 any bind v4 second")]
6643    #[test_case(DualStackBindAddr::V4Specific, false; "v4 specific bind v4 second")]
6644    fn dual_stack_bind_conflict(bind_addr: DualStackBindAddr, bind_v4_first: bool) {
6645        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6646            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6647            vec![],
6648        ));
6649
6650        let v4_listener = UdpApi::<Ipv4, _>::new(ctx.as_mut()).create();
6651        let v6_listener = UdpApi::<Ipv6, _>::new(ctx.as_mut()).create();
6652
6653        let bind_v4 = |mut api: UdpApi<Ipv4, _>| {
6654            api.listen(
6655                &v4_listener,
6656                SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6657                Some(LOCAL_PORT),
6658            )
6659        };
6660        let bind_v6 = |mut api: UdpApi<Ipv6, _>| {
6661            api.listen(
6662                &v6_listener,
6663                SpecifiedAddr::new(bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED))
6664                    .map(ZonedAddr::Unzoned),
6665                Some(LOCAL_PORT),
6666            )
6667        };
6668
6669        let second_bind_error = if bind_v4_first {
6670            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect("no conflict");
6671            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect_err("should conflict")
6672        } else {
6673            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect("no conflict");
6674            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect_err("should conflict")
6675        };
6676        assert_eq!(second_bind_error, Either::Right(LocalAddressError::AddressInUse));
6677    }
6678
6679    // Verifies that port availability in both the IPv4 and IPv6 bound socket
6680    // maps is considered when allocating a local port for a dual-stack UDP
6681    // socket listening in both stacks.
6682    #[test_case(IpVersion::V4; "v4_is_constrained")]
6683    #[test_case(IpVersion::V6; "v6_is_constrained")]
6684    fn dual_stack_local_port_alloc(ip_version_with_constrained_ports: IpVersion) {
6685        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6686            vec![
6687                SpecifiedAddr::new(V4_LOCAL_IP.to_ip_addr()).unwrap(),
6688                SpecifiedAddr::new(V6_LOCAL_IP.to_ip_addr()).unwrap(),
6689            ],
6690            vec![],
6691        ));
6692
6693        // Specifically selected to be in the `EPHEMERAL_RANGE`.
6694        const AVAILABLE_PORT: NonZeroU16 = NonZeroU16::new(54321).unwrap();
6695
6696        // Densely pack the port space for one IP Version.
6697        for port in 1..=u16::MAX {
6698            let port = NonZeroU16::new(port).unwrap();
6699            if port == AVAILABLE_PORT {
6700                continue;
6701            }
6702            match ip_version_with_constrained_ports {
6703                IpVersion::V4 => {
6704                    let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
6705                    let listener = api.create();
6706                    api.listen(
6707                        &listener,
6708                        SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6709                        Some(port),
6710                    )
6711                    .expect("listen v4 should succeed")
6712                }
6713                IpVersion::V6 => {
6714                    let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6715                    let listener = api.create();
6716                    api.listen(
6717                        &listener,
6718                        SpecifiedAddr::new(V6_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6719                        Some(port),
6720                    )
6721                    .expect("listen v6 should succeed")
6722                }
6723            }
6724        }
6725
6726        // Create a listener on the dualstack any address, expecting it to be
6727        // allocated `AVAILABLE_PORT`.
6728        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6729        let listener = api.create();
6730        api.listen(&listener, None, None).expect("dualstack listen should succeed");
6731        let port = assert_matches!(api.get_info(&listener), SocketInfo::Listener(info) => info.local_identifier);
6732        assert_eq!(port, AVAILABLE_PORT);
6733    }
6734
6735    #[test_case(DualStackBindAddr::V4Any; "v4 any")]
6736    #[test_case(DualStackBindAddr::V4Specific; "v4 specific")]
6737    fn dual_stack_enable(bind_addr: DualStackBindAddr) {
6738        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6739            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6740            vec![],
6741        ));
6742        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6743
6744        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
6745        let listener = api.create();
6746
6747        assert_eq!(api.get_dual_stack_enabled(&listener), Ok(true));
6748        api.set_dual_stack_enabled(&listener, false).expect("can set dual-stack enabled");
6749
6750        // With dual-stack behavior disabled, the IPv6 socket can't bind to
6751        // an IPv4-mapped IPv6 address.
6752        assert_eq!(
6753            api.listen(
6754                &listener,
6755                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6756                Some(LOCAL_PORT),
6757            ),
6758            Err(Either::Right(LocalAddressError::CannotBindToAddress))
6759        );
6760        api.set_dual_stack_enabled(&listener, true).expect("can set dual-stack enabled");
6761        // Try again now that dual-stack sockets are enabled.
6762        assert_eq!(
6763            api.listen(
6764                &listener,
6765                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6766                Some(LOCAL_PORT),
6767            ),
6768            Ok(())
6769        );
6770    }
6771
6772    #[test]
6773    fn dual_stack_bind_unassigned_v4_address() {
6774        const NOT_ASSIGNED_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
6775        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6776            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6777            vec![],
6778        ));
6779        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6780
6781        let listener = api.create();
6782        assert_eq!(
6783            api.listen(
6784                &listener,
6785                SpecifiedAddr::new(NOT_ASSIGNED_MAPPED).map(|a| ZonedAddr::Unzoned(a)),
6786                Some(LOCAL_PORT),
6787            ),
6788            Err(Either::Right(LocalAddressError::CannotBindToAddress))
6789        );
6790    }
6791
6792    // Calling `connect` on an already bound socket will cause the existing
6793    // `listener` entry in the bound state map to be upgraded to a `connected`
6794    // entry. Dual-stack listeners may exist in both the IPv4 and IPv6 bound
6795    // state maps, so make sure both entries are properly removed.
6796    #[test]
6797    fn dual_stack_connect_cleans_up_existing_listener() {
6798        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6799            vec![Ipv6::TEST_ADDRS.local_ip],
6800            vec![Ipv6::TEST_ADDRS.remote_ip],
6801        ));
6802
6803        const DUAL_STACK_ANY_ADDR: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, FakeDeviceId>> = None;
6804
6805        fn assert_listeners(core_ctx: &mut FakeUdpCoreCtx<FakeDeviceId>, expect_present: bool) {
6806            const V4_LISTENER_ADDR: ListenerAddr<
6807                ListenerIpAddr<Ipv4Addr, NonZeroU16>,
6808                FakeWeakDeviceId<FakeDeviceId>,
6809            > = ListenerAddr {
6810                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
6811                device: None,
6812            };
6813            const V6_LISTENER_ADDR: ListenerAddr<
6814                ListenerIpAddr<Ipv6Addr, NonZeroU16>,
6815                FakeWeakDeviceId<FakeDeviceId>,
6816            > = ListenerAddr {
6817                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
6818                device: None,
6819            };
6820
6821            DualStackBoundStateContext::with_both_bound_sockets_mut(
6822                get_dual_stack_context(&mut core_ctx.bound_sockets),
6823                |_core_ctx, v6_sockets, v4_sockets| {
6824                    let v4 = v4_sockets.bound_sockets.listeners().get_by_addr(&V4_LISTENER_ADDR);
6825                    let v6 = v6_sockets.bound_sockets.listeners().get_by_addr(&V6_LISTENER_ADDR);
6826                    if expect_present {
6827                        assert_matches!(v4, Some(_));
6828                        assert_matches!(v6, Some(_));
6829                    } else {
6830                        assert_matches!(v4, None);
6831                        assert_matches!(v6, None);
6832                    }
6833                },
6834            );
6835        }
6836
6837        // Create a socket and listen on the IPv6 any address. Verify we have
6838        // listener state for both IPv4 and IPv6.
6839        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6840        let socket = api.create();
6841        assert_eq!(api.listen(&socket, DUAL_STACK_ANY_ADDR, Some(LOCAL_PORT)), Ok(()));
6842        assert_listeners(api.core_ctx(), true);
6843
6844        // Connect the socket to a remote V6 address and verify that both
6845        // the IPv4 and IPv6 listener state has been removed.
6846        assert_eq!(
6847            api.connect(
6848                &socket,
6849                Some(ZonedAddr::Unzoned(Ipv6::TEST_ADDRS.remote_ip)),
6850                REMOTE_PORT.into(),
6851            ),
6852            Ok(())
6853        );
6854        assert_matches!(api.get_info(&socket), SocketInfo::Connected(_));
6855        assert_listeners(api.core_ctx(), false);
6856    }
6857
6858    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
6859    #[test_case(net_ip_v6!("::"), false; "v6 any")]
6860    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
6861    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
6862    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
6863    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
6864    fn dual_stack_get_info(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
6865        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
6866            SpecifiedAddr<IpAddr>,
6867        >(
6868            vec![
6869                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
6870                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
6871            ],
6872            vec![],
6873        ));
6874        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6875
6876        let listener = api.create();
6877        api.set_dual_stack_enabled(&listener, enable_dual_stack)
6878            .expect("can set dual-stack enabled");
6879        let bind_addr = SpecifiedAddr::new(bind_addr);
6880        assert_eq!(
6881            api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT),),
6882            Ok(())
6883        );
6884
6885        assert_eq!(
6886            api.get_info(&listener),
6887            SocketInfo::Listener(datagram::ListenerInfo {
6888                local_ip: bind_addr.map(StrictlyZonedAddr::new_unzoned_or_panic),
6889                local_identifier: LOCAL_PORT,
6890            })
6891        );
6892    }
6893
6894    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
6895    #[test_case(net_ip_v6!("::"), false; "v6 any")]
6896    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
6897    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
6898    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
6899    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
6900    fn dual_stack_remove_listener(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
6901        // Ensure that when a socket is removed, it doesn't leave behind state
6902        // in the demultiplexing maps. Do this by binding a new socket at the
6903        // same address and asserting success.
6904        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
6905            SpecifiedAddr<IpAddr>,
6906        >(
6907            vec![
6908                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
6909                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
6910            ],
6911            vec![],
6912        ));
6913        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6914
6915        let mut bind_listener = || {
6916            let listener = api.create();
6917            api.set_dual_stack_enabled(&listener, enable_dual_stack)
6918                .expect("can set dual-stack enabled");
6919            let bind_addr = SpecifiedAddr::new(bind_addr);
6920            assert_eq!(
6921                api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT)),
6922                Ok(())
6923            );
6924
6925            api.close(listener).into_removed();
6926        };
6927
6928        // The first time should succeed because the state is empty.
6929        bind_listener();
6930        // The second time should succeed because the first removal didn't
6931        // leave any state behind.
6932        bind_listener();
6933    }
6934
6935    #[test_case(V6_REMOTE_IP, true; "This stack with dualstack enabled")]
6936    #[test_case(V6_REMOTE_IP, false; "This stack with dualstack disabled")]
6937    #[test_case(V4_REMOTE_IP_MAPPED, true; "other stack with dualstack enabled")]
6938    fn dualstack_remove_connected(remote_ip: SpecifiedAddr<Ipv6Addr>, enable_dual_stack: bool) {
6939        // Ensure that when a socket is removed, it doesn't leave behind state
6940        // in the demultiplexing maps. Do this by binding a new socket at the
6941        // same address and asserting success.
6942        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6943            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
6944            remote_ip.into(),
6945            [FakeDeviceId {}],
6946            |device_configs| {
6947                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6948                    device_configs,
6949                ))
6950            },
6951        );
6952        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6953
6954        let mut bind_connected = || {
6955            let socket = api.create();
6956            api.set_dual_stack_enabled(&socket, enable_dual_stack)
6957                .expect("can set dual-stack enabled");
6958            assert_eq!(
6959                api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into(),),
6960                Ok(())
6961            );
6962
6963            api.close(socket).into_removed();
6964        };
6965
6966        // The first time should succeed because the state is empty.
6967        bind_connected();
6968        // The second time should succeed because the first removal didn't
6969        // leave any state behind.
6970        bind_connected();
6971    }
6972
6973    #[test_case(false, V6_REMOTE_IP, Ok(());
6974        "connect to this stack with dualstack disabled")]
6975    #[test_case(true, V6_REMOTE_IP, Ok(());
6976        "connect to this stack with dualstack enabled")]
6977    #[test_case(false, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
6978        "connect to other stack with dualstack disabled")]
6979    #[test_case(true, V4_REMOTE_IP_MAPPED, Ok(());
6980        "connect to other stack with dualstack enabled")]
6981    fn dualstack_connect_unbound(
6982        enable_dual_stack: bool,
6983        remote_ip: SpecifiedAddr<Ipv6Addr>,
6984        expected_outcome: Result<(), ConnectError>,
6985    ) {
6986        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6987            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
6988            remote_ip.into(),
6989            [FakeDeviceId {}],
6990            |device_configs| {
6991                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6992                    device_configs,
6993                ))
6994            },
6995        );
6996        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6997
6998        let socket = api.create();
6999
7000        api.set_dual_stack_enabled(&socket, enable_dual_stack).expect("can set dual-stack enabled");
7001
7002        assert_eq!(
7003            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
7004            expected_outcome
7005        );
7006
7007        if expected_outcome.is_ok() {
7008            assert_matches!(
7009                api.get_info(&socket),
7010                SocketInfo::Connected(datagram::ConnInfo{
7011                    local_ip: _,
7012                    local_identifier: _,
7013                    remote_ip: found_remote_ip,
7014                    remote_identifier: found_remote_port,
7015                }) if found_remote_ip.addr() == remote_ip &&
7016                    found_remote_port == u16::from(REMOTE_PORT)
7017            );
7018            // Disconnect the socket, returning it to the original state.
7019            assert_eq!(api.disconnect(&socket), Ok(()));
7020        }
7021
7022        // Verify the original state is preserved.
7023        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
7024    }
7025
7026    #[test_case(V6_LOCAL_IP, V6_REMOTE_IP, Ok(());
7027        "listener in this stack connected in this stack")]
7028    #[test_case(V6_LOCAL_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
7029        "listener in this stack connected in other stack")]
7030    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V6_REMOTE_IP, Ok(());
7031        "listener in both stacks connected in this stack")]
7032    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V4_REMOTE_IP_MAPPED, Ok(());
7033        "listener in both stacks connected in other stack")]
7034    #[test_case(V4_LOCAL_IP_MAPPED, V6_REMOTE_IP,
7035        Err(ConnectError::RemoteUnexpectedlyNonMapped);
7036        "listener in other stack connected in this stack")]
7037    #[test_case(V4_LOCAL_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
7038        "listener in other stack connected in other stack")]
7039    fn dualstack_connect_listener(
7040        local_ip: Ipv6Addr,
7041        remote_ip: SpecifiedAddr<Ipv6Addr>,
7042        expected_outcome: Result<(), ConnectError>,
7043    ) {
7044        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7045            local_ip.to_ip_addr(),
7046            remote_ip.into(),
7047            [FakeDeviceId {}],
7048            |device_configs| {
7049                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7050                    device_configs,
7051                ))
7052            },
7053        );
7054        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7055        let socket = api.create();
7056
7057        assert_eq!(
7058            api.listen(
7059                &socket,
7060                SpecifiedAddr::new(local_ip).map(|local_ip| ZonedAddr::Unzoned(local_ip)),
7061                Some(LOCAL_PORT),
7062            ),
7063            Ok(())
7064        );
7065
7066        assert_eq!(
7067            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
7068            expected_outcome
7069        );
7070
7071        if expected_outcome.is_ok() {
7072            assert_matches!(
7073                api.get_info(&socket),
7074                SocketInfo::Connected(datagram::ConnInfo{
7075                    local_ip: _,
7076                    local_identifier: _,
7077                    remote_ip: found_remote_ip,
7078                    remote_identifier: found_remote_port,
7079                }) if found_remote_ip.addr() == remote_ip &&
7080                    found_remote_port == u16::from(REMOTE_PORT)
7081            );
7082            // Disconnect the socket, returning it to the original state.
7083            assert_eq!(api.disconnect(&socket), Ok(()));
7084        }
7085
7086        // Verify the original state is preserved.
7087        assert_matches!(
7088            api.get_info(&socket),
7089            SocketInfo::Listener(datagram::ListenerInfo {
7090                local_ip: found_local_ip,
7091                local_identifier: found_local_port,
7092            }) if found_local_port == LOCAL_PORT &&
7093                local_ip == found_local_ip.map(
7094                    |a| a.addr().get()
7095                ).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS)
7096        );
7097    }
7098
7099    #[test_case(V6_REMOTE_IP, V6_REMOTE_IP, Ok(());
7100        "connected in this stack reconnected in this stack")]
7101    #[test_case(V6_REMOTE_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
7102        "connected in this stack reconnected in other stack")]
7103    #[test_case(V4_REMOTE_IP_MAPPED, V6_REMOTE_IP,
7104        Err(ConnectError::RemoteUnexpectedlyNonMapped);
7105        "connected in other stack reconnected in this stack")]
7106    #[test_case(V4_REMOTE_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
7107        "connected in other stack reconnected in other stack")]
7108    fn dualstack_connect_connected(
7109        original_remote_ip: SpecifiedAddr<Ipv6Addr>,
7110        new_remote_ip: SpecifiedAddr<Ipv6Addr>,
7111        expected_outcome: Result<(), ConnectError>,
7112    ) {
7113        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
7114            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
7115            original_remote_ip.into(),
7116            [FakeDeviceId {}],
7117            |device_configs| {
7118                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
7119                    device_configs,
7120                ))
7121            },
7122        );
7123
7124        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7125        let socket = api.create();
7126
7127        assert_eq!(
7128            api.connect(&socket, Some(ZonedAddr::Unzoned(original_remote_ip)), REMOTE_PORT.into(),),
7129            Ok(())
7130        );
7131
7132        assert_eq!(
7133            api.connect(
7134                &socket,
7135                Some(ZonedAddr::Unzoned(new_remote_ip)),
7136                OTHER_REMOTE_PORT.into(),
7137            ),
7138            expected_outcome
7139        );
7140
7141        let (expected_remote_ip, expected_remote_port) = if expected_outcome.is_ok() {
7142            (new_remote_ip, OTHER_REMOTE_PORT)
7143        } else {
7144            // Verify the original state is preserved.
7145            (original_remote_ip, REMOTE_PORT)
7146        };
7147        assert_matches!(
7148            api.get_info(&socket),
7149            SocketInfo::Connected(datagram::ConnInfo{
7150                local_ip: _,
7151                local_identifier: _,
7152                remote_ip: found_remote_ip,
7153                remote_identifier: found_remote_port,
7154            }) if found_remote_ip.addr() == expected_remote_ip &&
7155                found_remote_port == u16::from(expected_remote_port)
7156        );
7157
7158        // Disconnect the socket and verify it returns to unbound state.
7159        assert_eq!(api.disconnect(&socket), Ok(()));
7160        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
7161    }
7162
7163    type FakeBoundSocketMap<I> =
7164        UdpBoundSocketMap<I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
7165    type FakePortAlloc<'a, I> =
7166        UdpPortAlloc<'a, I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
7167
7168    fn listen<I: IpExt>(
7169        ip: I::Addr,
7170        port: u16,
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: None,
7177        })
7178    }
7179
7180    fn listen_device<I: IpExt>(
7181        ip: I::Addr,
7182        port: u16,
7183        device: FakeWeakDeviceId<FakeDeviceId>,
7184    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
7185        let addr = SpecifiedAddr::new(ip).map(|a| SocketIpAddr::try_from(a).unwrap());
7186        let port = NonZeroU16::new(port).expect("port must be nonzero");
7187        AddrVec::Listen(ListenerAddr {
7188            ip: ListenerIpAddr { addr, identifier: port },
7189            device: Some(device),
7190        })
7191    }
7192
7193    fn conn<I: IpExt>(
7194        local_ip: I::Addr,
7195        local_port: u16,
7196        remote_ip: I::Addr,
7197        remote_port: u16,
7198    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
7199        let local_ip = SocketIpAddr::new(local_ip).expect("addr must be specified & non-mapped");
7200        let local_port = NonZeroU16::new(local_port).expect("port must be nonzero");
7201        let remote_ip = SocketIpAddr::new(remote_ip).expect("addr must be specified & non-mapped");
7202        let remote_port = NonZeroU16::new(remote_port).expect("port must be nonzero").into();
7203        AddrVec::Conn(ConnAddr {
7204            ip: ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) },
7205            device: None,
7206        })
7207    }
7208
7209    const SHARING_DOMAIN1: SharingDomain = SharingDomain::new(1);
7210    const SHARING_DOMAIN2: SharingDomain = SharingDomain::new(42);
7211    const EXCLUSIVE: Sharing = Sharing { reuse_addr: false, reuse_port: ReusePortOption::Disabled };
7212    const REUSE_ADDR: Sharing = Sharing { reuse_addr: true, reuse_port: ReusePortOption::Disabled };
7213    const REUSE_PORT: Sharing =
7214        Sharing { reuse_addr: false, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN1) };
7215    const REUSE_ADDR_PORT: Sharing =
7216        Sharing { reuse_addr: true, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN1) };
7217    const REUSE_PORT2: Sharing =
7218        Sharing { reuse_addr: false, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN2) };
7219    const REUSE_ADDR_PORT2: Sharing =
7220        Sharing { reuse_addr: true, reuse_port: ReusePortOption::Enabled(SHARING_DOMAIN2) };
7221
7222    #[test_case([
7223        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
7224        (listen(ip_v4!("0.0.0.0"), 2), EXCLUSIVE)],
7225            Ok(()); "listen_any_ip_different_port")]
7226    #[test_case([
7227        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
7228        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE)],
7229            Err(InsertError::Exists); "any_ip_same_port")]
7230    #[test_case([
7231        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7232        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7233            Err(InsertError::Exists); "listen_same_specific_ip")]
7234    #[test_case([
7235        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7236        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7237            Ok(()); "listen_same_specific_ip_reuse_addr")]
7238    #[test_case([
7239        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7240        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7241            Ok(()); "listen_same_specific_ip_reuse_port")]
7242    #[test_case([
7243        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7244        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7245            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr")]
7246    #[test_case([
7247        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7248        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7249            Ok(()); "listen_same_specific_ip_reuse_addr_and_reuse_addr_port")]
7250    #[test_case([
7251        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7252        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7253            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_port")]
7254    #[test_case([
7255        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7256        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7257            Ok(()); "listen_same_specific_ip_reuse_port_and_reuse_addr_port")]
7258    #[test_case([
7259        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7260        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7261            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port")]
7262    #[test_case([
7263        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7264        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7265            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr")]
7266    #[test_case([
7267        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7268        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7269            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_exclusive")]
7270    #[test_case([
7271        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7272        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7273            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_port")]
7274    #[test_case([
7275        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7276        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7277            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_exclusive")]
7278    #[test_case([
7279        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7280        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
7281            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr_port")]
7282    #[test_case([
7283        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7284        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
7285            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_exclusive")]
7286    #[test_case([
7287        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7288        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
7289            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_reuse_addr")]
7290    #[test_case([
7291        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7292        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7293            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_reuse_port")]
7294    #[test_case([
7295        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7296        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7297        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),],
7298            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_port_and_reuse_addr")]
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),
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_and_reuse_port")]
7304    #[test_case([
7305        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7306        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT2)],
7307            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_and_reuse_port2")]
7308    #[test_case([
7309        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7310        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT2)],
7311            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port2")]
7312    #[test_case([
7313        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7314        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT2),
7315        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
7316            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port2_and_reuse_port")]
7317    #[test_case([
7318        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7319        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7320            Ok(()); "conn_shadows_listener_reuse_port")]
7321    #[test_case([
7322        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7323        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7324            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive")]
7325    #[test_case([
7326        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7327        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7328            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive_reuse_port")]
7329    #[test_case([
7330        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7331        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7332            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_reuse_port_exclusive")]
7333    #[test_case([
7334        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7335        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7336            Err(InsertError::IndirectConflict); "conn_indirect_conflict_specific_listener")]
7337    #[test_case([
7338        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7339        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7340            Err(InsertError::IndirectConflict); "conn_indirect_conflict_any_listener")]
7341    #[test_case([
7342        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7343        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7344            Err(InsertError::IndirectConflict); "specific_listener_indirect_conflict_conn")]
7345    #[test_case([
7346        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7347        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7348            Err(InsertError::IndirectConflict); "any_listener_indirect_conflict_conn")]
7349    fn bind_sequence<
7350        C: IntoIterator<Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing)>,
7351    >(
7352        spec: C,
7353        expected: Result<(), InsertError>,
7354    ) {
7355        let mut primary_ids = Vec::new();
7356
7357        let mut create_socket = || {
7358            let primary =
7359                datagram::testutil::create_primary_id((), Default::default(), &Default::default());
7360            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7361            primary_ids.push(primary);
7362            id
7363        };
7364
7365        let mut map = FakeBoundSocketMap::<Ipv4>::default();
7366        let mut spec = spec.into_iter().peekable();
7367        let mut try_insert = |(addr, options)| match addr {
7368            AddrVec::Conn(c) => map
7369                .conns_mut()
7370                .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7371                .map(|_| ())
7372                .map_err(|(e, _)| e),
7373            AddrVec::Listen(l) => map
7374                .listeners_mut()
7375                .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7376                .map(|_| ())
7377                .map_err(|(e, _)| e),
7378        };
7379        let last = loop {
7380            let one_spec = spec.next().expect("empty list of test cases");
7381            if spec.peek().is_none() {
7382                break one_spec;
7383            } else {
7384                try_insert(one_spec).expect("intermediate bind failed")
7385            }
7386        };
7387
7388        let result = try_insert(last);
7389        assert_eq!(result, expected);
7390    }
7391
7392    #[test_case([
7393            (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7394            (listen(ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7395        ]; "distinct")]
7396    #[test_case([
7397            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7398            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7399        ]; "listen_reuse_port")]
7400    #[test_case([
7401            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7402            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7403        ]; "conn_reuse_port")]
7404    fn remove_sequence<I>(spec: I)
7405    where
7406        I: IntoIterator<
7407            Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing),
7408        >,
7409        I::IntoIter: ExactSizeIterator,
7410    {
7411        enum Socket<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes, LI, RI> {
7412            Listener(UdpSocketId<I, D, BT>, ListenerAddr<ListenerIpAddr<I::Addr, LI>, D>),
7413            Conn(UdpSocketId<I, D, BT>, ConnAddr<ConnIpAddr<I::Addr, LI, RI>, D>),
7414        }
7415        let spec = spec.into_iter();
7416        let spec_len = spec.len();
7417
7418        let mut primary_ids = Vec::new();
7419
7420        let mut create_socket = || {
7421            let primary =
7422                datagram::testutil::create_primary_id((), Default::default(), &Default::default());
7423            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7424            primary_ids.push(primary);
7425            id
7426        };
7427
7428        for spec in spec.permutations(spec_len) {
7429            let mut map = FakeBoundSocketMap::<Ipv4>::default();
7430            let sockets = spec
7431                .into_iter()
7432                .map(|(addr, options)| match addr {
7433                    AddrVec::Conn(c) => map
7434                        .conns_mut()
7435                        .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7436                        .map(|entry| {
7437                            Socket::Conn(
7438                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7439                                entry.get_addr().clone(),
7440                            )
7441                        })
7442                        .expect("insert_failed"),
7443                    AddrVec::Listen(l) => map
7444                        .listeners_mut()
7445                        .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7446                        .map(|entry| {
7447                            Socket::Listener(
7448                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7449                                entry.get_addr().clone(),
7450                            )
7451                        })
7452                        .expect("insert_failed"),
7453                })
7454                .collect::<Vec<_>>();
7455
7456            for socket in sockets {
7457                match socket {
7458                    Socket::Listener(l, addr) => {
7459                        assert_matches!(
7460                            map.listeners_mut().remove(&EitherIpSocket::V4(l), &addr),
7461                            Ok(())
7462                        );
7463                    }
7464                    Socket::Conn(c, addr) => {
7465                        assert_matches!(
7466                            map.conns_mut().remove(&EitherIpSocket::V4(c), &addr),
7467                            Ok(())
7468                        );
7469                    }
7470                }
7471            }
7472        }
7473    }
7474
7475    enum OriginalSocketState {
7476        Unbound,
7477        Listener,
7478        Connected,
7479    }
7480
7481    impl OriginalSocketState {
7482        fn create_socket<I, C>(&self, api: &mut UdpApi<I, C>) -> UdpApiSocketId<I, C>
7483        where
7484            I: TestIpExt,
7485            C: ContextPair,
7486            C::CoreContext: StateContext<I, C::BindingsContext>
7487                + UdpCounterContext<
7488                    I,
7489                    <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
7490                    C::BindingsContext,
7491                >,
7492            C::BindingsContext:
7493                UdpBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
7494            <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>: Default,
7495            <C::BindingsContext as UdpBindingsTypes>::SocketWritableListener: Default,
7496            <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId:
7497                netstack3_base::InterfaceProperties<
7498                        <C::BindingsContext as MatcherBindingsTypes>::DeviceClass,
7499                    >,
7500        {
7501            let socket = api.create();
7502            match self {
7503                OriginalSocketState::Unbound => {}
7504                OriginalSocketState::Listener => {
7505                    api.listen(
7506                        &socket,
7507                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)),
7508                        Some(LOCAL_PORT),
7509                    )
7510                    .expect("listen should succeed");
7511                }
7512                OriginalSocketState::Connected => {
7513                    api.connect(
7514                        &socket,
7515                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
7516                        UdpRemotePort::Set(REMOTE_PORT),
7517                    )
7518                    .expect("connect should succeed");
7519                }
7520            }
7521            socket
7522        }
7523    }
7524
7525    #[test_case(OriginalSocketState::Unbound; "unbound")]
7526    #[test_case(OriginalSocketState::Listener; "listener")]
7527    #[test_case(OriginalSocketState::Connected; "connected")]
7528    fn set_get_dual_stack_enabled_v4(original_state: OriginalSocketState) {
7529        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7530            vec![Ipv4::TEST_ADDRS.local_ip],
7531            vec![Ipv4::TEST_ADDRS.remote_ip],
7532        ));
7533        let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
7534        let socket = original_state.create_socket(&mut api);
7535
7536        for enabled in [true, false] {
7537            assert_eq!(
7538                api.set_dual_stack_enabled(&socket, enabled),
7539                Err(NotDualStackCapableError.into())
7540            );
7541            assert_eq!(api.get_dual_stack_enabled(&socket), Err(NotDualStackCapableError));
7542        }
7543    }
7544
7545    #[test_case(OriginalSocketState::Unbound, Ok(()); "unbound")]
7546    #[test_case(OriginalSocketState::Listener, Err(SetDualStackEnabledError::SocketIsBound);
7547        "listener")]
7548    #[test_case(OriginalSocketState::Connected, Err(SetDualStackEnabledError::SocketIsBound);
7549        "connected")]
7550    fn set_get_dual_stack_enabled_v6(
7551        original_state: OriginalSocketState,
7552        expected_result: Result<(), SetDualStackEnabledError>,
7553    ) {
7554        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7555            vec![Ipv6::TEST_ADDRS.local_ip],
7556            vec![Ipv6::TEST_ADDRS.remote_ip],
7557        ));
7558        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7559        let socket = original_state.create_socket(&mut api);
7560
7561        // Expect dual stack to be enabled by default.
7562        const ORIGINALLY_ENABLED: bool = true;
7563        assert_eq!(api.get_dual_stack_enabled(&socket), Ok(ORIGINALLY_ENABLED));
7564
7565        for enabled in [false, true] {
7566            assert_eq!(api.set_dual_stack_enabled(&socket, enabled), expected_result);
7567            let expect_enabled = match expected_result {
7568                Ok(_) => enabled,
7569                // If the set was unsuccessful expect the state to be unchanged.
7570                Err(_) => ORIGINALLY_ENABLED,
7571            };
7572            assert_eq!(api.get_dual_stack_enabled(&socket), Ok(expect_enabled));
7573        }
7574    }
7575
7576    #[ip_test(I)]
7577    #[test_case::test_matrix(
7578        [MarkDomain::Mark1, MarkDomain::Mark2],
7579        [None, Some(0), Some(1)]
7580    )]
7581    fn udp_socket_marks<I: TestIpExt>(domain: MarkDomain, mark: Option<u32>) {
7582        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7583            vec![I::TEST_ADDRS.local_ip],
7584            vec![I::TEST_ADDRS.remote_ip],
7585        ));
7586        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
7587        let socket = api.create();
7588
7589        // Doesn't have a mark by default.
7590        assert_eq!(api.get_mark(&socket, domain), Mark(None));
7591
7592        let mark = Mark(mark);
7593        // We can set and get back the mark.
7594        api.set_mark(&socket, domain, mark);
7595        assert_eq!(api.get_mark(&socket, domain), mark);
7596    }
7597}