leodos_protocols/transport/cfdp/pdu/
mod.rs1pub mod file_data;
11pub mod file_directive;
13pub mod header;
15pub mod tlv;
17
18use core::fmt;
19use core::ops::Deref;
20use core::ops::DerefMut;
21
22use crate::transport::cfdp::pdu::file_data::with_meta::FileDataPduWithMeta;
23use crate::transport::cfdp::pdu::file_data::without_meta::FileDataPduWithoutMeta;
24use crate::transport::cfdp::pdu::file_data::FileDataPdu;
25use crate::transport::cfdp::pdu::file_directive::ack::AckPdu;
26use crate::transport::cfdp::pdu::file_directive::eof::EofPdu;
27use crate::transport::cfdp::pdu::file_directive::finished::FinishedPdu;
28use crate::transport::cfdp::pdu::file_directive::keepalive::large::KeepAlivePduLarge;
29use crate::transport::cfdp::pdu::file_directive::keepalive::small::KeepAlivePduSmall;
30use crate::transport::cfdp::pdu::file_directive::keepalive::KeepAlivePdu;
31use crate::transport::cfdp::pdu::file_directive::metadata::MetadataPdu;
32use crate::transport::cfdp::pdu::file_directive::nak::large::NakPduLarge;
33use crate::transport::cfdp::pdu::file_directive::nak::large::NakSegmentLarge;
34use crate::transport::cfdp::pdu::file_directive::nak::small::NakPduSmall;
35use crate::transport::cfdp::pdu::file_directive::nak::small::NakSegmentSmall;
36use crate::transport::cfdp::pdu::file_directive::nak::NakPdu;
37use crate::transport::cfdp::pdu::file_directive::prompt::PromptPdu;
38use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
39use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
40use crate::transport::cfdp::pdu::header::PduHeaderFixedPart;
41use crate::transport::cfdp::pdu::header::PduType;
42use crate::transport::cfdp::CfdpError;
43use crate::utils::min_len;
44use bon::bon;
45use zerocopy::FromBytes;
46use zerocopy::Immutable;
47use zerocopy::IntoBytes;
48use zerocopy::KnownLayout;
49use zerocopy::Unaligned;
50
51#[repr(C)]
85#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
86pub struct Pdu {
87 header_fixed: PduHeaderFixedPart,
89 rest: [u8],
91}
92
93#[derive(Debug)]
95pub enum PduVariant<'a> {
96 Eof(&'a EofPdu),
98 Finished(&'a FinishedPdu),
100 Ack(&'a AckPdu),
102 Metadata(&'a MetadataPdu),
104 FileData(FileDataPdu<'a>),
106 Nak(NakPdu<'a>),
108 Prompt(&'a PromptPdu),
110 KeepAlive(KeepAlivePdu<'a>),
112}
113
114fn write_to_slice(val: u64, slice: &mut [u8]) -> Result<(), CfdpError> {
116 let len = slice.len();
117 if len == 0 || len > 8 {
118 return Err(CfdpError::Custom("Invalid slice length for Entity ID"));
119 }
120 if len < min_len(val) {
122 return Err(CfdpError::Custom(
123 "Slice too small to represent Entity ID value",
124 ));
125 }
126
127 let full_bytes = val.to_be_bytes();
128 let relevant_bytes = &full_bytes[8 - len..];
129 slice.copy_from_slice(relevant_bytes);
130 Ok(())
131}
132
133#[derive(Copy, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
138pub struct EntityId(pub u64);
139
140impl EntityId {
141 pub fn from_bytes(slice: &[u8]) -> Result<Self, CfdpError> {
144 if slice.is_empty() || slice.len() > 8 {
145 return Err(CfdpError::Custom("Invalid Entity ID length"));
146 }
147 let mut bytes = [0u8; 8];
148 bytes[8 - slice.len()..].copy_from_slice(slice);
150 Ok(EntityId(u64::from_be_bytes(bytes)))
151 }
152
153 pub fn write_to_slice(&self, slice: &mut [u8]) -> Result<(), CfdpError> {
155 write_to_slice(self.0, slice)
156 }
157
158 pub fn len(&self) -> usize {
160 min_len(self.0)
161 }
162}
163
164impl fmt::Debug for EntityId {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(f, "EntityId(0x{:X})", self.0)
168 }
169}
170
171#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
173pub struct TransactionSeqNum(pub u64);
174
175impl TransactionSeqNum {
176 pub fn increment(&mut self) {
178 self.0 = self.0.wrapping_add(1);
179 }
180
181 pub fn from_bytes(slice: &[u8]) -> Result<Self, CfdpError> {
183 if slice.is_empty() || slice.len() > 8 {
184 return Err(CfdpError::Custom(
185 "Invalid Transaction Sequence Number length",
186 ));
187 }
188 let mut bytes = [0u8; 8];
189 bytes[8 - slice.len()..].copy_from_slice(slice);
190 Ok(TransactionSeqNum(u64::from_be_bytes(bytes)))
191 }
192
193 pub fn write_to_slice(&self, slice: &mut [u8]) -> Result<(), CfdpError> {
195 write_to_slice(self.0, slice)
196 }
197
198 pub fn len(&self) -> usize {
200 min_len(self.0)
201 }
202}
203
204impl Pdu {
205 pub fn header(&self) -> &PduHeaderFixedPart {
209 &self.header_fixed
210 }
211
212 pub fn header_mut(&mut self) -> &mut PduHeaderFixedPart {
214 &mut self.header_fixed
215 }
216
217 pub fn variant(&self) -> Result<PduVariant<'_>, CfdpError> {
219 let header = self.header();
220 let data = self.data_field()?;
221 match header.pdu_type() {
222 PduType::FileData => {
223 let large_file_flag = header.large_file_flag();
224 let seg_meta_flag = header.segment_metadata_flag();
225
226 if seg_meta_flag {
227 let file_data_pdu = FileDataPduWithMeta::ref_from_bytes(data)
228 .map_err(|_| CfdpError::Custom("Failed to parse FileDataPduWithMeta"))?;
229 let metadata_len = file_data_pdu.metadata_len();
230 let offset_len = if large_file_flag { 8 } else { 4 };
231 if file_data_pdu.rest().len() < metadata_len + offset_len {
232 return Err(CfdpError::Custom(
233 "Insufficient data for metadata and offset",
234 ));
235 }
236 Ok(PduVariant::FileData(FileDataPdu::WithMeta(file_data_pdu)))
237 } else {
238 let file_data_pdu = FileDataPduWithoutMeta::ref_from_bytes(data)
239 .map_err(|_| CfdpError::Custom("Failed to parse FileDataPduWithoutMeta"))?;
240 let offset_len = if large_file_flag { 8 } else { 4 };
241 if file_data_pdu.rest().len() < offset_len {
242 return Err(CfdpError::Custom("Insufficient data for offset"));
243 }
244 Ok(PduVariant::FileData(FileDataPdu::WithoutMeta(
245 file_data_pdu,
246 )))
247 }
248 }
249 PduType::FileDirective => {
250 let file_directive = FileDirectivePdu::ref_from_bytes(data)
251 .map_err(|_| CfdpError::Custom("Failed to parse FileDirectivePdu"))?;
252 let rest = file_directive.rest();
253
254 match file_directive.directive_code()? {
255 DirectiveCode::Eof => {
256 let pdu = EofPdu::ref_from_bytes(rest)
257 .map_err(|_| CfdpError::Custom("Failed to parse EofPdu"))?;
258 let min_rest_len = if header.large_file_flag() { 8 } else { 4 };
259 if pdu.rest().len() < min_rest_len {
260 return Err(CfdpError::Custom("Insufficient data for EOF file size"));
261 }
262 Ok(PduVariant::Eof(pdu))
263 }
264 DirectiveCode::Finished => {
265 let pdu = FinishedPdu::ref_from_bytes(rest)
266 .map_err(|_| CfdpError::Custom("Failed to parse FinishedPdu"))?;
267 Ok(PduVariant::Finished(pdu))
268 }
269 DirectiveCode::Ack => {
270 let pdu = AckPdu::ref_from_bytes(rest)
271 .map_err(|_| CfdpError::Custom("Failed to parse AckPdu"))?;
272 Ok(PduVariant::Ack(pdu))
273 }
274 DirectiveCode::Metadata => {
275 let pdu = MetadataPdu::ref_from_bytes(rest)
276 .map_err(|_| CfdpError::Custom("Failed to parse MetadataPdu"))?;
277 let min_rest_len = (if header.large_file_flag() { 8 } else { 4 }) + 1 + 1;
278 if pdu.rest().len() < min_rest_len {
279 return Err(CfdpError::Custom("Insufficient data for Metadata PDU"));
280 }
281 Ok(PduVariant::Metadata(pdu))
282 }
283 DirectiveCode::Nak => {
284 if header.large_file_flag() {
285 let pdu = NakPduLarge::ref_from_bytes(rest)
286 .map_err(|_| CfdpError::Custom("Failed to parse NakPduLarge"))?;
287 if pdu.rest().len() % core::mem::size_of::<NakSegmentLarge>() != 0 {
288 return Err(CfdpError::Custom(
289 "Invalid NAK segment requests length",
290 ));
291 }
292 Ok(PduVariant::Nak(NakPdu::Large(pdu)))
293 } else {
294 let pdu = NakPduSmall::ref_from_bytes(rest)
295 .map_err(|_| CfdpError::Custom("Failed to parse NakPduSmall"))?;
296 if pdu.rest().len() % core::mem::size_of::<NakSegmentSmall>() != 0 {
297 return Err(CfdpError::Custom(
298 "Invalid NAK segment requests length",
299 ));
300 }
301 Ok(PduVariant::Nak(NakPdu::Small(pdu)))
302 }
303 }
304 DirectiveCode::Prompt => {
305 let pdu = PromptPdu::ref_from_bytes(rest)
306 .map_err(|_| CfdpError::Custom("Failed to parse PromptPdu"))?;
307 Ok(PduVariant::Prompt(pdu))
308 }
309 DirectiveCode::KeepAlive => {
310 if header.large_file_flag() {
311 let pdu = KeepAlivePduLarge::ref_from_bytes(rest).map_err(|_| {
312 CfdpError::Custom("Failed to parse KeepAlivePduLarge")
313 })?;
314 Ok(PduVariant::KeepAlive(KeepAlivePdu::Large(pdu)))
315 } else {
316 let pdu = KeepAlivePduSmall::ref_from_bytes(rest).map_err(|_| {
317 CfdpError::Custom("Failed to parse KeepAlivePduSmall")
318 })?;
319 Ok(PduVariant::KeepAlive(KeepAlivePdu::Small(pdu)))
320 }
321 }
322 }
323 }
324 }
325 }
326
327 pub(crate) fn variable_header(&self) -> Result<&[u8], CfdpError> {
329 let len = self.header_fixed.variable_header_len();
330 self.rest
331 .get(0..len)
332 .ok_or_else(|| CfdpError::Custom("Invalid variable header slice"))
333 }
334
335 pub(crate) fn variable_header_mut(&mut self) -> Result<&mut [u8], CfdpError> {
337 let len = self.header_fixed.variable_header_len();
338 self.rest
339 .get_mut(0..len)
340 .ok_or_else(|| CfdpError::Custom("Invalid variable header slice"))
341 }
342
343 pub fn set_header(&mut self, header: PduHeaderFixedPart) {
345 self.header_fixed = header;
346 }
347
348 pub fn rest(&self) -> &[u8] {
350 &self.rest
351 }
352
353 pub fn header_len(&self) -> usize {
355 core::mem::size_of::<PduHeaderFixedPart>() + self.header_fixed.variable_header_len()
356 }
357
358 pub fn source_entity_id(&self) -> Result<EntityId, CfdpError> {
360 let entity_id_len = self.header_fixed.entity_id_len();
361 let bytes = self
362 .rest
363 .get(0..entity_id_len)
364 .ok_or_else(|| CfdpError::Custom("Invalid source entity ID slice"))?;
365 EntityId::from_bytes(bytes)
366 }
367
368 pub fn set_source_entity_id(&mut self, source_entity_id: EntityId) -> Result<(), CfdpError> {
370 let entity_id_len = self.header_fixed.entity_id_len();
371 let var_header_slice = self
372 .variable_header_mut()?
373 .get_mut(0..entity_id_len)
374 .ok_or(CfdpError::Custom("Invalid slice for source ID"))?;
375
376 source_entity_id.write_to_slice(var_header_slice)
377 }
378
379 pub fn transaction_seq_num(&self) -> Result<TransactionSeqNum, CfdpError> {
381 let entity_id_len = self.header_fixed.entity_id_len();
382 let txn_seq_num_len = self.header_fixed.txn_seq_num_len();
383 let offset = entity_id_len;
384 let bytes = self
385 .variable_header()?
386 .get(offset..offset + txn_seq_num_len)
387 .ok_or_else(|| CfdpError::Custom("Invalid transaction sequence number slice"))?;
388 TransactionSeqNum::from_bytes(bytes)
389 }
390
391 pub fn set_transaction_seq_num(
393 &mut self,
394 txn_seq_num: TransactionSeqNum,
395 ) -> Result<(), CfdpError> {
396 let entity_id_len = self.header_fixed.entity_id_len();
397 let txn_seq_num_len = self.header_fixed.txn_seq_num_len();
398 let offset = entity_id_len;
399
400 let var_header_slice = self
401 .variable_header_mut()?
402 .get_mut(offset..offset + txn_seq_num_len)
403 .ok_or(CfdpError::Custom("Invalid slice for seq num"))?;
404
405 txn_seq_num.write_to_slice(var_header_slice)
406 }
407
408 pub fn set_destination_entity_id(&mut self, dest_entity_id: EntityId) -> Result<(), CfdpError> {
410 let entity_id_len = self.header_fixed.entity_id_len();
411 let txn_seq_num_len = self.header_fixed.txn_seq_num_len();
412 let offset = entity_id_len + txn_seq_num_len;
413
414 let var_header_slice = self
415 .variable_header_mut()?
416 .get_mut(offset..offset + entity_id_len)
417 .ok_or(CfdpError::Custom("Invalid slice for dest ID"))?;
418
419 dest_entity_id.write_to_slice(var_header_slice)
420 }
421
422 pub fn data_field(&self) -> Result<&[u8], CfdpError> {
424 let start = self.header_fixed.variable_header_len();
425 self.rest
426 .get(start..)
427 .ok_or_else(|| CfdpError::Custom("Invalid data field slice"))
428 }
429
430 pub fn data_field_mut(&mut self) -> Result<&mut [u8], CfdpError> {
432 let start = self.header_fixed.variable_header_len();
433 self.rest
434 .get_mut(start..)
435 .ok_or_else(|| CfdpError::Custom("Invalid data field slice"))
436 }
437
438 pub fn from_bytes(buffer: &[u8]) -> Result<&Pdu, CfdpError> {
440 let (header_fixed, _rest) = PduHeaderFixedPart::ref_from_prefix(buffer)
441 .map_err(|_| CfdpError::Custom("Failed to parse PDU header fixed part"))?;
442
443 let expected_total_len = header_fixed.total_pdu_len();
444
445 if buffer.len() < expected_total_len {
446 return Err(CfdpError::Custom("Buffer too small for complete PDU"));
447 }
448
449 Pdu::ref_from_bytes(&buffer[..expected_total_len])
450 .map_err(|_| CfdpError::Custom("Failed to parse complete PDU"))
451 }
452
453 pub fn from_bytes_mut(buffer: &mut [u8]) -> Result<&mut Pdu, CfdpError> {
455 let buffer_len = buffer.len();
456
457 let (header_fixed, _rest) =
458 PduHeaderFixedPart::mut_from_prefix(buffer).map_err(|_| CfdpError::BufferTooSmall {
459 required: core::mem::size_of::<PduHeaderFixedPart>(),
460 provided: buffer_len,
461 })?;
462
463 let expected_total_len = header_fixed.total_pdu_len();
464
465 if buffer_len < expected_total_len {
466 return Err(CfdpError::BufferTooSmall {
467 required: expected_total_len,
468 provided: buffer_len,
469 });
470 }
471
472 Ok(Pdu::mut_from_bytes(&mut buffer[..expected_total_len])
473 .expect("Buffer size already validated"))
474 }
475}
476
477#[bon]
478impl Pdu {
479 #[builder]
481 pub fn new<'a>(
482 buffer: &'a mut [u8],
483 header_fixed: PduHeaderFixedPart,
484 source_entity_id: EntityId,
485 destination_entity_id: EntityId,
486 transaction_seq_num: TransactionSeqNum,
487 ) -> Result<&'a mut Pdu, CfdpError> {
488 let pdu = Pdu::from_bytes_mut(buffer)?;
489 pdu.set_header(header_fixed);
490 pdu.header_fixed.set_entity_id_len(core::cmp::max(
491 source_entity_id.len(),
492 destination_entity_id.len(),
493 ))?;
494 pdu.header_fixed
495 .set_txn_seq_num_len(transaction_seq_num.len())?;
496 pdu.set_source_entity_id(source_entity_id)?;
497 pdu.set_transaction_seq_num(transaction_seq_num)?;
498 pdu.set_destination_entity_id(destination_entity_id)?;
499 Ok(pdu)
500 }
501}
502
503impl Deref for Pdu {
504 type Target = PduHeaderFixedPart;
505
506 fn deref(&self) -> &Self::Target {
507 &self.header_fixed
508 }
509}
510
511impl DerefMut for Pdu {
512 fn deref_mut(&mut self) -> &mut Self::Target {
513 &mut self.header_fixed
514 }
515}