scion_stack/scionstack/scmp_handler/
error.rs1use scion_proto::{
17 packet::{ScionPacketRaw, ScionPacketScmp},
18 scmp::ScmpErrorMessage,
19};
20
21use super::ScmpHandler;
22use crate::{scionstack::scmp_handler::ScmpErrorReceiver, types::Subscribers};
23
24pub struct ScmpErrorHandler {
26 receivers: Subscribers<dyn ScmpErrorReceiver>,
27}
28
29impl ScmpErrorHandler {
30 pub fn new(receivers: Subscribers<dyn ScmpErrorReceiver>) -> Self {
32 Self { receivers }
33 }
34}
35
36impl ScmpHandler for ScmpErrorHandler {
37 fn handle(&self, pkt: ScionPacketRaw) -> Option<ScionPacketRaw> {
38 let path = pkt.headers.path();
39 let scmp_pkg: ScionPacketScmp = if let Ok(scmp_pkg) = pkt.try_into() {
40 scmp_pkg
41 } else {
42 return None;
43 };
44
45 let scmp_error: ScmpErrorMessage = match scmp_pkg.message.try_into() {
46 Ok(scmp_error) => scmp_error,
47 Err(_) => {
48 tracing::debug!("ignoring non error SCMP message");
49 return None;
50 }
51 };
52
53 tracing::debug!(err = ?scmp_error, "reporting SCMP error");
54 self.receivers.for_each(|receiver| {
55 receiver.report_scmp_error(scmp_error.clone(), &path);
56 });
57 None
58 }
59}
60
61#[cfg(test)]
62mod scmp_error_handler_tests {
63 use std::sync::Arc;
64
65 use bytes::Bytes;
66 use scion_proto::{
67 address::{Asn, EndhostAddr, Isd, IsdAsn},
68 path::{
69 Path,
70 test_builder::{TestPathBuilder, TestPathContext},
71 },
72 scmp::{
73 DestinationUnreachableCode, ScmpDestinationUnreachable, ScmpEchoReply, ScmpEchoRequest,
74 ScmpMessage,
75 },
76 };
77
78 use super::*;
79
80 fn test_context() -> TestPathContext {
81 let src = EndhostAddr::new(IsdAsn::new(Isd(1), Asn(10)), [192, 0, 2, 1].into());
82 let dst = EndhostAddr::new(IsdAsn::new(Isd(1), Asn(20)), [198, 51, 100, 1].into());
83 TestPathBuilder::new(src, dst)
84 .using_info_timestamp(42)
85 .up()
86 .add_hop(0, 11)
87 .add_hop(12, 0)
88 .build(77)
89 }
90
91 #[test]
92 fn forwards_scmp_error_messages_to_receivers() {
93 let ctx = test_context();
94 let scmp_msg = ScmpMessage::DestinationUnreachable(ScmpDestinationUnreachable::new(
95 DestinationUnreachableCode::AddressUnreachable,
96 Bytes::from_static(b"offending packet"),
97 ));
98 let packet = ctx.scion_packet_scmp(scmp_msg);
99 let expected_path = packet.headers.path();
100
101 let mut mock_receiver = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
102 mock_receiver
103 .expect_report_scmp_error()
104 .withf(move |error: &ScmpErrorMessage, p: &Path| {
105 matches!(error, ScmpErrorMessage::DestinationUnreachable(_)) && p == &expected_path
106 })
107 .times(1)
108 .returning(|_, _| {});
109
110 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
111 let subscribers = Subscribers::new();
112 subscribers.register(receiver_arc.clone());
113
114 let handler = ScmpErrorHandler::new(subscribers);
115 let result = handler.handle(packet.into());
116
117 assert!(result.is_none());
118 drop(receiver_arc); }
120
121 #[test]
122 fn ignores_non_error_scmp_messages() {
123 let ctx = test_context();
124 let mut mock_receiver = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
125 mock_receiver.expect_report_scmp_error().times(0);
126
127 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
128 let subscribers = Subscribers::new();
129 subscribers.register(receiver_arc.clone());
130
131 let handler = ScmpErrorHandler::new(subscribers);
132
133 let echo_request = ctx.scion_packet_scmp(ScmpMessage::EchoRequest(ScmpEchoRequest::new(
135 1,
136 2,
137 Bytes::from_static(b"data"),
138 )));
139 let result = handler.handle(echo_request.into());
140 assert!(result.is_none());
141
142 let echo_reply = ctx.scion_packet_scmp(ScmpMessage::EchoReply(ScmpEchoReply::new(
144 1,
145 2,
146 Bytes::from_static(b"data"),
147 )));
148 let result = handler.handle(echo_reply.into());
149 assert!(result.is_none());
150 drop(receiver_arc);
151 }
152
153 #[test]
154 fn ignores_invalid_packets() {
155 let ctx = test_context();
156 let mut mock_receiver = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
157 mock_receiver.expect_report_scmp_error().times(0);
158
159 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
160 let subscribers = Subscribers::new();
161 subscribers.register(receiver_arc.clone());
162
163 let handler = ScmpErrorHandler::new(subscribers);
164
165 let invalid_packet = ctx.scion_packet_raw(b"not scmp");
167 let result = handler.handle(invalid_packet);
168 assert!(result.is_none());
169 drop(receiver_arc);
170 }
171
172 #[test]
173 fn handles_multiple_receivers() {
174 let ctx = test_context();
175 let error_msg = ScmpMessage::DestinationUnreachable(ScmpDestinationUnreachable::new(
176 DestinationUnreachableCode::AddressUnreachable,
177 Bytes::from_static(b"offending packet"),
178 ));
179 let packet = ctx.scion_packet_scmp(error_msg);
180 let expected_path = packet.headers.path();
181
182 let expected_path_clone1 = expected_path.clone();
183 let expected_path_clone2 = expected_path.clone();
184 let mut mock_receiver1 = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
185 mock_receiver1
186 .expect_report_scmp_error()
187 .withf(move |error: &ScmpErrorMessage, p: &Path| {
188 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
189 && p == &expected_path_clone1
190 })
191 .times(1)
192 .returning(|_, _| {});
193
194 let mut mock_receiver2 = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
195 mock_receiver2
196 .expect_report_scmp_error()
197 .withf(move |error: &ScmpErrorMessage, p: &Path| {
198 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
199 && p == &expected_path_clone2
200 })
201 .times(1)
202 .returning(|_, _| {});
203
204 let receiver1_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver1);
205 let receiver2_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver2);
206 let subscribers = Subscribers::new();
207 subscribers.register(receiver1_arc.clone());
208 subscribers.register(receiver2_arc.clone());
209
210 let handler = ScmpErrorHandler::new(subscribers);
211 let result = handler.handle(packet.into());
212
213 assert!(result.is_none());
214 drop(receiver1_arc);
215 drop(receiver2_arc);
216 }
217
218 #[test]
219 fn handles_weak_references() {
220 let ctx = test_context();
221 let error_msg = ScmpMessage::DestinationUnreachable(ScmpDestinationUnreachable::new(
222 DestinationUnreachableCode::AddressUnreachable,
223 Bytes::from_static(b"offending packet"),
224 ));
225 let packet = ctx.scion_packet_scmp(error_msg);
226
227 let mut mock_receiver = crate::scionstack::scmp_handler::MockScmpErrorReceiver::new();
228 mock_receiver.expect_report_scmp_error().times(0);
231
232 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
233 let subscribers = Subscribers::new();
234 subscribers.register(receiver_arc);
235
236 let handler = ScmpErrorHandler::new(subscribers);
239 let result = handler.handle(packet.into());
240
241 assert!(result.is_none());
243 }
244}