scion_stack/scionstack.rs
1// Copyright 2025 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # The SCION endhost stack.
16//!
17//! [ScionStack] is a stateful object that is the conceptual equivalent of the
18//! TCP/IP-stack found in today's common operating systems. It is meant to be
19//! instantiated once per process.
20//!
21//! ## Basic Usage
22//!
23//! ### Creating a path-aware socket (recommended)
24//!
25//! ```
26//! use scion_proto::address::SocketAddr;
27//! use scion_stack::scionstack::{ScionStack, ScionStackBuilder};
28//! use url::Url;
29//!
30//! # async fn socket_example() -> Result<(), Box<dyn std::error::Error>> {
31//! // Create a SCION stack builder
32//! let control_plane_addr: url::Url = "http://127.0.0.1:1234".parse()?;
33//! let builder =
34//! ScionStackBuilder::new(control_plane_addr).with_auth_token("SNAP token".to_string());
35//!
36//! let scion_stack = builder.build().await?;
37//! let socket = scion_stack.bind(None).await?;
38//!
39//! // Parse destination address
40//! let destination: SocketAddr = "1-ff00:0:111,[192.168.1.1]:8080".parse()?;
41//!
42//! socket.send_to(b"hello", destination).await?;
43//! let mut buffer = [0u8; 1024];
44//! let (len, src) = socket.recv_from(&mut buffer).await?;
45//! println!("Received: {:?} from {:?}", &buffer[..len], src);
46//!
47//! # Ok(())
48//! # }
49//! ```
50//!
51//! ### Creating a connected socket.
52//!
53//! ```
54//! use scion_proto::address::SocketAddr;
55//! use scion_stack::scionstack::{ScionStack, ScionStackBuilder};
56//! use url::Url;
57//!
58//! # async fn connected_socket_example() -> Result<(), Box<dyn std::error::Error>> {
59//! // Create a SCION stack builder
60//! let control_plane_addr: url::Url = "http://127.0.0.1:1234".parse()?;
61//! let builder =
62//! ScionStackBuilder::new(control_plane_addr).with_auth_token("SNAP token".to_string());
63//!
64//! // Parse destination address
65//! let destination: SocketAddr = "1-ff00:0:111,[192.168.1.1]:8080".parse()?;
66//!
67//! let scion_stack = builder.build().await?;
68//! let connected_socket = scion_stack.connect(destination, None).await?;
69//! connected_socket.send(b"hello").await?;
70//! let mut buffer = [0u8; 1024];
71//! let len = connected_socket.recv(&mut buffer).await?;
72//! println!("Received: {:?}", &buffer[..len]);
73//!
74//! # Ok(())
75//! # }
76//! ```
77//!
78//! ### Creating a path-unaware socket
79//!
80//! ```
81//! use scion_proto::{address::SocketAddr, path::Path};
82//! use scion_stack::scionstack::{ScionStack, ScionStackBuilder};
83//! use url::Url;
84//!
85//! # async fn basic_socket_example() -> Result<(), Box<dyn std::error::Error>> {
86//! // Create a SCION stack builder
87//! let control_plane_addr: url::Url = "http://127.0.0.1:1234".parse()?;
88//! let builder =
89//! ScionStackBuilder::new(control_plane_addr).with_auth_token("SNAP token".to_string());
90//!
91//! // Parse addresses
92//! let bind_addr: SocketAddr = "1-ff00:0:110,[127.0.0.1]:8080".parse()?;
93//! let destination: SocketAddr = "1-ff00:0:111,[127.0.0.1]:9090".parse()?;
94//!
95//! // Create a local path for demonstration
96//! let path: scion_proto::path::Path<bytes::Bytes> = Path::local(bind_addr.isd_asn());
97//!
98//! let scion_stack = builder.build().await?;
99//! let socket = scion_stack.bind_path_unaware(Some(bind_addr)).await?;
100//! socket
101//! .send_to_via(b"hello", destination, &path.to_slice_path())
102//! .await?;
103//! let mut buffer = [0u8; 1024];
104//! let (len, sender) = socket.recv_from(&mut buffer).await?;
105//! println!("Received: {:?} from {:?}", &buffer[..len], sender);
106//!
107//! # Ok(())
108//! # }
109//! ```
110//!
111//! ## Advanced Usage
112//!
113//! ### Custom path selection
114//!
115//! ```
116//! // Implement your own path selection logic
117//! use std::sync::Arc;
118//!
119//! use bytes::Bytes;
120//! use chrono::{DateTime, Utc};
121//! use scion_proto::{
122//! address::{IsdAsn, SocketAddr},
123//! path::Path,
124//! };
125//! use scion_stack::{
126//! path::manager::PathManager,
127//! scionstack::{ScionStack, ScionStackBuilder, UdpScionSocket},
128//! types::ResFut,
129//! };
130//!
131//! struct MyCustomPathManager;
132//!
133//! impl scion_stack::path::manager::SyncPathManager for MyCustomPathManager {
134//! fn register_path(
135//! &self,
136//! _src: IsdAsn,
137//! _dst: IsdAsn,
138//! _now: DateTime<Utc>,
139//! _path: Path<Bytes>,
140//! ) {
141//! // Optionally implement registration logic
142//! }
143//!
144//! fn try_cached_path(
145//! &self,
146//! _src: IsdAsn,
147//! _dst: IsdAsn,
148//! _now: DateTime<Utc>,
149//! ) -> std::io::Result<Option<Path<Bytes>>> {
150//! todo!()
151//! }
152//! }
153//!
154//! impl scion_stack::path::manager::PathManager for MyCustomPathManager {
155//! fn path_wait(
156//! &self,
157//! src: IsdAsn,
158//! _dst: IsdAsn,
159//! _now: DateTime<Utc>,
160//! ) -> impl ResFut<'_, Path<Bytes>, scion_stack::path::manager::PathWaitError> {
161//! async move { Ok(Path::local(src)) }
162//! }
163//! }
164//!
165//! # async fn custom_pather_example() -> Result<(), Box<dyn std::error::Error>> {
166//! // Create a SCION stack builder
167//! let control_plane_addr: url::Url = "http://127.0.0.1:1234".parse()?;
168//! let builder =
169//! ScionStackBuilder::new(control_plane_addr).with_auth_token("SNAP token".to_string());
170//!
171//! // Parse addresses
172//! let bind_addr: SocketAddr = "1-ff00:0:110,[127.0.0.1]:8080".parse()?;
173//! let destination: SocketAddr = "1-ff00:0:111,[127.0.0.1]:9090".parse()?;
174//!
175//! let scion_stack = builder.build().await?;
176//! let path_unaware_socket = scion_stack.bind_path_unaware(Some(bind_addr)).await?;
177//! let socket = UdpScionSocket::new(path_unaware_socket, Arc::new(MyCustomPathManager), None);
178//! socket.send_to(b"hello", destination).await?;
179//!
180//! # Ok(())
181//! # }
182//! ```
183
184pub mod builder;
185pub mod quic;
186pub mod scmp_handler;
187mod socket;
188pub(crate) mod udp_polling;
189
190use std::{
191 borrow::Cow,
192 fmt,
193 pin::Pin,
194 sync::Arc,
195 task::{Context, Poll},
196 time::{Duration, Instant},
197};
198
199use anyhow::Context as _;
200use bytes::Bytes;
201use endhost_api_client::client::EndhostApiClient;
202use futures::future::BoxFuture;
203use quic::{AddressTranslator, Endpoint, ScionAsyncUdpSocket};
204use scion_proto::{
205 address::{EndhostAddr, IsdAsn, SocketAddr},
206 packet::ScionPacketRaw,
207 path::Path,
208};
209pub use socket::{
210 PathUnawareUdpScionSocket, RawScionSocket, ScmpScionSocket, SocketConfig, UdpScionSocket,
211};
212
213// Re-export the main types from the modules
214pub use self::builder::ScionStackBuilder;
215pub use self::scmp_handler::{DefaultScmpHandler, ScmpHandler};
216use crate::path::{
217 PathStrategy,
218 manager::{CachingPathManager, ConnectRpcSegmentFetcher, PathFetcherImpl},
219 ranking::Shortest,
220};
221
222/// Default duration to reserve a port when binding a socket.
223pub const DEFAULT_RESERVED_TIME: Duration = Duration::from_secs(3);
224
225/// The SCION stack can be used to create path-aware SCION sockets or even Quic over SCION
226/// connections.
227///
228/// The SCION stack abstracts over the underlay stack that is used for the underlying
229/// transport.
230pub struct ScionStack {
231 client: Arc<dyn EndhostApiClient>,
232 underlay: Arc<dyn DynUnderlayStack>,
233}
234
235impl fmt::Debug for ScionStack {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f.debug_struct("ScionStack")
238 .field("client", &"Arc<ConnectRpcClient>")
239 .field("underlay", &"Arc<dyn DynUnderlayStack>")
240 .finish()
241 }
242}
243
244impl ScionStack {
245 pub(crate) fn new(
246 client: Arc<dyn EndhostApiClient>,
247 underlay: Arc<dyn DynUnderlayStack>,
248 ) -> Self {
249 Self { client, underlay }
250 }
251
252 /// Create a path-aware SCION socket with automatic path management.
253 ///
254 /// # Arguments
255 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
256 /// used.
257 ///
258 /// # Returns
259 /// A path-aware SCION socket.
260 pub async fn bind(
261 &self,
262 bind_addr: Option<SocketAddr>,
263 ) -> Result<UdpScionSocket, ScionSocketBindError> {
264 self.bind_with_config(bind_addr, SocketConfig::default())
265 .await
266 }
267
268 /// Create a path-aware SCION socket with custom configuration.
269 ///
270 /// # Arguments
271 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
272 /// used.
273 /// * `socket_config` - Configuration for the socket.
274 ///
275 /// # Returns
276 /// A path-aware SCION socket.
277 pub async fn bind_with_config(
278 &self,
279 bind_addr: Option<SocketAddr>,
280 mut socket_config: SocketConfig,
281 ) -> Result<UdpScionSocket, ScionSocketBindError> {
282 let socket = self.bind_path_unaware(bind_addr).await?;
283 let fetcher = PathFetcherImpl::new(ConnectRpcSegmentFetcher::new(self.client.clone()));
284
285 // Default to Shortest path ranking if no ranking is configured.
286 if socket_config.path_strategy.ranking.is_empty() {
287 socket_config.path_strategy.add_ranking(Shortest);
288 }
289
290 let pather = Arc::new(CachingPathManager::start(
291 socket_config.path_strategy,
292 fetcher,
293 ));
294
295 Ok(UdpScionSocket::new(socket, pather, None))
296 }
297
298 /// Create a connected path-aware SCION socket with automatic path management.
299 ///
300 /// # Arguments
301 /// * `remote_addr` - The remote address to connect to.
302 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
303 /// used.
304 ///
305 /// # Returns
306 /// A connected path-aware SCION socket.
307 pub async fn connect(
308 &self,
309 remote_addr: SocketAddr,
310 bind_addr: Option<SocketAddr>,
311 ) -> Result<UdpScionSocket, ScionSocketBindError> {
312 let socket = self.bind(bind_addr).await?;
313 Ok(socket.connect(remote_addr))
314 }
315
316 /// Create a connected path-aware SCION socket with custom configuration.
317 ///
318 /// # Arguments
319 /// * `remote_addr` - The remote address to connect to.
320 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
321 /// used.
322 /// * `socket_config` - Configuration for the socket
323 ///
324 /// # Returns
325 /// A connected path-aware SCION socket.
326 pub async fn connect_with_config(
327 &self,
328 remote_addr: SocketAddr,
329 bind_addr: Option<SocketAddr>,
330 socket_config: SocketConfig,
331 ) -> Result<UdpScionSocket, ScionSocketBindError> {
332 let socket = self.bind_with_config(bind_addr, socket_config).await?;
333 Ok(socket.connect(remote_addr))
334 }
335
336 /// Bind a socket with controlled time for port allocation.
337 ///
338 /// This allows tests to control port reservation timing without sleeping.
339 pub async fn bind_with_time(
340 &self,
341 bind_addr: Option<SocketAddr>,
342 now: std::time::Instant,
343 ) -> Result<UdpScionSocket, ScionSocketBindError> {
344 let socket = self
345 .underlay
346 .bind_socket_with_time(SocketKind::Udp, bind_addr, now)
347 .await?;
348 let pather = self.create_path_manager();
349 Ok(UdpScionSocket::new(
350 PathUnawareUdpScionSocket::new(socket),
351 pather,
352 None,
353 ))
354 }
355
356 /// Create a socket that can send and receive SCMP messages.
357 ///
358 /// # Arguments
359 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
360 /// used.
361 ///
362 /// # Returns
363 /// A SCMP socket.
364 pub async fn bind_scmp(
365 &self,
366 bind_addr: Option<SocketAddr>,
367 ) -> Result<ScmpScionSocket, ScionSocketBindError> {
368 let socket = self
369 .underlay
370 .bind_socket(SocketKind::Scmp, bind_addr)
371 .await?;
372 Ok(ScmpScionSocket::new(socket))
373 }
374
375 /// Create a raw SCION socket.
376 /// A raw SCION socket can be used to send and receive raw SCION packets.
377 /// It is still bound to a specific UDP port because this is needed for packets
378 /// to be routed in a dispatcherless autonomous system. See <https://docs.scion.org/en/latest/dev/design/router-port-dispatch.html> for a more detailed explanation.
379 ///
380 /// # Arguments
381 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
382 /// used.
383 ///
384 /// # Returns
385 /// A raw SCION socket.
386 pub async fn bind_raw(
387 &self,
388 bind_addr: Option<SocketAddr>,
389 ) -> Result<RawScionSocket, ScionSocketBindError> {
390 let socket = self
391 .underlay
392 .bind_socket(SocketKind::Raw, bind_addr)
393 .await?;
394 Ok(RawScionSocket::new(socket))
395 }
396
397 /// Create a path-unaware SCION socket for advanced use cases.
398 ///
399 /// This socket can send and receive datagrams, but requires explicit paths for sending.
400 /// Use this when you need full control over path selection.
401 ///
402 /// # Arguments
403 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
404 /// used.
405 ///
406 /// # Returns
407 /// A path-unaware SCION socket.
408 pub async fn bind_path_unaware(
409 &self,
410 bind_addr: Option<SocketAddr>,
411 ) -> Result<PathUnawareUdpScionSocket, ScionSocketBindError> {
412 let socket = self
413 .underlay
414 .bind_socket(SocketKind::Udp, bind_addr)
415 .await?;
416 Ok(PathUnawareUdpScionSocket::new(socket))
417 }
418
419 /// Create a QUIC over SCION endpoint.
420 ///
421 /// This is a convenience method that creates a QUIC (quinn) endpoint over a SCION socket.
422 ///
423 /// # Arguments
424 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
425 /// used.
426 /// * `config` - The quinn endpoint configuration.
427 /// * `server_config` - The quinn server configuration.
428 /// * `runtime` - The runtime to spawn tasks on.
429 ///
430 /// # Returns
431 /// A QUIC endpoint that can be used to accept or create QUIC connections.
432 pub async fn quic_endpoint(
433 &self,
434 bind_addr: Option<SocketAddr>,
435 config: quinn::EndpointConfig,
436 server_config: Option<quinn::ServerConfig>,
437 runtime: Option<Arc<dyn quinn::Runtime>>,
438 ) -> anyhow::Result<Endpoint> {
439 self.quic_endpoint_with_config(
440 bind_addr,
441 config,
442 server_config,
443 runtime,
444 SocketConfig::default(),
445 )
446 .await
447 }
448
449 /// Create a QUIC over SCION endpoint using custom socket configuration.
450 ///
451 /// This is a convenience method that creates a QUIC (quinn) endpoint over a SCION socket.
452 ///
453 /// # Arguments
454 /// * `bind_addr` - The address to bind the socket to. If None, an available address will be
455 /// used.
456 /// * `config` - The quinn endpoint configuration.
457 /// * `server_config` - The quinn server configuration.
458 /// * `runtime` - The runtime to spawn tasks on.
459 /// * `socket_config` - Scion Socket configuration
460 ///
461 /// # Returns
462 /// A QUIC endpoint that can be used to accept or create QUIC connections.
463 pub async fn quic_endpoint_with_config(
464 &self,
465 bind_addr: Option<SocketAddr>,
466 config: quinn::EndpointConfig,
467 server_config: Option<quinn::ServerConfig>,
468 runtime: Option<Arc<dyn quinn::Runtime>>,
469 socket_config: SocketConfig,
470 ) -> anyhow::Result<Endpoint> {
471 let socket = self.underlay.bind_async_udp_socket(bind_addr).await?;
472 let address_translator = Arc::new(AddressTranslator::default());
473
474 let path_manager = {
475 let fetcher = PathFetcherImpl::new(ConnectRpcSegmentFetcher::new(self.client.clone()));
476
477 // Default to Shortest path ranking if no ranking is configured.
478 let mut strategy = socket_config.path_strategy;
479 if strategy.ranking.is_empty() {
480 strategy.add_ranking(Shortest);
481 }
482
483 Arc::new(CachingPathManager::start(strategy, fetcher))
484 };
485
486 let socket = Arc::new(ScionAsyncUdpSocket::new(
487 socket,
488 path_manager.clone(),
489 address_translator.clone(),
490 ));
491
492 let runtime = match runtime {
493 Some(runtime) => runtime,
494 None => quinn::default_runtime().context("No runtime found")?,
495 };
496
497 Ok(Endpoint::new_with_abstract_socket(
498 config,
499 server_config,
500 socket,
501 runtime,
502 path_manager,
503 address_translator,
504 )?)
505 }
506
507 /// Get the list of local addresses assigned to the endhost.
508 ///
509 /// # Returns
510 ///
511 /// A list of local addresses assigned to the endhost.
512 pub fn local_addresses(&self) -> Vec<EndhostAddr> {
513 self.underlay.local_addresses()
514 }
515
516 /// Creates a path manager with default configuration.
517 pub fn create_path_manager(&self) -> Arc<CachingPathManager> {
518 let fetcher = PathFetcherImpl::new(ConnectRpcSegmentFetcher::new(self.client.clone()));
519 let mut strategy = PathStrategy::default();
520
521 strategy.add_ranking(Shortest);
522
523 Arc::new(CachingPathManager::start(strategy, fetcher))
524 }
525}
526
527/// Error return when binding a socket.
528#[derive(Debug, thiserror::Error)]
529pub enum ScionSocketBindError {
530 /// The provided bind address cannot be bount to.
531 /// E.g. because it is not assigned to the endhost or because the address
532 /// type is not supported.
533 #[error("invalid bind address {0}: {1}")]
534 InvalidBindAddress(SocketAddr, String),
535 /// The provided port is already in use.
536 #[error("port {0} is already in use")]
537 PortAlreadyInUse(u16),
538 /// An error that is not covered by the variants above.
539 #[error("other error: {0}")]
540 Other(#[from] Box<dyn std::error::Error + Send + Sync>),
541 /// Internal error.
542 #[error(
543 "internal error in the SCION stack, this should never happen, please report this to the developers: {0}"
544 )]
545 Internal(String),
546}
547
548/// Available kinds of SCION sockets.
549#[derive(Hash, Eq, PartialEq, Clone, Debug, Ord, PartialOrd)]
550pub enum SocketKind {
551 /// UDP socket.
552 Udp,
553 /// SCMP socket.
554 Scmp,
555 /// Raw socket.
556 Raw,
557}
558/// A trait that defines the underlay stack.
559///
560/// The underlay stack is the underlying transport layer that is used to send and receive SCION
561/// packets. Sockets returned by the underlay stack have no path management but allow
562/// sending and receiving SCION packets.
563pub(crate) trait UnderlayStack: Send + Sync {
564 type Socket: UnderlaySocket + 'static;
565 type AsyncUdpSocket: AsyncUdpUnderlaySocket + 'static;
566
567 fn bind_socket(
568 &self,
569 kind: SocketKind,
570 bind_addr: Option<SocketAddr>,
571 ) -> BoxFuture<'_, Result<Self::Socket, ScionSocketBindError>>;
572
573 fn bind_socket_with_time(
574 &self,
575 kind: SocketKind,
576 bind_addr: Option<SocketAddr>,
577 now: Instant,
578 ) -> BoxFuture<'_, Result<Self::Socket, ScionSocketBindError>>;
579
580 fn bind_async_udp_socket(
581 &self,
582 bind_addr: Option<SocketAddr>,
583 ) -> BoxFuture<'_, Result<Self::AsyncUdpSocket, ScionSocketBindError>>;
584 /// Get the list of local addresses assigned to or configured on the endhost.
585 fn local_addresses(&self) -> Vec<EndhostAddr>;
586}
587
588/// Dyn safe trait for an underlay stack.
589pub(crate) trait DynUnderlayStack: Send + Sync {
590 fn bind_socket(
591 &self,
592 kind: SocketKind,
593 bind_addr: Option<SocketAddr>,
594 ) -> BoxFuture<'_, Result<Box<dyn UnderlaySocket>, ScionSocketBindError>>;
595
596 fn bind_socket_with_time(
597 &self,
598 kind: SocketKind,
599 bind_addr: Option<SocketAddr>,
600 now: Instant,
601 ) -> BoxFuture<'_, Result<Box<dyn UnderlaySocket>, ScionSocketBindError>>;
602
603 fn bind_async_udp_socket(
604 &self,
605 bind_addr: Option<SocketAddr>,
606 ) -> BoxFuture<'_, Result<Arc<dyn AsyncUdpUnderlaySocket>, ScionSocketBindError>>;
607
608 fn local_addresses(&self) -> Vec<EndhostAddr>;
609}
610
611impl<U: UnderlayStack> DynUnderlayStack for U {
612 fn bind_socket(
613 &self,
614 kind: SocketKind,
615 bind_addr: Option<SocketAddr>,
616 ) -> BoxFuture<'_, Result<Box<dyn UnderlaySocket>, ScionSocketBindError>> {
617 Box::pin(async move {
618 let socket = self.bind_socket(kind, bind_addr).await?;
619 Ok(Box::new(socket) as Box<dyn UnderlaySocket>)
620 })
621 }
622
623 fn bind_socket_with_time(
624 &self,
625 kind: SocketKind,
626 bind_addr: Option<SocketAddr>,
627 now: Instant,
628 ) -> BoxFuture<'_, Result<Box<dyn UnderlaySocket>, ScionSocketBindError>> {
629 Box::pin(async move {
630 let socket = self.bind_socket_with_time(kind, bind_addr, now).await?;
631 Ok(Box::new(socket) as Box<dyn UnderlaySocket>)
632 })
633 }
634
635 fn bind_async_udp_socket(
636 &self,
637 bind_addr: Option<SocketAddr>,
638 ) -> BoxFuture<'_, Result<Arc<dyn AsyncUdpUnderlaySocket>, ScionSocketBindError>> {
639 Box::pin(async move {
640 let socket = self.bind_async_udp_socket(bind_addr).await?;
641 Ok(Arc::new(socket) as Arc<dyn AsyncUdpUnderlaySocket>)
642 })
643 }
644
645 fn local_addresses(&self) -> Vec<EndhostAddr> {
646 <Self as UnderlayStack>::local_addresses(self)
647 }
648}
649
650/// SCION socket send errors.
651#[derive(Debug, thiserror::Error)]
652pub enum ScionSocketSendError {
653 /// There was an error looking up the path in the path registry.
654 #[error("path lookup error: {0}")]
655 PathLookupError(Cow<'static, str>),
656 /// The desination is not reachable. E.g. because no path is available.
657 #[error("network unreachable: {0}")]
658 NetworkUnreachable(NetworkError),
659 /// The provided packet is invalid. The underlying socket is
660 /// not able to process the packet.
661 #[error("invalid packet: {0}")]
662 InvalidPacket(Cow<'static, str>),
663 /// The underlying socket is closed.
664 #[error("underlying socket is closed")]
665 Closed,
666 /// IO Error from the underlying connection.
667 #[error("underlying connection returned an I/O error: {0:?}")]
668 IoError(#[from] std::io::Error),
669 /// Error return when send is called on a socket that is not connected.
670 #[error("socket is not connected")]
671 NotConnected,
672}
673
674/// Network errors.
675#[derive(Debug, thiserror::Error)]
676pub enum NetworkError {
677 /// The destination is unreachable.
678 #[error("destination unreachable: {0}")]
679 DestinationUnreachable(String),
680 /// Underlay next hop unreachable.
681 #[error("next hop unreachable: {isd_as}#{interface_id}: {msg}")]
682 UnderlayNextHopUnreachable {
683 /// ISD-AS of the next hop.
684 isd_as: IsdAsn,
685 /// Interface ID of the next hop.
686 interface_id: u16,
687 /// Additional message.
688 msg: String,
689 },
690}
691
692/// SCION socket receive errors.
693#[derive(Debug, thiserror::Error)]
694pub enum ScionSocketReceiveError {
695 /// Path buffer too small.
696 #[error("provided path buffer is too small (at least 1024 bytes required)")]
697 PathBufTooSmall,
698 /// I/O error.
699 #[error("i/o error: {0:?}")]
700 IoError(#[from] std::io::Error),
701 /// Error return when recv is called on a socket that is not connected.
702 #[error("socket is not connected")]
703 NotConnected,
704}
705
706/// A trait that defines an abstraction over an asynchronous underlay socket.
707pub(crate) trait UnderlaySocket: 'static + Send + Sync {
708 /// Send a raw packet. Takes a ScionPacketRaw because it needs to read the path
709 /// to resolve the underlay next hop.
710 fn send<'a>(
711 &'a self,
712 packet: ScionPacketRaw,
713 ) -> BoxFuture<'a, Result<(), ScionSocketSendError>>;
714
715 fn recv<'a>(&'a self) -> BoxFuture<'a, Result<ScionPacketRaw, ScionSocketReceiveError>>;
716
717 fn local_addr(&self) -> SocketAddr;
718}
719
720/// A trait that defines an asynchronous path unaware UDP socket.
721/// This can be used to implement the [quinn::AsyncUdpSocket] trait.
722pub(crate) trait AsyncUdpUnderlaySocket: Send + Sync {
723 fn create_io_poller(self: Arc<Self>) -> Pin<Box<dyn udp_polling::UdpPoller>>;
724 /// Try to send a raw SCION UDP packet. Path resolution and packet encoding is
725 /// left to the caller.
726 /// This function should return std::io::ErrorKind::WouldBlock if the packet cannot be sent
727 /// immediately.
728 fn try_send(&self, raw_packet: ScionPacketRaw) -> Result<(), std::io::Error>;
729 /// Poll for receiving a SCION packet with sender and path.
730 fn poll_recv_from_with_path(
731 &self,
732 cx: &mut Context,
733 ) -> Poll<std::io::Result<(SocketAddr, Bytes, Path)>>;
734 fn local_addr(&self) -> SocketAddr;
735}