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