1use crate::{
18 address::addr::ScionAddr,
19 core::{
20 convert::{FromView, TryFromView},
21 encode::{EncodeError, InvalidStructureError, WireEncode},
22 layout::Layout,
23 view::ViewConversionError,
24 write::unchecked_bit_range_be_write,
25 },
26 dataplane_path::{model::DpPath, types::PathType},
27 header::{
28 layout::{AddressHeaderLayout, CommonHeaderLayout, ScionHeaderLayout},
29 view::ScionHeaderView,
30 },
31 payload::ProtocolNumber,
32 scion::{
33 address::host_addr::{WireHostAddr, WireHostAddrType},
34 identifier::isd_asn::IsdAsn,
35 },
36};
37
38#[derive(Debug, Clone, PartialEq, Eq, Hash)]
43pub struct ScionPacketHeader {
44 pub common: CommonHeader,
46 pub address: AddressHeader,
48 pub path: DpPath,
50}
51impl ScionPacketHeader {
52 #[inline]
54 fn size_units(&self) -> u8 {
55 (self.required_size() / 4) as u8
56 }
57}
58impl ScionPacketHeader {
60 #[inline]
66 pub fn required_size(&self) -> usize {
67 CommonHeaderLayout::SIZE_BYTES + self.address.required_size() + self.path.required_size()
68 }
69
70 #[inline]
77 pub fn wire_valid(&self) -> Result<(), InvalidStructureError> {
78 let required_size = self.required_size();
79 if !required_size.is_multiple_of(4) {
80 return Err(InvalidStructureError::from(
81 "header size must be a multiple of 4 bytes",
82 ));
83 }
84
85 if required_size > ScionHeaderLayout::MAX_SIZE_BYTES {
86 return Err(InvalidStructureError::from(
87 "header size exceeds maximum encodeable value of 1020 bytes",
88 ));
89 }
90
91 self.common.valid()?;
92 self.address.wire_valid()?;
93 self.path.wire_valid()?;
94 Ok(())
95 }
96
97 #[inline]
105 pub unsafe fn encode_unchecked(&self, buf: &mut [u8], payload_size: u16) -> usize {
106 unsafe {
107 use CommonHeaderLayout as CHL;
108 self.common.encode_unchecked(
110 buf,
111 self.size_units(),
112 self.path.path_type(),
113 self.address.dst_addr_type(),
114 self.address.src_addr_type(),
115 payload_size,
116 );
117
118 let offset = CHL::SIZE_BYTES;
120 let address_buf = buf.split_at_mut_unchecked(offset).1;
121 self.address.encode_unchecked(address_buf);
122
123 let offset = offset + self.address.required_size();
125 let path_buf = buf.split_at_mut_unchecked(offset).1;
126 self.path.encode_unchecked(path_buf);
127 }
128
129 self.required_size()
130 }
131
132 #[inline]
139 pub fn try_encode(&self, buf: &mut [u8], payload_size: u16) -> Result<usize, EncodeError> {
140 self.wire_valid()?;
141
142 let required_size = self.required_size();
143 if buf.len() < required_size {
144 return Err(EncodeError::BufferTooSmall(required_size));
145 }
146
147 Ok(unsafe { self.encode_unchecked(buf, payload_size) })
149 }
150}
151impl TryFromView for ScionPacketHeader {
152 type ViewType = ScionHeaderView;
153 #[inline]
154 fn try_from_view(view: &Self::ViewType) -> Result<Self, ViewConversionError> {
155 Ok(ScionPacketHeader {
156 common: CommonHeader::from_view(view),
157 address: AddressHeader::try_from_view(view)?,
158 path: DpPath::from_view(&view.path()),
159 })
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165pub struct CommonHeader {
166 pub traffic_class: u8,
168 pub flow_id: u32,
170 pub next_header: ProtocolNumber,
174}
175impl CommonHeader {
177 #[inline]
179 pub fn valid(&self) -> Result<(), InvalidStructureError> {
180 use CommonHeaderLayout as CHL;
181
182 if self.flow_id > CHL::FLOW_ID_RNG.max_uint() as u32 {
183 return Err("flow_id exceeds maximum encodeable value".into());
184 }
185
186 Ok(())
190 }
191
192 const VERSION: u8 = 0;
193 #[inline]
198 pub unsafe fn encode_unchecked(
199 &self,
200 buf: &mut [u8],
201 header_len_units: u8,
202 path_type: PathType,
203 dst_addr_type: WireHostAddrType,
204 src_addr_type: WireHostAddrType,
205 payload_size: u16,
206 ) -> usize {
207 unsafe {
208 use CommonHeaderLayout as CHL;
209 unchecked_bit_range_be_write(buf, CHL::VERSION_RNG, Self::VERSION);
211 unchecked_bit_range_be_write(buf, CHL::TRAFFIC_CLASS_RNG, self.traffic_class);
212 unchecked_bit_range_be_write(buf, CHL::FLOW_ID_RNG, self.flow_id);
213 unchecked_bit_range_be_write::<u8>(buf, CHL::NEXT_HEADER_RNG, self.next_header.into());
214 unchecked_bit_range_be_write(buf, CHL::HEADER_LEN_RNG, header_len_units);
215 unchecked_bit_range_be_write(buf, CHL::PAYLOAD_LEN_RNG, payload_size);
216 unchecked_bit_range_be_write::<u8>(buf, CHL::PATH_TYPE_RNG, path_type.into());
217 let dst_addr_info: u8 = dst_addr_type.into();
218 unchecked_bit_range_be_write::<u8>(buf, CHL::DST_ADDR_INFO_RNG, dst_addr_info);
219 let src_addr_info: u8 = src_addr_type.into();
220 unchecked_bit_range_be_write::<u8>(buf, CHL::SRC_ADDR_INFO_RNG, src_addr_info);
221 unchecked_bit_range_be_write(buf, CHL::RSV_RNG, 0u16); }
223
224 CommonHeaderLayout::SIZE_BYTES
225 }
226}
227impl FromView for CommonHeader {
228 type ViewType = ScionHeaderView;
229 #[inline]
230 fn from_view(view: &Self::ViewType) -> Self {
231 debug_assert!(view.version() == Self::VERSION, "Unsupported SCION version");
232 CommonHeader {
233 traffic_class: view.traffic_class(),
234 flow_id: view.flow_id(),
235 next_header: view.next_header(),
236 }
237 }
238}
239
240#[derive(Debug, Clone, PartialEq, Eq, Hash)]
242pub struct AddressHeader {
243 pub dst_ia: IsdAsn,
245 pub src_ia: IsdAsn,
247 pub dst_host_addr: WireHostAddr,
249 pub src_host_addr: WireHostAddr,
251}
252impl AddressHeader {
253 #[inline]
255 pub fn new(src: ScionAddr, dst: ScionAddr) -> Self {
256 AddressHeader {
257 dst_ia: dst.isd_asn(),
258 src_ia: src.isd_asn(),
259 dst_host_addr: dst.host().into(),
260 src_host_addr: src.host().into(),
261 }
262 }
263
264 #[inline]
266 pub fn dst_addr_type(&self) -> WireHostAddrType {
267 self.dst_host_addr.addr_type()
268 }
269
270 #[inline]
272 pub fn src_addr_type(&self) -> WireHostAddrType {
273 self.src_host_addr.addr_type()
274 }
275}
276impl WireEncode for AddressHeader {
277 #[inline]
278 fn required_size(&self) -> usize {
279 AddressHeaderLayout::new(
280 self.dst_host_addr.required_size() as u8,
281 self.src_host_addr.required_size() as u8,
282 )
283 .size_bytes()
284 }
285
286 #[inline]
287 fn wire_valid(&self) -> Result<(), InvalidStructureError> {
288 self.dst_host_addr.wire_valid()?;
291 self.src_host_addr.wire_valid()?;
292
293 Ok(())
294 }
295
296 #[inline]
297 unsafe fn encode_unchecked(&self, buf: &mut [u8]) -> usize {
298 unsafe {
299 use AddressHeaderLayout as AHL;
300 unchecked_bit_range_be_write(buf, AHL::DST_ISD_RNG, self.dst_ia.isd().0);
301 unchecked_bit_range_be_write(buf, AHL::DST_AS_RNG, self.dst_ia.asn().0);
302 unchecked_bit_range_be_write(buf, AHL::SRC_ISD_RNG, self.src_ia.isd().0);
303 unchecked_bit_range_be_write(buf, AHL::SRC_AS_RNG, self.src_ia.asn().0);
304
305 let layout = AddressHeaderLayout::new(
306 self.src_host_addr.required_size() as u8,
307 self.dst_host_addr.required_size() as u8,
308 );
309
310 {
311 let dst_host_buf =
312 buf.get_unchecked_mut(layout.dst_host_addr_range().aligned_byte_range());
313 self.dst_host_addr.encode_unchecked(dst_host_buf);
314 }
315
316 {
317 let src_host_buf =
318 buf.get_unchecked_mut(layout.src_host_addr_range().aligned_byte_range());
319 self.src_host_addr.encode_unchecked(src_host_buf);
320 }
321 }
322
323 self.required_size()
324 }
325}
326impl TryFromView for AddressHeader {
327 type ViewType = ScionHeaderView;
328 #[inline]
329 fn try_from_view(view: &Self::ViewType) -> Result<Self, ViewConversionError> {
330 Ok(AddressHeader {
331 dst_ia: view.dst_ia(),
332 src_ia: view.src_ia(),
333 dst_host_addr: view
334 .dst_host_addr()
335 .map_err(|_| ViewConversionError::Other("invalid dst_host_addr"))?,
336 src_host_addr: view
337 .src_host_addr()
338 .map_err(|_| ViewConversionError::Other("invalid src_host_addr"))?,
339 })
340 }
341}
342
343#[cfg(feature = "proptest")]
345pub mod ptest {
346 use ::proptest::prelude::*;
347
348 use super::*;
349 use crate::dataplane_path::model::DpPath;
350
351 #[derive(Debug, Clone, Default)]
356 pub struct ArbitraryScionPacketHeaderParams {
357 pub dst_host_addr: <WireHostAddr as Arbitrary>::Parameters,
359 pub src_host_addr: <WireHostAddr as Arbitrary>::Parameters,
361 pub path: <DpPath as Arbitrary>::Parameters,
363 }
364
365 impl Arbitrary for ScionPacketHeader {
366 type Parameters = ArbitraryScionPacketHeaderParams;
367 type Strategy = BoxedStrategy<Self>;
368
369 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
370 let traffic_class = any::<u8>();
371 let flow_id = 0u32..=0xF_FFFFu32;
372 let next_header = any::<u8>();
373
374 let dst_ia = any::<IsdAsn>();
375 let src_ia = any::<IsdAsn>();
376
377 let dst_host_addr = WireHostAddr::arbitrary_with(params.dst_host_addr);
378 let src_host_addr = WireHostAddr::arbitrary_with(params.src_host_addr);
379
380 let path = DpPath::arbitrary_with(params.path);
381
382 (
383 traffic_class,
384 flow_id,
385 next_header,
386 dst_ia,
387 src_ia,
388 dst_host_addr,
389 src_host_addr,
390 path,
391 )
392 .prop_map(
393 |(
394 traffic_class,
395 flow_id,
396 next_header,
397 dst_ia,
398 src_ia,
399 dst_host_addr,
400 src_host_addr,
401 path,
402 )| {
403 Self {
404 common: CommonHeader {
405 traffic_class,
406 flow_id,
407 next_header: next_header.into(),
408 },
409 address: AddressHeader {
410 dst_ia,
411 src_ia,
412 dst_host_addr,
413 src_host_addr,
414 },
415 path,
416 }
417 },
418 )
419 .boxed()
420 }
421 }
422}