snap_dataplane/tunnel_gateway.rs
1// Copyright 2026 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//! Tunnel gateway
15
16use std::{sync::Arc, time::Instant};
17
18use scion_sdk_utils::task_handler::CancelTaskSet;
19use sciparse::identifier::isd_asn::IsdAsn;
20use snap_tun::server::SnapTunAuthorization;
21use tokio::net::UdpSocket;
22
23use crate::{
24 dispatcher::Dispatcher,
25 tunnel_gateway::{dispatcher::TunnelGatewayDispatcherReceiver, gateway::TunnelGateway},
26};
27
28pub mod dispatcher;
29pub mod gateway;
30pub mod metrics;
31pub(crate) mod packet_policy;
32pub mod state;
33
34/// The direction in which the observed packet crossed the SNAP tunnel.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum ObservedPacketDirection {
37 /// The packet arrived from the client over the SNAP tunnel and is headed
38 /// toward the SCION router.
39 Ingress,
40 /// The packet arrived from the SCION side and was encapsulated toward the
41 /// client over the SNAP tunnel.
42 Egress,
43}
44
45/// Packet metadata captured at the point where a packet has successfully
46/// crossed the SNAP tunnel boundary and can be accounted.
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct ObservedPacketMeta {
49 /// Source ISD-AS from the parsed SCION packet.
50 pub src_ia: IsdAsn,
51 /// Destination ISD-AS from the parsed SCION packet.
52 pub dst_ia: IsdAsn,
53 /// Total packet length in bytes used for accounting.
54 pub packet_len: usize,
55 /// Direction in which the packet crossed the tunnel.
56 pub direction: ObservedPacketDirection,
57}
58
59/// Observes successfully tunneled packets together with the session data that produced them.
60pub trait TunnelGatewayObserver<S>: Send + Sync {
61 /// Called once a packet has successfully crossed the SNAP tunnel boundary
62 /// and the gateway has recovered the relevant accounting metadata.
63 ///
64 /// `now` is the timestamp captured while processing the relevant packet path.
65 fn observe_packet(&self, now: Instant, session_data: &S, packet: ObservedPacketMeta);
66}
67
68/// A tunnel-gateway observer that ignores all observed packets.
69#[derive(Debug, Default)]
70pub struct NoopTunnelGatewayObserver;
71
72impl<S> TunnelGatewayObserver<S> for NoopTunnelGatewayObserver {
73 fn observe_packet(&self, _now: Instant, _session_data: &S, _packet: ObservedPacketMeta) {}
74}
75
76/// Start the tunnel gateway.
77///
78/// # Arguments
79/// * `tasks`: The task set used to launch the asynchronous tasks.
80/// * `socket`: The UDP socket that terminates SNAP tunnels.
81/// * `authz`: The authorization layer for the snaptun.
82/// * `dispatcher`: Receives validated SCION packets for forwarding.
83/// * `observer`: Receives observed packet metadata together with the resolved current session data.
84/// Used for flow accounting and metrics.
85/// * `tun_dispatcher_rx`: The receiving end of the dispatcher interface.
86/// * `server_static_secret`: The static secret of the tunnel gateway's tunnel endpoint.
87pub fn start_tunnel_gateway<A, D, O>(
88 tasks: &mut CancelTaskSet,
89 socket: UdpSocket,
90 authz: Arc<A>,
91 dispatcher: Arc<D>,
92 observer: Arc<O>,
93 tun_dispatcher_rx: TunnelGatewayDispatcherReceiver,
94 server_static_secret: x25519_dalek::StaticSecret,
95) where
96 A: SnapTunAuthorization + 'static,
97 D: Dispatcher + 'static,
98 O: TunnelGatewayObserver<A::SessionData> + ?Sized + 'static,
99{
100 let tun_gateway = TunnelGateway::new(
101 socket,
102 server_static_secret,
103 authz,
104 dispatcher,
105 observer,
106 tun_dispatcher_rx,
107 );
108 let token = tasks.cancellation_token();
109 tasks.spawn_cancellable_task(async move {
110 tun_gateway.start_server(token).await;
111 Ok(())
112 });
113}