1use std::{net, sync::Arc};
17
18use ana_gotatun::packet::{Packet, PacketBufPool};
19use scion_proto::{
20 address::{Isd, IsdAsn, ScionAddr, SocketAddr},
21 wire_encoding::WireEncodeVec,
22};
23use scion_sdk_reqwest_connect_rpc::token_source::TokenSource;
24use snap_tun::client::{PACKET_BUF_POOL_SIZE, SnapTunEndpoint};
25use socket2::{Domain, Protocol, Socket, Type};
26use tokio::net::UdpSocket;
27use url::Url;
28use x25519_dalek::StaticSecret;
29
30use crate::{
31 scionstack::{
32 AsyncUdpUnderlaySocket, DynUnderlayStack, InvalidBindAddressError, ScionSocketBindError,
33 SnapConnectionError, UnderlaySocket, builder::PreferredUnderlay, scmp_handler::ScmpHandler,
34 },
35 underlays::{
36 discovery::{UnderlayDiscovery, UnderlayInfo},
37 udp::{LocalIpResolver, UdpAsyncUdpUnderlaySocket, UdpUnderlaySocket},
38 },
39};
40
41pub mod discovery;
42pub mod snap;
43pub mod udp;
44
45pub struct SnapSocketConfig {
47 pub snap_token_source: Option<Arc<dyn TokenSource>>,
50}
51
52pub struct UnderlayStack {
54 preferred_underlay: PreferredUnderlay,
55 underlay_discovery: Arc<dyn UnderlayDiscovery>,
56 local_ip_resolver: Arc<dyn LocalIpResolver>,
58 snap_socket_config: SnapSocketConfig,
59 snap_tunnel_manager: Option<SnapTunEndpoint>,
60 pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
61}
62
63impl UnderlayStack {
64 pub fn new(
66 preferred_underlay: PreferredUnderlay,
67 underlay_discovery: Arc<dyn UnderlayDiscovery>,
68 local_ip_resolver: Arc<dyn LocalIpResolver>,
69 static_identity: StaticSecret,
70 default_snap_socket_config: SnapSocketConfig,
71 ) -> Self {
72 let snap_tunnel_manager = default_snap_socket_config
73 .snap_token_source
74 .as_ref()
75 .map(|token_source| SnapTunEndpoint::new(token_source.clone(), static_identity));
76 Self {
77 preferred_underlay,
78 underlay_discovery,
79 local_ip_resolver,
80 snap_socket_config: default_snap_socket_config,
81 snap_tunnel_manager,
82 pool: PacketBufPool::new(64),
83 }
84 }
85
86 fn select_underlay(&self, requested_isd_as: IsdAsn) -> Option<(IsdAsn, UnderlayInfo)> {
93 let underlays = self.underlay_discovery.underlays(requested_isd_as);
94 match self.preferred_underlay {
95 PreferredUnderlay::Snap => {
96 if let Some(underlay) = underlays
97 .iter()
98 .find(|(_, underlay)| matches!(underlay, UnderlayInfo::Snap(_)))
99 {
100 return Some(underlay.clone());
101 }
102 }
103 PreferredUnderlay::Udp => {
104 if let Some(underlay) = underlays
105 .iter()
106 .find(|(_, underlay)| matches!(underlay, UnderlayInfo::Udp(_)))
107 {
108 return Some(underlay.clone());
109 }
110 }
111 }
112 underlays.into_iter().next()
113 }
114
115 async fn bind_snap_socket(
116 &self,
117 requested_addr: Option<scion_proto::address::SocketAddr>,
118 isd_as: IsdAsn,
119 cp_url: Url,
120 ) -> Result<snap::SnapUnderlaySocket, ScionSocketBindError> {
121 let (Some(token_source), Some(snap_tunnel_manager)) = (
122 self.snap_socket_config.snap_token_source.as_ref(),
123 self.snap_tunnel_manager.as_ref(),
124 ) else {
125 return Err(ScionSocketBindError::SnapConnectionError(
126 SnapConnectionError::SnapTokenSourceMissing,
127 ))?;
128 };
129
130 let local_addr = match requested_addr {
131 Some(addr) => {
132 addr.local_address()
133 .ok_or(ScionSocketBindError::InvalidBindAddress(
134 InvalidBindAddressError::ServiceAddress(addr),
135 ))?
136 }
137 None => {
138 if let Some(cp_addr) = cp_url
139 .socket_addrs(|| None)
140 .ok()
141 .and_then(|addrs| addrs.first().cloned())
142 && let Some(ip) = source_ip_towards(cp_addr).await
143 {
144 Ok(net::SocketAddr::new(ip, 0))
145 } else {
146 Err(ScionSocketBindError::InvalidBindAddress(
147 InvalidBindAddressError::NoLocalIpAddressFound,
148 ))
149 }?
150 }
151 };
152
153 let bind_addr = SocketAddr::from_std(isd_as, local_addr);
154
155 let udp_socket = bind_udp_underlay_socket(local_addr)?;
156
157 let socket = snap::SnapUnderlaySocket::new(
158 bind_addr,
159 cp_url,
160 udp_socket,
161 snap_tunnel_manager,
162 token_source.clone(),
163 1024,
164 self.pool.clone(),
165 )
166 .await?;
167
168 let assigned_addr = socket.local_addr();
169 if let Some(requested_addr) = requested_addr
172 && requested_addr.isd_asn().matches(assigned_addr.isd_asn())
174 && let Some(requested_socket_addr) = requested_addr.local_address()
176 && let Some(assigned_socket_addr) = assigned_addr.local_address()
177 && ((!requested_socket_addr.ip().is_unspecified() && assigned_socket_addr.ip() != requested_socket_addr.ip())
178 || (requested_socket_addr.port() != 0 && assigned_socket_addr.port() != requested_socket_addr.port()))
180 {
181 return Err(crate::scionstack::ScionSocketBindError::InvalidBindAddress(
182 crate::scionstack::InvalidBindAddressError::AddressMismatch {
183 assigned_addr: SocketAddr::from_std(bind_addr.isd_asn(), requested_socket_addr),
184 bind_addr,
185 },
186 ));
187 }
188
189 Ok(socket)
190 }
191
192 async fn resolve_udp_bind_addr(
193 &self,
194 isd_as: IsdAsn,
195 bind_addr: Option<SocketAddr>,
196 ) -> Result<SocketAddr, ScionSocketBindError> {
197 let bind_addr = match bind_addr {
198 Some(addr) => {
199 if addr.is_service() {
200 return Err(ScionSocketBindError::InvalidBindAddress(
201 InvalidBindAddressError::ServiceAddress(addr),
202 ));
203 }
204 addr
205 }
206 None => {
207 let local_address = *self.local_ip_resolver.local_ips().await.first().ok_or(
208 ScionSocketBindError::InvalidBindAddress(
209 InvalidBindAddressError::NoLocalIpAddressFound,
210 ),
211 )?;
212 SocketAddr::new(ScionAddr::new(isd_as, local_address.into()), 0)
213 }
214 };
215 Ok(bind_addr)
216 }
217
218 async fn bind_udp_socket(
219 &self,
220 isd_as: IsdAsn,
221 bind_addr: Option<SocketAddr>,
222 ) -> Result<(SocketAddr, UdpSocket), ScionSocketBindError> {
223 let bind_addr = self.resolve_udp_bind_addr(isd_as, bind_addr).await?;
224 let local_addr: net::SocketAddr =
225 bind_addr
226 .local_address()
227 .ok_or(ScionSocketBindError::InvalidBindAddress(
228 InvalidBindAddressError::ServiceAddress(bind_addr),
229 ))?;
230 let socket = bind_udp_underlay_socket(local_addr)?;
231 let local_addr = socket.local_addr().map_err(|e| {
232 ScionSocketBindError::Other(
233 anyhow::anyhow!("failed to get local address: {e}").into_boxed_dyn_error(),
234 )
235 })?;
236 let bind_addr = SocketAddr::new(
237 ScionAddr::new(bind_addr.isd_asn(), local_addr.ip().into()),
238 local_addr.port(),
239 );
240 Ok((bind_addr, socket))
241 }
242}
243
244impl DynUnderlayStack for UnderlayStack {
245 fn bind_socket(
246 &self,
247 _kind: crate::scionstack::SocketKind,
248 bind_addr: Option<scion_proto::address::SocketAddr>,
249 ) -> futures::future::BoxFuture<
250 '_,
251 Result<Box<dyn crate::scionstack::UnderlaySocket>, crate::scionstack::ScionSocketBindError>,
252 > {
253 Box::pin(async move {
254 let requested_isd_as = bind_addr
255 .map(|addr| addr.isd_asn())
256 .unwrap_or(IsdAsn::WILDCARD);
257 match self.select_underlay(requested_isd_as) {
258 Some((isd_as, UnderlayInfo::Snap(cp_url))) => {
259 Ok(
260 Box::new(self.bind_snap_socket(bind_addr, isd_as, cp_url).await?)
261 as Box<dyn UnderlaySocket>,
262 )
263 }
264 Some((isd_as, UnderlayInfo::Udp(_))) => {
265 let (bind_addr, socket) = self.bind_udp_socket(isd_as, bind_addr).await?;
266 Ok(Box::new(UdpUnderlaySocket::new(
267 socket,
268 bind_addr,
269 self.underlay_discovery.clone(),
270 )) as Box<dyn UnderlaySocket>)
271 }
272 None => {
273 Err(
274 crate::scionstack::ScionSocketBindError::NoUnderlayAvailable(
275 requested_isd_as.isd(),
276 ),
277 )
278 }
279 }
280 })
281 }
282
283 fn bind_async_udp_socket(
284 &self,
285 bind_addr: Option<scion_proto::address::SocketAddr>,
286 scmp_handlers: Vec<Box<dyn ScmpHandler>>,
287 ) -> futures::future::BoxFuture<
288 '_,
289 Result<
290 std::sync::Arc<dyn crate::scionstack::AsyncUdpUnderlaySocket>,
291 crate::scionstack::ScionSocketBindError,
292 >,
293 > {
294 Box::pin(async move {
295 match self.select_underlay(
296 bind_addr
297 .map(|addr| addr.isd_asn())
298 .unwrap_or(IsdAsn::WILDCARD),
299 ) {
300 Some((isd_as, UnderlayInfo::Snap(cp_url))) => {
301 let socket = self.bind_snap_socket(bind_addr, isd_as, cp_url).await?;
302 let async_udp_socket = snap::SnapAsyncUdpSocket::new(socket, scmp_handlers);
303 Ok(Arc::new(async_udp_socket) as Arc<dyn AsyncUdpUnderlaySocket + 'static>)
304 }
305 Some((isd_as, UnderlayInfo::Udp(_))) => {
306 let (bind_addr, socket) = self.bind_udp_socket(isd_as, bind_addr).await?;
307 let async_udp_socket = UdpAsyncUdpUnderlaySocket::new(
308 bind_addr,
309 self.underlay_discovery.clone(),
310 socket,
311 scmp_handlers,
312 );
313 Ok(Arc::new(async_udp_socket) as Arc<dyn AsyncUdpUnderlaySocket + 'static>)
314 }
315 None => {
316 Err(
317 crate::scionstack::ScionSocketBindError::NoUnderlayAvailable(
318 bind_addr
319 .map(|addr| addr.isd_asn().isd())
320 .unwrap_or(Isd::WILDCARD),
321 ),
322 )
323 }
324 }
325 })
326 }
327
328 fn local_ases(&self) -> Vec<IsdAsn> {
329 let mut isd_ases: Vec<IsdAsn> = self.underlay_discovery.isd_ases().into_iter().collect();
330 isd_ases.sort();
331 isd_ases
332 }
333}
334
335#[cfg(windows)]
336fn set_exclusive_addr_use(sock: &Socket, enable: bool) -> std::io::Result<()> {
337 use std::{mem, os::windows::io::AsRawSocket};
338
339 use windows_sys::Win32::Networking::WinSock;
340
341 let val: u32 = if enable { 1 } else { 0 };
343
344 let rc = unsafe {
345 WinSock::setsockopt(
346 sock.as_raw_socket() as usize,
347 WinSock::SOL_SOCKET,
348 WinSock::SO_EXCLUSIVEADDRUSE,
349 &val as *const _ as *const _,
350 mem::size_of_val(&val) as _,
351 )
352 };
353
354 if rc == 0 {
355 Ok(())
356 } else {
357 Err(std::io::Error::last_os_error())
358 }
359}
360
361fn bind_udp_underlay_socket(
366 addr: net::SocketAddr,
367) -> Result<tokio::net::UdpSocket, ScionSocketBindError> {
368 let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
369 .map_err(|e| ScionSocketBindError::Other(Box::new(e)))?;
370 socket
371 .set_nonblocking(true)
372 .map_err(|e| ScionSocketBindError::Other(Box::new(e)))?;
373 if addr.is_ipv6()
374 && let Err(e) = socket.set_only_v6(false)
375 {
376 tracing::debug!(%e, "unable to make socket dual-stack");
377 }
378
379 #[cfg(windows)]
382 set_exclusive_addr_use(&socket, true).map_err(|e| ScionSocketBindError::Other(Box::new(e)))?;
383
384 socket.bind(&addr.into()).map_err(|e| {
385 match e.kind() {
386 std::io::ErrorKind::AddrInUse => ScionSocketBindError::PortAlreadyInUse(addr.port()),
387 std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::InvalidInput => {
388 ScionSocketBindError::InvalidBindAddress(
389 InvalidBindAddressError::CannotBindToRequestedAddress(
390 SocketAddr::from_std(IsdAsn::WILDCARD, addr),
391 format!("Failed to bind socket: {e:#}").into(),
392 ),
393 )
394 }
395 #[cfg(windows)]
396 std::io::ErrorKind::PermissionDenied => {
400 ScionSocketBindError::PortAlreadyInUse(addr.port())
401 }
402 _ => ScionSocketBindError::Other(Box::new(e)),
403 }
404 })?;
405
406 tokio::net::UdpSocket::from_std(std::net::UdpSocket::from(socket))
407 .map_err(|e| ScionSocketBindError::Other(Box::new(e)))
408}
409
410#[inline]
418pub(crate) fn wire_encode<W, const N: usize>(
419 packet: &W,
420 temp_buf: &mut Packet,
421 target_buf: &mut Packet,
422) -> Result<(), W::Error>
423where
424 W: WireEncodeVec<N>,
425{
426 temp_buf.truncate(0);
427 let parts = packet.encode_with(temp_buf.buf_mut())?;
428
429 let mut n = 0;
430 parts.iter().for_each(|x| {
431 target_buf.as_mut()[n..(n + x.len())].copy_from_slice(x);
432 n += x.len();
433 });
434 target_buf.truncate(n);
435 Ok(())
436}
437
438pub(crate) async fn source_ip_towards(dst: net::SocketAddr) -> Option<net::IpAddr> {
440 let bind_addr = match dst.ip() {
441 net::IpAddr::V4(_) => net::Ipv4Addr::UNSPECIFIED.into(),
442 net::IpAddr::V6(_) => net::Ipv6Addr::UNSPECIFIED.into(),
443 };
444 if let Ok(socket) = tokio::net::UdpSocket::bind(net::SocketAddr::new(bind_addr, 0)).await
445 && socket.connect(dst).await.is_ok()
446 && let Ok(addr) = socket.local_addr()
447 {
448 return Some(addr.ip());
449 }
450 None
451}