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