Skip to main content
SCION SDKv0.6.0

The ScionStack model

Everything you do with the SDK starts from a ScionStack. It is the SCION equivalent of your operating system's networking stack: the object you open sockets on. If you have used the standard library's UDP sockets, the shape will feel familiar. The difference is that a ScionStack also knows how to discover the local SCION infrastructure, look up paths, carry your packets over a transport underlay, and authenticate you to the network, so your application does not have to.

This page explains what a stack is, how to create one, and where the other concepts (addressing, paths and policies, and transport underlays) attach to it.

The mental model

You build one ScionStack per process and keep it for the lifetime of your application, opening as many sockets on it as you need. The stack owns the machinery that is shared across all of those sockets:

The ScionStack
The ScionStack
Application
Application
Sockets
Sockets
UDP
(path-aware)
UDP...
UDP
(path-unaware)
UDP...
raw
raw
SCMP
SCMP
QUIC / HTTP-3
(via scion-quic)
QUIC / HTTP-3...
ScionStack
ScionStack
Addressing
Addressing
Path
management
Path...
Transport underlays
(UDP | SNAP)
Transport underlays...
Authorization
(SNAP token)
Authorization...
One stack per process. Sockets are what your app touches; the stack owns the rest.
One stack per process. Sockets are what your app touches; the stack owns the rest.
Text is not SVG - cannot display
  • Sockets are what your application code touches. You bind and connect them, then send/recv, just like ordinary UDP.
  • Addressing: the stack works in SCION addresses (ISD-AS,[host]:port), which name both the network and the endpoint. See Addressing.
  • Path management: the stack fetches and maintains the set of SCION paths to the destinations you talk to, and picks one for each packet unless you choose yourself. See Paths and path policies.
  • Transport underlay: how the packets physically reach the SCION network, either directly over UDP/IP or through an authenticated tunnel to a SNAP. See Transport underlays.

Your application interacts with the top of this picture (sockets). The stack handles the rest.

Building a stack

You configure a stack with ScionStackBuilder and finish with build. At minimum the stack needs to know where the local endhost API is, the service that answers underlay and path-lookup queries, and, when it will use a SNAP, how to authenticate. The examples wrap this in a one-line helper:

examples/common/mod.rs
pub async fn build_stack(ps: &PsSetup, isd_as: IsdAsn) -> anyhow::Result<ScionStack> {
let endhost_api = ps
.endhost_api(isd_as)
.with_context(|| format!("PocketSCION has no endhost API for {isd_as}"))?;

ScionStackBuilder::new()
.with_endhost_api(endhost_api)
.with_auth_token(dev_auth_token())
.build()
.await
.with_context(|| format!("building SCION stack for {isd_as}"))
}

The builder is where the cross-cutting choices live: which underlay to prefer (with_preferred_underlay), how to reach the endhost API (with_endhost_api), and how to obtain a SNAP token (with_auth_token). Those knobs are covered on the transport underlays page. For the full set, see the ScionStackBuilder reference.

Lifecycle

build is async, and a built stack runs background tasks: discovering the underlay, keeping the path set fresh, and refreshing authentication tokens. Two consequences follow:

  • The SDK runs on the Tokio async runtime. Construct and use the stack from within an async context.
  • Keep the stack alive for as long as you need SCION connectivity. Dropping it tears down those background tasks and its sockets stop working, so store it somewhere durable rather than letting it fall out of scope.

The socket surfaces

All sockets are opened on the stack. The one you will use most is the path-aware UDP socket. The others exist for narrower needs.

MethodSocketUse it for
bindUdpScionSocketpath-aware UDP, the default; the stack picks paths for you
connectUdpScionSocketa bind fixed to one remote address
bind_path_unawarePathUnawareUdpScionSocketUDP where you supply the path on every send
bind_rawRawScionSocketsending and receiving raw SCION packets
bind_scmpScmpScionSocketSCMP, SCION's control and diagnostic messages (think ICMP)

Binding and using a socket looks exactly like the standard library. Here are the server and client from the udp_echo example:

examples/udp_echo.rs
// Bring up the echo server in AS 2-ff00:0:212 and let it run in the background.
let server_stack = common::build_stack(&ps, IA212).await?;
let server_socket = server_stack.bind(None).await?;
let server_addr = server_socket.local_addr();
println!("echo server listening on {server_addr}");
let server = tokio::spawn(echo_server(server_socket));
examples/udp_echo.rs
// Bring up the client in AS 1-ff00:0:132 and send one datagram.
let client_stack = common::build_stack(&ps, IA132).await?;
let client_socket = client_stack.bind(None).await?;

let reply = ping(&client_socket, server_addr, b"Hello, SCION!").await?;
println!("client received echo: {}", String::from_utf8_lossy(&reply));

recv_from yields the peer's SCION address and the socket remembers a return path to it automatically, so send_to can reply without your code ever touching path selection. Path awareness is there when you want it (see Paths and path policies) and out of the way when you do not.

Where to go next