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}