scion_stack/stack/scmp_handler/
error.rs1use sciparse::{
17 packet::{model::ScionRawPacket, view::ScionRawPacketView},
18 payload::scmp::view::ScmpMessageExt,
19};
20
21use super::ScmpHandler;
22use crate::{internal::Subscribers, stack::scmp_handler::ScmpErrorReceiver};
23
24pub(crate) struct ScmpErrorHandler {
26 receivers: Subscribers<dyn ScmpErrorReceiver>,
27}
28
29impl ScmpErrorHandler {
30 pub(crate) fn new(receivers: Subscribers<dyn ScmpErrorReceiver>) -> Self {
32 Self { receivers }
33 }
34}
35
36impl ScmpHandler for ScmpErrorHandler {
37 fn handle(&self, pkt: &ScionRawPacketView) -> Option<ScionRawPacket> {
38 let path = pkt.header().path();
39 let Ok(scmp_pkg) = pkt.try_as_scmp() else {
40 tracing::debug!("ignoring non SCMP packet");
41 return None;
42 };
43
44 if !scmp_pkg.scmp().message().is_error() {
45 tracing::debug!("ignoring non error SCMP message");
46 return None;
47 }
48
49 let scmp_error = scmp_pkg
50 .scmp()
51 .message()
52 .to_model()
53 .try_into_error_message()
54 .inspect_err(|e| {
55 debug_assert!(false, "scmp error was not an error: {e:?}");
56 })
57 .ok()?;
58
59 tracing::debug!(err = ?scmp_error, "reporting SCMP error");
60 let src_ia = pkt.header().src_ia();
61 self.receivers.for_each(|receiver| {
62 receiver.report_scmp_error(src_ia, scmp_error.clone(), path);
63 });
64 None
65 }
66}
67
68#[cfg(test)]
69mod scmp_error_handler_tests {
70 use std::sync::Arc;
71
72 use sciparse::{
73 address::ip_addr::ScionIpAddr,
74 core::model::Model,
75 dataplane_path::view::{ScionDpPathViewExt, ScionDpPathViewRef},
76 identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn},
77 payload::scmp::{
78 model::{ScmpDestinationUnreachable, ScmpEchoReply, ScmpEchoRequest, ScmpErrorMessage},
79 types::ScmpDestinationUnreachableCode,
80 },
81 util::test_builder::{TestPathBuilder, TestPathContext},
82 };
83
84 use super::*;
85
86 fn test_context() -> TestPathContext {
87 let src = ScionIpAddr::new(IsdAsn::new(Isd(1), Asn(10)), [192, 0, 2, 1].into());
88 let dst = ScionIpAddr::new(IsdAsn::new(Isd(1), Asn(20)), [198, 51, 100, 1].into());
89 TestPathBuilder::new(src.into(), dst.into())
90 .using_info_timestamp(42)
91 .up()
92 .add_hop(0, 11)
93 .add_hop(12, 0)
94 .build(77)
95 }
96
97 #[test]
98 fn forwards_scmp_error_messages_to_receivers() {
99 let ctx = test_context();
100 let scmp_msg = ScmpDestinationUnreachable::new(
101 ScmpDestinationUnreachableCode::AddressUnreachable,
102 b"offending packet".to_vec(),
103 )
104 .into();
105
106 let packet = ctx
107 .scion_packet_scmp(scmp_msg)
108 .into_raw()
109 .try_encode_to_owned_view()
110 .expect("should encode");
111
112 let expected_path = packet.header().path().to_owned_view();
113
114 let expected_src_ia = packet.header().src_ia();
115 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
116 mock_receiver
117 .expect_report_scmp_error()
118 .withf(
119 move |src_ia: &IsdAsn, error: &ScmpErrorMessage, path: &ScionDpPathViewRef| {
120 *src_ia == expected_src_ia
121 && matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
122 && *path == expected_path.as_ref()
123 },
124 )
125 .times(1)
126 .returning(|_, _, _| {});
127
128 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
129 let subscribers = Subscribers::new();
130 subscribers.register(receiver_arc.clone());
131
132 let handler = ScmpErrorHandler::new(subscribers);
133 let result = handler.handle(&packet);
134
135 assert!(result.is_none());
136 drop(receiver_arc); }
138
139 #[test]
140 fn ignores_non_error_scmp_messages() {
141 let ctx = test_context();
142 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
143 mock_receiver.expect_report_scmp_error().times(0);
144
145 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
146 let subscribers = Subscribers::new();
147 subscribers.register(receiver_arc.clone());
148
149 let handler = ScmpErrorHandler::new(subscribers);
150
151 let echo_request = ctx
153 .scion_packet_scmp(ScmpEchoRequest::new(1, 2, b"data".to_vec()).into())
154 .into_raw()
155 .try_encode_to_owned_view()
156 .expect("should encode");
157 let result = handler.handle(&echo_request);
158 assert!(result.is_none());
159
160 let echo_reply = ctx
162 .scion_packet_scmp(ScmpEchoReply::new(1, 2, b"data".to_vec()).into())
163 .into_raw()
164 .try_encode_to_owned_view()
165 .expect("should encode");
166 let result = handler.handle(&echo_reply);
167 assert!(result.is_none());
168 drop(receiver_arc);
169 }
170
171 #[test]
172 fn ignores_invalid_packets() {
173 let ctx = test_context();
174 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
175 mock_receiver.expect_report_scmp_error().times(0);
176
177 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
178 let subscribers = Subscribers::new();
179 subscribers.register(receiver_arc.clone());
180
181 let handler = ScmpErrorHandler::new(subscribers);
182
183 let invalid_packet = ctx.scion_packet_raw(b"not scmp");
185 let result = handler.handle(
186 &invalid_packet
187 .try_encode_to_owned_view()
188 .expect("failed to encode packet"),
189 );
190 assert!(result.is_none());
191 drop(receiver_arc);
192 }
193
194 #[test]
195 fn handles_multiple_receivers() {
196 let ctx = test_context();
197 let error_msg = ScmpDestinationUnreachable::new(
198 ScmpDestinationUnreachableCode::AddressUnreachable,
199 b"offending packet".to_vec(),
200 )
201 .into();
202
203 let packet = ctx
204 .scion_packet_scmp(error_msg)
205 .into_raw()
206 .try_encode_to_owned_view()
207 .expect("should encode");
208 let expected_path = packet.header().path().to_owned_view();
209
210 let expected_path_clone1 = expected_path.clone();
211 let expected_path_clone2 = expected_path.clone();
212 let mut mock_receiver1 = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
213 mock_receiver1
214 .expect_report_scmp_error()
215 .withf(
216 move |_src_ia: &IsdAsn, error: &ScmpErrorMessage, p: &ScionDpPathViewRef| {
217 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
218 && p == &expected_path_clone1.as_ref()
219 },
220 )
221 .times(1)
222 .returning(|_, _, _| {});
223
224 let mut mock_receiver2 = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
225 mock_receiver2
226 .expect_report_scmp_error()
227 .withf(
228 move |_src_ia: &IsdAsn, error: &ScmpErrorMessage, p: &ScionDpPathViewRef| {
229 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
230 && p == &expected_path_clone2.as_ref()
231 },
232 )
233 .times(1)
234 .returning(|_, _, _| {});
235
236 let receiver1_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver1);
237 let receiver2_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver2);
238 let subscribers = Subscribers::new();
239 subscribers.register(receiver1_arc.clone());
240 subscribers.register(receiver2_arc.clone());
241
242 let handler = ScmpErrorHandler::new(subscribers);
243 let result = handler.handle(&packet);
244
245 assert!(result.is_none());
246 drop(receiver1_arc);
247 drop(receiver2_arc);
248 }
249
250 #[test]
251 fn handles_weak_references() {
252 let ctx = test_context();
253 let error_msg = ScmpDestinationUnreachable::new(
254 ScmpDestinationUnreachableCode::AddressUnreachable,
255 b"offending packet".to_vec(),
256 )
257 .into();
258
259 let packet = ctx
260 .scion_packet_scmp(error_msg)
261 .into_raw()
262 .try_encode_to_owned_view()
263 .expect("should encode");
264
265 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
266 mock_receiver.expect_report_scmp_error().times(0);
269
270 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
271 let subscribers = Subscribers::new();
272 subscribers.register(receiver_arc);
273
274 let handler = ScmpErrorHandler::new(subscribers);
277 let result = handler.handle(&packet);
278
279 assert!(result.is_none());
281 }
282}