Skip to main content

netstack3_udp/
base.rs

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