scion_stack/scionstack/
scmp_handler.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//! SCION stack SCMP handler.
15
16use std::{pin::Pin, sync::Arc};
17
18use scion_proto::{
19    packet::{ByEndpoint, ScionPacketScmp},
20    scmp::{ScmpEchoReply, ScmpMessage},
21    wire_encoding::WireEncodeVec as _,
22};
23
24use crate::snap_tunnel::SnapTunnel;
25
26/// A trait for handling SCMP packets when they are received.
27/// It will be called with all SCMP packets that are received irrespective
28/// whether a destination port is specified or not.
29pub trait ScmpHandler: Send + Sync {
30    /// Handle a received SCMP packet.
31    fn handle_packet(
32        &self,
33        packet: ScionPacketScmp,
34    ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
35}
36
37/// Default SCMP handller.
38pub struct DefaultScmpHandler {
39    tunnel_sender: Arc<SnapTunnel>,
40}
41
42impl Clone for DefaultScmpHandler {
43    fn clone(&self) -> Self {
44        Self {
45            tunnel_sender: self.tunnel_sender.clone(),
46        }
47    }
48}
49
50impl DefaultScmpHandler {
51    /// Creates a new default SCMP handler.
52    pub fn new(tunnel_sender: Arc<SnapTunnel>) -> Self {
53        Self { tunnel_sender }
54    }
55}
56
57impl ScmpHandler for DefaultScmpHandler {
58    fn handle_packet(&self, p: ScionPacketScmp) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
59        Box::pin(async move {
60            let reply = match p.message {
61                ScmpMessage::EchoRequest(r) => {
62                    tracing::debug!("Echo request received, sending echo reply");
63                    ScmpMessage::EchoReply(ScmpEchoReply::new(
64                        r.identifier,
65                        r.sequence_number,
66                        r.data,
67                    ))
68                }
69                _ => return,
70            };
71
72            let reply_path = match p.headers.reversed_path(None) {
73                Ok(path) => path.data_plane_path,
74                Err(e) => {
75                    tracing::debug!(error = %e, "Error reversing path of SCMP packet");
76                    return;
77                }
78            };
79
80            let src = match p.headers.address.source() {
81                Some(src) => src,
82                None => {
83                    tracing::debug!("Error decoding source address of SCMP packet");
84                    return;
85                }
86            };
87            let dst = match p.headers.address.destination() {
88                Some(dst) => dst,
89                None => {
90                    tracing::debug!("Error decoding destination address of SCMP packet");
91                    return;
92                }
93            };
94
95            let reply_packet = match ScionPacketScmp::new(
96                ByEndpoint {
97                    source: dst,
98                    destination: src,
99                },
100                reply_path,
101                reply,
102            ) {
103                Ok(packet) => packet,
104                Err(e) => {
105                    tracing::debug!(error = %e, "Error encoding SCMP reply");
106                    return;
107                }
108            };
109            match self
110                .tunnel_sender
111                .send_datagram(reply_packet.encode_to_bytes_vec().concat().into())
112            {
113                Ok(_) => {}
114                Err(e) => {
115                    tracing::warn!(error = %e, "Error sending SCMP reply");
116                }
117            }
118        })
119    }
120}