1use std::{
16 io,
17 net::{self},
18 sync::Arc,
19 task::{Poll, ready},
20};
21
22use anyhow::Context;
23use bytes::BytesMut;
24use futures::future::BoxFuture;
25use scion_proto::{
26 address::{IsdAsn, SocketAddr},
27 packet::{
28 ByEndpoint, PacketClassification, ScionPacketRaw, ScionPacketUdp, classify_scion_packet,
29 },
30 path::{DataPlanePath, Path, PathInterface},
31 scmp::SCMP_PROTOCOL_NUMBER,
32 wire_encoding::{WireDecode as _, WireEncodeVec as _},
33};
34use tokio::{io::ReadBuf, net::UdpSocket};
35
36use crate::{
37 scionstack::{
38 AsyncUdpUnderlaySocket, ScionSocketSendError, UnderlaySocket, scmp_handler::ScmpHandler,
39 udp_polling::UdpPollHelper,
40 },
41 underlays::{discovery::UnderlayDiscovery, source_ip_towards},
42};
43
44const UDP_DATAGRAM_BUFFER_SIZE: usize = 65535;
45
46#[async_trait::async_trait]
48pub trait LocalIpResolver: Send + Sync {
49 async fn local_ips(&self) -> Vec<net::IpAddr>;
51}
52
53#[async_trait::async_trait]
54impl LocalIpResolver for Vec<net::IpAddr> {
55 async fn local_ips(&self) -> Vec<net::IpAddr> {
56 self.clone()
57 }
58}
59
60pub(crate) struct TargetAddrLocalIpResolver {
63 api_socket_address: net::SocketAddr,
64}
65
66impl TargetAddrLocalIpResolver {
67 pub fn new(api_address: url::Url) -> anyhow::Result<Self> {
68 let socket_addr = api_address
69 .socket_addrs(|| None)
70 .context("invalid api address")?
71 .first()
72 .ok_or(anyhow::anyhow!("failed to resolve api socket address"))?
73 .to_owned();
74 Ok(Self {
75 api_socket_address: socket_addr,
76 })
77 }
78}
79
80#[async_trait::async_trait]
81impl LocalIpResolver for TargetAddrLocalIpResolver {
82 async fn local_ips(&self) -> Vec<net::IpAddr> {
85 match source_ip_towards(self.api_socket_address).await {
86 Some(ip) => vec![ip],
87 None => vec![],
88 }
89 }
90}
91
92pub struct UdpUnderlaySocket {
94 pub(crate) socket: UdpSocket,
95 pub(crate) bind_addr: SocketAddr,
96 pub(crate) underlay_discovery: Arc<dyn UnderlayDiscovery>,
97}
98
99impl UdpUnderlaySocket {
100 pub(crate) fn new(
101 socket: UdpSocket,
102 bind_addr: SocketAddr,
103 underlay_discovery: Arc<dyn UnderlayDiscovery>,
104 ) -> Self {
105 Self {
106 socket,
107 bind_addr,
108 underlay_discovery,
109 }
110 }
111
112 fn resolve_local_dispatch_addr(
114 &self,
115 packet: &ScionPacketRaw,
116 ) -> Result<net::SocketAddr, ScionSocketSendError> {
117 let dst_addr = packet
118 .headers
119 .address
120 .destination()
121 .ok_or(crate::scionstack::ScionSocketSendError::InvalidPacket(
122 "Packet to local endhost has no destination address".into(),
123 ))?
124 .local_address()
125 .ok_or(crate::scionstack::ScionSocketSendError::InvalidPacket(
126 "Cannot forward packet to local service address".into(),
127 ))?;
128 let classification = classify_scion_packet(packet.clone()).map_err(|e| {
129 crate::scionstack::ScionSocketSendError::InvalidPacket(
130 format!("Cannot classify packet to local endhost: {e:#}").into(),
131 )
132 })?;
133 let dst_port = match classification {
134 PacketClassification::Udp(udp_packet) => udp_packet.dst_port(),
135 PacketClassification::ScmpWithDestination(port, _) => port,
136 PacketClassification::ScmpWithoutDestination(_) | PacketClassification::Other(_) => {
137 return Err(crate::scionstack::ScionSocketSendError::InvalidPacket(
138 "Cannot deduce port for packet to local endhost".into(),
139 ));
140 }
141 };
142 Ok(net::SocketAddr::new(dst_addr, dst_port))
143 }
144
145 fn try_dispatch_local(&self, packet: ScionPacketRaw) -> Result<(), ScionSocketSendError> {
146 let dst_addr = self.resolve_local_dispatch_addr(&packet)?;
147 let packet_bytes = packet.encode_to_bytes_vec().concat();
148 self.socket
149 .try_send_to(&packet_bytes, dst_addr)
150 .map_err(|e| {
151 Self::map_send_io_error(e, packet.headers.address.ia.source, 0, dst_addr)
152 })?;
153 Ok(())
154 }
155
156 async fn dispatch_local(
158 &self,
159 packet: ScionPacketRaw,
160 ) -> Result<(), crate::scionstack::ScionSocketSendError> {
161 let dst_addr = self.resolve_local_dispatch_addr(&packet)?;
162 let packet_bytes = packet.encode_to_bytes_vec().concat();
163 self.socket
164 .send_to(&packet_bytes, dst_addr)
165 .await
166 .map_err(|e| {
167 Self::map_send_io_error(e, packet.headers.address.ia.source, 0, dst_addr)
168 })?;
169 Ok(())
170 }
171
172 fn map_send_io_error(
174 e: io::Error,
175 src: IsdAsn,
176 interface_id: u16,
177 next_hop: net::SocketAddr,
178 ) -> ScionSocketSendError {
179 use std::io::ErrorKind::*;
180 match e.kind() {
181 HostUnreachable | NetworkUnreachable => {
182 ScionSocketSendError::UnderlayNextHopUnreachable {
183 isd_as: src,
184 interface_id,
185 address: Some(next_hop),
186 msg: e.to_string(),
187 }
188 }
189 ConnectionAborted | ConnectionReset | BrokenPipe => ScionSocketSendError::Closed,
190 _ => ScionSocketSendError::IoError(e),
191 }
192 }
193}
194
195impl UnderlaySocket for UdpUnderlaySocket {
196 fn send<'a>(
197 &'a self,
198 packet: ScionPacketRaw,
199 ) -> BoxFuture<'a, Result<(), ScionSocketSendError>> {
200 let source_ia = packet.headers.address.ia.source;
201 if packet.headers.address.ia.destination == source_ia {
202 return Box::pin(async move {
203 self.dispatch_local(packet).await?;
204 Ok(())
205 });
206 }
207
208 let interface_id = if let DataPlanePath::Standard(standard_path) = &packet.headers.path
210 && let Some(interface_id) = standard_path.iter_interfaces().next()
211 {
212 interface_id
213 } else {
214 return Box::pin(async move {
215 Err(ScionSocketSendError::InvalidPacket(
216 "Path does not contain first hop.".into(),
217 ))
218 });
219 };
220
221 let next_hop = match self
222 .underlay_discovery
223 .resolve_udp_underlay_next_hop(PathInterface {
224 isd_asn: source_ia,
225 id: interface_id.get(),
226 })
227 .ok_or(ScionSocketSendError::UnderlayNextHopUnreachable {
228 isd_as: source_ia,
229 interface_id: interface_id.get(),
230 address: None,
231 msg: "next hop not found".to_string(),
232 }) {
233 Ok(next_hop) => next_hop,
234 Err(e) => {
235 return Box::pin(async move { Err(e) });
236 }
237 };
238
239 let packet_bytes = packet.encode_to_bytes_vec().concat();
240 Box::pin(async move {
241 self.socket
242 .send_to(&packet_bytes, next_hop)
243 .await
244 .map_err(|e| {
245 use std::io::ErrorKind::*;
246 match e.kind() {
247 HostUnreachable | NetworkUnreachable => {
248 ScionSocketSendError::UnderlayNextHopUnreachable {
249 isd_as: source_ia,
250 interface_id: interface_id.get(),
251 address: Some(next_hop),
252 msg: e.to_string(),
253 }
254 }
255 ConnectionAborted | ConnectionReset | BrokenPipe => {
256 ScionSocketSendError::Closed
257 }
258 _ => ScionSocketSendError::IoError(e),
259 }
260 })?;
261 Ok(())
262 })
263 }
264
265 fn try_send(&self, packet: ScionPacketRaw) -> Result<(), ScionSocketSendError> {
268 let source_ia = packet.headers.address.ia.source;
269 if packet.headers.address.ia.destination == source_ia {
270 return self.try_dispatch_local(packet);
271 }
272
273 let interface_id = if let DataPlanePath::Standard(standard_path) = &packet.headers.path
275 && let Some(interface_id) = standard_path.iter_interfaces().next()
276 {
277 interface_id
278 } else {
279 return Err(ScionSocketSendError::InvalidPacket(
280 "Path does not contain first hop.".into(),
281 ));
282 };
283
284 let next_hop = match self
285 .underlay_discovery
286 .resolve_udp_underlay_next_hop(PathInterface {
287 isd_asn: source_ia,
288 id: interface_id.get(),
289 })
290 .ok_or(ScionSocketSendError::UnderlayNextHopUnreachable {
291 isd_as: source_ia,
292 interface_id: interface_id.get(),
293 address: None,
294 msg: "next hop not found".to_string(),
295 }) {
296 Ok(next_hop) => next_hop,
297 Err(e) => {
298 return Err(e);
299 }
300 };
301
302 self.socket
303 .try_send_to(&packet.encode_to_bytes_vec().concat(), next_hop)
304 .map_err(|e| Self::map_send_io_error(e, source_ia, interface_id.get(), next_hop))?;
305 Ok(())
306 }
307
308 fn recv<'a>(
309 &'a self,
310 ) -> BoxFuture<'a, Result<ScionPacketRaw, crate::scionstack::ScionSocketReceiveError>> {
311 Box::pin(async move {
312 let mut buf = [0u8; UDP_DATAGRAM_BUFFER_SIZE];
313 loop {
314 let (n, _) = self.socket.recv_from(&mut buf).await?;
315 let packet = match ScionPacketRaw::decode(&mut BytesMut::from(&buf[..n])) {
316 Ok(packet) => packet,
317 Err(e) => {
318 tracing::error!(error = %e, "Failed to decode SCION packet");
319 continue;
320 }
321 };
322
323 let dst = packet.headers.address.destination();
325 if let Some(dst) = dst
326 && dst != self.bind_addr.scion_address()
327 {
328 tracing::debug!(destination = ?dst, assigned_addr = %self.bind_addr.scion_address(), "Packet destination does not match assigned address, skipping");
329 continue;
330 }
331 return Ok(packet);
332 }
333 })
334 }
335
336 fn local_addr(&self) -> scion_proto::address::SocketAddr {
337 self.bind_addr
338 }
339
340 fn snap_data_plane(&self) -> Option<net::SocketAddr> {
341 None
342 }
343}
344
345pub struct UdpAsyncUdpUnderlaySocket {
347 local_addr: SocketAddr,
348 discovery: Arc<dyn UnderlayDiscovery>,
349 inner: UdpSocket,
350 scmp_handlers: Vec<Box<dyn ScmpHandler>>,
351}
352
353impl UdpAsyncUdpUnderlaySocket {
354 pub(crate) fn new(
355 local_addr: SocketAddr,
356 discovery: Arc<dyn UnderlayDiscovery>,
357 inner: UdpSocket,
358 scmp_handlers: Vec<Box<dyn ScmpHandler>>,
359 ) -> Self {
360 Self {
361 local_addr,
362 discovery,
363 inner,
364 scmp_handlers,
365 }
366 }
367
368 fn try_dispatch_local(&self, packet: ScionPacketRaw) -> io::Result<()> {
370 let dst_addr = packet
371 .headers
372 .address
373 .destination()
374 .ok_or(io::Error::new(
375 io::ErrorKind::InvalidInput,
376 "Packet to local endhost has no destination address".to_string(),
377 ))?
378 .local_address()
379 .ok_or(io::Error::new(
380 io::ErrorKind::InvalidInput,
381 "Cannot forward packet with service address".to_string(),
382 ))?;
383 let classification = classify_scion_packet(packet.clone()).map_err(|e| {
384 io::Error::new(
385 io::ErrorKind::InvalidInput,
386 format!("Cannot classify packet to local endhost: {e:#}"),
387 )
388 })?;
389 let dst_port = match classification {
390 PacketClassification::Udp(udp_packet) => udp_packet.dst_port(),
391 PacketClassification::ScmpWithDestination(port, _) => port,
392 PacketClassification::ScmpWithoutDestination(_) | PacketClassification::Other(_) => {
393 return Err(io::Error::new(
394 io::ErrorKind::InvalidInput,
395 "Cannot deduce port for packet to local endhost",
396 ));
397 }
398 };
399 let packet_bytes = packet.encode_to_bytes_vec().concat();
400 let dst_addr = net::SocketAddr::new(dst_addr, dst_port);
401 self.inner.try_send_to(&packet_bytes, dst_addr)?;
402 Ok(())
403 }
404}
405
406impl AsyncUdpUnderlaySocket for UdpAsyncUdpUnderlaySocket {
407 fn create_io_poller(
408 self: Arc<Self>,
409 ) -> std::pin::Pin<Box<dyn crate::scionstack::udp_polling::UdpPoller>> {
410 Box::pin(UdpPollHelper::new(move || {
411 let self_clone = self.clone();
412 async move { self_clone.inner.writable().await }
413 }))
414 }
415
416 fn try_send(&self, packet: ScionPacketRaw) -> Result<(), std::io::Error> {
417 let source_ia = packet.headers.address.ia.source;
418 if packet.headers.address.ia.destination == source_ia {
419 return self.try_dispatch_local(packet);
420 }
421
422 let interface_id = if let DataPlanePath::Standard(standard_path) = &packet.headers.path
424 && let Some(interface_id) = standard_path.iter_interfaces().next()
425 {
426 interface_id
427 } else {
428 return Err(std::io::Error::new(
429 std::io::ErrorKind::InvalidInput,
430 "Path does not contain first hop.".to_string(),
431 ));
432 };
433
434 let next_hop = self
435 .discovery
436 .resolve_udp_underlay_next_hop(PathInterface {
437 isd_asn: source_ia,
438 id: interface_id.get(),
439 })
440 .ok_or(std::io::Error::new(
441 std::io::ErrorKind::InvalidInput,
442 "could not resolve next hop",
443 ))?;
444
445 let packet_bytes = packet.encode_to_bytes_vec().concat();
446 match self.inner.try_send_to(&packet_bytes, next_hop) {
449 Ok(_) => Ok(()),
450 Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
451 Err(e) => {
452 tracing::warn!(err = ?e, "Error sending packet");
453 Ok(())
454 }
455 }?;
456 Ok(())
457 }
458
459 fn poll_recv_from_with_path(
460 &self,
461 cx: &mut std::task::Context,
462 ) -> Poll<std::io::Result<(SocketAddr, bytes::Bytes, scion_proto::path::Path)>> {
463 loop {
464 let mut raw_buf = [0u8; UDP_DATAGRAM_BUFFER_SIZE];
465 let mut buf = ReadBuf::new(&mut raw_buf);
466 let _ = ready!(self.inner.poll_recv_from(cx, &mut buf))?;
467
468 let packet = match ScionPacketRaw::decode(&mut BytesMut::from(buf.initialized())) {
469 Ok(packet) => packet,
470 Err(e) => {
471 tracing::trace!(error = %e, "Received non SCION packet, dropping");
472 continue;
473 }
474 };
475 if packet.headers.common.next_header == SCMP_PROTOCOL_NUMBER {
477 tracing::debug!("SCMP packet received, forwarding to SCMP handlers");
478 for handler in &self.scmp_handlers {
479 if let Some(reply) = handler.handle(packet.clone())
480 && let Err(e) = self.try_send(reply)
481 {
482 tracing::warn!(error = %e, "failed to send SCMP reply");
483 }
484 }
485 continue;
486 };
487
488 let fallible = || {
489 let src = packet
490 .headers
491 .address
492 .source()
493 .context("reading source address")?;
494 let dst = packet
495 .headers
496 .address
497 .destination()
498 .context("reading destination address")?;
499
500 if dst != self.local_addr.scion_address() {
502 anyhow::bail!(
503 "Packet destination does not match assigned address, skipping (dst: {}, assigned: {})",
504 dst,
505 self.local_addr.scion_address()
506 );
507 }
508
509 let path = Path::new(
510 packet.headers.path.clone(),
511 ByEndpoint {
512 source: src.isd_asn(),
513 destination: dst.isd_asn(),
514 },
515 None,
516 );
517
518 let packet: ScionPacketUdp = packet.try_into().context("parsing UDP packet")?;
519
520 anyhow::Ok((
521 SocketAddr::new(src, packet.src_port()),
522 packet.datagram.payload,
523 path,
524 ))
525 };
526
527 match fallible() {
528 Ok(result) => return Poll::Ready(Ok(result)),
529 Err(e) => {
530 tracing::warn!(error = %e, "Received invalid packet, skipping");
531 continue;
532 }
533 }
534 }
535 }
536
537 fn local_addr(&self) -> SocketAddr {
538 self.local_addr
539 }
540
541 fn snap_data_plane(&self) -> Option<net::SocketAddr> {
542 None
543 }
544}