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::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((packet.udp().payload().len(), 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((packet.udp().payload().len(), 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
300pub struct ScmpScionSocket {
302 inner: Box<dyn UnderlaySocket>,
303 local_addr: ScionSocketIpAddr,
305 snap_data_plane: Option<net::SocketAddr>,
307}
308
309impl ScmpScionSocket {
310 pub(crate) fn new(bound: BoundUnderlaySocket) -> Self {
311 Self {
312 inner: bound.socket,
313 local_addr: bound.local_addr,
314 snap_data_plane: bound.snap_data_plane,
315 }
316 }
317}
318
319impl ScmpScionSocket {
320 pub async fn send_to_via(
322 &self,
323 message: ScmpMessage,
324 destination: ScionAddr,
325 path: &ScionPath,
326 ) -> Result<(), ScionSocketSendError> {
327 let packet = ScionScmpPacket::new(
328 self.local_addr.scion_ip_addr().into(),
329 destination,
330 path.dp_path().to_model(),
331 message,
332 )
333 .try_encode_to_owned_view()
334 .map_err(|e| {
335 ScionSocketSendError::InvalidPacket(format!("error encoding packet: {e}").into())
336 })?
337 .into_raw();
338 self.inner.send(&packet).await
339 }
340
341 #[allow(clippy::type_complexity)]
343 pub async fn recv_from_with_path(
344 &self,
345 ) -> Result<(Box<ScmpPayloadView>, ScionAddr, ScionPath), ScionSocketReceiveError> {
346 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
347 loop {
348 let n = self.inner.recv(&mut scratch).await?;
349 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
350 .expect("underlay recv returns a decoded packet");
351 let packet = match packet.try_as_scmp() {
352 Ok(packet) => packet,
353 Err(e) => {
354 tracing::debug!(error = %e, "Received invalid SCMP packet, dropping");
355 continue;
356 }
357 };
358
359 let src_addr = match packet.src_scion_addr() {
360 Ok(source) => source,
361 Err(e) => {
362 tracing::debug!(error = %e, "Failed to decode packet source address, skipping");
363 continue;
364 }
365 };
366
367 let path = ScionPath::new(
368 packet.header().src_ia(),
369 packet.header().dst_ia(),
370 packet.header().path().to_owned_view(),
371 None,
372 None,
373 );
374
375 return Ok((packet.scmp().to_boxed(), src_addr, path));
376 }
377 }
378
379 pub async fn recv_from(
381 &self,
382 ) -> Result<(Box<ScmpPayloadView>, ScionAddr), ScionSocketReceiveError> {
383 let mut scratch = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
384 loop {
385 let n = self.inner.recv(&mut scratch).await?;
386 let (packet, _rest) = ScionRawPacketView::try_from_slice(&scratch[..n])
387 .expect("underlay recv returns a decoded packet");
388 let packet = match packet.try_as_scmp() {
389 Ok(packet) => packet,
390 Err(e) => {
391 tracing::debug!(error = %e, "Received invalid SCMP packet, skipping");
392 continue;
393 }
394 };
395 let src_addr = match packet.src_scion_addr() {
396 Ok(source) => source,
397 Err(e) => {
398 tracing::debug!(error = %e, "Failed to decode packet source address, skipping");
399 continue;
400 }
401 };
402 return Ok((packet.scmp().to_boxed(), src_addr));
403 }
404 }
405
406 pub fn local_addr(&self) -> ScionSocketIpAddr {
408 self.local_addr
409 }
410
411 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
413 self.snap_data_plane
414 }
415}
416
417pub struct RawScionSocket {
419 inner: Box<dyn UnderlaySocket>,
420 local_addr: ScionSocketIpAddr,
422 snap_data_plane: Option<net::SocketAddr>,
424}
425
426impl RawScionSocket {
427 pub(crate) fn new(bound: BoundUnderlaySocket) -> Self {
428 Self {
429 inner: bound.socket,
430 local_addr: bound.local_addr,
431 snap_data_plane: bound.snap_data_plane,
432 }
433 }
434}
435
436impl RawScionSocket {
437 pub async fn send(&self, packet: &ScionRawPacketView) -> Result<(), ScionSocketSendError> {
439 self.inner.send(packet).await
440 }
441
442 pub async fn recv(&self) -> Result<Box<ScionRawPacketView>, ScionSocketReceiveError> {
444 let mut buf = vec![0u8; MAX_UNDERLAY_PACKET_SIZE];
445 let n = self.inner.recv(&mut buf).await?;
446 let (view, _rest) = ScionRawPacketView::try_from_slice(&buf[..n])
447 .expect("underlay recv returns a decoded packet");
448 Ok(view.to_boxed())
449 }
450
451 pub fn local_addr(&self) -> ScionSocketIpAddr {
453 self.local_addr
454 }
455
456 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
458 self.snap_data_plane
459 }
460}
461
462pub trait SendErrorReceiver: Send + Sync {
464 fn report_send_error(&self, error: &ScionSocketSendError);
467}
468
469pub struct UdpScionSocket<P: PathManager = MultiPathManager> {
477 socket: PathUnawareUdpScionSocket,
478 pather: Arc<P>,
479 connect_timeout: Duration,
480 remote_addr: Option<ScionSocketIpAddr>,
481 send_error_receivers: Subscribers<dyn SendErrorReceiver>,
482}
483
484#[allow(clippy::missing_fields_in_debug)]
486impl<P: PathManager> std::fmt::Debug for UdpScionSocket<P> {
487 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
488 f.debug_struct("UdpScionSocket")
489 .field("local_addr", &self.socket.local_addr())
490 .field("remote_addr", &self.remote_addr)
491 .finish()
492 }
493}
494
495impl<P: PathManager> UdpScionSocket<P> {
496 pub(crate) fn new(
498 socket: PathUnawareUdpScionSocket,
499 pather: Arc<P>,
500 connect_timeout: Duration,
501 send_error_receivers: Subscribers<dyn SendErrorReceiver>,
502 ) -> Self {
503 Self {
504 socket,
505 pather,
506 connect_timeout,
507 remote_addr: None,
508 send_error_receivers,
509 }
510 }
511
512 pub async fn connect(
518 self,
519 remote_addr: ScionSocketIpAddr,
520 ) -> Result<Self, ScionSocketConnectError> {
521 let _path = self
523 .pather
524 .path_timeout(
525 self.socket.local_addr().isd_asn(),
526 remote_addr.isd_asn(),
527 SystemTime::now(),
528 self.connect_timeout,
529 )
530 .await?;
531
532 Ok(Self {
533 remote_addr: Some(remote_addr),
534 ..self
535 })
536 }
537
538 pub async fn send(&self, payload: &[u8]) -> Result<(), ScionSocketSendError> {
545 if let Some(remote_addr) = self.remote_addr {
546 self.send_to(payload, remote_addr).await
547 } else {
548 Err(ScionSocketSendError::NotConnected)
549 }
550 }
551
552 pub async fn send_to(
560 &self,
561 payload: &[u8],
562 destination: ScionSocketIpAddr,
563 ) -> Result<(), ScionSocketSendError> {
564 let path = &self
565 .pather
566 .path_wait(
567 self.socket.local_addr().isd_asn(),
568 destination.isd_asn(),
569 SystemTime::now(),
570 )
571 .await?;
572 self.socket.send_to_via(payload, destination, path).await
573 }
574
575 pub async fn send_to_via(
582 &self,
583 payload: &[u8],
584 destination: ScionSocketIpAddr,
585 path: &ScionPath,
586 ) -> Result<(), ScionSocketSendError> {
587 self.socket
588 .send_to_via(payload, destination, path)
589 .await
590 .inspect_err(|e| {
591 self.send_error_receivers
592 .for_each(|receiver| receiver.report_send_error(e));
593 })
594 }
595
596 pub async fn recv_from_with_path(
607 &self,
608 buffer: &mut [u8],
609 ) -> Result<(usize, ScionSocketIpAddr, ScionPath), ScionSocketReceiveError> {
610 let (len, sender_addr, path): (usize, ScionSocketIpAddr, ScionPath) =
611 self.socket.recv_from_with_path(buffer).await?;
612
613 match path.clone().try_into_reversed() {
614 Ok(reversed_path) => {
615 self.pather.register_path(
617 self.socket.local_addr().isd_asn(),
618 sender_addr.isd_asn(),
619 SystemTime::now(),
620 reversed_path,
621 );
622 }
623 Err((_, e)) => {
624 tracing::trace!(error = ?e, "Failed to reverse path for registration");
625 }
626 }
627
628 tracing::trace!(
629 src = %self.socket.local_addr(),
630 dst = %sender_addr,
631 "Registered reverse path",
632 );
633
634 Ok((len, sender_addr, path))
635 }
636
637 pub async fn recv_from(
649 &self,
650 buffer: &mut [u8],
651 ) -> Result<(usize, ScionSocketIpAddr), ScionSocketReceiveError> {
652 let (len, sender_addr, _) = self.recv_from_with_path(buffer).await?;
653 Ok((len, sender_addr))
654 }
655
656 pub async fn recv(&self, buffer: &mut [u8]) -> Result<usize, ScionSocketReceiveError> {
668 if self.remote_addr.is_none() {
669 return Err(ScionSocketReceiveError::NotConnected);
670 }
671 loop {
672 let (len, sender_addr) = self.recv_from(buffer).await?;
673
674 match self.remote_addr {
676 Some(remote_addr) => {
677 if sender_addr == remote_addr {
678 return Ok(len);
679 }
680 }
681 None => return Err(ScionSocketReceiveError::NotConnected),
682 }
683 }
684 }
685
686 pub fn local_addr(&self) -> ScionSocketIpAddr {
688 self.socket.local_addr()
689 }
690
691 pub fn snap_data_plane(&self) -> Option<net::SocketAddr> {
693 self.socket.snap_data_plane()
694 }
695}
696
697#[async_trait::async_trait]
700impl<P: PathManager + Sync + Send + 'static> GenericScionUdpSocket for UdpScionSocket<P> {
701 async fn send_to(
703 &self,
704 payload: &[u8],
705 destination: ScionSocketIpAddr,
706 ) -> Result<(), BoxedSocketError> {
707 self.send_to(payload, destination)
708 .await
709 .map_err(|e| Box::new(e) as BoxedSocketError)
710 }
711
712 async fn recv_from(
715 &self,
716 buf: &mut [u8],
717 ) -> Result<(usize, ScionSocketIpAddr), BoxedSocketError> {
718 self.recv_from(buf)
719 .await
720 .map_err(|e| Box::new(e) as BoxedSocketError)
721 }
722
723 fn local_addr(&self) -> ScionSocketIpAddr {
725 self.local_addr()
726 }
727}
728
729#[cfg(test)]
730mod cancel_safety_tests {
731 use std::{
757 io,
758 net::Ipv4Addr,
759 sync::{Arc, Mutex},
760 time::SystemTime,
761 };
762
763 use async_trait::async_trait;
764 use sciparse::{
765 identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn},
766 util::test_builder::{TestPathBuilder, TestPathContext},
767 };
768
769 use super::*;
770 use crate::{
771 internal::Subscribers,
772 path::manager::traits::{PathWaitError, SyncPathManager},
773 stack::{ScionSocketReceiveError, ScionSocketSendError, UnderlaySocket},
774 };
775
776 struct ManualUnderlaySocket {
777 rx: tokio::sync::Mutex<tokio::sync::mpsc::Receiver<Box<ScionRawPacketView>>>,
778 peeked: tokio::sync::Mutex<Option<Box<ScionRawPacketView>>>,
781 }
782
783 impl ManualUnderlaySocket {
784 fn new() -> (Self, tokio::sync::mpsc::Sender<Box<ScionRawPacketView>>) {
785 let (inject_tx, recv_rx) = tokio::sync::mpsc::channel::<Box<ScionRawPacketView>>(64);
787 let socket = Self {
788 rx: tokio::sync::Mutex::new(recv_rx),
789 peeked: tokio::sync::Mutex::new(None),
790 };
791 (socket, inject_tx)
792 }
793 }
794
795 #[async_trait]
796 impl UnderlaySocket for ManualUnderlaySocket {
797 fn try_send(&self, _packet: &ScionRawPacketView) -> Result<(), ScionSocketSendError> {
798 Ok(())
799 }
800
801 async fn writeable(&self) {}
802
803 fn try_recv(&self, buf: &mut [u8]) -> Result<usize, ScionSocketReceiveError> {
804 let would_block =
805 || ScionSocketReceiveError::IoError(io::Error::from(io::ErrorKind::WouldBlock));
806
807 let packet: Box<ScionRawPacketView> = {
808 let Ok(mut peeked) = self.peeked.try_lock() else {
809 return Err(would_block());
810 };
811 match peeked.take() {
812 Some(packet) => packet,
813 None => {
814 let Ok(mut rx) = self.rx.try_lock() else {
815 return Err(would_block());
816 };
817 match rx.try_recv() {
818 Ok(packet) => packet,
819 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
820 return Err(would_block());
821 }
822 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
823 return Err(ScionSocketReceiveError::IoError(io::Error::other(
824 "channel closed",
825 )));
826 }
827 }
828 }
829 }
830 };
831
832 let bytes = packet.as_slice();
833 let n = bytes.len();
834 buf[..n].copy_from_slice(bytes);
835 Ok(n)
836 }
837
838 async fn readable(&self) {
839 let mut peeked = self.peeked.lock().await;
840 if peeked.is_some() {
841 return;
842 }
843 if let Some(packet) = self.rx.lock().await.recv().await {
846 *peeked = Some(packet);
847 }
848 }
849 }
850
851 #[derive(Default)]
852 struct ImmediatePathManager {
853 registered_paths: Mutex<Vec<ScionPath>>,
854 }
855
856 impl SyncPathManager for ImmediatePathManager {
857 fn register_path(&self, _src: IsdAsn, _dst: IsdAsn, _now: SystemTime, path: ScionPath) {
858 self.registered_paths.lock().expect("poisoned").push(path);
859 }
860
861 fn try_cached_path(
862 &self,
863 src: IsdAsn,
864 _dst: IsdAsn,
865 _now: SystemTime,
866 ) -> io::Result<Option<ScionPath>> {
867 Ok(Some(
868 ScionPath::local(src).expect("src is not a wildcard IA"),
869 ))
870 }
871 }
872
873 impl PathManager for ImmediatePathManager {
874 fn path_wait(
875 &self,
876 src: IsdAsn,
877 _dst: IsdAsn,
878 _now: SystemTime,
879 ) -> impl std::future::Future<Output = Result<ScionPath, PathWaitError>> + Send + '_
880 {
881 async move { Ok(ScionPath::local(src).expect("src is not a wildcard IA")) }
882 }
883 }
884
885 const LOCAL_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(1));
886 const REMOTE_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(2));
887 const OTHER_ISD_ASN: IsdAsn = IsdAsn::new(Isd(1), Asn(3));
888
889 fn local_addr() -> ScionSocketIpAddr {
890 ScionSocketIpAddr::new(LOCAL_ISD_ASN, Ipv4Addr::LOCALHOST.into(), 8080)
891 }
892
893 fn remote_addr() -> ScionSocketIpAddr {
894 ScionSocketIpAddr::new(REMOTE_ISD_ASN, Ipv4Addr::new(127, 0, 0, 2).into(), 9090)
895 }
896
897 fn other_addr() -> ScionSocketIpAddr {
898 ScionSocketIpAddr::new(OTHER_ISD_ASN, Ipv4Addr::new(127, 0, 0, 3).into(), 7070)
899 }
900
901 fn test_path_ctx(src: ScionAddr, dst: ScionAddr) -> TestPathContext {
903 TestPathBuilder::new(src, dst)
904 .using_info_timestamp(1_000_000)
905 .up()
906 .add_hop(0, 1)
907 .add_hop(1, 0)
908 .build(1_000_000)
909 }
910
911 fn make_udp_raw(
914 src: ScionSocketIpAddr,
915 dst: ScionSocketIpAddr,
916 payload: &[u8],
917 ) -> Box<ScionRawPacketView> {
918 let ctx = test_path_ctx(src.scion_addr(), dst.scion_addr());
919 ctx.scion_packet_udp(payload, src.port(), dst.port())
920 .try_encode_to_owned_view()
921 .expect("should encode")
922 .into()
923 }
924
925 fn build_socket() -> (
928 UdpScionSocket<ImmediatePathManager>,
929 tokio::sync::mpsc::Sender<Box<ScionRawPacketView>>,
930 Arc<ImmediatePathManager>,
931 ) {
932 let (underlay, inject_tx) = ManualUnderlaySocket::new();
933 let pather = Arc::new(ImmediatePathManager::default());
934 let path_unaware = PathUnawareUdpScionSocket::new(
935 BoundUnderlaySocket {
936 socket: Box::new(underlay),
937 local_addr: local_addr(),
938 snap_data_plane: None,
939 },
940 vec![], );
942 let socket = UdpScionSocket::new(
943 path_unaware,
944 pather.clone(),
945 std::time::Duration::from_secs(5),
946 Subscribers::new(),
947 );
948 (socket, inject_tx, pather)
949 }
950
951 #[tokio::test]
959 async fn recv_from_with_path_cancel_while_pending_does_not_lose_packet() {
960 let (socket, inject_tx, _pather) = build_socket();
961
962 {
964 let mut buf = [0u8; 64];
965 let mut fut = std::pin::pin!(socket.recv_from_with_path(&mut buf));
966 let waker = futures::task::noop_waker();
967 let mut cx = std::task::Context::from_waker(&waker);
968 assert!(fut.as_mut().poll(&mut cx).is_pending());
970 }
972
973 let payload = b"cancel-safe";
975 inject_tx
976 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
977 .unwrap();
978
979 let mut buf2 = vec![0u8; 64];
981 let (len, sender, _path) = socket.recv_from_with_path(&mut buf2).await.unwrap();
982
983 assert_eq!(len, payload.len());
984 assert_eq!(&buf2[..len], payload);
985 assert_eq!(sender, remote_addr());
986 }
987
988 #[tokio::test]
991 async fn recv_filters_wrong_sender_and_delivers_correct_packet() {
992 let (mut socket, inject_tx, _pather) = build_socket();
993 socket.remote_addr = Some(remote_addr());
995
996 inject_tx
998 .try_send(make_udp_raw(other_addr(), local_addr(), b"wrong"))
999 .unwrap();
1000 inject_tx
1001 .try_send(make_udp_raw(remote_addr(), local_addr(), b"correct"))
1002 .unwrap();
1003
1004 let mut buf = [0u8; 64];
1005 let len = socket.recv(&mut buf).await.unwrap();
1006 assert_eq!(&buf[..len], b"correct");
1007 }
1008
1009 #[tokio::test]
1012 async fn recv_cancel_during_filtering_socket_remains_usable() {
1013 let (mut socket, inject_tx, _pather) = build_socket();
1014 socket.remote_addr = Some(remote_addr());
1015
1016 inject_tx
1019 .try_send(make_udp_raw(other_addr(), local_addr(), b"wrong"))
1020 .unwrap();
1021
1022 {
1026 let mut filter_buf = [0u8; 64];
1027 let mut fut = std::pin::pin!(socket.recv(&mut filter_buf));
1028 let waker = futures::task::noop_waker();
1029 let mut cx = std::task::Context::from_waker(&waker);
1030 assert!(
1031 fut.as_mut().poll(&mut cx).is_pending(),
1032 "recv must be Pending after consuming wrong-sender packet"
1033 );
1034 }
1036
1037 inject_tx
1039 .try_send(make_udp_raw(remote_addr(), local_addr(), b"after-cancel"))
1040 .unwrap();
1041
1042 let mut buf = [0u8; 64];
1043 let len = socket.recv(&mut buf).await.unwrap();
1044 assert_eq!(&buf[..len], b"after-cancel");
1045 }
1046
1047 #[tokio::test]
1050 async fn recv_from_buffer_valid_only_after_ok() {
1051 let (socket, inject_tx, _pather) = build_socket();
1052
1053 let mut buf = [0xFFu8; 64];
1055
1056 {
1058 let mut fut = std::pin::pin!(socket.recv_from(&mut buf));
1059 let waker = futures::task::noop_waker();
1060 let mut cx = std::task::Context::from_waker(&waker);
1061 assert!(fut.as_mut().poll(&mut cx).is_pending());
1062 }
1063
1064 let payload = b"real-data";
1066 inject_tx
1067 .try_send(make_udp_raw(remote_addr(), local_addr(), payload))
1068 .unwrap();
1069
1070 let (len, _sender) = socket.recv_from(&mut buf).await.unwrap();
1071 assert_eq!(len, payload.len());
1072 assert_eq!(
1073 &buf[..len],
1074 payload,
1075 "buffer must contain the real payload after Ok return"
1076 );
1077 }
1078}