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