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 self.receivers.for_each(|receiver| {
61 receiver.report_scmp_error(scmp_error.clone(), path);
62 });
63 None
64 }
65}
66
67#[cfg(test)]
68mod scmp_error_handler_tests {
69 use std::sync::Arc;
70
71 use sciparse::{
72 address::ip_addr::ScionIpAddr,
73 core::model::Model,
74 dataplane_path::view::{ScionDpPathViewExt, ScionDpPathViewRef},
75 identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn},
76 payload::scmp::{
77 model::{ScmpDestinationUnreachable, ScmpEchoReply, ScmpEchoRequest, ScmpErrorMessage},
78 types::ScmpDestinationUnreachableCode,
79 },
80 util::test_builder::{TestPathBuilder, TestPathContext},
81 };
82
83 use super::*;
84
85 fn test_context() -> TestPathContext {
86 let src = ScionIpAddr::new(IsdAsn::new(Isd(1), Asn(10)), [192, 0, 2, 1].into());
87 let dst = ScionIpAddr::new(IsdAsn::new(Isd(1), Asn(20)), [198, 51, 100, 1].into());
88 TestPathBuilder::new(src.into(), dst.into())
89 .using_info_timestamp(42)
90 .up()
91 .add_hop(0, 11)
92 .add_hop(12, 0)
93 .build(77)
94 }
95
96 #[test]
97 fn forwards_scmp_error_messages_to_receivers() {
98 let ctx = test_context();
99 let scmp_msg = ScmpDestinationUnreachable::new(
100 ScmpDestinationUnreachableCode::AddressUnreachable,
101 b"offending packet".to_vec(),
102 )
103 .into();
104
105 let packet = ctx
106 .scion_packet_scmp(scmp_msg)
107 .into_raw()
108 .try_encode_to_owned_view()
109 .expect("should encode");
110
111 let expected_path = packet.header().path().to_owned_view();
112
113 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
114 mock_receiver
115 .expect_report_scmp_error()
116 .withf(move |error: &ScmpErrorMessage, path: &ScionDpPathViewRef| {
117 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
118 && *path == expected_path.as_ref()
119 })
120 .times(1)
121 .returning(|_, _| {});
122
123 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
124 let subscribers = Subscribers::new();
125 subscribers.register(receiver_arc.clone());
126
127 let handler = ScmpErrorHandler::new(subscribers);
128 let result = handler.handle(&packet);
129
130 assert!(result.is_none());
131 drop(receiver_arc); }
133
134 #[test]
135 fn ignores_non_error_scmp_messages() {
136 let ctx = test_context();
137 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
138 mock_receiver.expect_report_scmp_error().times(0);
139
140 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
141 let subscribers = Subscribers::new();
142 subscribers.register(receiver_arc.clone());
143
144 let handler = ScmpErrorHandler::new(subscribers);
145
146 let echo_request = ctx
148 .scion_packet_scmp(ScmpEchoRequest::new(1, 2, b"data".to_vec()).into())
149 .into_raw()
150 .try_encode_to_owned_view()
151 .expect("should encode");
152 let result = handler.handle(&echo_request);
153 assert!(result.is_none());
154
155 let echo_reply = ctx
157 .scion_packet_scmp(ScmpEchoReply::new(1, 2, b"data".to_vec()).into())
158 .into_raw()
159 .try_encode_to_owned_view()
160 .expect("should encode");
161 let result = handler.handle(&echo_reply);
162 assert!(result.is_none());
163 drop(receiver_arc);
164 }
165
166 #[test]
167 fn ignores_invalid_packets() {
168 let ctx = test_context();
169 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
170 mock_receiver.expect_report_scmp_error().times(0);
171
172 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
173 let subscribers = Subscribers::new();
174 subscribers.register(receiver_arc.clone());
175
176 let handler = ScmpErrorHandler::new(subscribers);
177
178 let invalid_packet = ctx.scion_packet_raw(b"not scmp");
180 let result = handler.handle(
181 &invalid_packet
182 .try_encode_to_owned_view()
183 .expect("failed to encode packet"),
184 );
185 assert!(result.is_none());
186 drop(receiver_arc);
187 }
188
189 #[test]
190 fn handles_multiple_receivers() {
191 let ctx = test_context();
192 let error_msg = ScmpDestinationUnreachable::new(
193 ScmpDestinationUnreachableCode::AddressUnreachable,
194 b"offending packet".to_vec(),
195 )
196 .into();
197
198 let packet = ctx
199 .scion_packet_scmp(error_msg)
200 .into_raw()
201 .try_encode_to_owned_view()
202 .expect("should encode");
203 let expected_path = packet.header().path().to_owned_view();
204
205 let expected_path_clone1 = expected_path.clone();
206 let expected_path_clone2 = expected_path.clone();
207 let mut mock_receiver1 = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
208 mock_receiver1
209 .expect_report_scmp_error()
210 .withf(move |error: &ScmpErrorMessage, p: &ScionDpPathViewRef| {
211 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
212 && p == &expected_path_clone1.as_ref()
213 })
214 .times(1)
215 .returning(|_, _| {});
216
217 let mut mock_receiver2 = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
218 mock_receiver2
219 .expect_report_scmp_error()
220 .withf(move |error: &ScmpErrorMessage, p: &ScionDpPathViewRef| {
221 matches!(error, ScmpErrorMessage::DestinationUnreachable(_))
222 && p == &expected_path_clone2.as_ref()
223 })
224 .times(1)
225 .returning(|_, _| {});
226
227 let receiver1_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver1);
228 let receiver2_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver2);
229 let subscribers = Subscribers::new();
230 subscribers.register(receiver1_arc.clone());
231 subscribers.register(receiver2_arc.clone());
232
233 let handler = ScmpErrorHandler::new(subscribers);
234 let result = handler.handle(&packet);
235
236 assert!(result.is_none());
237 drop(receiver1_arc);
238 drop(receiver2_arc);
239 }
240
241 #[test]
242 fn handles_weak_references() {
243 let ctx = test_context();
244 let error_msg = ScmpDestinationUnreachable::new(
245 ScmpDestinationUnreachableCode::AddressUnreachable,
246 b"offending packet".to_vec(),
247 )
248 .into();
249
250 let packet = ctx
251 .scion_packet_scmp(error_msg)
252 .into_raw()
253 .try_encode_to_owned_view()
254 .expect("should encode");
255
256 let mut mock_receiver = crate::stack::scmp_handler::MockScmpErrorReceiver::new();
257 mock_receiver.expect_report_scmp_error().times(0);
260
261 let receiver_arc: Arc<dyn ScmpErrorReceiver> = Arc::new(mock_receiver);
262 let subscribers = Subscribers::new();
263 subscribers.register(receiver_arc);
264
265 let handler = ScmpErrorHandler::new(subscribers);
268 let result = handler.handle(&packet);
269
270 assert!(result.is_none());
272 }
273}