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();
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::{IpAddr, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6SourceAddr};
2716    use net_types::{
2717        AddrAndZone, LinkLocalAddr, MulticastAddr, Scope as _, ScopeableAddress as _, ZonedAddr,
2718    };
2719    use netstack3_base::socket::{SocketIpAddrExt as _, StrictlyZonedAddr};
2720    use netstack3_base::sync::PrimaryRc;
2721    use netstack3_base::testutil::{
2722        set_logger_for_test, FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeReferencyDeviceId,
2723        FakeSocketWritableListener, FakeStrongDeviceId, FakeWeakDeviceId, MultipleDevicesId,
2724        TestIpExt as _,
2725    };
2726    use netstack3_base::{
2727        CtxPair, RemoteAddressError, ResourceCounterContext, SendFrameErrorReason,
2728        UninstantiableWrapper,
2729    };
2730    use netstack3_datagram::MulticastInterfaceSelector;
2731    use netstack3_ip::device::IpDeviceStateIpExt;
2732    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeDualStackIpSocketCtx};
2733    use netstack3_ip::testutil::{DualStackSendIpPacketMeta, FakeIpHeaderInfo};
2734    use netstack3_ip::{IpPacketDestination, ResolveRouteError, SendIpPacketMeta};
2735    use packet::Buf;
2736    use test_case::test_case;
2737
2738    use crate::internal::counters::testutil::{
2739        CounterExpectationsWithSocket, CounterExpectationsWithoutSocket,
2740    };
2741
2742    use super::*;
2743
2744    /// A packet received on a socket.
2745    #[derive(Debug, Derivative, PartialEq)]
2746    #[derivative(Default(bound = ""))]
2747    struct SocketReceived<I: Ip> {
2748        packets: Vec<ReceivedPacket<I>>,
2749    }
2750
2751    #[derive(Debug, PartialEq)]
2752    struct ReceivedPacket<I: Ip> {
2753        meta: UdpPacketMeta<I>,
2754        body: Vec<u8>,
2755    }
2756
2757    impl<D: FakeStrongDeviceId> FakeUdpCoreCtx<D> {
2758        fn new_with_device<I: TestIpExt>(device: D) -> Self {
2759            Self::with_local_remote_ip_addrs_and_device(
2760                vec![local_ip::<I>()],
2761                vec![remote_ip::<I>()],
2762                device,
2763            )
2764        }
2765
2766        fn with_local_remote_ip_addrs_and_device<A: Into<SpecifiedAddr<IpAddr>>>(
2767            local_ips: Vec<A>,
2768            remote_ips: Vec<A>,
2769            device: D,
2770        ) -> Self {
2771            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
2772                device,
2773                local_ips,
2774                remote_ips,
2775            }]))
2776        }
2777
2778        fn with_ip_socket_ctx_state(state: FakeDualStackIpSocketCtx<D>) -> Self {
2779            Self {
2780                all_sockets: Default::default(),
2781                bound_sockets: FakeUdpBoundSocketsCtx {
2782                    bound_sockets: Default::default(),
2783                    ip_socket_ctx: InnerIpSocketCtx::with_state(state),
2784                },
2785            }
2786        }
2787    }
2788
2789    impl FakeUdpCoreCtx<FakeDeviceId> {
2790        fn new_fake_device<I: TestIpExt>() -> Self {
2791            Self::new_with_device::<I>(FakeDeviceId)
2792        }
2793
2794        fn with_local_remote_ip_addrs<A: Into<SpecifiedAddr<IpAddr>>>(
2795            local_ips: Vec<A>,
2796            remote_ips: Vec<A>,
2797        ) -> Self {
2798            Self::with_local_remote_ip_addrs_and_device(local_ips, remote_ips, FakeDeviceId)
2799        }
2800    }
2801
2802    /// UDP tests context pair.
2803    type FakeUdpCtx<D> = CtxPair<FakeUdpCoreCtx<D>, FakeUdpBindingsCtx<D>>;
2804
2805    #[derive(Derivative)]
2806    #[derivative(Default(bound = ""))]
2807    struct FakeBoundSockets<D: StrongDeviceIdentifier> {
2808        v4: BoundSockets<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
2809        v6: BoundSockets<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
2810    }
2811
2812    impl<D: StrongDeviceIdentifier> FakeBoundSockets<D> {
2813        fn bound_sockets<I: IpExt>(&self) -> &BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
2814            I::map_ip_out(self, |state| &state.v4, |state| &state.v6)
2815        }
2816
2817        fn bound_sockets_mut<I: IpExt>(
2818            &mut self,
2819        ) -> &mut BoundSockets<I, D::Weak, FakeUdpBindingsCtx<D>> {
2820            I::map_ip_out(self, |state| &mut state.v4, |state| &mut state.v6)
2821        }
2822    }
2823
2824    struct FakeUdpBoundSocketsCtx<D: FakeStrongDeviceId> {
2825        bound_sockets: FakeBoundSockets<D>,
2826        ip_socket_ctx: InnerIpSocketCtx<D>,
2827    }
2828
2829    /// `FakeBindingsCtx` specialized for UDP.
2830    type FakeUdpBindingsCtx<D> = FakeBindingsCtx<(), (), FakeBindingsCtxState<D>, ()>;
2831
2832    /// The inner context providing a fake IP socket context to
2833    /// [`FakeUdpBoundSocketsCtx`].
2834    type InnerIpSocketCtx<D> =
2835        FakeCoreCtx<FakeDualStackIpSocketCtx<D>, DualStackSendIpPacketMeta<D>, D>;
2836
2837    type UdpFakeDeviceCtx = FakeUdpCtx<FakeDeviceId>;
2838    type UdpFakeDeviceCoreCtx = FakeUdpCoreCtx<FakeDeviceId>;
2839
2840    #[derive(Derivative)]
2841    #[derivative(Default(bound = ""))]
2842    struct FakeBindingsCtxState<D: StrongDeviceIdentifier> {
2843        received_v4:
2844            HashMap<WeakUdpSocketId<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv4>>,
2845        received_v6:
2846            HashMap<WeakUdpSocketId<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<Ipv6>>,
2847    }
2848
2849    impl<D: StrongDeviceIdentifier> FakeBindingsCtxState<D> {
2850        fn received<I: TestIpExt>(
2851            &self,
2852        ) -> &HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
2853        {
2854            #[derive(GenericOverIp)]
2855            #[generic_over_ip(I, Ip)]
2856            struct Wrap<'a, I: TestIpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
2857                &'a HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
2858            );
2859            let Wrap(map) = I::map_ip_out(
2860                self,
2861                |state| Wrap(&state.received_v4),
2862                |state| Wrap(&state.received_v6),
2863            );
2864            map
2865        }
2866
2867        fn received_mut<I: IpExt>(
2868            &mut self,
2869        ) -> &mut HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, SocketReceived<I>>
2870        {
2871            #[derive(GenericOverIp)]
2872            #[generic_over_ip(I, Ip)]
2873            struct Wrap<'a, I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
2874                &'a mut HashMap<WeakUdpSocketId<I, D, BT>, SocketReceived<I>>,
2875            );
2876            let Wrap(map) = I::map_ip_out(
2877                self,
2878                |state| Wrap(&mut state.received_v4),
2879                |state| Wrap(&mut state.received_v6),
2880            );
2881            map
2882        }
2883
2884        fn socket_data<I: TestIpExt>(
2885            &self,
2886        ) -> HashMap<WeakUdpSocketId<I, D::Weak, FakeUdpBindingsCtx<D>>, Vec<&'_ [u8]>> {
2887            self.received::<I>()
2888                .iter()
2889                .map(|(id, SocketReceived { packets })| {
2890                    (
2891                        id.clone(),
2892                        packets.iter().map(|ReceivedPacket { meta: _, body }| &body[..]).collect(),
2893                    )
2894                })
2895                .collect()
2896        }
2897    }
2898
2899    impl<I: IpExt, D: StrongDeviceIdentifier> UdpReceiveBindingsContext<I, D>
2900        for FakeUdpBindingsCtx<D>
2901    {
2902        fn receive_udp<B: BufferMut>(
2903            &mut self,
2904            id: &UdpSocketId<I, D::Weak, Self>,
2905            _device_id: &D,
2906            meta: UdpPacketMeta<I>,
2907            body: &B,
2908        ) {
2909            self.state
2910                .received_mut::<I>()
2911                .entry(id.downgrade())
2912                .or_default()
2913                .packets
2914                .push(ReceivedPacket { meta, body: body.as_ref().to_owned() })
2915        }
2916    }
2917
2918    impl<D: StrongDeviceIdentifier> UdpBindingsTypes for FakeUdpBindingsCtx<D> {
2919        type ExternalData<I: Ip> = ();
2920        type SocketWritableListener = FakeSocketWritableListener;
2921    }
2922
2923    /// Utilities for accessing locked internal state in tests.
2924    impl<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes> UdpSocketId<I, D, BT> {
2925        fn get(&self) -> impl Deref<Target = UdpSocketState<I, D, BT>> + '_ {
2926            self.state().read()
2927        }
2928
2929        fn get_mut(&self) -> impl DerefMut<Target = UdpSocketState<I, D, BT>> + '_ {
2930            self.state().write()
2931        }
2932    }
2933
2934    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpCoreCtx<D> {
2935        type DeviceId = D;
2936        type WeakDeviceId = FakeWeakDeviceId<D>;
2937    }
2938
2939    impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeUdpBoundSocketsCtx<D> {
2940        type DeviceId = D;
2941        type WeakDeviceId = FakeWeakDeviceId<D>;
2942    }
2943
2944    impl<I: TestIpExt, D: FakeStrongDeviceId> StateContext<I, FakeUdpBindingsCtx<D>>
2945        for FakeUdpCoreCtx<D>
2946    {
2947        type SocketStateCtx<'a> = FakeUdpBoundSocketsCtx<D>;
2948
2949        fn with_all_sockets_mut<
2950            O,
2951            F: FnOnce(&mut UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
2952        >(
2953            &mut self,
2954            cb: F,
2955        ) -> O {
2956            cb(self.all_sockets.socket_set_mut())
2957        }
2958
2959        fn with_all_sockets<
2960            O,
2961            F: FnOnce(&UdpSocketSet<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>) -> O,
2962        >(
2963            &mut self,
2964            cb: F,
2965        ) -> O {
2966            cb(self.all_sockets.socket_set())
2967        }
2968
2969        fn with_socket_state<
2970            O,
2971            F: FnOnce(
2972                &mut Self::SocketStateCtx<'_>,
2973                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
2974            ) -> O,
2975        >(
2976            &mut self,
2977            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
2978            cb: F,
2979        ) -> O {
2980            cb(&mut self.bound_sockets, &id.get())
2981        }
2982
2983        fn with_socket_state_mut<
2984            O,
2985            F: FnOnce(
2986                &mut Self::SocketStateCtx<'_>,
2987                &mut UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
2988            ) -> O,
2989        >(
2990            &mut self,
2991            id: &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
2992            cb: F,
2993        ) -> O {
2994            cb(&mut self.bound_sockets, &mut id.get_mut())
2995        }
2996
2997        fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
2998            &mut self,
2999            cb: F,
3000        ) -> O {
3001            cb(&mut self.bound_sockets)
3002        }
3003
3004        fn for_each_socket<
3005            F: FnMut(
3006                &mut Self::SocketStateCtx<'_>,
3007                &UdpSocketId<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3008                &UdpSocketState<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3009            ),
3010        >(
3011            &mut self,
3012            mut cb: F,
3013        ) {
3014            self.all_sockets.socket_set().keys().for_each(|id| {
3015                let id = UdpSocketId::from(id.clone());
3016                cb(&mut self.bound_sockets, &id, &id.get());
3017            })
3018        }
3019    }
3020
3021    impl<I: TestIpExt, D: FakeStrongDeviceId> BoundStateContext<I, FakeUdpBindingsCtx<D>>
3022        for FakeUdpBoundSocketsCtx<D>
3023    {
3024        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3025        type DualStackContext = I::UdpDualStackBoundStateContext<D>;
3026        type NonDualStackContext = I::UdpNonDualStackBoundStateContext<D>;
3027
3028        fn with_bound_sockets<
3029            O,
3030            F: FnOnce(
3031                &mut Self::IpSocketsCtx<'_>,
3032                &BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3033            ) -> O,
3034        >(
3035            &mut self,
3036            cb: F,
3037        ) -> O {
3038            let Self { bound_sockets, ip_socket_ctx } = self;
3039            cb(ip_socket_ctx, bound_sockets.bound_sockets())
3040        }
3041
3042        fn with_bound_sockets_mut<
3043            O,
3044            F: FnOnce(
3045                &mut Self::IpSocketsCtx<'_>,
3046                &mut BoundSockets<I, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3047            ) -> O,
3048        >(
3049            &mut self,
3050            cb: F,
3051        ) -> O {
3052            let Self { bound_sockets, ip_socket_ctx } = self;
3053            cb(ip_socket_ctx, bound_sockets.bound_sockets_mut())
3054        }
3055
3056        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3057            &mut self,
3058            cb: F,
3059        ) -> O {
3060            cb(&mut self.ip_socket_ctx)
3061        }
3062
3063        fn dual_stack_context(
3064            &mut self,
3065        ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
3066            struct Wrap<'a, I: TestIpExt, D: FakeStrongDeviceId + 'static>(
3067                MaybeDualStack<
3068                    &'a mut I::UdpDualStackBoundStateContext<D>,
3069                    &'a mut I::UdpNonDualStackBoundStateContext<D>,
3070                >,
3071            );
3072            // TODO(https://fxbug.dev/42082123): Replace this with a derived impl.
3073            impl<'a, I: TestIpExt, NewIp: TestIpExt, D: FakeStrongDeviceId + 'static>
3074                GenericOverIp<NewIp> for Wrap<'a, I, D>
3075            {
3076                type Type = Wrap<'a, NewIp, D>;
3077            }
3078
3079            let Wrap(context) = I::map_ip_out(
3080                self,
3081                |this| Wrap(MaybeDualStack::NotDualStack(this)),
3082                |this| Wrap(MaybeDualStack::DualStack(this)),
3083            );
3084            context
3085        }
3086    }
3087
3088    impl<D: FakeStrongDeviceId + 'static> UdpStateContext for FakeUdpBoundSocketsCtx<D> {}
3089
3090    impl<D: FakeStrongDeviceId> NonDualStackBoundStateContext<Ipv4, FakeUdpBindingsCtx<D>>
3091        for FakeUdpBoundSocketsCtx<D>
3092    {
3093    }
3094
3095    impl<D: FakeStrongDeviceId> DualStackBoundStateContext<Ipv6, FakeUdpBindingsCtx<D>>
3096        for FakeUdpBoundSocketsCtx<D>
3097    {
3098        type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
3099
3100        fn with_both_bound_sockets_mut<
3101            O,
3102            F: FnOnce(
3103                &mut Self::IpSocketsCtx<'_>,
3104                &mut BoundSockets<Ipv6, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3105                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3106            ) -> O,
3107        >(
3108            &mut self,
3109            cb: F,
3110        ) -> O {
3111            let Self { ip_socket_ctx, bound_sockets: FakeBoundSockets { v4, v6 } } = self;
3112            cb(ip_socket_ctx, v6, v4)
3113        }
3114
3115        fn with_other_bound_sockets_mut<
3116            O,
3117            F: FnOnce(
3118                &mut Self::IpSocketsCtx<'_>,
3119                &mut BoundSockets<Ipv4, Self::WeakDeviceId, FakeUdpBindingsCtx<D>>,
3120            ) -> O,
3121        >(
3122            &mut self,
3123            cb: F,
3124        ) -> O {
3125            DualStackBoundStateContext::with_both_bound_sockets_mut(
3126                self,
3127                |core_ctx, _bound, other_bound| cb(core_ctx, other_bound),
3128            )
3129        }
3130
3131        fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
3132            &mut self,
3133            cb: F,
3134        ) -> O {
3135            cb(&mut self.ip_socket_ctx)
3136        }
3137    }
3138
3139    /// Ip packet delivery for the [`FakeUdpCoreCtx`].
3140    impl<I: IpExt + IpDeviceStateIpExt + TestIpExt, D: FakeStrongDeviceId>
3141        IpTransportContext<I, FakeUdpBindingsCtx<D>, FakeUdpCoreCtx<D>> for UdpIpTransportContext
3142    {
3143        fn receive_icmp_error(
3144            _core_ctx: &mut FakeUdpCoreCtx<D>,
3145            _bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3146            _device: &D,
3147            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3148            _original_dst_ip: SpecifiedAddr<I::Addr>,
3149            _original_udp_packet: &[u8],
3150            _err: I::ErrorCode,
3151        ) {
3152            unimplemented!()
3153        }
3154
3155        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3156            core_ctx: &mut FakeUdpCoreCtx<D>,
3157            bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3158            device: &D,
3159            src_ip: I::RecvSrcAddr,
3160            dst_ip: SpecifiedAddr<I::Addr>,
3161            buffer: B,
3162            info: &LocalDeliveryPacketInfo<I, H>,
3163        ) -> Result<(), (B, TransportReceiveError)> {
3164            receive_ip_packet::<I, _, _, _, _>(
3165                core_ctx,
3166                bindings_ctx,
3167                device,
3168                src_ip,
3169                dst_ip,
3170                buffer,
3171                info,
3172            )
3173        }
3174    }
3175
3176    #[derive(Derivative)]
3177    #[derivative(Default(bound = ""))]
3178    struct FakeDualStackSocketState<D: StrongDeviceIdentifier> {
3179        v4: UdpSocketSet<Ipv4, D::Weak, FakeUdpBindingsCtx<D>>,
3180        v6: UdpSocketSet<Ipv6, D::Weak, FakeUdpBindingsCtx<D>>,
3181        udpv4_counters_with_socket: UdpCountersWithSocket<Ipv4>,
3182        udpv6_counters_with_socket: UdpCountersWithSocket<Ipv6>,
3183        udpv4_counters_without_socket: UdpCountersWithoutSocket<Ipv4>,
3184        udpv6_counters_without_socket: UdpCountersWithoutSocket<Ipv6>,
3185    }
3186
3187    impl<D: StrongDeviceIdentifier> FakeDualStackSocketState<D> {
3188        fn socket_set<I: IpExt>(&self) -> &UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3189            I::map_ip_out(self, |dual| &dual.v4, |dual| &dual.v6)
3190        }
3191
3192        fn socket_set_mut<I: IpExt>(
3193            &mut self,
3194        ) -> &mut UdpSocketSet<I, D::Weak, FakeUdpBindingsCtx<D>> {
3195            I::map_ip_out(self, |dual| &mut dual.v4, |dual| &mut dual.v6)
3196        }
3197
3198        fn udp_counters_with_socket<I: Ip>(&self) -> &UdpCountersWithSocket<I> {
3199            I::map_ip_out(
3200                self,
3201                |dual| &dual.udpv4_counters_with_socket,
3202                |dual| &dual.udpv6_counters_with_socket,
3203            )
3204        }
3205        fn udp_counters_without_socket<I: Ip>(&self) -> &UdpCountersWithoutSocket<I> {
3206            I::map_ip_out(
3207                self,
3208                |dual| &dual.udpv4_counters_without_socket,
3209                |dual| &dual.udpv6_counters_without_socket,
3210            )
3211        }
3212    }
3213    struct FakeUdpCoreCtx<D: FakeStrongDeviceId> {
3214        bound_sockets: FakeUdpBoundSocketsCtx<D>,
3215        // NB: socket sets are last in the struct so all the strong refs are
3216        // dropped before the primary refs contained herein.
3217        all_sockets: FakeDualStackSocketState<D>,
3218    }
3219
3220    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithSocket<I>> for FakeUdpCoreCtx<D> {
3221        fn counters(&self) -> &UdpCountersWithSocket<I> {
3222            &self.all_sockets.udp_counters_with_socket()
3223        }
3224    }
3225
3226    impl<I: Ip, D: FakeStrongDeviceId> CounterContext<UdpCountersWithoutSocket<I>>
3227        for FakeUdpCoreCtx<D>
3228    {
3229        fn counters(&self) -> &UdpCountersWithoutSocket<I> {
3230            &self.all_sockets.udp_counters_without_socket()
3231        }
3232    }
3233
3234    impl<I: DualStackIpExt, D: FakeStrongDeviceId>
3235        ResourceCounterContext<
3236            UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3237            UdpCountersWithSocket<I>,
3238        > for FakeUdpCoreCtx<D>
3239    {
3240        fn per_resource_counters<'a>(
3241            &'a self,
3242            resource: &'a UdpSocketId<I, FakeWeakDeviceId<D>, FakeUdpBindingsCtx<D>>,
3243        ) -> &'a UdpCountersWithSocket<I> {
3244            resource.counters()
3245        }
3246    }
3247
3248    fn local_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3249        I::get_other_ip_address(1)
3250    }
3251
3252    fn remote_ip<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
3253        I::get_other_ip_address(2)
3254    }
3255
3256    trait BaseTestIpExt: netstack3_base::testutil::TestIpExt + IpExt + IpDeviceStateIpExt {
3257        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3258            DualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3259        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static>:
3260            NonDualStackDatagramBoundStateContext<Self, FakeUdpBindingsCtx<D>, Udp<FakeUdpBindingsCtx<D>>, DeviceId=D, WeakDeviceId=D::Weak>;
3261        fn into_recv_src_addr(addr: Self::Addr) -> Self::RecvSrcAddr;
3262    }
3263
3264    impl BaseTestIpExt for Ipv4 {
3265        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3266            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3267
3268        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3269            FakeUdpBoundSocketsCtx<D>;
3270
3271        fn into_recv_src_addr(addr: Ipv4Addr) -> Ipv4Addr {
3272            addr
3273        }
3274    }
3275
3276    impl BaseTestIpExt for Ipv6 {
3277        type UdpDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3278            FakeUdpBoundSocketsCtx<D>;
3279        type UdpNonDualStackBoundStateContext<D: FakeStrongDeviceId + 'static> =
3280            UninstantiableWrapper<FakeUdpBoundSocketsCtx<D>>;
3281
3282        fn into_recv_src_addr(addr: Ipv6Addr) -> Ipv6SourceAddr {
3283            Ipv6SourceAddr::new(addr).unwrap_or_else(|| panic!("{addr} is not a valid source addr"))
3284        }
3285    }
3286
3287    trait TestIpExt: BaseTestIpExt<OtherVersion: BaseTestIpExt> {}
3288    impl<I: BaseTestIpExt<OtherVersion: BaseTestIpExt>> TestIpExt for I {}
3289
3290    /// Helper function to inject an UDP packet with the provided parameters.
3291    fn receive_udp_packet<
3292        I: TestIpExt,
3293        D: FakeStrongDeviceId,
3294        CC: DeviceIdContext<AnyDevice, DeviceId = D>,
3295    >(
3296        core_ctx: &mut CC,
3297        bindings_ctx: &mut FakeUdpBindingsCtx<D>,
3298        device: D,
3299        meta: UdpPacketMeta<I>,
3300        body: &[u8],
3301    ) -> Result<(), TransportReceiveError>
3302    where
3303        UdpIpTransportContext: IpTransportContext<I, FakeUdpBindingsCtx<D>, CC>,
3304    {
3305        let UdpPacketMeta { src_ip, src_port, dst_ip, dst_port, dscp_and_ecn } = meta;
3306        let builder = UdpPacketBuilder::new(src_ip, dst_ip, src_port, dst_port);
3307
3308        let buffer = Buf::new(body.to_owned(), ..)
3309            .encapsulate(builder)
3310            .serialize_vec_outer()
3311            .unwrap()
3312            .into_inner();
3313        <UdpIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
3314            core_ctx,
3315            bindings_ctx,
3316            &device,
3317            I::into_recv_src_addr(src_ip),
3318            SpecifiedAddr::new(dst_ip).unwrap(),
3319            buffer,
3320            &LocalDeliveryPacketInfo {
3321                header_info: FakeIpHeaderInfo { dscp_and_ecn, ..Default::default() },
3322                ..Default::default()
3323            },
3324        )
3325        .map_err(|(_buffer, e)| e)
3326    }
3327
3328    const LOCAL_PORT: NonZeroU16 = NonZeroU16::new(100).unwrap();
3329    const OTHER_LOCAL_PORT: NonZeroU16 = LOCAL_PORT.checked_add(1).unwrap();
3330    const REMOTE_PORT: NonZeroU16 = NonZeroU16::new(200).unwrap();
3331    const OTHER_REMOTE_PORT: NonZeroU16 = REMOTE_PORT.checked_add(1).unwrap();
3332
3333    fn conn_addr<I>(
3334        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3335    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3336    where
3337        I: TestIpExt,
3338    {
3339        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3340        let remote_ip = SocketIpAddr::try_from(remote_ip::<I>()).unwrap();
3341        ConnAddr {
3342            ip: ConnIpAddr {
3343                local: (local_ip, LOCAL_PORT),
3344                remote: (remote_ip, REMOTE_PORT.into()),
3345            },
3346            device,
3347        }
3348        .into()
3349    }
3350
3351    fn local_listener<I>(
3352        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3353    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3354    where
3355        I: TestIpExt,
3356    {
3357        let local_ip = SocketIpAddr::try_from(local_ip::<I>()).unwrap();
3358        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: Some(local_ip) }, device }
3359            .into()
3360    }
3361
3362    fn wildcard_listener<I>(
3363        device: Option<FakeWeakDeviceId<FakeDeviceId>>,
3364    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>
3365    where
3366        I: TestIpExt,
3367    {
3368        ListenerAddr { ip: ListenerIpAddr { identifier: LOCAL_PORT, addr: None }, device }.into()
3369    }
3370
3371    #[track_caller]
3372    fn assert_counters<
3373        'a,
3374        I: IpExt,
3375        D: WeakDeviceIdentifier,
3376        BT: UdpBindingsTypes,
3377        CC: UdpCounterContext<I, D, BT>,
3378    >(
3379        core_ctx: &CC,
3380        with_socket_expects: CounterExpectationsWithSocket,
3381        without_socket_expects: CounterExpectationsWithoutSocket,
3382        per_socket_expects: impl IntoIterator<
3383            Item = (&'a UdpSocketId<I, D, BT>, CounterExpectationsWithSocket),
3384        >,
3385    ) {
3386        assert_eq!(
3387            CounterExpectationsWithSocket::from(
3388                CounterContext::<UdpCountersWithSocket<I>>::counters(core_ctx).as_ref()
3389            ),
3390            with_socket_expects
3391        );
3392        assert_eq!(
3393            CounterExpectationsWithoutSocket::from(
3394                CounterContext::<UdpCountersWithoutSocket<I>>::counters(core_ctx).as_ref()
3395            ),
3396            without_socket_expects
3397        );
3398        for (id, expects) in per_socket_expects.into_iter() {
3399            assert_eq!(
3400                CounterExpectationsWithSocket::from(core_ctx.per_resource_counters(id).as_ref()),
3401                expects
3402            );
3403        }
3404    }
3405
3406    #[ip_test(I)]
3407    #[test_case(conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))), [
3408            conn_addr(None), local_listener(Some(FakeWeakDeviceId(FakeDeviceId))), local_listener(None),
3409            wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)
3410        ]; "conn with device")]
3411    #[test_case(local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3412        [local_listener(None), wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), wildcard_listener(None)];
3413        "local listener with device")]
3414    #[test_case(wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))), [wildcard_listener(None)];
3415        "wildcard listener with device")]
3416    #[test_case(conn_addr(None), [local_listener(None), wildcard_listener(None)]; "conn no device")]
3417    #[test_case(local_listener(None), [wildcard_listener(None)]; "local listener no device")]
3418    #[test_case(wildcard_listener(None), []; "wildcard listener no device")]
3419    fn test_udp_addr_vec_iter_shadows_conn<I: IpExt, D: WeakDeviceIdentifier, const N: usize>(
3420        addr: AddrVec<I, D, UdpAddrSpec>,
3421        expected_shadows: [AddrVec<I, D, UdpAddrSpec>; N],
3422    ) {
3423        assert_eq!(addr.iter_shadows().collect::<HashSet<_>>(), HashSet::from(expected_shadows));
3424    }
3425
3426    #[ip_test(I)]
3427    fn test_iter_receiving_addrs<I: TestIpExt>() {
3428        let addr = ConnIpAddr {
3429            local: (SocketIpAddr::try_from(local_ip::<I>()).unwrap(), LOCAL_PORT),
3430            remote: (SocketIpAddr::try_from(remote_ip::<I>()).unwrap(), REMOTE_PORT.into()),
3431        };
3432        assert_eq!(
3433            iter_receiving_addrs::<I, _>(addr, FakeWeakDeviceId(FakeDeviceId)).collect::<Vec<_>>(),
3434            vec![
3435                // A socket connected on exactly the receiving vector has precedence.
3436                conn_addr(Some(FakeWeakDeviceId(FakeDeviceId))),
3437                // Connected takes precedence over listening with device match.
3438                conn_addr(None),
3439                local_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3440                // Specific IP takes precedence over device match.
3441                local_listener(None),
3442                wildcard_listener(Some(FakeWeakDeviceId(FakeDeviceId))),
3443                // Fallback to least specific
3444                wildcard_listener(None)
3445            ]
3446        );
3447    }
3448
3449    /// Tests UDP listeners over different IP versions.
3450    ///
3451    /// Tests that a listener can be created, that the context receives packet
3452    /// notifications for that listener, and that we can send data using that
3453    /// listener.
3454    #[ip_test(I)]
3455    fn test_listen_udp<I: TestIpExt>() {
3456        set_logger_for_test();
3457        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3458        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3459        let local_ip = local_ip::<I>();
3460        let remote_ip = remote_ip::<I>();
3461        let socket = api.create();
3462        // Create a listener on the local port, bound to the local IP:
3463        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3464            .expect("listen_udp failed");
3465
3466        // Inject a packet and check that the context receives it:
3467        let body = [1, 2, 3, 4, 5];
3468        let (core_ctx, bindings_ctx) = api.contexts();
3469        let meta = UdpPacketMeta::<I> {
3470            src_ip: remote_ip.get(),
3471            src_port: Some(REMOTE_PORT),
3472            dst_ip: local_ip.get(),
3473            dst_port: LOCAL_PORT,
3474            dscp_and_ecn: DscpAndEcn::default(),
3475        };
3476        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body[..])
3477            .expect("receive udp packet should succeed");
3478
3479        assert_eq!(
3480            bindings_ctx.state.received::<I>(),
3481            &HashMap::from([(
3482                socket.downgrade(),
3483                SocketReceived { packets: vec![ReceivedPacket { meta, body: body.into() }] }
3484            )])
3485        );
3486
3487        // Send a packet providing a local ip:
3488        api.send_to(
3489            &socket,
3490            Some(ZonedAddr::Unzoned(remote_ip)),
3491            REMOTE_PORT.into(),
3492            Buf::new(body.to_vec(), ..),
3493        )
3494        .expect("send_to suceeded");
3495
3496        // And send a packet that doesn't:
3497        api.send_to(
3498            &socket,
3499            Some(ZonedAddr::Unzoned(remote_ip)),
3500            REMOTE_PORT.into(),
3501            Buf::new(body.to_vec(), ..),
3502        )
3503        .expect("send_to succeeded");
3504        let frames = api.core_ctx().bound_sockets.ip_socket_ctx.frames();
3505        assert_eq!(frames.len(), 2);
3506        let check_frame =
3507            |(meta, frame_body): &(DualStackSendIpPacketMeta<FakeDeviceId>, Vec<u8>)| {
3508                let SendIpPacketMeta {
3509                    device: _,
3510                    src_ip,
3511                    dst_ip,
3512                    destination,
3513                    proto,
3514                    ttl: _,
3515                    mtu: _,
3516                    dscp_and_ecn: _,
3517                } = meta.try_as::<I>().unwrap();
3518                assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
3519                assert_eq!(src_ip, &local_ip);
3520                assert_eq!(dst_ip, &remote_ip);
3521                assert_eq!(proto, &IpProto::Udp.into());
3522                let mut buf = &frame_body[..];
3523                let udp_packet =
3524                    UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
3525                        .expect("Parsed sent UDP packet");
3526                assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
3527                assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
3528                assert_eq!(udp_packet.body(), &body[..]);
3529            };
3530        check_frame(&frames[0]);
3531        check_frame(&frames[1]);
3532    }
3533
3534    /// Tests that UDP packets without a connection are dropped.
3535    ///
3536    /// Tests that receiving a UDP packet on a port over which there isn't a
3537    /// listener causes the packet to be dropped correctly.
3538    #[ip_test(I)]
3539    fn test_udp_drop<I: TestIpExt>() {
3540        set_logger_for_test();
3541        let UdpFakeDeviceCtx { mut core_ctx, mut bindings_ctx } =
3542            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3543        let local_ip = local_ip::<I>();
3544        let remote_ip = remote_ip::<I>();
3545
3546        let meta = UdpPacketMeta::<I> {
3547            src_ip: remote_ip.get(),
3548            src_port: Some(REMOTE_PORT),
3549            dst_ip: local_ip.get(),
3550            dst_port: LOCAL_PORT,
3551            dscp_and_ecn: DscpAndEcn::default(),
3552        };
3553        let body = [1, 2, 3, 4, 5];
3554        assert_matches!(
3555            receive_udp_packet(&mut core_ctx, &mut bindings_ctx, FakeDeviceId, meta, &body[..]),
3556            Err(TransportReceiveError::PortUnreachable)
3557        );
3558        assert_eq!(&bindings_ctx.state.socket_data::<I>(), &HashMap::new());
3559    }
3560
3561    /// Tests that UDP connections can be created and data can be transmitted
3562    /// over it.
3563    ///
3564    /// Only tests with specified local port and address bounds.
3565    #[ip_test(I)]
3566    fn test_udp_conn_basic<I: TestIpExt>() {
3567        set_logger_for_test();
3568        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3569        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3570        let local_ip = local_ip::<I>();
3571        let remote_ip = remote_ip::<I>();
3572        let socket = api.create();
3573        // Create a UDP connection with a specified local port and local IP.
3574        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3575            .expect("listen_udp failed");
3576        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3577            .expect("connect failed");
3578
3579        // Inject a UDP packet and see if we receive it on the context.
3580        let meta = UdpPacketMeta::<I> {
3581            src_ip: remote_ip.get(),
3582            src_port: Some(REMOTE_PORT),
3583            dst_ip: local_ip.get(),
3584            dst_port: LOCAL_PORT,
3585            dscp_and_ecn: DscpAndEcn::default(),
3586        };
3587        let body = [1, 2, 3, 4, 5];
3588        let (core_ctx, bindings_ctx) = api.contexts();
3589        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..])
3590            .expect("receive udp packet should succeed");
3591
3592        assert_eq!(
3593            bindings_ctx.state.socket_data(),
3594            HashMap::from([(socket.downgrade(), vec![&body[..]])])
3595        );
3596
3597        // Now try to send something over this new connection.
3598        api.send(&socket, Buf::new(body.to_vec(), ..)).expect("send_udp_conn returned an error");
3599
3600        let (meta, frame_body) =
3601            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
3602        // Check first frame.
3603        let SendIpPacketMeta {
3604            device: _,
3605            src_ip,
3606            dst_ip,
3607            destination,
3608            proto,
3609            ttl: _,
3610            mtu: _,
3611            dscp_and_ecn: _,
3612        } = meta.try_as::<I>().unwrap();
3613        assert_eq!(destination, &IpPacketDestination::Neighbor(remote_ip));
3614        assert_eq!(src_ip, &local_ip);
3615        assert_eq!(dst_ip, &remote_ip);
3616        assert_eq!(proto, &IpProto::Udp.into());
3617        let mut buf = &frame_body[..];
3618        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
3619            .expect("Parsed sent UDP packet");
3620        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
3621        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
3622        assert_eq!(udp_packet.body(), &body[..]);
3623
3624        let expects_with_socket =
3625            || CounterExpectationsWithSocket { rx_delivered: 1, tx: 1, ..Default::default() };
3626        assert_counters(
3627            api.core_ctx(),
3628            expects_with_socket(),
3629            CounterExpectationsWithoutSocket { rx: 1, ..Default::default() },
3630            [(&socket, expects_with_socket())],
3631        )
3632    }
3633
3634    /// Tests that UDP connections fail with an appropriate error for
3635    /// non-routable remote addresses.
3636    #[ip_test(I)]
3637    fn test_udp_conn_unroutable<I: TestIpExt>() {
3638        set_logger_for_test();
3639        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3640        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3641        // Set fake context callback to treat all addresses as unroutable.
3642        let remote_ip = I::get_other_ip_address(127);
3643        // Create a UDP connection with a specified local port and local IP.
3644        let unbound = api.create();
3645        let conn_err = api
3646            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3647            .unwrap_err();
3648
3649        assert_eq!(conn_err, ConnectError::Ip(ResolveRouteError::Unreachable.into()));
3650    }
3651
3652    /// Tests that UDP listener creation fails with an appropriate error when
3653    /// local address is non-local.
3654    #[ip_test(I)]
3655    fn test_udp_conn_cannot_bind<I: TestIpExt>() {
3656        set_logger_for_test();
3657        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3658        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3659
3660        // Use remote address to trigger IpSockCreationError::LocalAddrNotAssigned.
3661        let remote_ip = remote_ip::<I>();
3662        // Create a UDP listener with a specified local port and local ip:
3663        let unbound = api.create();
3664        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT));
3665
3666        assert_eq!(result, Err(Either::Right(LocalAddressError::CannotBindToAddress)));
3667    }
3668
3669    #[test]
3670    fn test_udp_conn_picks_link_local_source_address() {
3671        set_logger_for_test();
3672        // When the remote address has global scope but the source address
3673        // is link-local, make sure that the socket implicitly has its bound
3674        // device set.
3675        set_logger_for_test();
3676        let local_ip = SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap();
3677        let remote_ip = SpecifiedAddr::new(net_ip_v6!("1:2:3:4::")).unwrap();
3678        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
3679            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
3680        );
3681        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
3682        let socket = api.create();
3683        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3684            .expect("can connect");
3685
3686        let info = api.get_info(&socket);
3687        let (conn_local_ip, conn_remote_ip) = assert_matches!(
3688            info,
3689            SocketInfo::Connected(datagram::ConnInfo {
3690                local_ip: conn_local_ip,
3691                remote_ip: conn_remote_ip,
3692                local_identifier: _,
3693                remote_identifier: _,
3694            }) => (conn_local_ip, conn_remote_ip)
3695        );
3696        assert_eq!(
3697            conn_local_ip,
3698            StrictlyZonedAddr::new_with_zone(local_ip, || FakeWeakDeviceId(FakeDeviceId)),
3699        );
3700        assert_eq!(conn_remote_ip, StrictlyZonedAddr::new_unzoned_or_panic(remote_ip));
3701
3702        // Double-check that the bound device can't be changed after being set
3703        // implicitly.
3704        assert_eq!(
3705            api.set_device(&socket, None),
3706            Err(SocketError::Local(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)))
3707        );
3708    }
3709
3710    #[ip_test(I)]
3711    #[test_case(
3712        true,
3713        Err(IpSockCreationError::Route(ResolveRouteError::Unreachable).into()); "remove device")]
3714    #[test_case(false, Ok(()); "dont remove device")]
3715    fn test_udp_conn_device_removed<I: TestIpExt>(
3716        remove_device: bool,
3717        expected: Result<(), ConnectError>,
3718    ) {
3719        set_logger_for_test();
3720        let device = FakeReferencyDeviceId::default();
3721        let mut ctx =
3722            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
3723        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3724
3725        let unbound = api.create();
3726        api.set_device(&unbound, Some(&device)).unwrap();
3727
3728        if remove_device {
3729            device.mark_removed();
3730        }
3731
3732        let remote_ip = remote_ip::<I>();
3733        assert_eq!(
3734            api.connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
3735            expected,
3736        );
3737    }
3738
3739    /// Tests that UDP connections fail with an appropriate error when local
3740    /// ports are exhausted.
3741    #[ip_test(I)]
3742    fn test_udp_conn_exhausted<I: TestIpExt>() {
3743        // NB: We don't enable logging for this test because it's very spammy.
3744        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3745        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3746
3747        let local_ip = local_ip::<I>();
3748        // Exhaust local ports to trigger FailedToAllocateLocalPort error.
3749        for port_num in FakePortAlloc::<I>::EPHEMERAL_RANGE {
3750            let socket = api.create();
3751            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), NonZeroU16::new(port_num))
3752                .unwrap();
3753        }
3754
3755        let remote_ip = remote_ip::<I>();
3756        let unbound = api.create();
3757        let conn_err = api
3758            .connect(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3759            .unwrap_err();
3760
3761        assert_eq!(conn_err, ConnectError::CouldNotAllocateLocalPort);
3762    }
3763
3764    #[ip_test(I)]
3765    fn test_connect_success<I: TestIpExt>() {
3766        set_logger_for_test();
3767        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3768        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3769
3770        let local_ip = local_ip::<I>();
3771        let remote_ip = remote_ip::<I>();
3772        let multicast_addr = I::get_multicast_addr(3);
3773        let socket = api.create();
3774
3775        // Set some properties on the socket that should be preserved.
3776        api.set_posix_reuse_port(&socket, true).expect("is unbound");
3777        api.set_multicast_membership(
3778            &socket,
3779            multicast_addr,
3780            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
3781            true,
3782        )
3783        .expect("join multicast group should succeed");
3784
3785        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3786            .expect("Initial call to listen_udp was expected to succeed");
3787
3788        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3789            .expect("connect should succeed");
3790
3791        // Check that socket options set on the listener are propagated to the
3792        // connected socket.
3793        assert!(api.get_posix_reuse_port(&socket));
3794        assert_eq!(
3795            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
3796            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
3797        );
3798        assert_eq!(
3799            api.set_multicast_membership(
3800                &socket,
3801                multicast_addr,
3802                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
3803                true
3804            ),
3805            Err(SetMulticastMembershipError::GroupAlreadyJoined)
3806        );
3807    }
3808
3809    #[ip_test(I)]
3810    fn test_connect_fails<I: TestIpExt>() {
3811        set_logger_for_test();
3812        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3813        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3814        let local_ip = local_ip::<I>();
3815        let remote_ip = I::get_other_ip_address(127);
3816        let multicast_addr = I::get_multicast_addr(3);
3817        let socket = api.create();
3818
3819        // Set some properties on the socket that should be preserved.
3820        api.set_posix_reuse_port(&socket, true).expect("is unbound");
3821        api.set_multicast_membership(
3822            &socket,
3823            multicast_addr,
3824            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
3825            true,
3826        )
3827        .expect("join multicast group should succeed");
3828
3829        // Create a UDP connection with a specified local port and local IP.
3830        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3831            .expect("Initial call to listen_udp was expected to succeed");
3832
3833        assert_matches!(
3834            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
3835            Err(ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable)))
3836        );
3837
3838        // Check that the listener was unchanged by the failed connection.
3839        assert!(api.get_posix_reuse_port(&socket));
3840        assert_eq!(
3841            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
3842            HashMap::from([((FakeDeviceId, multicast_addr), NonZeroUsize::new(1).unwrap())])
3843        );
3844        assert_eq!(
3845            api.set_multicast_membership(
3846                &socket,
3847                multicast_addr,
3848                MulticastInterfaceSelector::LocalAddress(local_ip).into(),
3849                true
3850            ),
3851            Err(SetMulticastMembershipError::GroupAlreadyJoined)
3852        );
3853    }
3854
3855    #[ip_test(I)]
3856    fn test_reconnect_udp_conn_success<I: TestIpExt>() {
3857        set_logger_for_test();
3858
3859        let local_ip = local_ip::<I>();
3860        let remote_ip = remote_ip::<I>();
3861        let other_remote_ip = I::get_other_ip_address(3);
3862
3863        let mut ctx =
3864            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
3865                vec![local_ip],
3866                vec![remote_ip, other_remote_ip],
3867            ));
3868        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3869
3870        let socket = api.create();
3871        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3872            .expect("listen should succeed");
3873
3874        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3875            .expect("connect was expected to succeed");
3876
3877        api.connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
3878            .expect("connect should succeed");
3879        assert_eq!(
3880            api.get_info(&socket),
3881            SocketInfo::Connected(datagram::ConnInfo {
3882                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
3883                local_identifier: LOCAL_PORT,
3884                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(other_remote_ip),
3885                remote_identifier: OTHER_REMOTE_PORT.into(),
3886            })
3887        );
3888    }
3889
3890    #[ip_test(I)]
3891    fn test_reconnect_udp_conn_fails<I: TestIpExt>() {
3892        set_logger_for_test();
3893        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3894        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3895        let local_ip = local_ip::<I>();
3896        let remote_ip = remote_ip::<I>();
3897        let other_remote_ip = I::get_other_ip_address(3);
3898
3899        let socket = api.create();
3900        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3901            .expect("listen should succeed");
3902
3903        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3904            .expect("connect was expected to succeed");
3905        let error = api
3906            .connect(&socket, Some(ZonedAddr::Unzoned(other_remote_ip)), OTHER_REMOTE_PORT.into())
3907            .expect_err("connect should fail");
3908        assert_matches!(
3909            error,
3910            ConnectError::Ip(IpSockCreationError::Route(ResolveRouteError::Unreachable))
3911        );
3912
3913        assert_eq!(
3914            api.get_info(&socket),
3915            SocketInfo::Connected(datagram::ConnInfo {
3916                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip),
3917                local_identifier: LOCAL_PORT,
3918                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip),
3919                remote_identifier: REMOTE_PORT.into()
3920            })
3921        );
3922    }
3923
3924    #[ip_test(I)]
3925    fn test_send_to<I: TestIpExt>() {
3926        set_logger_for_test();
3927
3928        let local_ip = local_ip::<I>();
3929        let remote_ip = remote_ip::<I>();
3930        let other_remote_ip = I::get_other_ip_address(3);
3931
3932        let mut ctx =
3933            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
3934                vec![local_ip],
3935                vec![remote_ip, other_remote_ip],
3936            ));
3937        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3938
3939        let socket = api.create();
3940        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
3941            .expect("listen should succeed");
3942        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
3943            .expect("connect should succeed");
3944
3945        let body = [1, 2, 3, 4, 5];
3946        // Try to send something with send_to
3947        api.send_to(
3948            &socket,
3949            Some(ZonedAddr::Unzoned(other_remote_ip)),
3950            REMOTE_PORT.into(),
3951            Buf::new(body.to_vec(), ..),
3952        )
3953        .expect("send_to failed");
3954
3955        // The socket should not have been affected.
3956        let info = api.get_info(&socket);
3957        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
3958        assert_eq!(info.local_ip.into_inner(), ZonedAddr::Unzoned(local_ip));
3959        assert_eq!(info.remote_ip.into_inner(), ZonedAddr::Unzoned(remote_ip));
3960        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
3961
3962        // Check first frame.
3963        let (meta, frame_body) =
3964            assert_matches!(api.core_ctx().bound_sockets.ip_socket_ctx.frames(), [frame] => frame);
3965        let SendIpPacketMeta {
3966            device: _,
3967            src_ip,
3968            dst_ip,
3969            destination,
3970            proto,
3971            ttl: _,
3972            mtu: _,
3973            dscp_and_ecn: _,
3974        } = meta.try_as::<I>().unwrap();
3975
3976        assert_eq!(destination, &IpPacketDestination::Neighbor(other_remote_ip));
3977        assert_eq!(src_ip, &local_ip);
3978        assert_eq!(dst_ip, &other_remote_ip);
3979        assert_eq!(proto, &I::Proto::from(IpProto::Udp));
3980        let mut buf = &frame_body[..];
3981        let udp_packet = UdpPacket::parse(&mut buf, UdpParseArgs::new(src_ip.get(), dst_ip.get()))
3982            .expect("Parsed sent UDP packet");
3983        assert_eq!(udp_packet.src_port().unwrap(), LOCAL_PORT);
3984        assert_eq!(udp_packet.dst_port(), REMOTE_PORT);
3985        assert_eq!(udp_packet.body(), &body[..]);
3986    }
3987
3988    /// Tests that UDP send failures are propagated as errors.
3989    ///
3990    /// Only tests with specified local port and address bounds.
3991    #[ip_test(I)]
3992    fn test_send_udp_conn_failure<I: TestIpExt>() {
3993        set_logger_for_test();
3994        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
3995        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
3996        let remote_ip = remote_ip::<I>();
3997        // Create a UDP connection with a specified local port and local IP.
3998        let socket = api.create();
3999        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4000            .expect("connect failed");
4001
4002        // Instruct the fake frame context to throw errors.
4003        api.core_ctx().bound_sockets.ip_socket_ctx.frames.set_should_error_for_frame(
4004            |_frame_meta| Some(SendFrameErrorReason::SizeConstraintsViolation),
4005        );
4006
4007        // Now try to send something over this new connection:
4008        let send_err = api.send(&socket, Buf::new(Vec::new(), ..)).unwrap_err();
4009        assert_eq!(send_err, Either::Left(SendError::IpSock(IpSockSendError::Mtu)));
4010
4011        let expects_with_socket =
4012            || CounterExpectationsWithSocket { tx: 1, tx_error: 1, ..Default::default() };
4013        assert_counters(
4014            api.core_ctx(),
4015            expects_with_socket(),
4016            Default::default(),
4017            [(&socket, expects_with_socket())],
4018        )
4019    }
4020
4021    #[ip_test(I)]
4022    fn test_send_udp_conn_device_removed<I: TestIpExt>() {
4023        set_logger_for_test();
4024        let device = FakeReferencyDeviceId::default();
4025        let mut ctx =
4026            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
4027        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4028        let remote_ip = remote_ip::<I>();
4029        let socket = api.create();
4030        api.set_device(&socket, Some(&device)).unwrap();
4031        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4032            .expect("connect failed");
4033
4034        for (device_removed, expected_res) in [
4035            (false, Ok(())),
4036            (
4037                true,
4038                Err(Either::Left(SendError::IpSock(IpSockSendError::Unroutable(
4039                    ResolveRouteError::Unreachable,
4040                )))),
4041            ),
4042        ] {
4043            if device_removed {
4044                device.mark_removed();
4045            }
4046
4047            assert_eq!(api.send(&socket, Buf::new(Vec::new(), ..)), expected_res)
4048        }
4049    }
4050
4051    #[ip_test(I)]
4052    #[test_case(false, ShutdownType::Send; "shutdown send then send")]
4053    #[test_case(false, ShutdownType::SendAndReceive; "shutdown both then send")]
4054    #[test_case(true, ShutdownType::Send; "shutdown send then sendto")]
4055    #[test_case(true, ShutdownType::SendAndReceive; "shutdown both then sendto")]
4056    fn test_send_udp_after_shutdown<I: TestIpExt>(send_to: bool, shutdown: ShutdownType) {
4057        set_logger_for_test();
4058
4059        #[derive(Debug)]
4060        struct NotWriteableError;
4061
4062        let send = |remote_ip, api: &mut UdpApi<_, _>, id| -> Result<(), NotWriteableError> {
4063            match remote_ip {
4064                Some(remote_ip) => api.send_to(
4065                    id,
4066                    Some(remote_ip),
4067                    REMOTE_PORT.into(),
4068                    Buf::new(Vec::new(), ..),
4069                )
4070                .map_err(
4071                    |e| assert_matches!(e, Either::Right(SendToError::NotWriteable) => NotWriteableError)
4072                ),
4073                None => api.send(
4074                    id,
4075                    Buf::new(Vec::new(), ..),
4076                )
4077                .map_err(|e| assert_matches!(e, Either::Left(SendError::NotWriteable) => NotWriteableError)),
4078            }
4079        };
4080
4081        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4082        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4083
4084        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
4085        let send_to_ip = send_to.then_some(remote_ip);
4086
4087        let socket = api.create();
4088        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
4089
4090        send(send_to_ip, &mut api, &socket).expect("can send");
4091        api.shutdown(&socket, shutdown).expect("is connected");
4092
4093        assert_matches!(send(send_to_ip, &mut api, &socket), Err(NotWriteableError));
4094    }
4095
4096    #[ip_test(I)]
4097    #[test_case(ShutdownType::Receive; "receive")]
4098    #[test_case(ShutdownType::SendAndReceive; "both")]
4099    fn test_marked_for_receive_shutdown<I: TestIpExt>(which: ShutdownType) {
4100        set_logger_for_test();
4101
4102        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4103        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4104
4105        let socket = api.create();
4106        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
4107            .expect("can bind");
4108        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
4109            .expect("can connect");
4110
4111        // Receive once, then set the shutdown flag, then receive again and
4112        // check that it doesn't get to the socket.
4113
4114        let meta = UdpPacketMeta::<I> {
4115            src_ip: remote_ip::<I>().get(),
4116            src_port: Some(REMOTE_PORT),
4117            dst_ip: local_ip::<I>().get(),
4118            dst_port: LOCAL_PORT,
4119            dscp_and_ecn: DscpAndEcn::default(),
4120        };
4121        let packet = [1, 1, 1, 1];
4122        let (core_ctx, bindings_ctx) = api.contexts();
4123
4124        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &packet[..])
4125            .expect("receive udp packet should succeed");
4126
4127        assert_eq!(
4128            bindings_ctx.state.socket_data(),
4129            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4130        );
4131        api.shutdown(&socket, which).expect("is connected");
4132        let (core_ctx, bindings_ctx) = api.contexts();
4133        assert_matches!(
4134            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &packet[..]),
4135            Err(TransportReceiveError::PortUnreachable)
4136        );
4137        assert_eq!(
4138            bindings_ctx.state.socket_data(),
4139            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4140        );
4141
4142        // Calling shutdown for the send direction doesn't change anything.
4143        api.shutdown(&socket, ShutdownType::Send).expect("is connected");
4144        let (core_ctx, bindings_ctx) = api.contexts();
4145        assert_matches!(
4146            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &packet[..]),
4147            Err(TransportReceiveError::PortUnreachable)
4148        );
4149        assert_eq!(
4150            bindings_ctx.state.socket_data(),
4151            HashMap::from([(socket.downgrade(), vec![&packet[..]])])
4152        );
4153    }
4154
4155    /// Tests that if we have multiple listeners and connections, demuxing the
4156    /// flows is performed correctly.
4157    #[ip_test(I)]
4158    fn test_udp_demux<I: TestIpExt>() {
4159        set_logger_for_test();
4160        let local_ip = local_ip::<I>();
4161        let remote_ip_a = I::get_other_ip_address(70);
4162        let remote_ip_b = I::get_other_ip_address(72);
4163        let local_port_a = NonZeroU16::new(100).unwrap();
4164        let local_port_b = NonZeroU16::new(101).unwrap();
4165        let local_port_c = NonZeroU16::new(102).unwrap();
4166        let local_port_d = NonZeroU16::new(103).unwrap();
4167
4168        let mut ctx =
4169            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
4170                vec![local_ip],
4171                vec![remote_ip_a, remote_ip_b],
4172            ));
4173        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4174
4175        // Create some UDP connections and listeners:
4176        // conn2 has just a remote addr different than conn1, which requires
4177        // allowing them to share the local port.
4178        let [conn1, conn2] = [remote_ip_a, remote_ip_b].map(|remote_ip| {
4179            let socket = api.create();
4180            api.set_posix_reuse_port(&socket, true).expect("is unbound");
4181            api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_d))
4182                .expect("listen_udp failed");
4183            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
4184                .expect("connect failed");
4185            socket
4186        });
4187        let list1 = api.create();
4188        api.listen(&list1, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_a))
4189            .expect("listen_udp failed");
4190        let list2 = api.create();
4191        api.listen(&list2, Some(ZonedAddr::Unzoned(local_ip)), Some(local_port_b))
4192            .expect("listen_udp failed");
4193        let wildcard_list = api.create();
4194        api.listen(&wildcard_list, None, Some(local_port_c)).expect("listen_udp failed");
4195
4196        let mut expectations = HashMap::<WeakUdpSocketId<I, _, _>, SocketReceived<I>>::new();
4197        // Now inject UDP packets that each of the created connections should
4198        // receive.
4199        let meta = UdpPacketMeta {
4200            src_ip: remote_ip_a.get(),
4201            src_port: Some(REMOTE_PORT),
4202            dst_ip: local_ip.get(),
4203            dst_port: local_port_d,
4204            dscp_and_ecn: DscpAndEcn::default(),
4205        };
4206        let body_conn1 = [1, 1, 1, 1];
4207        let (core_ctx, bindings_ctx) = api.contexts();
4208        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_conn1[..])
4209            .expect("receive udp packet should succeed");
4210        expectations
4211            .entry(conn1.downgrade())
4212            .or_default()
4213            .packets
4214            .push(ReceivedPacket { meta: meta, body: body_conn1.into() });
4215        assert_eq!(bindings_ctx.state.received(), &expectations);
4216
4217        let meta = UdpPacketMeta {
4218            src_ip: remote_ip_b.get(),
4219            src_port: Some(REMOTE_PORT),
4220            dst_ip: local_ip.get(),
4221            dst_port: local_port_d,
4222            dscp_and_ecn: DscpAndEcn::default(),
4223        };
4224        let body_conn2 = [2, 2, 2, 2];
4225        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_conn2[..])
4226            .expect("receive udp packet should succeed");
4227        expectations
4228            .entry(conn2.downgrade())
4229            .or_default()
4230            .packets
4231            .push(ReceivedPacket { meta: meta, body: body_conn2.into() });
4232        assert_eq!(bindings_ctx.state.received(), &expectations);
4233
4234        let meta = UdpPacketMeta {
4235            src_ip: remote_ip_a.get(),
4236            src_port: Some(REMOTE_PORT),
4237            dst_ip: local_ip.get(),
4238            dst_port: local_port_a,
4239            dscp_and_ecn: DscpAndEcn::default(),
4240        };
4241        let body_list1 = [3, 3, 3, 3];
4242        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_list1[..])
4243            .expect("receive udp packet should succeed");
4244        expectations
4245            .entry(list1.downgrade())
4246            .or_default()
4247            .packets
4248            .push(ReceivedPacket { meta: meta, body: body_list1.into() });
4249        assert_eq!(bindings_ctx.state.received(), &expectations);
4250
4251        let meta = UdpPacketMeta {
4252            src_ip: remote_ip_a.get(),
4253            src_port: Some(REMOTE_PORT),
4254            dst_ip: local_ip.get(),
4255            dst_port: local_port_b,
4256            dscp_and_ecn: DscpAndEcn::default(),
4257        };
4258        let body_list2 = [4, 4, 4, 4];
4259        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body_list2[..])
4260            .expect("receive udp packet should succeed");
4261        expectations
4262            .entry(list2.downgrade())
4263            .or_default()
4264            .packets
4265            .push(ReceivedPacket { meta: meta, body: body_list2.into() });
4266        assert_eq!(bindings_ctx.state.received(), &expectations);
4267
4268        let meta = UdpPacketMeta {
4269            src_ip: remote_ip_a.get(),
4270            src_port: Some(REMOTE_PORT),
4271            dst_ip: local_ip.get(),
4272            dst_port: local_port_c,
4273            dscp_and_ecn: DscpAndEcn::default(),
4274        };
4275        let body_wildcard_list = [5, 5, 5, 5];
4276        receive_udp_packet(
4277            core_ctx,
4278            bindings_ctx,
4279            FakeDeviceId,
4280            meta.clone(),
4281            &body_wildcard_list[..],
4282        )
4283        .expect("receive udp packet should succeed");
4284        expectations
4285            .entry(wildcard_list.downgrade())
4286            .or_default()
4287            .packets
4288            .push(ReceivedPacket { meta: meta, body: body_wildcard_list.into() });
4289        assert_eq!(bindings_ctx.state.received(), &expectations);
4290    }
4291
4292    /// Tests UDP wildcard listeners for different IP versions.
4293    #[ip_test(I)]
4294    fn test_wildcard_listeners<I: TestIpExt>() {
4295        set_logger_for_test();
4296        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4297        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4298        let local_ip_a = I::get_other_ip_address(1);
4299        let local_ip_b = I::get_other_ip_address(2);
4300        let remote_ip_a = I::get_other_ip_address(70);
4301        let remote_ip_b = I::get_other_ip_address(72);
4302        let listener = api.create();
4303        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4304
4305        let body = [1, 2, 3, 4, 5];
4306        let (core_ctx, bindings_ctx) = api.contexts();
4307        let meta_1 = UdpPacketMeta {
4308            src_ip: remote_ip_a.get(),
4309            src_port: Some(REMOTE_PORT),
4310            dst_ip: local_ip_a.get(),
4311            dst_port: LOCAL_PORT,
4312            dscp_and_ecn: DscpAndEcn::default(),
4313        };
4314        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta_1.clone(), &body[..])
4315            .expect("receive udp packet should succeed");
4316
4317        // Receive into a different local IP.
4318        let meta_2 = UdpPacketMeta {
4319            src_ip: remote_ip_b.get(),
4320            src_port: Some(REMOTE_PORT),
4321            dst_ip: local_ip_b.get(),
4322            dst_port: LOCAL_PORT,
4323            dscp_and_ecn: DscpAndEcn::default(),
4324        };
4325        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta_2.clone(), &body[..])
4326            .expect("receive udp packet should succeed");
4327
4328        // Check that we received both packets for the listener.
4329        assert_eq!(
4330            bindings_ctx.state.received::<I>(),
4331            &HashMap::from([(
4332                listener.downgrade(),
4333                SocketReceived {
4334                    packets: vec![
4335                        ReceivedPacket { meta: meta_1, body: body.into() },
4336                        ReceivedPacket { meta: meta_2, body: body.into() }
4337                    ]
4338                }
4339            )])
4340        );
4341    }
4342
4343    #[ip_test(I)]
4344    fn test_receive_source_port_zero_on_listener<I: TestIpExt>() {
4345        set_logger_for_test();
4346        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4347        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4348        let listener = api.create();
4349        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4350
4351        let body = [];
4352        let meta = UdpPacketMeta::<I> {
4353            src_ip: I::TEST_ADDRS.remote_ip.get(),
4354            src_port: None,
4355            dst_ip: I::TEST_ADDRS.local_ip.get(),
4356            dst_port: LOCAL_PORT,
4357            dscp_and_ecn: DscpAndEcn::default(),
4358        };
4359
4360        let (core_ctx, bindings_ctx) = api.contexts();
4361        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta.clone(), &body[..])
4362            .expect("receive udp packet should succeed");
4363        // Check that we received both packets for the listener.
4364        assert_eq!(
4365            bindings_ctx.state.received(),
4366            &HashMap::from([(
4367                listener.downgrade(),
4368                SocketReceived { packets: vec![ReceivedPacket { meta, body: vec![] }] }
4369            )])
4370        );
4371    }
4372
4373    #[ip_test(I)]
4374    fn test_receive_source_addr_unspecified_on_listener<I: TestIpExt>() {
4375        set_logger_for_test();
4376        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4377        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4378        let listener = api.create();
4379        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4380
4381        let meta = UdpPacketMeta::<I> {
4382            src_ip: I::UNSPECIFIED_ADDRESS,
4383            src_port: Some(REMOTE_PORT),
4384            dst_ip: I::TEST_ADDRS.local_ip.get(),
4385            dst_port: LOCAL_PORT,
4386            dscp_and_ecn: DscpAndEcn::default(),
4387        };
4388        let body = [];
4389        let (core_ctx, bindings_ctx) = api.contexts();
4390        receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body[..])
4391            .expect("receive udp packet should succeed");
4392        // Check that we received the packet on the listener.
4393        assert_eq!(
4394            bindings_ctx.state.socket_data(),
4395            HashMap::from([(listener.downgrade(), vec![&body[..]])])
4396        );
4397    }
4398
4399    #[ip_test(I)]
4400    #[test_case(NonZeroU16::new(u16::MAX).unwrap(), Ok(NonZeroU16::new(u16::MAX).unwrap()); "ephemeral available")]
4401    #[test_case(NonZeroU16::new(100).unwrap(), Err(LocalAddressError::FailedToAllocateLocalPort);
4402        "no ephemeral available")]
4403    fn test_bind_picked_port_all_others_taken<I: TestIpExt>(
4404        available_port: NonZeroU16,
4405        expected_result: Result<NonZeroU16, LocalAddressError>,
4406    ) {
4407        // NB: We don't enable logging for this test because it's very spammy.
4408        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4409        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4410
4411        for port in 1..=u16::MAX {
4412            let port = NonZeroU16::new(port).unwrap();
4413            if port == available_port {
4414                continue;
4415            }
4416            let unbound = api.create();
4417            api.listen(&unbound, None, Some(port)).expect("uncontested bind");
4418        }
4419
4420        // Now that all but the LOCAL_PORT are occupied, ask the stack to
4421        // select a port.
4422        let socket = api.create();
4423        let result = api
4424            .listen(&socket, None, None)
4425            .map(|()| {
4426                let info = api.get_info(&socket);
4427                assert_matches!(info, SocketInfo::Listener(info) => info.local_identifier)
4428            })
4429            .map_err(Either::unwrap_right);
4430        assert_eq!(result, expected_result);
4431    }
4432
4433    #[ip_test(I)]
4434    fn test_receive_multicast_packet<I: TestIpExt>() {
4435        set_logger_for_test();
4436        let local_ip = local_ip::<I>();
4437        let remote_ip = I::get_other_ip_address(70);
4438        let multicast_addr = I::get_multicast_addr(0);
4439        let multicast_addr_other = I::get_multicast_addr(1);
4440
4441        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
4442            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![remote_ip]),
4443        );
4444        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4445
4446        // Create 3 sockets: one listener for all IPs, two listeners on the same
4447        // local address.
4448        let any_listener = {
4449            let socket = api.create();
4450            api.set_posix_reuse_port(&socket, true).expect("is unbound");
4451            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4452            socket
4453        };
4454
4455        let specific_listeners = [(); 2].map(|()| {
4456            let socket = api.create();
4457            api.set_posix_reuse_port(&socket, true).expect("is unbound");
4458            api.listen(
4459                &socket,
4460                Some(ZonedAddr::Unzoned(multicast_addr.into_specified())),
4461                Some(LOCAL_PORT),
4462            )
4463            .expect("listen_udp failed");
4464            socket
4465        });
4466
4467        let (core_ctx, bindings_ctx) = api.contexts();
4468        let mut receive_packet = |body, local_ip: MulticastAddr<I::Addr>| {
4469            let meta = UdpPacketMeta::<I> {
4470                src_ip: remote_ip.get(),
4471                src_port: Some(REMOTE_PORT),
4472                dst_ip: local_ip.get(),
4473                dst_port: LOCAL_PORT,
4474                dscp_and_ecn: DscpAndEcn::default(),
4475            };
4476            let body = [body];
4477            receive_udp_packet(core_ctx, bindings_ctx, FakeDeviceId, meta, &body)
4478                .expect("receive udp packet should succeed")
4479        };
4480
4481        // These packets should be received by all listeners.
4482        receive_packet(1, multicast_addr);
4483        receive_packet(2, multicast_addr);
4484
4485        // This packet should be received only by the all-IPs listener.
4486        receive_packet(3, multicast_addr_other);
4487
4488        assert_eq!(
4489            bindings_ctx.state.socket_data(),
4490            HashMap::from([
4491                (specific_listeners[0].downgrade(), vec![[1].as_slice(), &[2]]),
4492                (specific_listeners[1].downgrade(), vec![&[1], &[2]]),
4493                (any_listener.downgrade(), vec![&[1], &[2], &[3]]),
4494            ]),
4495        );
4496
4497        assert_counters(
4498            api.core_ctx(),
4499            CounterExpectationsWithSocket { rx_delivered: 7, ..Default::default() },
4500            CounterExpectationsWithoutSocket { rx: 3, ..Default::default() },
4501            [
4502                (
4503                    &any_listener,
4504                    CounterExpectationsWithSocket { rx_delivered: 3, ..Default::default() },
4505                ),
4506                (
4507                    &specific_listeners[0],
4508                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
4509                ),
4510                (
4511                    &specific_listeners[1],
4512                    CounterExpectationsWithSocket { rx_delivered: 2, ..Default::default() },
4513                ),
4514            ],
4515        )
4516    }
4517
4518    type UdpMultipleDevicesCtx = FakeUdpCtx<MultipleDevicesId>;
4519    type UdpMultipleDevicesCoreCtx = FakeUdpCoreCtx<MultipleDevicesId>;
4520    type UdpMultipleDevicesBindingsCtx = FakeUdpBindingsCtx<MultipleDevicesId>;
4521
4522    impl FakeUdpCoreCtx<MultipleDevicesId> {
4523        fn new_multiple_devices<I: TestIpExt>() -> Self {
4524            let remote_ips = vec![I::get_other_remote_ip_address(1)];
4525            Self::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
4526                MultipleDevicesId::all().into_iter().enumerate().map(|(i, device)| {
4527                    FakeDeviceConfig {
4528                        device,
4529                        local_ips: vec![Self::local_ip(i)],
4530                        remote_ips: remote_ips.clone(),
4531                    }
4532                }),
4533            ))
4534        }
4535
4536        fn local_ip<A: IpAddress>(index: usize) -> SpecifiedAddr<A>
4537        where
4538            A::Version: TestIpExt,
4539        {
4540            A::Version::get_other_ip_address((index + 1).try_into().unwrap())
4541        }
4542    }
4543
4544    /// Tests that if sockets are bound to devices, they will only receive
4545    /// packets that are received on those devices.
4546    #[ip_test(I)]
4547    fn test_bound_to_device_receive<I: TestIpExt>() {
4548        set_logger_for_test();
4549        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4550            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4551        );
4552        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4553        let bound_first_device = api.create();
4554        api.listen(
4555            &bound_first_device,
4556            Some(ZonedAddr::Unzoned(local_ip::<I>())),
4557            Some(LOCAL_PORT),
4558        )
4559        .expect("listen should succeed");
4560        api.connect(
4561            &bound_first_device,
4562            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
4563            REMOTE_PORT.into(),
4564        )
4565        .expect("connect should succeed");
4566        api.set_device(&bound_first_device, Some(&MultipleDevicesId::A))
4567            .expect("bind should succeed");
4568
4569        let bound_second_device = api.create();
4570        api.set_device(&bound_second_device, Some(&MultipleDevicesId::B)).unwrap();
4571        api.listen(&bound_second_device, None, Some(LOCAL_PORT)).expect("listen should succeed");
4572
4573        // Inject a packet received on `MultipleDevicesId::A` from the specified
4574        // remote; this should go to the first socket.
4575        let meta = UdpPacketMeta::<I> {
4576            src_ip: I::get_other_remote_ip_address(1).get(),
4577            src_port: Some(REMOTE_PORT),
4578            dst_ip: local_ip::<I>().get(),
4579            dst_port: LOCAL_PORT,
4580            dscp_and_ecn: DscpAndEcn::default(),
4581        };
4582        let body = [1, 2, 3, 4, 5];
4583        let (core_ctx, bindings_ctx) = api.contexts();
4584        receive_udp_packet(core_ctx, bindings_ctx, MultipleDevicesId::A, meta.clone(), &body[..])
4585            .expect("receive udp packet should succeed");
4586
4587        // A second packet received on `MultipleDevicesId::B` will go to the
4588        // second socket.
4589        receive_udp_packet(core_ctx, bindings_ctx, MultipleDevicesId::B, meta, &body[..])
4590            .expect("receive udp packet should succeed");
4591        assert_eq!(
4592            bindings_ctx.state.socket_data(),
4593            HashMap::from([
4594                (bound_first_device.downgrade(), vec![&body[..]]),
4595                (bound_second_device.downgrade(), vec![&body[..]])
4596            ])
4597        );
4598    }
4599
4600    /// Tests that if sockets are bound to devices, they will send packets out
4601    /// of those devices.
4602    #[ip_test(I)]
4603    fn test_bound_to_device_send<I: TestIpExt>() {
4604        set_logger_for_test();
4605        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4606            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4607        );
4608        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4609        let bound_on_devices = MultipleDevicesId::all().map(|device| {
4610            let socket = api.create();
4611            api.set_device(&socket, Some(&device)).unwrap();
4612            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
4613            socket
4614        });
4615
4616        // Send a packet from each socket.
4617        let body = [1, 2, 3, 4, 5];
4618        for socket in bound_on_devices {
4619            api.send_to(
4620                &socket,
4621                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
4622                REMOTE_PORT.into(),
4623                Buf::new(body.to_vec(), ..),
4624            )
4625            .expect("send should succeed");
4626        }
4627
4628        let mut received_devices = api
4629            .core_ctx()
4630            .bound_sockets
4631            .ip_socket_ctx
4632            .frames()
4633            .iter()
4634            .map(|(meta, _body)| {
4635                let SendIpPacketMeta {
4636                    device,
4637                    src_ip: _,
4638                    dst_ip,
4639                    destination: _,
4640                    proto,
4641                    ttl: _,
4642                    mtu: _,
4643                    dscp_and_ecn: _,
4644                } = meta.try_as::<I>().unwrap();
4645                assert_eq!(proto, &IpProto::Udp.into());
4646                assert_eq!(dst_ip, &I::get_other_remote_ip_address(1));
4647                *device
4648            })
4649            .collect::<Vec<_>>();
4650        received_devices.sort();
4651        assert_eq!(received_devices, &MultipleDevicesId::all());
4652    }
4653
4654    fn receive_packet_on<I: TestIpExt>(
4655        core_ctx: &mut UdpMultipleDevicesCoreCtx,
4656        bindings_ctx: &mut UdpMultipleDevicesBindingsCtx,
4657        device: MultipleDevicesId,
4658    ) -> Result<(), TransportReceiveError> {
4659        let meta = UdpPacketMeta::<I> {
4660            src_ip: I::get_other_remote_ip_address(1).get(),
4661            src_port: Some(REMOTE_PORT),
4662            dst_ip: local_ip::<I>().get(),
4663            dst_port: LOCAL_PORT,
4664            dscp_and_ecn: DscpAndEcn::default(),
4665        };
4666        const BODY: [u8; 5] = [1, 2, 3, 4, 5];
4667        receive_udp_packet(core_ctx, bindings_ctx, device, meta, &BODY[..])
4668    }
4669
4670    /// Check that sockets can be bound to and unbound from devices.
4671    #[ip_test(I)]
4672    fn test_bind_unbind_device<I: TestIpExt>() {
4673        set_logger_for_test();
4674        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4675            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4676        );
4677        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4678
4679        // Start with `socket` bound to a device.
4680        let socket = api.create();
4681        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
4682        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen failed");
4683
4684        // Since it is bound, it does not receive a packet from another device.
4685        let (core_ctx, bindings_ctx) = api.contexts();
4686        assert_matches!(
4687            receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B),
4688            Err(TransportReceiveError::PortUnreachable)
4689        );
4690        let received = &bindings_ctx.state.socket_data::<I>();
4691        assert_eq!(received, &HashMap::new());
4692
4693        // When unbound, the socket can receive packets on the other device.
4694        api.set_device(&socket, None).expect("clearing bound device failed");
4695        let (core_ctx, bindings_ctx) = api.contexts();
4696        receive_packet_on::<I>(core_ctx, bindings_ctx, MultipleDevicesId::B)
4697            .expect("receive udp packet should succeed");
4698        let received = bindings_ctx.state.received::<I>().iter().collect::<Vec<_>>();
4699        let (rx_socket, socket_received) =
4700            assert_matches!(received[..], [(rx_socket, packets)] => (rx_socket, packets));
4701        assert_eq!(rx_socket, &socket);
4702        assert_matches!(socket_received.packets[..], [_]);
4703    }
4704
4705    /// Check that bind fails as expected when it would cause illegal shadowing.
4706    #[ip_test(I)]
4707    fn test_unbind_device_fails<I: TestIpExt>() {
4708        set_logger_for_test();
4709        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4710            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4711        );
4712        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4713
4714        let bound_on_devices = MultipleDevicesId::all().map(|device| {
4715            let socket = api.create();
4716            api.set_device(&socket, Some(&device)).unwrap();
4717            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
4718            socket
4719        });
4720
4721        // Clearing the bound device is not allowed for either socket since it
4722        // would then be shadowed by the other socket.
4723        for socket in bound_on_devices {
4724            assert_matches!(
4725                api.set_device(&socket, None),
4726                Err(SocketError::Local(LocalAddressError::AddressInUse))
4727            );
4728        }
4729    }
4730
4731    /// Check that binding a device fails if it would make a connected socket
4732    /// unroutable.
4733    #[ip_test(I)]
4734    fn test_bind_conn_socket_device_fails<I: TestIpExt>() {
4735        set_logger_for_test();
4736        let device_configs = HashMap::from(
4737            [(MultipleDevicesId::A, 1), (MultipleDevicesId::B, 2)].map(|(device, i)| {
4738                (
4739                    device,
4740                    FakeDeviceConfig {
4741                        device,
4742                        local_ips: vec![I::get_other_ip_address(i)],
4743                        remote_ips: vec![I::get_other_remote_ip_address(i)],
4744                    },
4745                )
4746            }),
4747        );
4748        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4749            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
4750                device_configs.iter().map(|(_, v)| v).cloned(),
4751            )),
4752        );
4753        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4754        let socket = api.create();
4755        api.connect(
4756            &socket,
4757            Some(ZonedAddr::Unzoned(device_configs[&MultipleDevicesId::A].remote_ips[0])),
4758            REMOTE_PORT.into(),
4759        )
4760        .expect("connect should succeed");
4761
4762        // `socket` is not explicitly bound to device `A` but its route must
4763        // go through it because of the destination address. Therefore binding
4764        // to device `B` wil not work.
4765        assert_matches!(
4766            api.set_device(&socket, Some(&MultipleDevicesId::B)),
4767            Err(SocketError::Remote(RemoteAddressError::NoRoute))
4768        );
4769
4770        // Binding to device `A` should be fine.
4771        api.set_device(&socket, Some(&MultipleDevicesId::A)).expect("routing picked A already");
4772    }
4773
4774    #[ip_test(I)]
4775    fn test_bound_device_receive_multicast_packet<I: TestIpExt>() {
4776        set_logger_for_test();
4777        let remote_ip = I::get_other_ip_address(1);
4778        let multicast_addr = I::get_multicast_addr(0);
4779
4780        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4781            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4782        );
4783        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4784
4785        // Create 3 sockets: one listener bound on each device and one not bound
4786        // to a device.
4787
4788        let bound_on_devices = MultipleDevicesId::all().map(|device| {
4789            let listener = api.create();
4790            api.set_device(&listener, Some(&device)).unwrap();
4791            api.set_posix_reuse_port(&listener, true).expect("is unbound");
4792            api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
4793
4794            (device, listener)
4795        });
4796
4797        let listener = api.create();
4798        api.set_posix_reuse_port(&listener, true).expect("is unbound");
4799        api.listen(&listener, None, Some(LOCAL_PORT)).expect("listen should succeed");
4800
4801        fn index_for_device(id: MultipleDevicesId) -> u8 {
4802            match id {
4803                MultipleDevicesId::A => 0,
4804                MultipleDevicesId::B => 1,
4805                MultipleDevicesId::C => 2,
4806            }
4807        }
4808
4809        let (core_ctx, bindings_ctx) = api.contexts();
4810        let mut receive_packet = |remote_ip: SpecifiedAddr<I::Addr>, device: MultipleDevicesId| {
4811            let meta = UdpPacketMeta::<I> {
4812                src_ip: remote_ip.get(),
4813                src_port: Some(REMOTE_PORT),
4814                dst_ip: multicast_addr.get(),
4815                dst_port: LOCAL_PORT,
4816                dscp_and_ecn: DscpAndEcn::default(),
4817            };
4818            let body = vec![index_for_device(device)];
4819            receive_udp_packet(core_ctx, bindings_ctx, device, meta, &body)
4820                .expect("receive udp packet should succeed")
4821        };
4822
4823        // Receive packets from the remote IP on each device (2 packets total).
4824        // Listeners bound on devices should receive one, and the other listener
4825        // should receive both.
4826        for device in MultipleDevicesId::all() {
4827            receive_packet(remote_ip, device);
4828        }
4829
4830        let per_socket_data = bindings_ctx.state.socket_data();
4831        for (device, listener) in bound_on_devices {
4832            assert_eq!(per_socket_data[&listener.downgrade()], vec![&[index_for_device(device)]]);
4833        }
4834        let expected_listener_data = &MultipleDevicesId::all().map(|d| vec![index_for_device(d)]);
4835        assert_eq!(&per_socket_data[&listener.downgrade()], expected_listener_data);
4836    }
4837
4838    /// Tests establishing a UDP connection without providing a local IP
4839    #[ip_test(I)]
4840    fn test_conn_unspecified_local_ip<I: TestIpExt>() {
4841        set_logger_for_test();
4842        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
4843        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4844        let socket = api.create();
4845        api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
4846        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
4847            .expect("connect failed");
4848        let info = api.get_info(&socket);
4849        assert_eq!(
4850            info,
4851            SocketInfo::Connected(datagram::ConnInfo {
4852                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(local_ip::<I>()),
4853                local_identifier: LOCAL_PORT,
4854                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<I>()),
4855                remote_identifier: REMOTE_PORT.into(),
4856            })
4857        );
4858    }
4859
4860    #[ip_test(I)]
4861    fn test_multicast_sendto<I: TestIpExt>() {
4862        set_logger_for_test();
4863
4864        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4865            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4866        );
4867
4868        // Add multicsat route for every device.
4869        for device in MultipleDevicesId::all().iter() {
4870            ctx.core_ctx
4871                .bound_sockets
4872                .ip_socket_ctx
4873                .state
4874                .add_subnet_route(*device, I::MULTICAST_SUBNET);
4875        }
4876
4877        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4878        let socket = api.create();
4879
4880        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
4881            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
4882                .expect("bind should succeed");
4883
4884            let multicast_ip = I::get_multicast_addr(i.try_into().unwrap());
4885            api.send_to(
4886                &socket,
4887                Some(ZonedAddr::Unzoned(multicast_ip.into())),
4888                REMOTE_PORT.into(),
4889                Buf::new(b"packet".to_vec(), ..),
4890            )
4891            .expect("send should succeed");
4892
4893            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
4894            assert_eq!(packets.len(), 1usize);
4895            for (meta, _body) in packets {
4896                let meta = meta.try_as::<I>().unwrap();
4897                assert_eq!(meta.device, *target_device);
4898                assert_eq!(meta.proto, IpProto::Udp.into());
4899                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
4900                assert_eq!(meta.dst_ip, multicast_ip.into());
4901                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
4902            }
4903        }
4904    }
4905
4906    #[ip_test(I)]
4907    fn test_multicast_send<I: TestIpExt>() {
4908        set_logger_for_test();
4909
4910        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
4911            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
4912        );
4913
4914        // Add multicsat route for every device.
4915        for device in MultipleDevicesId::all().iter() {
4916            ctx.core_ctx
4917                .bound_sockets
4918                .ip_socket_ctx
4919                .state
4920                .add_subnet_route(*device, I::MULTICAST_SUBNET);
4921        }
4922
4923        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4924        let multicast_ip = I::get_multicast_addr(42);
4925
4926        for (i, target_device) in MultipleDevicesId::all().iter().enumerate() {
4927            let socket = api.create();
4928
4929            api.set_multicast_interface(&socket, Some(&target_device), I::VERSION)
4930                .expect("set_multicast_interface should succeed");
4931
4932            api.connect(&socket, Some(ZonedAddr::Unzoned(multicast_ip.into())), REMOTE_PORT.into())
4933                .expect("send should succeed");
4934
4935            api.send(&socket, Buf::new(b"packet".to_vec(), ..)).expect("send should succeed");
4936
4937            let packets = api.core_ctx().bound_sockets.ip_socket_ctx.take_frames();
4938            assert_eq!(packets.len(), 1usize);
4939            for (meta, _body) in packets {
4940                let meta = meta.try_as::<I>().unwrap();
4941                assert_eq!(meta.device, *target_device);
4942                assert_eq!(meta.proto, IpProto::Udp.into());
4943                assert_eq!(meta.src_ip, UdpMultipleDevicesCoreCtx::local_ip(i));
4944                assert_eq!(meta.dst_ip, multicast_ip.into());
4945                assert_eq!(meta.destination, IpPacketDestination::Multicast(multicast_ip));
4946            }
4947        }
4948    }
4949
4950    /// Tests local port allocation for [`connect`].
4951    ///
4952    /// Tests that calling [`connect`] causes a valid local port to be
4953    /// allocated.
4954    #[ip_test(I)]
4955    fn test_udp_local_port_alloc<I: TestIpExt>() {
4956        let local_ip = local_ip::<I>();
4957        let ip_a = I::get_other_ip_address(100);
4958        let ip_b = I::get_other_ip_address(200);
4959
4960        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
4961            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ip_a, ip_b]),
4962        );
4963        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
4964
4965        let conn_a = api.create();
4966        api.connect(&conn_a, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
4967            .expect("connect failed");
4968        let conn_b = api.create();
4969        api.connect(&conn_b, Some(ZonedAddr::Unzoned(ip_b)), REMOTE_PORT.into())
4970            .expect("connect failed");
4971        let conn_c = api.create();
4972        api.connect(&conn_c, Some(ZonedAddr::Unzoned(ip_a)), OTHER_REMOTE_PORT.into())
4973            .expect("connect failed");
4974        let conn_d = api.create();
4975        api.connect(&conn_d, Some(ZonedAddr::Unzoned(ip_a)), REMOTE_PORT.into())
4976            .expect("connect failed");
4977        let valid_range = &FakePortAlloc::<I>::EPHEMERAL_RANGE;
4978        let mut get_conn_port = |id| {
4979            let info = api.get_info(&id);
4980            let info = assert_matches!(info, SocketInfo::Connected(info) => info);
4981            let datagram::ConnInfo {
4982                local_ip: _,
4983                local_identifier,
4984                remote_ip: _,
4985                remote_identifier: _,
4986            } = info;
4987            local_identifier
4988        };
4989        let port_a = get_conn_port(conn_a).get();
4990        let port_b = get_conn_port(conn_b).get();
4991        let port_c = get_conn_port(conn_c).get();
4992        let port_d = get_conn_port(conn_d).get();
4993        assert!(valid_range.contains(&port_a));
4994        assert!(valid_range.contains(&port_b));
4995        assert!(valid_range.contains(&port_c));
4996        assert!(valid_range.contains(&port_d));
4997        assert_ne!(port_a, port_b);
4998        assert_ne!(port_a, port_c);
4999        assert_ne!(port_a, port_d);
5000    }
5001
5002    /// Tests that if `listen_udp` fails, it can be retried later.
5003    #[ip_test(I)]
5004    fn test_udp_retry_listen_after_removing_conflict<I: TestIpExt>() {
5005        set_logger_for_test();
5006        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5007        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5008
5009        let listen_unbound = |api: &mut UdpApi<_, _>, socket: &UdpSocketId<_, _, _>| {
5010            api.listen(socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5011        };
5012
5013        // Tie up the address so the second call to `connect` fails.
5014        let listener = api.create();
5015        listen_unbound(&mut api, &listener)
5016            .expect("Initial call to listen_udp was expected to succeed");
5017
5018        // Trying to connect on the same address should fail.
5019        let unbound = api.create();
5020        assert_eq!(
5021            listen_unbound(&mut api, &unbound),
5022            Err(Either::Right(LocalAddressError::AddressInUse))
5023        );
5024
5025        // Once the first listener is removed, the second socket can be
5026        // connected.
5027        api.close(listener).into_removed();
5028
5029        listen_unbound(&mut api, &unbound).expect("listen should succeed");
5030    }
5031
5032    /// Tests local port allocation for [`listen_udp`].
5033    ///
5034    /// Tests that calling [`listen_udp`] causes a valid local port to be
5035    /// allocated when no local port is passed.
5036    #[ip_test(I)]
5037    fn test_udp_listen_port_alloc<I: TestIpExt>() {
5038        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5039        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5040        let local_ip = local_ip::<I>();
5041
5042        let wildcard_list = api.create();
5043        api.listen(&wildcard_list, None, None).expect("listen_udp failed");
5044        let specified_list = api.create();
5045        api.listen(&specified_list, Some(ZonedAddr::Unzoned(local_ip)), None)
5046            .expect("listen_udp failed");
5047        let mut get_listener_port = |id| {
5048            let info = api.get_info(&id);
5049            let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5050            let datagram::ListenerInfo { local_ip: _, local_identifier } = info;
5051            local_identifier
5052        };
5053        let wildcard_port = get_listener_port(wildcard_list);
5054        let specified_port = get_listener_port(specified_list);
5055        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&wildcard_port.get()));
5056        assert!(FakePortAlloc::<I>::EPHEMERAL_RANGE.contains(&specified_port.get()));
5057        assert_ne!(wildcard_port, specified_port);
5058    }
5059
5060    #[ip_test(I)]
5061    fn test_bind_multiple_reuse_port<I: TestIpExt>() {
5062        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5063        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5064        let listeners = [(), ()].map(|()| {
5065            let socket = api.create();
5066            api.set_posix_reuse_port(&socket, true).expect("is unbound");
5067            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5068            socket
5069        });
5070
5071        for listener in listeners {
5072            assert_eq!(
5073                api.get_info(&listener),
5074                SocketInfo::Listener(datagram::ListenerInfo {
5075                    local_ip: None,
5076                    local_identifier: LOCAL_PORT
5077                })
5078            );
5079        }
5080    }
5081
5082    #[ip_test(I)]
5083    fn test_set_unset_reuse_port_unbound<I: TestIpExt>() {
5084        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5085        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5086        let unbound = api.create();
5087        api.set_posix_reuse_port(&unbound, true).expect("is unbound");
5088        api.set_posix_reuse_port(&unbound, false).expect("is unbound");
5089        api.listen(&unbound, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5090
5091        // Because there is already a listener bound without `SO_REUSEPORT` set,
5092        // the next bind to the same address should fail.
5093        assert_eq!(
5094            {
5095                let unbound = api.create();
5096                api.listen(&unbound, None, Some(LOCAL_PORT))
5097            },
5098            Err(Either::Right(LocalAddressError::AddressInUse))
5099        );
5100    }
5101
5102    #[ip_test(I)]
5103    #[test_case(bind_as_listener)]
5104    #[test_case(bind_as_connected)]
5105    fn test_set_unset_reuse_port_bound<I: TestIpExt>(
5106        set_up_socket: impl FnOnce(
5107            &mut UdpMultipleDevicesCtx,
5108            &UdpSocketId<
5109                I,
5110                FakeWeakDeviceId<MultipleDevicesId>,
5111                FakeUdpBindingsCtx<MultipleDevicesId>,
5112            >,
5113        ),
5114    ) {
5115        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5116            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5117        );
5118        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5119        set_up_socket(&mut ctx, &socket);
5120
5121        // Per src/connectivity/network/netstack3/docs/POSIX_COMPATIBILITY.md,
5122        // Netstack3 only allows setting SO_REUSEPORT on unbound sockets.
5123        assert_matches!(
5124            UdpApi::<I, _>::new(ctx.as_mut()).set_posix_reuse_port(&socket, false),
5125            Err(ExpectedUnboundError)
5126        )
5127    }
5128
5129    /// Tests [`remove_udp`]
5130    #[ip_test(I)]
5131    fn test_remove_udp_conn<I: TestIpExt>() {
5132        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5133        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5134
5135        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5136        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
5137        let socket = api.create();
5138        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).unwrap();
5139        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
5140        api.close(socket).into_removed();
5141    }
5142
5143    /// Tests [`remove_udp`]
5144    #[ip_test(I)]
5145    fn test_remove_udp_listener<I: TestIpExt>() {
5146        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5147        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5148        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5149
5150        // Test removing a specified listener.
5151        let specified = api.create();
5152        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5153        api.close(specified).into_removed();
5154
5155        // Test removing a wildcard listener.
5156        let wildcard = api.create();
5157        api.listen(&wildcard, None, Some(LOCAL_PORT)).expect("listen_udp failed");
5158        api.close(wildcard).into_removed();
5159    }
5160
5161    fn try_join_leave_multicast<I: TestIpExt>(
5162        mcast_addr: MulticastAddr<I::Addr>,
5163        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
5164        set_up_ctx: impl FnOnce(&mut UdpMultipleDevicesCtx),
5165        set_up_socket: impl FnOnce(
5166            &mut UdpMultipleDevicesCtx,
5167            &UdpSocketId<
5168                I,
5169                FakeWeakDeviceId<MultipleDevicesId>,
5170                FakeUdpBindingsCtx<MultipleDevicesId>,
5171            >,
5172        ),
5173    ) -> (
5174        Result<(), SetMulticastMembershipError>,
5175        HashMap<(MultipleDevicesId, MulticastAddr<I::Addr>), NonZeroUsize>,
5176    ) {
5177        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5178            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5179        );
5180        set_up_ctx(&mut ctx);
5181
5182        let socket = UdpApi::<I, _>::new(ctx.as_mut()).create();
5183        set_up_socket(&mut ctx, &socket);
5184        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5185        let result = api.set_multicast_membership(&socket, mcast_addr, interface, true);
5186
5187        let memberships_snapshot =
5188            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>();
5189        if let Ok(()) = result {
5190            api.set_multicast_membership(&socket, mcast_addr, interface, false)
5191                .expect("leaving group failed");
5192        }
5193        assert_eq!(
5194            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5195            HashMap::default()
5196        );
5197
5198        (result, memberships_snapshot)
5199    }
5200
5201    fn leave_unbound<I: TestIpExt>(
5202        _ctx: &mut UdpMultipleDevicesCtx,
5203        _unbound: &UdpSocketId<
5204            I,
5205            FakeWeakDeviceId<MultipleDevicesId>,
5206            FakeUdpBindingsCtx<MultipleDevicesId>,
5207        >,
5208    ) {
5209    }
5210
5211    fn bind_as_listener<I: TestIpExt>(
5212        ctx: &mut UdpMultipleDevicesCtx,
5213        unbound: &UdpSocketId<
5214            I,
5215            FakeWeakDeviceId<MultipleDevicesId>,
5216            FakeUdpBindingsCtx<MultipleDevicesId>,
5217        >,
5218    ) {
5219        UdpApi::<I, _>::new(ctx.as_mut())
5220            .listen(unbound, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5221            .expect("listen should succeed")
5222    }
5223
5224    fn bind_as_connected<I: TestIpExt>(
5225        ctx: &mut UdpMultipleDevicesCtx,
5226        unbound: &UdpSocketId<
5227            I,
5228            FakeWeakDeviceId<MultipleDevicesId>,
5229            FakeUdpBindingsCtx<MultipleDevicesId>,
5230        >,
5231    ) {
5232        UdpApi::<I, _>::new(ctx.as_mut())
5233            .connect(
5234                unbound,
5235                Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5236                REMOTE_PORT.into(),
5237            )
5238            .expect("connect should succeed")
5239    }
5240
5241    fn iface_id<A: IpAddress>(
5242        id: MultipleDevicesId,
5243    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
5244        MulticastInterfaceSelector::Interface(id).into()
5245    }
5246    fn iface_addr<A: IpAddress>(
5247        addr: SpecifiedAddr<A>,
5248    ) -> MulticastMembershipInterfaceSelector<A, MultipleDevicesId> {
5249        MulticastInterfaceSelector::LocalAddress(addr).into()
5250    }
5251
5252    #[ip_test(I)]
5253    #[test_case(iface_id(MultipleDevicesId::A), leave_unbound::<I>; "device_no_addr_unbound")]
5254    #[test_case(iface_addr(local_ip::<I>()), leave_unbound::<I>; "addr_no_device_unbound")]
5255    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, leave_unbound::<I>;
5256        "any_interface_unbound")]
5257    #[test_case(iface_id(MultipleDevicesId::A), bind_as_listener::<I>; "device_no_addr_listener")]
5258    #[test_case(iface_addr(local_ip::<I>()), bind_as_listener::<I>; "addr_no_device_listener")]
5259    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_listener::<I>;
5260        "any_interface_listener")]
5261    #[test_case(iface_id(MultipleDevicesId::A), bind_as_connected::<I>; "device_no_addr_connected")]
5262    #[test_case(iface_addr(local_ip::<I>()), bind_as_connected::<I>; "addr_no_device_connected")]
5263    #[test_case(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute, bind_as_connected::<I>;
5264        "any_interface_connected")]
5265    fn test_join_leave_multicast_succeeds<I: TestIpExt>(
5266        interface: MulticastMembershipInterfaceSelector<I::Addr, MultipleDevicesId>,
5267        set_up_socket: impl FnOnce(
5268            &mut UdpMultipleDevicesCtx,
5269            &UdpSocketId<
5270                I,
5271                FakeWeakDeviceId<MultipleDevicesId>,
5272                FakeUdpBindingsCtx<MultipleDevicesId>,
5273            >,
5274        ),
5275    ) {
5276        let mcast_addr = I::get_multicast_addr(3);
5277
5278        let set_up_ctx = |ctx: &mut UdpMultipleDevicesCtx| {
5279            // Ensure there is a route to the multicast address, if the interface
5280            // selector requires it.
5281            match interface {
5282                MulticastMembershipInterfaceSelector::Specified(_) => {}
5283                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
5284                    ctx.core_ctx
5285                        .bound_sockets
5286                        .ip_socket_ctx
5287                        .state
5288                        .add_route(MultipleDevicesId::A, mcast_addr.into_specified().into());
5289                }
5290            }
5291        };
5292
5293        let (result, ip_options) =
5294            try_join_leave_multicast(mcast_addr, interface, set_up_ctx, set_up_socket);
5295        assert_eq!(result, Ok(()));
5296        assert_eq!(
5297            ip_options,
5298            HashMap::from([((MultipleDevicesId::A, mcast_addr), NonZeroUsize::new(1).unwrap())])
5299        );
5300    }
5301
5302    #[ip_test(I)]
5303    #[test_case(leave_unbound::<I>; "unbound")]
5304    #[test_case(bind_as_listener::<I>; "listener")]
5305    #[test_case(bind_as_connected::<I>; "connected")]
5306    fn test_join_multicast_fails_without_route<I: TestIpExt>(
5307        set_up_socket: impl FnOnce(
5308            &mut UdpMultipleDevicesCtx,
5309            &UdpSocketId<
5310                I,
5311                FakeWeakDeviceId<MultipleDevicesId>,
5312                FakeUdpBindingsCtx<MultipleDevicesId>,
5313            >,
5314        ),
5315    ) {
5316        let mcast_addr = I::get_multicast_addr(3);
5317
5318        let (result, ip_options) = try_join_leave_multicast(
5319            mcast_addr,
5320            MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
5321            |_: &mut UdpMultipleDevicesCtx| { /* Don't install a route to `mcast_addr` */ },
5322            set_up_socket,
5323        );
5324        assert_eq!(result, Err(SetMulticastMembershipError::NoDeviceAvailable));
5325        assert_eq!(ip_options, HashMap::new());
5326    }
5327
5328    #[ip_test(I)]
5329    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), leave_unbound, Ok(());
5330        "with_ip_unbound")]
5331    #[test_case(MultipleDevicesId::A, None, leave_unbound, Ok(());
5332        "without_ip_unbound")]
5333    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_listener, Ok(());
5334        "with_ip_listener")]
5335    #[test_case(MultipleDevicesId::A, Some(local_ip::<I>()), bind_as_connected, Ok(());
5336        "with_ip_connected")]
5337    fn test_join_leave_multicast_interface_inferred_from_bound_device<I: TestIpExt>(
5338        bound_device: MultipleDevicesId,
5339        interface_addr: Option<SpecifiedAddr<I::Addr>>,
5340        set_up_socket: impl FnOnce(
5341            &mut UdpMultipleDevicesCtx,
5342            &UdpSocketId<
5343                I,
5344                FakeWeakDeviceId<MultipleDevicesId>,
5345                FakeUdpBindingsCtx<MultipleDevicesId>,
5346            >,
5347        ),
5348        expected_result: Result<(), SetMulticastMembershipError>,
5349    ) {
5350        let mcast_addr = I::get_multicast_addr(3);
5351        let (result, ip_options) = try_join_leave_multicast(
5352            mcast_addr,
5353            interface_addr
5354                .map(MulticastInterfaceSelector::LocalAddress)
5355                .map(Into::into)
5356                .unwrap_or(MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute),
5357            |_: &mut UdpMultipleDevicesCtx| { /* No ctx setup required */ },
5358            |ctx, unbound| {
5359                UdpApi::<I, _>::new(ctx.as_mut())
5360                    .set_device(&unbound, Some(&bound_device))
5361                    .unwrap();
5362                set_up_socket(ctx, &unbound)
5363            },
5364        );
5365        assert_eq!(result, expected_result);
5366        assert_eq!(
5367            ip_options,
5368            expected_result.map_or(HashMap::default(), |()| HashMap::from([(
5369                (bound_device, mcast_addr),
5370                NonZeroUsize::new(1).unwrap()
5371            )]))
5372        );
5373    }
5374
5375    #[ip_test(I)]
5376    fn test_multicast_membership_with_removed_device<I: TestIpExt>() {
5377        let device = FakeReferencyDeviceId::default();
5378        let mut ctx =
5379            FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::new_with_device::<I>(device.clone()));
5380        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5381
5382        let unbound = api.create();
5383        api.set_device(&unbound, Some(&device)).unwrap();
5384
5385        device.mark_removed();
5386
5387        let group = I::get_multicast_addr(4);
5388        assert_eq!(
5389            api.set_multicast_membership(
5390                &unbound,
5391                group,
5392                // Will use the socket's bound device.
5393                MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute,
5394                true,
5395            ),
5396            Err(SetMulticastMembershipError::DeviceDoesNotExist),
5397        );
5398
5399        // Should not have updated the device's multicast state.
5400        //
5401        // Note that even though we mock the device being removed above, its
5402        // state still exists in the fake IP socket context so we can inspect
5403        // it here.
5404        assert_eq!(
5405            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5406            HashMap::default(),
5407        );
5408    }
5409
5410    #[ip_test(I)]
5411    fn test_remove_udp_unbound_leaves_multicast_groups<I: TestIpExt>() {
5412        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5413            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5414        );
5415        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5416
5417        let unbound = api.create();
5418        let group = I::get_multicast_addr(4);
5419        api.set_multicast_membership(
5420            &unbound,
5421            group,
5422            MulticastInterfaceSelector::LocalAddress(local_ip::<I>()).into(),
5423            true,
5424        )
5425        .expect("join group failed");
5426
5427        assert_eq!(
5428            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5429            HashMap::from([((MultipleDevicesId::A, group), NonZeroUsize::new(1).unwrap())])
5430        );
5431
5432        api.close(unbound).into_removed();
5433        assert_eq!(
5434            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5435            HashMap::default()
5436        );
5437    }
5438
5439    #[ip_test(I)]
5440    fn test_remove_udp_listener_leaves_multicast_groups<I: TestIpExt>() {
5441        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5442            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5443        );
5444        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5445        let local_ip = local_ip::<I>();
5446
5447        let socket = api.create();
5448        let first_group = I::get_multicast_addr(4);
5449        api.set_multicast_membership(
5450            &socket,
5451            first_group,
5452            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5453            true,
5454        )
5455        .expect("join group failed");
5456
5457        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
5458            .expect("listen_udp failed");
5459        let second_group = I::get_multicast_addr(5);
5460        api.set_multicast_membership(
5461            &socket,
5462            second_group,
5463            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5464            true,
5465        )
5466        .expect("join group failed");
5467
5468        assert_eq!(
5469            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5470            HashMap::from([
5471                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
5472                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
5473            ])
5474        );
5475
5476        api.close(socket).into_removed();
5477        assert_eq!(
5478            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5479            HashMap::default()
5480        );
5481    }
5482
5483    #[ip_test(I)]
5484    fn test_remove_udp_connected_leaves_multicast_groups<I: TestIpExt>() {
5485        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5486            UdpMultipleDevicesCoreCtx::new_multiple_devices::<I>(),
5487        );
5488        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5489        let local_ip = local_ip::<I>();
5490
5491        let socket = api.create();
5492        let first_group = I::get_multicast_addr(4);
5493        api.set_multicast_membership(
5494            &socket,
5495            first_group,
5496            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5497            true,
5498        )
5499        .expect("join group failed");
5500
5501        api.connect(
5502            &socket,
5503            Some(ZonedAddr::Unzoned(I::get_other_remote_ip_address(1))),
5504            REMOTE_PORT.into(),
5505        )
5506        .expect("connect failed");
5507
5508        let second_group = I::get_multicast_addr(5);
5509        api.set_multicast_membership(
5510            &socket,
5511            second_group,
5512            MulticastInterfaceSelector::LocalAddress(local_ip).into(),
5513            true,
5514        )
5515        .expect("join group failed");
5516
5517        assert_eq!(
5518            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5519            HashMap::from([
5520                ((MultipleDevicesId::A, first_group), NonZeroUsize::new(1).unwrap()),
5521                ((MultipleDevicesId::A, second_group), NonZeroUsize::new(1).unwrap())
5522            ])
5523        );
5524
5525        api.close(socket).into_removed();
5526        assert_eq!(
5527            api.core_ctx().bound_sockets.ip_socket_ctx.state.multicast_memberships::<I>(),
5528            HashMap::default()
5529        );
5530    }
5531
5532    #[ip_test(I)]
5533    #[should_panic(expected = "listen again failed")]
5534    fn test_listen_udp_removes_unbound<I: TestIpExt>() {
5535        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5536        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5537        let local_ip = local_ip::<I>();
5538        let socket = api.create();
5539
5540        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT))
5541            .expect("listen_udp failed");
5542
5543        // Attempting to create a new listener from the same unbound ID should
5544        // panic since the unbound socket ID is now invalid.
5545        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(OTHER_LOCAL_PORT))
5546            .expect("listen again failed");
5547    }
5548
5549    #[ip_test(I)]
5550    fn test_get_conn_info<I: TestIpExt>() {
5551        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5552        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5553        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5554        let remote_ip = ZonedAddr::Unzoned(remote_ip::<I>());
5555        // Create a UDP connection with a specified local port and local IP.
5556        let socket = api.create();
5557        api.listen(&socket, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5558        api.connect(&socket, Some(remote_ip), REMOTE_PORT.into()).expect("connect failed");
5559        let info = api.get_info(&socket);
5560        let info = assert_matches!(info, SocketInfo::Connected(info) => info);
5561        assert_eq!(info.local_ip.into_inner(), local_ip.map_zone(FakeWeakDeviceId));
5562        assert_eq!(info.local_identifier, LOCAL_PORT);
5563        assert_eq!(info.remote_ip.into_inner(), remote_ip.map_zone(FakeWeakDeviceId));
5564        assert_eq!(info.remote_identifier, u16::from(REMOTE_PORT));
5565    }
5566
5567    #[ip_test(I)]
5568    fn test_get_listener_info<I: TestIpExt>() {
5569        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5570        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5571        let local_ip = ZonedAddr::Unzoned(local_ip::<I>());
5572
5573        // Check getting info on specified listener.
5574        let specified = api.create();
5575        api.listen(&specified, Some(local_ip), Some(LOCAL_PORT)).expect("listen_udp failed");
5576        let info = api.get_info(&specified);
5577        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5578        assert_eq!(info.local_ip.unwrap().into_inner(), local_ip.map_zone(FakeWeakDeviceId));
5579        assert_eq!(info.local_identifier, LOCAL_PORT);
5580
5581        // Check getting info on wildcard listener.
5582        let wildcard = api.create();
5583        api.listen(&wildcard, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
5584        let info = api.get_info(&wildcard);
5585        let info = assert_matches!(info, SocketInfo::Listener(info) => info);
5586        assert_eq!(info.local_ip, None);
5587        assert_eq!(info.local_identifier, OTHER_LOCAL_PORT);
5588    }
5589
5590    #[ip_test(I)]
5591    fn test_get_reuse_port<I: TestIpExt>() {
5592        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5593        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5594        let first = api.create();
5595        assert_eq!(api.get_posix_reuse_port(&first), false);
5596
5597        api.set_posix_reuse_port(&first, true).expect("is unbound");
5598
5599        assert_eq!(api.get_posix_reuse_port(&first), true);
5600
5601        api.listen(&first, Some(ZonedAddr::Unzoned(local_ip::<I>())), None).expect("listen failed");
5602        assert_eq!(api.get_posix_reuse_port(&first), true);
5603        api.close(first).into_removed();
5604
5605        let second = api.create();
5606        api.set_posix_reuse_port(&second, true).expect("is unbound");
5607        api.connect(&second, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5608            .expect("connect failed");
5609
5610        assert_eq!(api.get_posix_reuse_port(&second), true);
5611    }
5612
5613    #[ip_test(I)]
5614    fn test_get_bound_device_unbound<I: TestIpExt>() {
5615        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5616        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5617        let unbound = api.create();
5618
5619        assert_eq!(api.get_bound_device(&unbound), None);
5620
5621        api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
5622        assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
5623    }
5624
5625    #[ip_test(I)]
5626    fn test_get_bound_device_listener<I: TestIpExt>() {
5627        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5628        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5629        let socket = api.create();
5630
5631        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
5632        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip::<I>())), Some(LOCAL_PORT))
5633            .expect("failed to listen");
5634        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
5635
5636        api.set_device(&socket, None).expect("failed to set device");
5637        assert_eq!(api.get_bound_device(&socket), None);
5638    }
5639
5640    #[ip_test(I)]
5641    fn test_get_bound_device_connected<I: TestIpExt>() {
5642        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5643        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5644        let socket = api.create();
5645        api.set_device(&socket, Some(&FakeDeviceId)).unwrap();
5646        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<I>())), REMOTE_PORT.into())
5647            .expect("failed to connect");
5648        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
5649        api.set_device(&socket, None).expect("failed to set device");
5650        assert_eq!(api.get_bound_device(&socket), None);
5651    }
5652
5653    #[ip_test(I)]
5654    fn test_listen_udp_forwards_errors<I: TestIpExt>() {
5655        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
5656        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5657        let remote_ip = remote_ip::<I>();
5658
5659        // Check listening to a non-local IP fails.
5660        let unbound = api.create();
5661        let listen_err = api
5662            .listen(&unbound, Some(ZonedAddr::Unzoned(remote_ip)), Some(LOCAL_PORT))
5663            .expect_err("listen_udp unexpectedly succeeded");
5664        assert_eq!(listen_err, Either::Right(LocalAddressError::CannotBindToAddress));
5665
5666        let unbound = api.create();
5667        let _ = api.listen(&unbound, None, Some(OTHER_LOCAL_PORT)).expect("listen_udp failed");
5668        let unbound = api.create();
5669        let listen_err = api
5670            .listen(&unbound, None, Some(OTHER_LOCAL_PORT))
5671            .expect_err("listen_udp unexpectedly succeeded");
5672        assert_eq!(listen_err, Either::Right(LocalAddressError::AddressInUse));
5673    }
5674
5675    const IPV6_LINK_LOCAL_ADDR: Ipv6Addr = net_ip_v6!("fe80::1234");
5676    #[test_case(IPV6_LINK_LOCAL_ADDR, IPV6_LINK_LOCAL_ADDR; "unicast")]
5677    #[test_case(IPV6_LINK_LOCAL_ADDR, MulticastAddr::new(net_ip_v6!("ff02::1234")).unwrap().get(); "multicast")]
5678    fn test_listen_udp_ipv6_link_local_requires_zone(
5679        interface_addr: Ipv6Addr,
5680        bind_addr: Ipv6Addr,
5681    ) {
5682        type I = Ipv6;
5683        let interface_addr = LinkLocalAddr::new(interface_addr).unwrap().into_specified();
5684
5685        let mut ctx =
5686            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
5687                vec![interface_addr],
5688                vec![remote_ip::<I>()],
5689            ));
5690        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5691
5692        let bind_addr = LinkLocalAddr::new(bind_addr).unwrap().into_specified();
5693        assert!(bind_addr.scope().can_have_zone());
5694
5695        let unbound = api.create();
5696        let result = api.listen(&unbound, Some(ZonedAddr::Unzoned(bind_addr)), Some(LOCAL_PORT));
5697        assert_eq!(
5698            result,
5699            Err(Either::Right(LocalAddressError::Zone(ZonedAddressError::RequiredZoneNotProvided)))
5700        );
5701    }
5702
5703    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
5704    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "not matching")]
5705    fn test_listen_udp_ipv6_link_local_with_bound_device_set(
5706        zone_id: MultipleDevicesId,
5707        expected_result: Result<(), LocalAddressError>,
5708    ) {
5709        type I = Ipv6;
5710        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5711        assert!(ll_addr.scope().can_have_zone());
5712
5713        let remote_ips = vec![remote_ip::<I>()];
5714        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5715            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5716                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
5717                    |(device, local_ip)| FakeDeviceConfig {
5718                        device,
5719                        local_ips: vec![local_ip],
5720                        remote_ips: remote_ips.clone(),
5721                    },
5722                ),
5723            )),
5724        );
5725        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5726
5727        let socket = api.create();
5728        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
5729
5730        let result = api
5731            .listen(
5732                &socket,
5733                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
5734                Some(LOCAL_PORT),
5735            )
5736            .map_err(Either::unwrap_right);
5737        assert_eq!(result, expected_result);
5738    }
5739
5740    #[test_case(MultipleDevicesId::A, Ok(()); "matching")]
5741    #[test_case(MultipleDevicesId::B, Err(LocalAddressError::AddressMismatch); "not matching")]
5742    fn test_listen_udp_ipv6_link_local_with_zone_requires_addr_assigned_to_device(
5743        zone_id: MultipleDevicesId,
5744        expected_result: Result<(), LocalAddressError>,
5745    ) {
5746        type I = Ipv6;
5747        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5748        assert!(ll_addr.scope().can_have_zone());
5749
5750        let remote_ips = vec![remote_ip::<I>()];
5751        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5752            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5753                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
5754                    |(device, local_ip)| FakeDeviceConfig {
5755                        device,
5756                        local_ips: vec![local_ip],
5757                        remote_ips: remote_ips.clone(),
5758                    },
5759                ),
5760            )),
5761        );
5762        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5763
5764        let socket = api.create();
5765        let result = api
5766            .listen(
5767                &socket,
5768                Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, zone_id).unwrap())),
5769                Some(LOCAL_PORT),
5770            )
5771            .map_err(Either::unwrap_right);
5772        assert_eq!(result, expected_result);
5773    }
5774
5775    #[test_case(None, Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "clear device")]
5776    #[test_case(Some(MultipleDevicesId::A), Ok(()); "set same device")]
5777    #[test_case(Some(MultipleDevicesId::B),
5778                Err(LocalAddressError::Zone(ZonedAddressError::DeviceZoneMismatch)); "change device")]
5779    fn test_listen_udp_ipv6_listen_link_local_update_bound_device(
5780        new_device: Option<MultipleDevicesId>,
5781        expected_result: Result<(), LocalAddressError>,
5782    ) {
5783        type I = Ipv6;
5784        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5785        assert!(ll_addr.scope().can_have_zone());
5786
5787        let remote_ips = vec![remote_ip::<I>()];
5788        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5789            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5790                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<I>())].map(
5791                    |(device, local_ip)| FakeDeviceConfig {
5792                        device,
5793                        local_ips: vec![local_ip],
5794                        remote_ips: remote_ips.clone(),
5795                    },
5796                ),
5797            )),
5798        );
5799        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5800
5801        let socket = api.create();
5802        api.listen(
5803            &socket,
5804            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap())),
5805            Some(LOCAL_PORT),
5806        )
5807        .expect("listen failed");
5808
5809        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(MultipleDevicesId::A)));
5810
5811        assert_eq!(
5812            api.set_device(&socket, new_device.as_ref()),
5813            expected_result.map_err(SocketError::Local),
5814        );
5815    }
5816
5817    #[test_case(None; "bind all IPs")]
5818    #[test_case(Some(ZonedAddr::Unzoned(local_ip::<Ipv6>())); "bind unzoned")]
5819    #[test_case(Some(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
5820        MultipleDevicesId::A).unwrap())); "bind with same zone")]
5821    fn test_udp_ipv6_connect_with_unzoned(
5822        bound_addr: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>>,
5823    ) {
5824        let remote_ips = vec![remote_ip::<Ipv6>()];
5825
5826        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5827            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
5828                FakeDeviceConfig {
5829                    device: MultipleDevicesId::A,
5830                    local_ips: vec![
5831                        local_ip::<Ipv6>(),
5832                        SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
5833                    ],
5834                    remote_ips: remote_ips.clone(),
5835                },
5836                FakeDeviceConfig {
5837                    device: MultipleDevicesId::B,
5838                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
5839                    remote_ips: remote_ips,
5840                },
5841            ])),
5842        );
5843        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
5844
5845        let socket = api.create();
5846
5847        api.listen(&socket, bound_addr, Some(LOCAL_PORT)).unwrap();
5848
5849        assert_matches!(
5850            api.connect(
5851                &socket,
5852                Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())),
5853                REMOTE_PORT.into(),
5854            ),
5855            Ok(())
5856        );
5857    }
5858
5859    #[test]
5860    fn test_udp_ipv6_connect_zoned_get_info() {
5861        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5862        assert!(ll_addr.must_have_zone());
5863
5864        let remote_ips = vec![remote_ip::<Ipv6>()];
5865        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5866            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5867                [(MultipleDevicesId::A, ll_addr), (MultipleDevicesId::B, local_ip::<Ipv6>())].map(
5868                    |(device, local_ip)| FakeDeviceConfig {
5869                        device,
5870                        local_ips: vec![local_ip],
5871                        remote_ips: remote_ips.clone(),
5872                    },
5873                ),
5874            )),
5875        );
5876
5877        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
5878        let socket = api.create();
5879        api.set_device(&socket, Some(&MultipleDevicesId::A)).unwrap();
5880
5881        let zoned_local_addr =
5882            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
5883        api.listen(&socket, Some(zoned_local_addr), Some(LOCAL_PORT)).unwrap();
5884
5885        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip::<Ipv6>())), REMOTE_PORT.into())
5886            .expect("connect should succeed");
5887
5888        assert_eq!(
5889            api.get_info(&socket),
5890            SocketInfo::Connected(datagram::ConnInfo {
5891                local_ip: StrictlyZonedAddr::new_with_zone(ll_addr, || FakeWeakDeviceId(
5892                    MultipleDevicesId::A
5893                )),
5894                local_identifier: LOCAL_PORT,
5895                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(remote_ip::<Ipv6>()),
5896                remote_identifier: REMOTE_PORT.into(),
5897            })
5898        );
5899    }
5900
5901    #[test_case(ZonedAddr::Zoned(AddrAndZone::new(SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap(),
5902        MultipleDevicesId::B).unwrap()),
5903        Err(ConnectError::Zone(ZonedAddressError::DeviceZoneMismatch));
5904        "connect to different zone")]
5905    #[test_case(ZonedAddr::Unzoned(SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()),
5906        Ok(FakeWeakDeviceId(MultipleDevicesId::A)); "connect implicit zone")]
5907    fn test_udp_ipv6_bind_zoned(
5908        remote_addr: ZonedAddr<SpecifiedAddr<Ipv6Addr>, MultipleDevicesId>,
5909        expected: Result<FakeWeakDeviceId<MultipleDevicesId>, ConnectError>,
5910    ) {
5911        let remote_ips = vec![SpecifiedAddr::new(net_ip_v6!("fe80::3")).unwrap()];
5912
5913        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5914            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new([
5915                FakeDeviceConfig {
5916                    device: MultipleDevicesId::A,
5917                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()],
5918                    remote_ips: remote_ips.clone(),
5919                },
5920                FakeDeviceConfig {
5921                    device: MultipleDevicesId::B,
5922                    local_ips: vec![SpecifiedAddr::new(net_ip_v6!("fe80::2")).unwrap()],
5923                    remote_ips: remote_ips,
5924                },
5925            ])),
5926        );
5927
5928        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
5929
5930        let socket = api.create();
5931
5932        api.listen(
5933            &socket,
5934            Some(ZonedAddr::Zoned(
5935                AddrAndZone::new(
5936                    SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap(),
5937                    MultipleDevicesId::A,
5938                )
5939                .unwrap(),
5940            )),
5941            Some(LOCAL_PORT),
5942        )
5943        .unwrap();
5944
5945        let result = api
5946            .connect(&socket, Some(remote_addr), REMOTE_PORT.into())
5947            .map(|()| api.get_bound_device(&socket).unwrap());
5948        assert_eq!(result, expected);
5949    }
5950
5951    #[ip_test(I)]
5952    fn test_listen_udp_loopback_no_zone_is_required<I: TestIpExt>() {
5953        let loopback_addr = I::LOOPBACK_ADDRESS;
5954        let remote_ips = vec![remote_ip::<I>()];
5955
5956        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
5957            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
5958                [(MultipleDevicesId::A, loopback_addr), (MultipleDevicesId::B, local_ip::<I>())]
5959                    .map(|(device, local_ip)| FakeDeviceConfig {
5960                        device,
5961                        local_ips: vec![local_ip],
5962                        remote_ips: remote_ips.clone(),
5963                    }),
5964            )),
5965        );
5966        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
5967
5968        let unbound = api.create();
5969        api.set_device(&unbound, Some(&MultipleDevicesId::A)).unwrap();
5970
5971        let result =
5972            api.listen(&unbound, Some(ZonedAddr::Unzoned(loopback_addr)), Some(LOCAL_PORT));
5973        assert_matches!(result, Ok(_));
5974    }
5975
5976    #[test_case(None, true, Ok(()); "connected success")]
5977    #[test_case(None, false, Ok(()); "listening success")]
5978    #[test_case(Some(MultipleDevicesId::A), true, Ok(()); "conn bind same device")]
5979    #[test_case(Some(MultipleDevicesId::A), false, Ok(()); "listen bind same device")]
5980    #[test_case(
5981        Some(MultipleDevicesId::B),
5982        true,
5983        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
5984        "conn bind different device")]
5985    #[test_case(
5986        Some(MultipleDevicesId::B),
5987        false,
5988        Err(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch));
5989        "listen bind different device")]
5990    fn test_udp_ipv6_send_to_zoned(
5991        bind_device: Option<MultipleDevicesId>,
5992        connect: bool,
5993        expected: Result<(), SendToError>,
5994    ) {
5995        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
5996        assert!(ll_addr.must_have_zone());
5997        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
5998
5999        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6000            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6001                [
6002                    (MultipleDevicesId::A, Ipv6::get_other_ip_address(1)),
6003                    (MultipleDevicesId::B, Ipv6::get_other_ip_address(2)),
6004                ]
6005                .map(|(device, local_ip)| FakeDeviceConfig {
6006                    device,
6007                    local_ips: vec![local_ip],
6008                    remote_ips: vec![ll_addr, conn_remote_ip],
6009                }),
6010            )),
6011        );
6012
6013        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6014        let socket = api.create();
6015
6016        if let Some(device) = bind_device {
6017            api.set_device(&socket, Some(&device)).unwrap();
6018        }
6019
6020        let send_to_remote_addr =
6021            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::A).unwrap());
6022        let result = if connect {
6023            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6024                .expect("connect should succeed");
6025            api.send_to(
6026                &socket,
6027                Some(send_to_remote_addr),
6028                REMOTE_PORT.into(),
6029                Buf::new(Vec::new(), ..),
6030            )
6031        } else {
6032            api.listen(&socket, None, Some(LOCAL_PORT)).expect("listen should succeed");
6033            api.send_to(
6034                &socket,
6035                Some(send_to_remote_addr),
6036                REMOTE_PORT.into(),
6037                Buf::new(Vec::new(), ..),
6038            )
6039        };
6040
6041        assert_eq!(result.map_err(|err| assert_matches!(err, Either::Right(e) => e)), expected);
6042    }
6043
6044    #[test_case(true; "connected")]
6045    #[test_case(false; "listening")]
6046    fn test_udp_ipv6_bound_zoned_send_to_zoned(connect: bool) {
6047        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::5678")).unwrap().into_specified();
6048        let device_a_local_ip = net_ip_v6!("fe80::1111");
6049        let conn_remote_ip = Ipv6::get_other_remote_ip_address(1);
6050
6051        let mut ctx = UdpMultipleDevicesCtx::with_core_ctx(
6052            UdpMultipleDevicesCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6053                [
6054                    (MultipleDevicesId::A, device_a_local_ip),
6055                    (MultipleDevicesId::B, net_ip_v6!("fe80::2222")),
6056                ]
6057                .map(|(device, local_ip)| FakeDeviceConfig {
6058                    device,
6059                    local_ips: vec![LinkLocalAddr::new(local_ip).unwrap().into_specified()],
6060                    remote_ips: vec![ll_addr, conn_remote_ip],
6061                }),
6062            )),
6063        );
6064        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6065
6066        let socket = api.create();
6067        api.listen(
6068            &socket,
6069            Some(ZonedAddr::Zoned(
6070                AddrAndZone::new(
6071                    SpecifiedAddr::new(device_a_local_ip).unwrap(),
6072                    MultipleDevicesId::A,
6073                )
6074                .unwrap(),
6075            )),
6076            Some(LOCAL_PORT),
6077        )
6078        .expect("listen should succeed");
6079
6080        // Use a remote address on device B, while the socket is listening on
6081        // device A. This should cause a failure when sending.
6082        let send_to_remote_addr =
6083            ZonedAddr::Zoned(AddrAndZone::new(ll_addr, MultipleDevicesId::B).unwrap());
6084
6085        let result = if connect {
6086            api.connect(&socket, Some(ZonedAddr::Unzoned(conn_remote_ip)), REMOTE_PORT.into())
6087                .expect("connect should succeed");
6088            api.send_to(
6089                &socket,
6090                Some(send_to_remote_addr),
6091                REMOTE_PORT.into(),
6092                Buf::new(Vec::new(), ..),
6093            )
6094        } else {
6095            api.send_to(
6096                &socket,
6097                Some(send_to_remote_addr),
6098                REMOTE_PORT.into(),
6099                Buf::new(Vec::new(), ..),
6100            )
6101        };
6102
6103        assert_matches!(
6104            result,
6105            Err(Either::Right(SendToError::Zone(ZonedAddressError::DeviceZoneMismatch)))
6106        );
6107    }
6108
6109    #[test_case(None; "removes implicit")]
6110    #[test_case(Some(FakeDeviceId); "preserves implicit")]
6111    fn test_connect_disconnect_affects_bound_device(bind_device: Option<FakeDeviceId>) {
6112        // If a socket is bound to an unzoned address, whether or not it has a
6113        // bound device should be restored after `connect` then `disconnect`.
6114        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6115        assert!(ll_addr.must_have_zone());
6116
6117        let local_ip = local_ip::<Ipv6>();
6118        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6119            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6120        );
6121        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6122
6123        let socket = api.create();
6124        api.set_device(&socket, bind_device.as_ref()).unwrap();
6125
6126        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6127        api.connect(
6128            &socket,
6129            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6130            REMOTE_PORT.into(),
6131        )
6132        .expect("connect should succeed");
6133
6134        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6135
6136        api.disconnect(&socket).expect("was connected");
6137
6138        assert_eq!(api.get_bound_device(&socket), bind_device.map(FakeWeakDeviceId));
6139    }
6140
6141    #[test]
6142    fn test_bind_zoned_addr_connect_disconnect() {
6143        // If a socket is bound to a zoned address, the address's device should
6144        // be retained after `connect` then `disconnect`.
6145        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6146        assert!(ll_addr.must_have_zone());
6147
6148        let remote_ip = remote_ip::<Ipv6>();
6149        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6150            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![ll_addr], vec![remote_ip]),
6151        );
6152
6153        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6154
6155        let socket = api.create();
6156        api.listen(
6157            &socket,
6158            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6159            Some(LOCAL_PORT),
6160        )
6161        .unwrap();
6162        api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into())
6163            .expect("connect should succeed");
6164
6165        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6166
6167        api.disconnect(&socket).expect("was connected");
6168        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6169    }
6170
6171    #[test]
6172    fn test_bind_device_after_connect_persists_after_disconnect() {
6173        // If a socket is bound to an unzoned address, connected to a zoned address, and then has
6174        // its device set, the device should be *retained* after `disconnect`.
6175        let ll_addr = LinkLocalAddr::new(net_ip_v6!("fe80::1234")).unwrap().into_specified();
6176        assert!(ll_addr.must_have_zone());
6177
6178        let local_ip = local_ip::<Ipv6>();
6179        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(
6180            UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(vec![local_ip], vec![ll_addr]),
6181        );
6182        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6183        let socket = api.create();
6184        api.listen(&socket, Some(ZonedAddr::Unzoned(local_ip)), Some(LOCAL_PORT)).unwrap();
6185        api.connect(
6186            &socket,
6187            Some(ZonedAddr::Zoned(AddrAndZone::new(ll_addr, FakeDeviceId).unwrap())),
6188            REMOTE_PORT.into(),
6189        )
6190        .expect("connect should succeed");
6191
6192        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6193
6194        // This is a no-op functionally since the socket is already bound to the
6195        // device but it implies that we shouldn't unbind the device on
6196        // disconnect.
6197        api.set_device(&socket, Some(&FakeDeviceId)).expect("binding same device should succeed");
6198
6199        api.disconnect(&socket).expect("was connected");
6200        assert_eq!(api.get_bound_device(&socket), Some(FakeWeakDeviceId(FakeDeviceId)));
6201    }
6202
6203    #[ip_test(I)]
6204    fn test_remove_udp_unbound<I: TestIpExt>() {
6205        let mut ctx = UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::new_fake_device::<I>());
6206        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6207        let unbound = api.create();
6208        api.close(unbound).into_removed();
6209    }
6210
6211    #[ip_test(I)]
6212    fn test_hop_limits_used_for_sending_packets<I: TestIpExt>() {
6213        let some_multicast_addr: MulticastAddr<I::Addr> = I::map_ip(
6214            (),
6215            |()| Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
6216            |()| MulticastAddr::new(net_ip_v6!("ff0e::1")).unwrap(),
6217        );
6218
6219        let mut ctx =
6220            UdpFakeDeviceCtx::with_core_ctx(UdpFakeDeviceCoreCtx::with_local_remote_ip_addrs(
6221                vec![local_ip::<I>()],
6222                vec![remote_ip::<I>(), some_multicast_addr.into_specified()],
6223            ));
6224        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
6225        let listener = api.create();
6226
6227        const UNICAST_HOPS: NonZeroU8 = NonZeroU8::new(23).unwrap();
6228        const MULTICAST_HOPS: NonZeroU8 = NonZeroU8::new(98).unwrap();
6229        api.set_unicast_hop_limit(&listener, Some(UNICAST_HOPS), I::VERSION).unwrap();
6230        api.set_multicast_hop_limit(&listener, Some(MULTICAST_HOPS), I::VERSION).unwrap();
6231
6232        api.listen(&listener, None, None).expect("listen failed");
6233
6234        let mut send_and_get_ttl = |remote_ip| {
6235            api.send_to(
6236                &listener,
6237                Some(ZonedAddr::Unzoned(remote_ip)),
6238                REMOTE_PORT.into(),
6239                Buf::new(vec![], ..),
6240            )
6241            .expect("send failed");
6242
6243            let (meta, _body) = api.core_ctx().bound_sockets.ip_socket_ctx.frames().last().unwrap();
6244            let SendIpPacketMeta { dst_ip, ttl, .. } = meta.try_as::<I>().unwrap();
6245            assert_eq!(*dst_ip, remote_ip);
6246            *ttl
6247        };
6248
6249        assert_eq!(send_and_get_ttl(some_multicast_addr.into_specified()), Some(MULTICAST_HOPS));
6250        assert_eq!(send_and_get_ttl(remote_ip::<I>()), Some(UNICAST_HOPS));
6251    }
6252
6253    const DUAL_STACK_ANY_ADDR: Ipv6Addr = net_ip_v6!("::");
6254    const DUAL_STACK_V4_ANY_ADDR: Ipv6Addr = net_ip_v6!("::FFFF:0.0.0.0");
6255
6256    #[derive(Copy, Clone, Debug)]
6257    enum DualStackBindAddr {
6258        Any,
6259        V4Any,
6260        V4Specific,
6261    }
6262
6263    impl DualStackBindAddr {
6264        const fn v6_addr(&self) -> Option<Ipv6Addr> {
6265            match self {
6266                Self::Any => Some(DUAL_STACK_ANY_ADDR),
6267                Self::V4Any => Some(DUAL_STACK_V4_ANY_ADDR),
6268                Self::V4Specific => None,
6269            }
6270        }
6271    }
6272    const V4_LOCAL_IP: Ipv4Addr = ip_v4!("192.168.1.10");
6273    const V4_LOCAL_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:192.168.1.10");
6274    const V6_LOCAL_IP: Ipv6Addr = net_ip_v6!("2201::1");
6275    const V6_REMOTE_IP: SpecifiedAddr<Ipv6Addr> =
6276        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("2001:db8::1")) };
6277    const V4_REMOTE_IP_MAPPED: SpecifiedAddr<Ipv6Addr> =
6278        unsafe { SpecifiedAddr::new_unchecked(net_ip_v6!("::FFFF:192.0.2.1")) };
6279
6280    fn get_dual_stack_context<
6281        'a,
6282        BC: UdpBindingsTypes + 'a,
6283        CC: DatagramBoundStateContext<Ipv6, BC, Udp<BC>>,
6284    >(
6285        core_ctx: &'a mut CC,
6286    ) -> &'a mut CC::DualStackContext {
6287        match core_ctx.dual_stack_context() {
6288            MaybeDualStack::NotDualStack(_) => unreachable!("UDP is a dual stack enabled protocol"),
6289            MaybeDualStack::DualStack(ds) => ds,
6290        }
6291    }
6292
6293    #[test_case(DualStackBindAddr::Any; "dual-stack")]
6294    #[test_case(DualStackBindAddr::V4Any; "v4 any")]
6295    #[test_case(DualStackBindAddr::V4Specific; "v4 specific")]
6296    fn dual_stack_delivery(bind_addr: DualStackBindAddr) {
6297        const REMOTE_IP: Ipv4Addr = ip_v4!("8.8.8.8");
6298        const REMOTE_IP_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
6299        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
6300        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6301            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6302            vec![SpecifiedAddr::new(REMOTE_IP).unwrap()],
6303        ));
6304
6305        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6306        let listener = api.create();
6307        api.listen(
6308            &listener,
6309            SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6310            Some(LOCAL_PORT),
6311        )
6312        .expect("can bind");
6313
6314        const BODY: &[u8] = b"abcde";
6315        let (core_ctx, bindings_ctx) = api.contexts();
6316        receive_udp_packet(
6317            core_ctx,
6318            bindings_ctx,
6319            FakeDeviceId,
6320            UdpPacketMeta::<Ipv4> {
6321                src_ip: REMOTE_IP,
6322                src_port: Some(REMOTE_PORT),
6323                dst_ip: V4_LOCAL_IP,
6324                dst_port: LOCAL_PORT,
6325                dscp_and_ecn: DscpAndEcn::default(),
6326            },
6327            BODY,
6328        )
6329        .expect("receive udp packet should succeed");
6330
6331        assert_eq!(
6332            bindings_ctx.state.received::<Ipv6>(),
6333            &HashMap::from([(
6334                listener.downgrade(),
6335                SocketReceived {
6336                    packets: vec![ReceivedPacket {
6337                        body: BODY.into(),
6338                        meta: UdpPacketMeta::<Ipv6> {
6339                            src_ip: REMOTE_IP_MAPPED,
6340                            src_port: Some(REMOTE_PORT),
6341                            dst_ip: V4_LOCAL_IP_MAPPED,
6342                            dst_port: LOCAL_PORT,
6343                            dscp_and_ecn: DscpAndEcn::default(),
6344                        }
6345                    }]
6346                }
6347            )])
6348        );
6349    }
6350
6351    #[test_case(DualStackBindAddr::Any, true; "dual-stack any bind v4 first")]
6352    #[test_case(DualStackBindAddr::V4Any, true; "v4 any bind v4 first")]
6353    #[test_case(DualStackBindAddr::V4Specific, true; "v4 specific bind v4 first")]
6354    #[test_case(DualStackBindAddr::Any, false; "dual-stack any bind v4 second")]
6355    #[test_case(DualStackBindAddr::V4Any, false; "v4 any bind v4 second")]
6356    #[test_case(DualStackBindAddr::V4Specific, false; "v4 specific bind v4 second")]
6357    fn dual_stack_bind_conflict(bind_addr: DualStackBindAddr, bind_v4_first: bool) {
6358        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6359            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6360            vec![],
6361        ));
6362
6363        let v4_listener = UdpApi::<Ipv4, _>::new(ctx.as_mut()).create();
6364        let v6_listener = UdpApi::<Ipv6, _>::new(ctx.as_mut()).create();
6365
6366        let bind_v4 = |mut api: UdpApi<Ipv4, _>| {
6367            api.listen(
6368                &v4_listener,
6369                SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6370                Some(LOCAL_PORT),
6371            )
6372        };
6373        let bind_v6 = |mut api: UdpApi<Ipv6, _>| {
6374            api.listen(
6375                &v6_listener,
6376                SpecifiedAddr::new(bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED))
6377                    .map(ZonedAddr::Unzoned),
6378                Some(LOCAL_PORT),
6379            )
6380        };
6381
6382        let second_bind_error = if bind_v4_first {
6383            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect("no conflict");
6384            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect_err("should conflict")
6385        } else {
6386            bind_v6(UdpApi::<Ipv6, _>::new(ctx.as_mut())).expect("no conflict");
6387            bind_v4(UdpApi::<Ipv4, _>::new(ctx.as_mut())).expect_err("should conflict")
6388        };
6389        assert_eq!(second_bind_error, Either::Right(LocalAddressError::AddressInUse));
6390    }
6391
6392    // Verifies that port availability in both the IPv4 and IPv6 bound socket
6393    // maps is considered when allocating a local port for a dual-stack UDP
6394    // socket listening in both stacks.
6395    #[test_case(IpVersion::V4; "v4_is_constrained")]
6396    #[test_case(IpVersion::V6; "v6_is_constrained")]
6397    fn dual_stack_local_port_alloc(ip_version_with_constrained_ports: IpVersion) {
6398        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6399            vec![
6400                SpecifiedAddr::new(V4_LOCAL_IP.to_ip_addr()).unwrap(),
6401                SpecifiedAddr::new(V6_LOCAL_IP.to_ip_addr()).unwrap(),
6402            ],
6403            vec![],
6404        ));
6405
6406        // Specifically selected to be in the `EPHEMERAL_RANGE`.
6407        const AVAILABLE_PORT: NonZeroU16 = NonZeroU16::new(54321).unwrap();
6408
6409        // Densely pack the port space for one IP Version.
6410        for port in 1..=u16::MAX {
6411            let port = NonZeroU16::new(port).unwrap();
6412            if port == AVAILABLE_PORT {
6413                continue;
6414            }
6415            match ip_version_with_constrained_ports {
6416                IpVersion::V4 => {
6417                    let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
6418                    let listener = api.create();
6419                    api.listen(
6420                        &listener,
6421                        SpecifiedAddr::new(V4_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6422                        Some(port),
6423                    )
6424                    .expect("listen v4 should succeed")
6425                }
6426                IpVersion::V6 => {
6427                    let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6428                    let listener = api.create();
6429                    api.listen(
6430                        &listener,
6431                        SpecifiedAddr::new(V6_LOCAL_IP).map(|a| ZonedAddr::Unzoned(a)),
6432                        Some(port),
6433                    )
6434                    .expect("listen v6 should succeed")
6435                }
6436            }
6437        }
6438
6439        // Create a listener on the dualstack any address, expecting it to be
6440        // allocated `AVAILABLE_PORT`.
6441        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6442        let listener = api.create();
6443        api.listen(&listener, None, None).expect("dualstack listen should succeed");
6444        let port = assert_matches!(api.get_info(&listener), SocketInfo::Listener(info) => info.local_identifier);
6445        assert_eq!(port, AVAILABLE_PORT);
6446    }
6447
6448    #[test_case(DualStackBindAddr::V4Any; "v4 any")]
6449    #[test_case(DualStackBindAddr::V4Specific; "v4 specific")]
6450    fn dual_stack_enable(bind_addr: DualStackBindAddr) {
6451        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6452            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6453            vec![],
6454        ));
6455        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6456
6457        let bind_addr = bind_addr.v6_addr().unwrap_or(V4_LOCAL_IP_MAPPED);
6458        let listener = api.create();
6459
6460        assert_eq!(api.get_dual_stack_enabled(&listener), Ok(true));
6461        api.set_dual_stack_enabled(&listener, false).expect("can set dual-stack enabled");
6462
6463        // With dual-stack behavior disabled, the IPv6 socket can't bind to
6464        // an IPv4-mapped IPv6 address.
6465        assert_eq!(
6466            api.listen(
6467                &listener,
6468                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6469                Some(LOCAL_PORT),
6470            ),
6471            Err(Either::Right(LocalAddressError::CannotBindToAddress))
6472        );
6473        api.set_dual_stack_enabled(&listener, true).expect("can set dual-stack enabled");
6474        // Try again now that dual-stack sockets are enabled.
6475        assert_eq!(
6476            api.listen(
6477                &listener,
6478                SpecifiedAddr::new(bind_addr).map(|a| ZonedAddr::Unzoned(a)),
6479                Some(LOCAL_PORT),
6480            ),
6481            Ok(())
6482        );
6483    }
6484
6485    #[test]
6486    fn dual_stack_bind_unassigned_v4_address() {
6487        const NOT_ASSIGNED_MAPPED: Ipv6Addr = net_ip_v6!("::ffff:8.8.8.8");
6488        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6489            vec![SpecifiedAddr::new(V4_LOCAL_IP).unwrap()],
6490            vec![],
6491        ));
6492        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6493
6494        let listener = api.create();
6495        assert_eq!(
6496            api.listen(
6497                &listener,
6498                SpecifiedAddr::new(NOT_ASSIGNED_MAPPED).map(|a| ZonedAddr::Unzoned(a)),
6499                Some(LOCAL_PORT),
6500            ),
6501            Err(Either::Right(LocalAddressError::CannotBindToAddress))
6502        );
6503    }
6504
6505    // Calling `connect` on an already bound socket will cause the existing
6506    // `listener` entry in the bound state map to be upgraded to a `connected`
6507    // entry. Dual-stack listeners may exist in both the IPv4 and IPv6 bound
6508    // state maps, so make sure both entries are properly removed.
6509    #[test]
6510    fn dual_stack_connect_cleans_up_existing_listener() {
6511        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
6512            vec![Ipv6::TEST_ADDRS.local_ip],
6513            vec![Ipv6::TEST_ADDRS.remote_ip],
6514        ));
6515
6516        const DUAL_STACK_ANY_ADDR: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, FakeDeviceId>> = None;
6517
6518        fn assert_listeners(core_ctx: &mut FakeUdpCoreCtx<FakeDeviceId>, expect_present: bool) {
6519            const V4_LISTENER_ADDR: ListenerAddr<
6520                ListenerIpAddr<Ipv4Addr, NonZeroU16>,
6521                FakeWeakDeviceId<FakeDeviceId>,
6522            > = ListenerAddr {
6523                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
6524                device: None,
6525            };
6526            const V6_LISTENER_ADDR: ListenerAddr<
6527                ListenerIpAddr<Ipv6Addr, NonZeroU16>,
6528                FakeWeakDeviceId<FakeDeviceId>,
6529            > = ListenerAddr {
6530                ip: ListenerIpAddr { addr: None, identifier: LOCAL_PORT },
6531                device: None,
6532            };
6533
6534            DualStackBoundStateContext::with_both_bound_sockets_mut(
6535                get_dual_stack_context(&mut core_ctx.bound_sockets),
6536                |_core_ctx, v6_sockets, v4_sockets| {
6537                    let v4 = v4_sockets.bound_sockets.listeners().get_by_addr(&V4_LISTENER_ADDR);
6538                    let v6 = v6_sockets.bound_sockets.listeners().get_by_addr(&V6_LISTENER_ADDR);
6539                    if expect_present {
6540                        assert_matches!(v4, Some(_));
6541                        assert_matches!(v6, Some(_));
6542                    } else {
6543                        assert_matches!(v4, None);
6544                        assert_matches!(v6, None);
6545                    }
6546                },
6547            );
6548        }
6549
6550        // Create a socket and listen on the IPv6 any address. Verify we have
6551        // listener state for both IPv4 and IPv6.
6552        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6553        let socket = api.create();
6554        assert_eq!(api.listen(&socket, DUAL_STACK_ANY_ADDR, Some(LOCAL_PORT)), Ok(()));
6555        assert_listeners(api.core_ctx(), true);
6556
6557        // Connect the socket to a remote V6 address and verify that both
6558        // the IPv4 and IPv6 listener state has been removed.
6559        assert_eq!(
6560            api.connect(
6561                &socket,
6562                Some(ZonedAddr::Unzoned(Ipv6::TEST_ADDRS.remote_ip)),
6563                REMOTE_PORT.into(),
6564            ),
6565            Ok(())
6566        );
6567        assert_matches!(api.get_info(&socket), SocketInfo::Connected(_));
6568        assert_listeners(api.core_ctx(), false);
6569    }
6570
6571    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
6572    #[test_case(net_ip_v6!("::"), false; "v6 any")]
6573    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
6574    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
6575    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
6576    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
6577    fn dual_stack_get_info(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
6578        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
6579            SpecifiedAddr<IpAddr>,
6580        >(
6581            vec![
6582                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
6583                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
6584            ],
6585            vec![],
6586        ));
6587        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6588
6589        let listener = api.create();
6590        api.set_dual_stack_enabled(&listener, enable_dual_stack)
6591            .expect("can set dual-stack enabled");
6592        let bind_addr = SpecifiedAddr::new(bind_addr);
6593        assert_eq!(
6594            api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT),),
6595            Ok(())
6596        );
6597
6598        assert_eq!(
6599            api.get_info(&listener),
6600            SocketInfo::Listener(datagram::ListenerInfo {
6601                local_ip: bind_addr.map(StrictlyZonedAddr::new_unzoned_or_panic),
6602                local_identifier: LOCAL_PORT,
6603            })
6604        );
6605    }
6606
6607    #[test_case(net_ip_v6!("::"), true; "dual stack any")]
6608    #[test_case(net_ip_v6!("::"), false; "v6 any")]
6609    #[test_case(net_ip_v6!("::ffff:0.0.0.0"), true; "v4 unspecified")]
6610    #[test_case(V4_LOCAL_IP_MAPPED, true; "v4 specified")]
6611    #[test_case(V6_LOCAL_IP, true; "v6 specified dual stack enabled")]
6612    #[test_case(V6_LOCAL_IP, false; "v6 specified dual stack disabled")]
6613    fn dual_stack_remove_listener(bind_addr: Ipv6Addr, enable_dual_stack: bool) {
6614        // Ensure that when a socket is removed, it doesn't leave behind state
6615        // in the demultiplexing maps. Do this by binding a new socket at the
6616        // same address and asserting success.
6617        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs::<
6618            SpecifiedAddr<IpAddr>,
6619        >(
6620            vec![
6621                SpecifiedAddr::new(V4_LOCAL_IP).unwrap().into(),
6622                SpecifiedAddr::new(V6_LOCAL_IP).unwrap().into(),
6623            ],
6624            vec![],
6625        ));
6626        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6627
6628        let mut bind_listener = || {
6629            let listener = api.create();
6630            api.set_dual_stack_enabled(&listener, enable_dual_stack)
6631                .expect("can set dual-stack enabled");
6632            let bind_addr = SpecifiedAddr::new(bind_addr);
6633            assert_eq!(
6634                api.listen(&listener, bind_addr.map(|a| ZonedAddr::Unzoned(a)), Some(LOCAL_PORT)),
6635                Ok(())
6636            );
6637
6638            api.close(listener).into_removed();
6639        };
6640
6641        // The first time should succeed because the state is empty.
6642        bind_listener();
6643        // The second time should succeed because the first removal didn't
6644        // leave any state behind.
6645        bind_listener();
6646    }
6647
6648    #[test_case(V6_REMOTE_IP, true; "This stack with dualstack enabled")]
6649    #[test_case(V6_REMOTE_IP, false; "This stack with dualstack disabled")]
6650    #[test_case(V4_REMOTE_IP_MAPPED, true; "other stack with dualstack enabled")]
6651    fn dualstack_remove_connected(remote_ip: SpecifiedAddr<Ipv6Addr>, enable_dual_stack: bool) {
6652        // Ensure that when a socket is removed, it doesn't leave behind state
6653        // in the demultiplexing maps. Do this by binding a new socket at the
6654        // same address and asserting success.
6655        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6656            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
6657            remote_ip.into(),
6658            [FakeDeviceId {}],
6659            |device_configs| {
6660                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6661                    device_configs,
6662                ))
6663            },
6664        );
6665        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6666
6667        let mut bind_connected = || {
6668            let socket = api.create();
6669            api.set_dual_stack_enabled(&socket, enable_dual_stack)
6670                .expect("can set dual-stack enabled");
6671            assert_eq!(
6672                api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into(),),
6673                Ok(())
6674            );
6675
6676            api.close(socket).into_removed();
6677        };
6678
6679        // The first time should succeed because the state is empty.
6680        bind_connected();
6681        // The second time should succeed because the first removal didn't
6682        // leave any state behind.
6683        bind_connected();
6684    }
6685
6686    #[test_case(false, V6_REMOTE_IP, Ok(());
6687        "connect to this stack with dualstack disabled")]
6688    #[test_case(true, V6_REMOTE_IP, Ok(());
6689        "connect to this stack with dualstack enabled")]
6690    #[test_case(false, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
6691        "connect to other stack with dualstack disabled")]
6692    #[test_case(true, V4_REMOTE_IP_MAPPED, Ok(());
6693        "connect to other stack with dualstack enabled")]
6694    fn dualstack_connect_unbound(
6695        enable_dual_stack: bool,
6696        remote_ip: SpecifiedAddr<Ipv6Addr>,
6697        expected_outcome: Result<(), ConnectError>,
6698    ) {
6699        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6700            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
6701            remote_ip.into(),
6702            [FakeDeviceId {}],
6703            |device_configs| {
6704                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6705                    device_configs,
6706                ))
6707            },
6708        );
6709        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6710
6711        let socket = api.create();
6712
6713        api.set_dual_stack_enabled(&socket, enable_dual_stack).expect("can set dual-stack enabled");
6714
6715        assert_eq!(
6716            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
6717            expected_outcome
6718        );
6719
6720        if expected_outcome.is_ok() {
6721            assert_matches!(
6722                api.get_info(&socket),
6723                SocketInfo::Connected(datagram::ConnInfo{
6724                    local_ip: _,
6725                    local_identifier: _,
6726                    remote_ip: found_remote_ip,
6727                    remote_identifier: found_remote_port,
6728                }) if found_remote_ip.addr() == remote_ip &&
6729                    found_remote_port == u16::from(REMOTE_PORT)
6730            );
6731            // Disconnect the socket, returning it to the original state.
6732            assert_eq!(api.disconnect(&socket), Ok(()));
6733        }
6734
6735        // Verify the original state is preserved.
6736        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
6737    }
6738
6739    #[test_case(V6_LOCAL_IP, V6_REMOTE_IP, Ok(());
6740        "listener in this stack connected in this stack")]
6741    #[test_case(V6_LOCAL_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
6742        "listener in this stack connected in other stack")]
6743    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V6_REMOTE_IP, Ok(());
6744        "listener in both stacks connected in this stack")]
6745    #[test_case(Ipv6::UNSPECIFIED_ADDRESS, V4_REMOTE_IP_MAPPED, Ok(());
6746        "listener in both stacks connected in other stack")]
6747    #[test_case(V4_LOCAL_IP_MAPPED, V6_REMOTE_IP,
6748        Err(ConnectError::RemoteUnexpectedlyNonMapped);
6749        "listener in other stack connected in this stack")]
6750    #[test_case(V4_LOCAL_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
6751        "listener in other stack connected in other stack")]
6752    fn dualstack_connect_listener(
6753        local_ip: Ipv6Addr,
6754        remote_ip: SpecifiedAddr<Ipv6Addr>,
6755        expected_outcome: Result<(), ConnectError>,
6756    ) {
6757        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6758            local_ip.to_ip_addr(),
6759            remote_ip.into(),
6760            [FakeDeviceId {}],
6761            |device_configs| {
6762                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6763                    device_configs,
6764                ))
6765            },
6766        );
6767        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6768        let socket = api.create();
6769
6770        assert_eq!(
6771            api.listen(
6772                &socket,
6773                SpecifiedAddr::new(local_ip).map(|local_ip| ZonedAddr::Unzoned(local_ip)),
6774                Some(LOCAL_PORT),
6775            ),
6776            Ok(())
6777        );
6778
6779        assert_eq!(
6780            api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT.into()),
6781            expected_outcome
6782        );
6783
6784        if expected_outcome.is_ok() {
6785            assert_matches!(
6786                api.get_info(&socket),
6787                SocketInfo::Connected(datagram::ConnInfo{
6788                    local_ip: _,
6789                    local_identifier: _,
6790                    remote_ip: found_remote_ip,
6791                    remote_identifier: found_remote_port,
6792                }) if found_remote_ip.addr() == remote_ip &&
6793                    found_remote_port == u16::from(REMOTE_PORT)
6794            );
6795            // Disconnect the socket, returning it to the original state.
6796            assert_eq!(api.disconnect(&socket), Ok(()));
6797        }
6798
6799        // Verify the original state is preserved.
6800        assert_matches!(
6801            api.get_info(&socket),
6802            SocketInfo::Listener(datagram::ListenerInfo {
6803                local_ip: found_local_ip,
6804                local_identifier: found_local_port,
6805            }) if found_local_port == LOCAL_PORT &&
6806                local_ip == found_local_ip.map(
6807                    |a| a.addr().get()
6808                ).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS)
6809        );
6810    }
6811
6812    #[test_case(V6_REMOTE_IP, V6_REMOTE_IP, Ok(());
6813        "connected in this stack reconnected in this stack")]
6814    #[test_case(V6_REMOTE_IP, V4_REMOTE_IP_MAPPED, Err(ConnectError::RemoteUnexpectedlyMapped);
6815        "connected in this stack reconnected in other stack")]
6816    #[test_case(V4_REMOTE_IP_MAPPED, V6_REMOTE_IP,
6817        Err(ConnectError::RemoteUnexpectedlyNonMapped);
6818        "connected in other stack reconnected in this stack")]
6819    #[test_case(V4_REMOTE_IP_MAPPED, V4_REMOTE_IP_MAPPED, Ok(());
6820        "connected in other stack reconnected in other stack")]
6821    fn dualstack_connect_connected(
6822        original_remote_ip: SpecifiedAddr<Ipv6Addr>,
6823        new_remote_ip: SpecifiedAddr<Ipv6Addr>,
6824        expected_outcome: Result<(), ConnectError>,
6825    ) {
6826        let mut ctx = datagram::testutil::setup_fake_ctx_with_dualstack_conn_addrs(
6827            Ipv6::UNSPECIFIED_ADDRESS.to_ip_addr(),
6828            original_remote_ip.into(),
6829            [FakeDeviceId {}],
6830            |device_configs| {
6831                FakeUdpCoreCtx::with_ip_socket_ctx_state(FakeDualStackIpSocketCtx::new(
6832                    device_configs,
6833                ))
6834            },
6835        );
6836
6837        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
6838        let socket = api.create();
6839
6840        assert_eq!(
6841            api.connect(&socket, Some(ZonedAddr::Unzoned(original_remote_ip)), REMOTE_PORT.into(),),
6842            Ok(())
6843        );
6844
6845        assert_eq!(
6846            api.connect(
6847                &socket,
6848                Some(ZonedAddr::Unzoned(new_remote_ip)),
6849                OTHER_REMOTE_PORT.into(),
6850            ),
6851            expected_outcome
6852        );
6853
6854        let (expected_remote_ip, expected_remote_port) = if expected_outcome.is_ok() {
6855            (new_remote_ip, OTHER_REMOTE_PORT)
6856        } else {
6857            // Verify the original state is preserved.
6858            (original_remote_ip, REMOTE_PORT)
6859        };
6860        assert_matches!(
6861            api.get_info(&socket),
6862            SocketInfo::Connected(datagram::ConnInfo{
6863                local_ip: _,
6864                local_identifier: _,
6865                remote_ip: found_remote_ip,
6866                remote_identifier: found_remote_port,
6867            }) if found_remote_ip.addr() == expected_remote_ip &&
6868                found_remote_port == u16::from(expected_remote_port)
6869        );
6870
6871        // Disconnect the socket and verify it returns to unbound state.
6872        assert_eq!(api.disconnect(&socket), Ok(()));
6873        assert_eq!(api.get_info(&socket), SocketInfo::Unbound);
6874    }
6875
6876    type FakeBoundSocketMap<I> =
6877        UdpBoundSocketMap<I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
6878    type FakePortAlloc<'a, I> =
6879        UdpPortAlloc<'a, I, FakeWeakDeviceId<FakeDeviceId>, FakeUdpBindingsCtx<FakeDeviceId>>;
6880
6881    fn listen<I: IpExt>(
6882        ip: I::Addr,
6883        port: u16,
6884    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
6885        let addr = SpecifiedAddr::new(ip).map(|a| SocketIpAddr::try_from(a).unwrap());
6886        let port = NonZeroU16::new(port).expect("port must be nonzero");
6887        AddrVec::Listen(ListenerAddr {
6888            ip: ListenerIpAddr { addr, identifier: port },
6889            device: None,
6890        })
6891    }
6892
6893    fn listen_device<I: IpExt>(
6894        ip: I::Addr,
6895        port: u16,
6896        device: FakeWeakDeviceId<FakeDeviceId>,
6897    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
6898        let addr = SpecifiedAddr::new(ip).map(|a| SocketIpAddr::try_from(a).unwrap());
6899        let port = NonZeroU16::new(port).expect("port must be nonzero");
6900        AddrVec::Listen(ListenerAddr {
6901            ip: ListenerIpAddr { addr, identifier: port },
6902            device: Some(device),
6903        })
6904    }
6905
6906    fn conn<I: IpExt>(
6907        local_ip: I::Addr,
6908        local_port: u16,
6909        remote_ip: I::Addr,
6910        remote_port: u16,
6911    ) -> AddrVec<I, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec> {
6912        let local_ip = SocketIpAddr::new(local_ip).expect("addr must be specified & non-mapped");
6913        let local_port = NonZeroU16::new(local_port).expect("port must be nonzero");
6914        let remote_ip = SocketIpAddr::new(remote_ip).expect("addr must be specified & non-mapped");
6915        let remote_port = NonZeroU16::new(remote_port).expect("port must be nonzero").into();
6916        AddrVec::Conn(ConnAddr {
6917            ip: ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) },
6918            device: None,
6919        })
6920    }
6921
6922    const EXCLUSIVE: Sharing = Sharing { reuse_addr: false, reuse_port: false };
6923    const REUSE_ADDR: Sharing = Sharing { reuse_addr: true, reuse_port: false };
6924    const REUSE_PORT: Sharing = Sharing { reuse_addr: false, reuse_port: true };
6925    const REUSE_ADDR_PORT: Sharing = Sharing { reuse_addr: true, reuse_port: true };
6926
6927    #[test_case([
6928        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
6929        (listen(ip_v4!("0.0.0.0"), 2), EXCLUSIVE)],
6930            Ok(()); "listen_any_ip_different_port")]
6931    #[test_case([
6932        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE),
6933        (listen(ip_v4!("0.0.0.0"), 1), EXCLUSIVE)],
6934            Err(InsertError::Exists); "any_ip_same_port")]
6935    #[test_case([
6936        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
6937        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
6938            Err(InsertError::Exists); "listen_same_specific_ip")]
6939    #[test_case([
6940        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
6941        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
6942            Ok(()); "listen_same_specific_ip_reuse_addr")]
6943    #[test_case([
6944        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
6945        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
6946            Ok(()); "listen_same_specific_ip_reuse_port")]
6947    #[test_case([
6948        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
6949        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
6950            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr")]
6951    #[test_case([
6952        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
6953        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
6954            Ok(()); "listen_same_specific_ip_reuse_addr_and_reuse_addr_port")]
6955    #[test_case([
6956        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
6957        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
6958            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_port")]
6959    #[test_case([
6960        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
6961        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
6962            Ok(()); "listen_same_specific_ip_reuse_port_and_reuse_addr_port")]
6963    #[test_case([
6964        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
6965        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
6966            Ok(()); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_port")]
6967    #[test_case([
6968        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
6969        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
6970            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr")]
6971    #[test_case([
6972        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
6973        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
6974            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_exclusive")]
6975    #[test_case([
6976        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
6977        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
6978            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_port")]
6979    #[test_case([
6980        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
6981        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
6982            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_exclusive")]
6983    #[test_case([
6984        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
6985        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT)],
6986            Err(InsertError::Exists); "listen_same_specific_ip_exclusive_reuse_addr_port")]
6987    #[test_case([
6988        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
6989        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE)],
6990            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_exclusive")]
6991    #[test_case([
6992        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
6993        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR)],
6994            Err(InsertError::Exists); "listen_same_specific_ip_reuse_port_reuse_addr")]
6995    #[test_case([
6996        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
6997        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT)],
6998            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_reuse_port")]
6999    #[test_case([
7000        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7001        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7002        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),],
7003            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_port_and_reuse_addr")]
7004    #[test_case([
7005        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR_PORT),
7006        (listen(ip_v4!("1.1.1.1"), 1), REUSE_ADDR),
7007        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),],
7008            Err(InsertError::Exists); "listen_same_specific_ip_reuse_addr_port_and_reuse_addr_and_reuse_port")]
7009    #[test_case([
7010        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7011        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7012            Ok(()); "conn_shadows_listener_reuse_port")]
7013    #[test_case([
7014        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7015        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7016            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive")]
7017    #[test_case([
7018        (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7019        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), REUSE_PORT)],
7020            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_exclusive_reuse_port")]
7021    #[test_case([
7022        (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7023        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7024            Err(InsertError::ShadowAddrExists); "conn_shadows_listener_reuse_port_exclusive")]
7025    #[test_case([
7026        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7027        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7028            Err(InsertError::IndirectConflict); "conn_indirect_conflict_specific_listener")]
7029    #[test_case([
7030        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE),
7031        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE)],
7032            Err(InsertError::IndirectConflict); "conn_indirect_conflict_any_listener")]
7033    #[test_case([
7034        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7035        (listen_device(ip_v4!("1.1.1.1"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7036            Err(InsertError::IndirectConflict); "specific_listener_indirect_conflict_conn")]
7037    #[test_case([
7038        (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7039        (listen_device(ip_v4!("0.0.0.0"), 1, FakeWeakDeviceId(FakeDeviceId)), EXCLUSIVE)],
7040            Err(InsertError::IndirectConflict); "any_listener_indirect_conflict_conn")]
7041    fn bind_sequence<
7042        C: IntoIterator<Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing)>,
7043    >(
7044        spec: C,
7045        expected: Result<(), InsertError>,
7046    ) {
7047        let mut primary_ids = Vec::new();
7048
7049        let mut create_socket = || {
7050            let primary = datagram::testutil::create_primary_id((), Default::default());
7051            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7052            primary_ids.push(primary);
7053            id
7054        };
7055
7056        let mut map = FakeBoundSocketMap::<Ipv4>::default();
7057        let mut spec = spec.into_iter().peekable();
7058        let mut try_insert = |(addr, options)| match addr {
7059            AddrVec::Conn(c) => map
7060                .conns_mut()
7061                .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7062                .map(|_| ())
7063                .map_err(|(e, _)| e),
7064            AddrVec::Listen(l) => map
7065                .listeners_mut()
7066                .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7067                .map(|_| ())
7068                .map_err(|(e, _)| e),
7069        };
7070        let last = loop {
7071            let one_spec = spec.next().expect("empty list of test cases");
7072            if spec.peek().is_none() {
7073                break one_spec;
7074            } else {
7075                try_insert(one_spec).expect("intermediate bind failed")
7076            }
7077        };
7078
7079        let result = try_insert(last);
7080        assert_eq!(result, expected);
7081    }
7082
7083    #[test_case([
7084            (listen(ip_v4!("1.1.1.1"), 1), EXCLUSIVE),
7085            (listen(ip_v4!("2.2.2.2"), 2), EXCLUSIVE),
7086        ]; "distinct")]
7087    #[test_case([
7088            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7089            (listen(ip_v4!("1.1.1.1"), 1), REUSE_PORT),
7090        ]; "listen_reuse_port")]
7091    #[test_case([
7092            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7093            (conn(ip_v4!("1.1.1.1"), 1, ip_v4!("2.2.2.2"), 3), REUSE_PORT),
7094        ]; "conn_reuse_port")]
7095    fn remove_sequence<I>(spec: I)
7096    where
7097        I: IntoIterator<
7098            Item = (AddrVec<Ipv4, FakeWeakDeviceId<FakeDeviceId>, UdpAddrSpec>, Sharing),
7099        >,
7100        I::IntoIter: ExactSizeIterator,
7101    {
7102        enum Socket<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes, LI, RI> {
7103            Listener(UdpSocketId<I, D, BT>, ListenerAddr<ListenerIpAddr<I::Addr, LI>, D>),
7104            Conn(UdpSocketId<I, D, BT>, ConnAddr<ConnIpAddr<I::Addr, LI, RI>, D>),
7105        }
7106        let spec = spec.into_iter();
7107        let spec_len = spec.len();
7108
7109        let mut primary_ids = Vec::new();
7110
7111        let mut create_socket = || {
7112            let primary = datagram::testutil::create_primary_id((), Default::default());
7113            let id = UdpSocketId(PrimaryRc::clone_strong(&primary));
7114            primary_ids.push(primary);
7115            id
7116        };
7117
7118        for spec in spec.permutations(spec_len) {
7119            let mut map = FakeBoundSocketMap::<Ipv4>::default();
7120            let sockets = spec
7121                .into_iter()
7122                .map(|(addr, options)| match addr {
7123                    AddrVec::Conn(c) => map
7124                        .conns_mut()
7125                        .try_insert(c, options, EitherIpSocket::V4(create_socket()))
7126                        .map(|entry| {
7127                            Socket::Conn(
7128                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7129                                entry.get_addr().clone(),
7130                            )
7131                        })
7132                        .expect("insert_failed"),
7133                    AddrVec::Listen(l) => map
7134                        .listeners_mut()
7135                        .try_insert(l, options, EitherIpSocket::V4(create_socket()))
7136                        .map(|entry| {
7137                            Socket::Listener(
7138                                assert_matches!(entry.id(), EitherIpSocket::V4(id) => id.clone()),
7139                                entry.get_addr().clone(),
7140                            )
7141                        })
7142                        .expect("insert_failed"),
7143                })
7144                .collect::<Vec<_>>();
7145
7146            for socket in sockets {
7147                match socket {
7148                    Socket::Listener(l, addr) => {
7149                        assert_matches!(
7150                            map.listeners_mut().remove(&EitherIpSocket::V4(l), &addr),
7151                            Ok(())
7152                        );
7153                    }
7154                    Socket::Conn(c, addr) => {
7155                        assert_matches!(
7156                            map.conns_mut().remove(&EitherIpSocket::V4(c), &addr),
7157                            Ok(())
7158                        );
7159                    }
7160                }
7161            }
7162        }
7163    }
7164
7165    enum OriginalSocketState {
7166        Unbound,
7167        Listener,
7168        Connected,
7169    }
7170
7171    impl OriginalSocketState {
7172        fn create_socket<I, C>(&self, api: &mut UdpApi<I, C>) -> UdpApiSocketId<I, C>
7173        where
7174            I: TestIpExt,
7175            C: ContextPair,
7176            C::CoreContext: StateContext<I, C::BindingsContext>
7177                + UdpCounterContext<
7178                    I,
7179                    <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
7180                    C::BindingsContext,
7181                >,
7182            C::BindingsContext:
7183                UdpBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
7184            <C::BindingsContext as UdpBindingsTypes>::ExternalData<I>: Default,
7185            <C::BindingsContext as UdpBindingsTypes>::SocketWritableListener: Default,
7186        {
7187            let socket = api.create();
7188            match self {
7189                OriginalSocketState::Unbound => {}
7190                OriginalSocketState::Listener => {
7191                    api.listen(
7192                        &socket,
7193                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)),
7194                        Some(LOCAL_PORT),
7195                    )
7196                    .expect("listen should succeed");
7197                }
7198                OriginalSocketState::Connected => {
7199                    api.connect(
7200                        &socket,
7201                        Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
7202                        UdpRemotePort::Set(REMOTE_PORT),
7203                    )
7204                    .expect("connect should succeed");
7205                }
7206            }
7207            socket
7208        }
7209    }
7210
7211    #[test_case(OriginalSocketState::Unbound; "unbound")]
7212    #[test_case(OriginalSocketState::Listener; "listener")]
7213    #[test_case(OriginalSocketState::Connected; "connected")]
7214    fn set_get_dual_stack_enabled_v4(original_state: OriginalSocketState) {
7215        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7216            vec![Ipv4::TEST_ADDRS.local_ip],
7217            vec![Ipv4::TEST_ADDRS.remote_ip],
7218        ));
7219        let mut api = UdpApi::<Ipv4, _>::new(ctx.as_mut());
7220        let socket = original_state.create_socket(&mut api);
7221
7222        for enabled in [true, false] {
7223            assert_eq!(
7224                api.set_dual_stack_enabled(&socket, enabled),
7225                Err(SetDualStackEnabledError::NotCapable)
7226            );
7227            assert_eq!(api.get_dual_stack_enabled(&socket), Err(NotDualStackCapableError));
7228        }
7229    }
7230
7231    #[test_case(OriginalSocketState::Unbound, Ok(()); "unbound")]
7232    #[test_case(OriginalSocketState::Listener, Err(SetDualStackEnabledError::SocketIsBound);
7233        "listener")]
7234    #[test_case(OriginalSocketState::Connected, Err(SetDualStackEnabledError::SocketIsBound);
7235        "connected")]
7236    fn set_get_dual_stack_enabled_v6(
7237        original_state: OriginalSocketState,
7238        expected_result: Result<(), SetDualStackEnabledError>,
7239    ) {
7240        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7241            vec![Ipv6::TEST_ADDRS.local_ip],
7242            vec![Ipv6::TEST_ADDRS.remote_ip],
7243        ));
7244        let mut api = UdpApi::<Ipv6, _>::new(ctx.as_mut());
7245        let socket = original_state.create_socket(&mut api);
7246
7247        // Expect dual stack to be enabled by default.
7248        const ORIGINALLY_ENABLED: bool = true;
7249        assert_eq!(api.get_dual_stack_enabled(&socket), Ok(ORIGINALLY_ENABLED));
7250
7251        for enabled in [false, true] {
7252            assert_eq!(api.set_dual_stack_enabled(&socket, enabled), expected_result);
7253            let expect_enabled = match expected_result {
7254                Ok(_) => enabled,
7255                // If the set was unsuccessful expect the state to be unchanged.
7256                Err(_) => ORIGINALLY_ENABLED,
7257            };
7258            assert_eq!(api.get_dual_stack_enabled(&socket), Ok(expect_enabled));
7259        }
7260    }
7261
7262    #[ip_test(I)]
7263    #[test_case::test_matrix(
7264        [MarkDomain::Mark1, MarkDomain::Mark2],
7265        [None, Some(0), Some(1)]
7266    )]
7267    fn udp_socket_marks<I: TestIpExt>(domain: MarkDomain, mark: Option<u32>) {
7268        let mut ctx = FakeUdpCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
7269            vec![I::TEST_ADDRS.local_ip],
7270            vec![I::TEST_ADDRS.remote_ip],
7271        ));
7272        let mut api = UdpApi::<I, _>::new(ctx.as_mut());
7273        let socket = api.create();
7274
7275        // Doesn't have a mark by default.
7276        assert_eq!(api.get_mark(&socket, domain), Mark(None));
7277
7278        let mark = Mark(mark);
7279        // We can set and get back the mark.
7280        api.set_mark(&socket, domain, mark);
7281        assert_eq!(api.get_mark(&socket, domain), mark);
7282    }
7283}