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