1use std::{
17 net::{self},
18 sync::Arc,
19 time::{Duration, SystemTime},
20};
21
22use scion_quic::socket::{BoxedSocketError, GenericScionUdpSocket};
23use sciparse::{
24 address::{addr::ScionAddr, ip_socket_addr::ScionSocketIpAddr},
25 core::{model::Model, view::View},
26 dataplane_path::view::ScionDpPathViewExt,
27 packet::{
28 model::{ScionScmpPacket, ScionUdpPacket},
29 view::ScionRawPacketView,
30 },
31 path::ScionPath,
32 payload::{
33 ProtocolNumber,
34 scmp::{model::ScmpMessage, view::ScmpPayloadView},
35 },
36};
37
38use super::{BoundUnderlaySocket, MAX_UNDERLAY_PACKET_SIZE, UnderlaySocket, UnderlaySocketExt};
39use crate::{
40 internal::Subscribers,
41 path::manager::{MultiPathManager, traits::PathManager},
42 stack::{
43 ScionSocketConnectError, ScionSocketReceiveError, ScionSocketSendError,
44 scmp_handler::{ScmpErrorHandler, ScmpHandler},
45 },
46};
47
48pub struct PathUnawareUdpScionSocket {
50 inner: Box<dyn UnderlaySocket>,
51 local_addr: ScionSocketIpAddr,
53 snap_data_plane: Option<net::SocketAddr>,
55 scmp_handlers: Vec<Box<dyn ScmpHandler>>,
57}
58
59#[allow(clippy::missing_fields_in_debug)]
61impl std::fmt::Debug for PathUnawareUdpScionSocket {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("PathUnawareUdpScionSocket")
64 .field("local_addr", &self.local_addr)
65 .finish()
66 }
67}
68
69impl PathUnawareUdpScionSocket {
70 pub(crate) fn new(
71 bound: BoundUnderlaySocket,
72 scmp_handlers: Vec<Box<dyn ScmpHandler>>,
73 ) -> Self {
74 Self {
75 inner: bound.socket,
76 local_addr: bound.local_addr,
77 snap_data_plane: bound.snap_data_plane,
78 scmp_handlers,
79 }
80 }
81
82 pub async fn send_to_via(
89 &self,
90 payload: &[u8],
91 destination: ScionSocketIpAddr,
92 path: &ScionPath,
93 ) -> Result<(), ScionSocketSendError> {
94 let packet = ScionUdpPacket::new(
96 self.local_addr.into(),
97 destination.into(),
98 path.dp_path().to_model(),
99 payload.to_vec(),
100 )
101 .try_encode_to_owned_view()
102 .map_err(|e| {
103 ScionSocketSendError::InvalidPacket(format!("error encoding packet: {e}").into())
104 })?
105 .into_raw();
106
107 self.inner.send(&packet).await
108 }
109
110 #[allow(clippy::type_complexity)]
120 pub async fn recv_from_with_path(
121 &self,
122 buffer: &mut [u8],
123 ) -> Result<(usize, ScionSocketIpAddr, ScionPath), ScionSocketReceiveError> {
124 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
125 loop {
126 let n = self.inner.recv(&mut scratch).await?;
127 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
128 .expect("underlay recv returns a decoded packet");
129
130 match packet.header().next_header() {
131 ProtocolNumber::Udp => {}
132 ProtocolNumber::Scmp => {
133 tracing::debug!("SCMP packet received, forwarding to SCMP handlers");
134 for handler in &self.scmp_handlers {
135 let Some(reply) = handler.handle(packet) else {
137 continue;
138 };
139
140 let reply = match reply.try_encode_to_owned_view() {
141 Ok(reply) => reply,
142 Err(e) => {
143 tracing::warn!(error = %e, "failed to encode SCMP reply");
144 continue;
145 }
146 };
147
148 if let Err(e) = self.inner.try_send(&reply) {
149 tracing::warn!(error = %e, "failed to send SCMP reply");
150 }
151 }
152 continue;
153 }
154 next_header => {
155 tracing::debug!(%next_header, "Packet with unexpected next layer protocol, skipping");
156 continue;
157 }
158 }
159
160 let packet = match packet.try_as_udp() {
161 Ok(packet) => packet,
162 Err(e) => {
163 tracing::debug!(error = %e, "Received invalid UDP packet, skipping");
164 continue;
165 }
166 };
167 let src_addr = match packet.src_socket_addr() {
168 Ok(src_addr) => src_addr,
169 Err(err) => {
170 tracing::debug!(
171 %err,
172 "Failed to decode packet source address, skipping"
173 );
174 continue;
175 }
176 };
177
178 tracing::trace!(
179 src = %src_addr,
180 length = packet.udp().payload().len(),
181 "received packet",
182 );
183
184 let Some(src_addr) = src_addr.try_to_scion_sock_ip_addr() else {
185 tracing::debug!("Received packet with non-IP source address, skipping");
186 continue;
187 };
188
189 let max_read = std::cmp::min(buffer.len(), packet.udp().payload().len());
190 buffer[..max_read].copy_from_slice(&packet.udp().payload()[..max_read]);
191
192 let path = ScionPath::new(
196 src_addr.isd_asn(),
197 packet.header().dst_ia(),
198 packet.header().path().to_owned_view(),
199 None,
200 None,
201 );
202
203 return Ok((max_read, src_addr, path));
204 }
205 }
206
207 pub async fn recv_from(
215 &self,
216 buffer: &mut [u8],
217 ) -> Result<(usize, ScionSocketIpAddr), ScionSocketReceiveError> {
218 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
219 loop {
220 let n = self.inner.recv(&mut scratch).await?;
221 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
222 .expect("underlay recv returns a decoded packet");
223
224 match packet.header().next_header() {
225 ProtocolNumber::Udp => {}
226 ProtocolNumber::Scmp => {
227 tracing::debug!("SCMP packet received, forwarding to SCMP handlers");
228 for handler in &self.scmp_handlers {
229 let Some(reply) = handler.handle(packet) else {
231 continue;
232 };
233
234 let reply = match reply.try_encode_to_owned_view() {
235 Ok(reply) => reply,
236 Err(e) => {
237 tracing::warn!(error = %e, "failed to encode SCMP reply");
238 continue;
239 }
240 };
241
242 if let Err(e) = self.inner.try_send(&reply) {
243 tracing::warn!(error = %e, "failed to send SCMP reply");
244 }
245 }
246 continue;
247 }
248 next_header => {
249 tracing::debug!(%next_header, "Packet with unknown next layer protocol, skipping");
250 continue;
251 }
252 }
253
254 let packet = match packet.try_as_udp() {
255 Ok(packet) => packet,
256 Err(e) => {
257 tracing::debug!(error = %e, "Received invalid UDP packet, dropping");
258 continue;
259 }
260 };
261
262 let src_addr = match packet.src_socket_addr() {
263 Ok(src_addr) => src_addr,
264 Err(err) => {
265 tracing::debug!(%err, "Failed to decode packet source address, skipping");
266 continue;
267 }
268 };
269
270 tracing::trace!(
271 src = %src_addr,
272 length = packet.udp().payload().len(),
273 buffer_size = buffer.len(),
274 "received packet",
275 );
276
277 let Some(src_addr) = src_addr.try_to_scion_sock_ip_addr() else {
278 tracing::debug!("Received packet with non-IP source address, skipping");
279 continue;
280 };
281
282 let max_read = std::cmp::min(buffer.len(), packet.udp().payload().len());
283 buffer[..max_read].copy_from_slice(&packet.udp().payload()[..max_read]);
284
285 return Ok((max_read, src_addr));
286 }
287 }
288
289 pub fn local_addr(&self) -> ScionSocketIpAddr {
291 self.local_addr
292 }
293
294 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
296 self.snap_data_plane
297 }
298
299 pub fn into_path_aware<P: PathManager>(
310 mut self,
311 path_manager: Arc<P>,
312 connect_timeout: Duration,
313 ) -> UdpScionSocket<P> {
314 if let Some(receiver) = path_manager.clone().scmp_error_receiver() {
315 let scmp_error_receivers = Subscribers::new();
316 scmp_error_receivers.register(receiver);
317 self.scmp_handlers
318 .push(Box::new(ScmpErrorHandler::new(scmp_error_receivers)));
319 }
320 let send_error_receivers = Subscribers::new();
321 if let Some(receiver) = path_manager.clone().send_error_receiver() {
322 send_error_receivers.register(receiver);
323 }
324 UdpScionSocket::new(self, path_manager, connect_timeout, send_error_receivers)
325 }
326}
327
328pub struct ScmpScionSocket {
330 inner: Box<dyn UnderlaySocket>,
331 local_addr: ScionSocketIpAddr,
333 snap_data_plane: Option<net::SocketAddr>,
335}
336
337impl ScmpScionSocket {
338 pub(crate) fn new(bound: BoundUnderlaySocket) -> Self {
339 Self {
340 inner: bound.socket,
341 local_addr: bound.local_addr,
342 snap_data_plane: bound.snap_data_plane,
343 }
344 }
345}
346
347impl ScmpScionSocket {
348 pub async fn send_to_via(
350 &self,
351 message: ScmpMessage,
352 destination: ScionAddr,
353 path: &ScionPath,
354 ) -> Result<(), ScionSocketSendError> {
355 let packet = ScionScmpPacket::new(
356 self.local_addr.scion_ip_addr().into(),
357 destination,
358 path.dp_path().to_model(),
359 message,
360 )
361 .try_encode_to_owned_view()
362 .map_err(|e| {
363 ScionSocketSendError::InvalidPacket(format!("error encoding packet: {e}").into())
364 })?
365 .into_raw();
366 self.inner.send(&packet).await
367 }
368
369 #[allow(clippy::type_complexity)]
371 pub async fn recv_from_with_path(
372 &self,
373 ) -> Result<(Box<ScmpPayloadView>, ScionAddr, ScionPath), ScionSocketReceiveError> {
374 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
375 loop {
376 let n = self.inner.recv(&mut scratch).await?;
377 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
378 .expect("underlay recv returns a decoded packet");
379 let packet = match packet.try_as_scmp() {
380 Ok(packet) => packet,
381 Err(e) => {
382 tracing::debug!(error = %e, "Received invalid SCMP packet, dropping");
383 continue;
384 }
385 };
386
387 let src_addr = match packet.src_scion_addr() {
388 Ok(source) => source,
389 Err(e) => {
390 tracing::debug!(error = %e, "Failed to decode packet source address, skipping");
391 continue;
392 }
393 };
394
395 let path = ScionPath::new(
396 packet.header().src_ia(),
397 packet.header().dst_ia(),
398 packet.header().path().to_owned_view(),
399 None,
400 None,
401 );
402
403 return Ok((packet.scmp().to_boxed(), src_addr, path));
404 }
405 }
406
407 pub async fn recv_from(
409 &self,
410 ) -> Result<(Box<ScmpPayloadView>, ScionAddr), ScionSocketReceiveError> {
411 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
412 loop {
413 let n = self.inner.recv(&mut scratch).await?;
414 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
415 .expect("underlay recv returns a decoded packet");
416 let packet = match packet.try_as_scmp() {
417 Ok(packet) => packet,
418 Err(e) => {
419 tracing::debug!(error = %e, "Received invalid SCMP packet, skipping");
420 continue;
421 }
422 };
423 let src_addr = match packet.src_scion_addr() {
424 Ok(source) => source,
425 Err(e) => {
426 tracing::debug!(error = %e, "Failed to decode packet source address, skipping");
427 continue;
428 }
429 };
430 return Ok((packet.scmp().to_boxed(), src_addr));
431 }
432 }
433
434 pub fn local_addr(&self) -> ScionSocketIpAddr {
436 self.local_addr
437 }
438
439 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
441 self.snap_data_plane
442 }
443}
444
445pub struct RawScionSocket {
447 inner: Box<dyn UnderlaySocket>,
448 local_addr: ScionSocketIpAddr,
450 snap_data_plane: Option<net::SocketAddr>,
452}
453
454impl RawScionSocket {
455 pub(crate) fn new(bound: BoundUnderlaySocket) -> Self {
456 Self {
457 inner: bound.socket,
458 local_addr: bound.local_addr,
459 snap_data_plane: bound.snap_data_plane,
460 }
461 }
462}
463
464impl RawScionSocket {
465 pub async fn send(&self, packet: &ScionRawPacketView) -> Result<(), ScionSocketSendError> {
467 self.inner.send(packet).await
468 }
469
470 pub async fn recv(&self) -> Result<Box<ScionRawPacketView>, ScionSocketReceiveError> {
472 let mut buf = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
473 let n = self.inner.recv(&mut buf).await?;
474 let (view, _rest) = ScionRawPacketView::try_from_slice(&buf[..n])
475 .expect("underlay recv returns a decoded packet");
476 Ok(view.to_boxed())
477 }
478
479 pub fn local_addr(&self) -> ScionSocketIpAddr {
481 self.local_addr
482 }
483
484 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
486 self.snap_data_plane
487 }
488}
489
490pub trait SendErrorReceiver: Send + Sync {
492 fn report_send_error(&self, error: &ScionSocketSendError);
495}
496
497pub struct UdpScionSocket<P: PathManager = MultiPathManager> {
505 socket: PathUnawareUdpScionSocket,
506 pather: Arc<P>,
507 connect_timeout: Duration,
508 remote_addr: Option<ScionSocketIpAddr>,
509 send_error_receivers: Subscribers<dyn SendErrorReceiver>,
510}
511
512#[allow(clippy::missing_fields_in_debug)]
514impl<P: PathManager> std::fmt::Debug for UdpScionSocket<P> {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 f.debug_struct("UdpScionSocket")
517 .field("local_addr", &self.socket.local_addr())
518 .field("remote_addr", &self.remote_addr)
519 .finish()
520 }
521}
522
523impl<P: PathManager> UdpScionSocket<P> {
524 pub(crate) fn new(
526 socket: PathUnawareUdpScionSocket,
527 pather: Arc<P>,
528 connect_timeout: Duration,
529 send_error_receivers: Subscribers<dyn SendErrorReceiver>,
530 ) -> Self {
531 Self {
532 socket,
533 pather,
534 connect_timeout,
535 remote_addr: None,
536 send_error_receivers,
537 }
538 }
539
540 pub async fn connect(
546 self,
547 remote_addr: ScionSocketIpAddr,
548 ) -> Result<Self, ScionSocketConnectError> {
549 let _path = self
551 .pather
552 .path_timeout(
553 self.socket.local_addr().isd_asn(),
554 remote_addr.isd_asn(),
555 SystemTime::now(),
556 self.connect_timeout,
557 )
558 .await?;
559
560 Ok(Self {
561 remote_addr: Some(remote_addr),
562 ..self
563 })
564 }
565
566 pub async fn send(&self, payload: &[u8]) -> Result<(), ScionSocketSendError> {
573 if let Some(remote_addr) = self.remote_addr {
574 self.send_to(payload, remote_addr).await
575 } else {
576 Err(ScionSocketSendError::NotConnected)
577 }
578 }
579
580 pub async fn send_to(
588 &self,
589 payload: &[u8],
590 destination: ScionSocketIpAddr,
591 ) -> Result<(), ScionSocketSendError> {
592 let path = &self
593 .pather
594 .path_wait(
595 self.socket.local_addr().isd_asn(),
596 destination.isd_asn(),
597 SystemTime::now(),
598 )
599 .await?;
600 self.socket.send_to_via(payload, destination, path).await
601 }
602
603 pub async fn send_to_via(
610 &self,
611 payload: &[u8],
612 destination: ScionSocketIpAddr,
613 path: &ScionPath,
614 ) -> Result<(), ScionSocketSendError> {
615 self.socket
616 .send_to_via(payload, destination, path)
617 .await
618 .inspect_err(|e| {
619 self.send_error_receivers
620 .for_each(|receiver| receiver.report_send_error(e));
621 })
622 }
623
624 pub async fn recv_from_with_path(
635 &self,
636 buffer: &mut [u8],
637 ) -> Result<(usize, ScionSocketIpAddr, ScionPath), ScionSocketReceiveError> {
638 let (len, sender_addr, path): (usize, ScionSocketIpAddr, ScionPath) =
639 self.socket.recv_from_with_path(buffer).await?;
640
641 match path.clone().try_into_reversed() {
642 Ok(reversed_path) => {
643 self.pather.register_path(
645 self.socket.local_addr().isd_asn(),
646 sender_addr.isd_asn(),
647 SystemTime::now(),
648 reversed_path,
649 );
650 }
651 Err((_, e)) => {
652 tracing::trace!(error = ?e, "Failed to reverse path for registration");
653 }
654 }
655
656 tracing::trace!(
657 src = %self.socket.local_addr(),
658 dst = %sender_addr,
659 "Registered reverse path",
660 );
661
662 Ok((len, sender_addr, path))
663 }
664
665 pub async fn recv_from(
677 &self,
678 buffer: &mut [u8],
679 ) -> Result<(usize, ScionSocketIpAddr), ScionSocketReceiveError> {
680 let (len, sender_addr, _) = self.recv_from_with_path(buffer).await?;
681 Ok((len, sender_addr))
682 }
683
684 pub async fn recv(&self, buffer: &mut [u8]) -> Result<usize, ScionSocketReceiveError> {
696 if self.remote_addr.is_none() {
697 return Err(ScionSocketReceiveError::NotConnected);
698 }
699 loop {
700 let (len, sender_addr) = self.recv_from(buffer).await?;
701
702 match self.remote_addr {
704 Some(remote_addr) => {
705 if sender_addr == remote_addr {
706 return Ok(len);
707 }
708 }
709 None => return Err(ScionSocketReceiveError::NotConnected),
710 }
711 }
712 }
713
714 pub fn local_addr(&self) -> ScionSocketIpAddr {
716 self.socket.local_addr()
717 }
718
719 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
721 self.socket.snap_data_plane()
722 }
723}
724
725#[async_trait::async_trait]
728impl<P: PathManager + Sync + Send + 'static> GenericScionUdpSocket for UdpScionSocket<P> {
729 async fn send_to(
731 &self,
732 payload: &[u8],
733 destination: ScionSocketIpAddr,
734 ) -> Result<(), BoxedSocketError> {
735 self.send_to(payload, destination)
736 .await
737 .map_err(|e| Box::new(e) as BoxedSocketError)
738 }
739
740 async fn recv_from(
743 &self,
744 buf: &mut [u8],
745 ) -> Result<(usize, ScionSocketIpAddr), BoxedSocketError> {
746 self.recv_from(buf)
747 .await
748 .map_err(|e| Box::new(e) as BoxedSocketError)
749 }
750
751 fn local_addr(&self) -> ScionSocketIpAddr {
753 self.local_addr()
754 }
755}
756
757#[cfg(test)]
758mod cancel_safety_tests {
759 use std::{
785 io,
786 net::Ipv4Addr,
787 sync::{Arc, Mutex},
788 time::SystemTime,
789 };
790
791 use async_trait::async_trait;
792 use sciparse::{
793 identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn},
794 util::test_builder::{TestPathBuilder, TestPathContext},
795 };
796
797 use super::*;
798 use crate::{
799 internal::Subscribers,
800 path::manager::traits::{PathWaitError, SyncPathManager},
801 stack::{ScionSocketReceiveError, ScionSocketSendError, UnderlaySocket},
802 };
803
804 struct ManualUnderlaySocket {
805 rx: tokio::sync::Mutex<tokio::sync::mpsc::Receiver<Box<ScionRawPacketView>>>,
806 peeked: tokio::sync::Mutex<Option<Box<ScionRawPacketView>>>,
809 }
810
811 impl ManualUnderlaySocket {
812 fn new() -> (Self, tokio::sync::mpsc::Sender<Box<ScionRawPacketView>>) {
813 let (inject_tx, recv_rx) = tokio::sync::mpsc::channel::<Box<ScionRawPacketView>>(64);
815 let socket = Self {
816 rx: tokio::sync::Mutex::new(recv_rx),
817 peeked: tokio::sync::Mutex::new(None),
818 };
819 (socket, inject_tx)
820 }
821 }
822
823 #[async_trait]
824 impl UnderlaySocket for ManualUnderlaySocket {
825 fn try_send(&self, _packet: &ScionRawPacketView) -> Result<(), ScionSocketSendError> {
826 Ok(())
827 }
828
829 async fn writeable(&self) {}
830
831 fn try_recv(&self, buf: &mut [u8]) -> Result<usize, ScionSocketReceiveError> {
832 let would_block =
833 || ScionSocketReceiveError::IoError(io::Error::from(io::ErrorKind::WouldBlock));
834
835 let packet: Box<ScionRawPacketView> = {
836 let Ok(mut peeked) = self.peeked.try_lock() else {
837 return Err(would_block());
838 };
839 match peeked.take() {
840 Some(packet) => packet,
841 None => {
842 let Ok(mut rx) = self.rx.try_lock() else {
843 return Err(would_block());
844 };
845 match rx.try_recv() {
846 Ok(packet) => packet,
847 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
848 return Err(would_block());
849 }
850 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
851 return Err(ScionSocketReceiveError::IoError(io::Error::other(
852 "channel closed",
853 )));
854 }
855 }
856 }
857 }
858 };
859
860 let bytes = packet.as_slice();
861 let n = bytes.len();
862 buf[..n].copy_from_slice(bytes);
863 Ok(n)
864 }
865
866 async fn readable(&self) {
867 let mut peeked = self.peeked.lock().await;
868 if peeked.is_some() {
869 return;
870 }
871 if let Some(packet) = self.rx.lock().await.recv().await {
874 *peeked = Some(packet);
875 }
876 }
877 }
878
879 #[derive(Default)]
880 struct ImmediatePathManager {
881 registered_paths: Mutex<Vec<ScionPath>>,
882 }
883
884 impl SyncPathManager for ImmediatePathManager {
885 fn register_path(&self, _src: IsdAsn, _dst: IsdAsn, _now: SystemTime, path: ScionPath) {
886 self.registered_paths.lock().expect("poisoned").push(path);
887 }
888
889 fn try_cached_path(
890 &self,
891 src: IsdAsn,
892 _dst: IsdAsn,
893 _now: SystemTime,
894 ) -> io::Result<Option<ScionPath>> {
895 Ok(Some(
896 ScionPath::local(src).expect("src is not a wildcard IA"),
897 ))
898 }
899 }
900
901 impl PathManager for ImmediatePathManager {
902 fn path_wait(
903 &self,
904 src: IsdAsn,
905 _dst: IsdAsn,
906 _now: SystemTime,
907 ) -> impl std::future::Future<Output = Result<ScionPath, PathWaitError>> + Send + '_
908 {
909 async move { Ok(ScionPath::local(src).expect("src is not a wildcard IA")) }
910 }
911 }
912
913 const LOCAL_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(1));
914 const REMOTE_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(2));
915 const OTHER_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(3));
916
917 fn local_addr() -> ScionSocketIpAddr {
918 ScionSocketIpAddr::new(LOCAL_ISD_ASN, Ipv4Addr::LOCALHOST.into(), 8080)
919 }
920
921 fn remote_addr() -> ScionSocketIpAddr {
922 ScionSocketIpAddr::new(REMOTE_ISD_ASN, Ipv4Addr::new(127, 0, 0, 2).into(), 9090)
923 }
924
925 fn other_addr() -> ScionSocketIpAddr {
926 ScionSocketIpAddr::new(OTHER_ISD_ASN, Ipv4Addr::new(127, 0, 0, 3).into(), 7070)
927 }
928
929 fn test_path_ctx(src: ScionAddr, dst: ScionAddr) -> TestPathContext {
931 TestPathBuilder::new(src, dst)
932 .using_info_timestamp(1_000_000)
933 .up()
934 .add_hop(0, 1)
935 .add_hop(1, 0)
936 .build(1_000_000)
937 }
938
939 fn make_udp_raw(
942 src: ScionSocketIpAddr,
943 dst: ScionSocketIpAddr,
944 payload: &[u8],
945 ) -> Box<ScionRawPacketView> {
946 let ctx = test_path_ctx(src.scion_addr(), dst.scion_addr());
947 ctx.scion_packet_udp(payload, src.port(), dst.port())
948 .try_encode_to_owned_view()
949 .expect("should encode")
950 .into()
951 }
952
953 fn build_socket() -> (
956 UdpScionSocket<ImmediatePathManager>,
957 tokio::sync::mpsc::Sender<Box<ScionRawPacketView>>,
958 Arc<ImmediatePathManager>,
959 ) {
960 let (underlay, inject_tx) = ManualUnderlaySocket::new();
961 let pather = Arc::new(ImmediatePathManager::default());
962 let path_unaware = PathUnawareUdpScionSocket::new(
963 BoundUnderlaySocket {
964 socket: Box::new(underlay),
965 local_addr: local_addr(),
966 snap_data_plane: None,
967 },
968 vec![], );
970 let socket = UdpScionSocket::new(
971 path_unaware,
972 pather.clone(),
973 std::time::Duration::from_secs(5),
974 Subscribers::new(),
975 );
976 (socket, inject_tx, pather)
977 }
978
979 #[tokio::test]
985 async fn recv_from_with_path_cancel_while_pending_does_not_lose_packet() {
986 let (socket, inject_tx, _pather) = build_socket();
987
988 {
990 let mut buf = [0u8; 64];
991 let mut fut = std::pin::pin!(socket.recv_from_with_path(&mut buf));
992 let waker = futures::task::noop_waker();
993 let mut cx = std::task::Context::from_waker(&waker);
994 assert!(fut.as_mut().poll(&mut cx).is_pending());
996 }
998
999 let payload = b"cancel-safe";
1001 inject_tx
1002 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
1003 .unwrap();
1004
1005 let mut buf2 = vec![0u8; 64];
1007 let (len, sender, _path) = socket.recv_from_with_path(&mut buf2).await.unwrap();
1008
1009 assert_eq!(len, payload.len());
1010 assert_eq!(&buf2[..len], payload);
1011 assert_eq!(sender, remote_addr());
1012 }
1013
1014 #[tokio::test]
1017 async fn recv_filters_wrong_sender_and_delivers_correct_packet() {
1018 let (mut socket, inject_tx, _pather) = build_socket();
1019 socket.remote_addr = Some(remote_addr());
1021
1022 inject_tx
1024 .try_send(make_udp_raw(other_addr(), local_addr(), b"wrong"))
1025 .unwrap();
1026 inject_tx
1027 .try_send(make_udp_raw(remote_addr(), local_addr(), b"correct"))
1028 .unwrap();
1029
1030 let mut buf = [0u8; 64];
1031 let len = socket.recv(&mut buf).await.unwrap();
1032 assert_eq!(&buf[..len], b"correct");
1033 }
1034
1035 #[tokio::test]
1038 async fn recv_cancel_during_filtering_socket_remains_usable() {
1039 let (mut socket, inject_tx, _pather) = build_socket();
1040 socket.remote_addr = Some(remote_addr());
1041
1042 inject_tx
1045 .try_send(make_udp_raw(other_addr(), local_addr(), b"wrong"))
1046 .unwrap();
1047
1048 {
1052 let mut filter_buf = [0u8; 64];
1053 let mut fut = std::pin::pin!(socket.recv(&mut filter_buf));
1054 let waker = futures::task::noop_waker();
1055 let mut cx = std::task::Context::from_waker(&waker);
1056 assert!(
1057 fut.as_mut().poll(&mut cx).is_pending(),
1058 "recv must be Pending after consuming wrong-sender packet"
1059 );
1060 }
1062
1063 inject_tx
1065 .try_send(make_udp_raw(remote_addr(), local_addr(), b"after-cancel"))
1066 .unwrap();
1067
1068 let mut buf = [0u8; 64];
1069 let len = socket.recv(&mut buf).await.unwrap();
1070 assert_eq!(&buf[..len], b"after-cancel");
1071 }
1072
1073 #[tokio::test]
1076 async fn recv_from_buffer_valid_only_after_ok() {
1077 let (socket, inject_tx, _pather) = build_socket();
1078
1079 let mut buf = [0xFFu8; 64];
1081
1082 {
1084 let mut fut = std::pin::pin!(socket.recv_from(&mut buf));
1085 let waker = futures::task::noop_waker();
1086 let mut cx = std::task::Context::from_waker(&waker);
1087 assert!(fut.as_mut().poll(&mut cx).is_pending());
1088 }
1089
1090 let payload = b"real-data";
1092 inject_tx
1093 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
1094 .unwrap();
1095
1096 let (len, _sender) = socket.recv_from(&mut buf).await.unwrap();
1097 assert_eq!(len, payload.len());
1098 assert_eq!(
1099 &buf[..len],
1100 payload,
1101 "buffer must contain the real payload after Ok return"
1102 );
1103 }
1104
1105 #[tokio::test]
1107 async fn recv_from_truncates_to_buffer_size() {
1108 let (socket, inject_tx, _pather) = build_socket();
1109 let payload = b"truncated-payload";
1110 inject_tx
1111 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
1112 .unwrap();
1113
1114 let mut buf = [0u8; 4];
1115 let (len, _sender) = socket.recv_from(&mut buf).await.unwrap();
1116 assert_eq!(len, buf.len());
1117 assert_eq!(&buf[..], &payload[..4]);
1118 }
1119
1120 #[tokio::test]
1122 async fn recv_from_with_path_truncates_to_buffer_size() {
1123 let (socket, inject_tx, _pather) = build_socket();
1124 let payload = b"truncated-payload";
1125 inject_tx
1126 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
1127 .unwrap();
1128
1129 let mut buf = [0u8; 4];
1130 let (len, _sender, _path) = socket.recv_from_with_path(&mut buf).await.unwrap();
1131 assert_eq!(len, buf.len());
1132 assert_eq!(&buf[..], &payload[..4]);
1133 }
1134}