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