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