CFDP initial packet support #14
@ -1,8 +1,8 @@
|
|||||||
use crate::cfdp::lv::Lv;
|
use crate::cfdp::lv::Lv;
|
||||||
use crate::cfdp::pdu::{FileDirectiveType, PduError, PduHeader};
|
use crate::cfdp::pdu::{FileDirectiveType, PduError, PduHeader};
|
||||||
use crate::cfdp::tlv::Tlv;
|
use crate::cfdp::tlv::Tlv;
|
||||||
use crate::cfdp::{ChecksumType, LargeFileFlag};
|
use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag};
|
||||||
use crate::{ByteConversionError, SizeMissmatch};
|
use crate::{ByteConversionError, SizeMissmatch, CRC_CCITT_FALSE};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@ -74,12 +74,25 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_opts(
|
pub fn new_with_opts(
|
||||||
pdu_header: PduHeader,
|
mut pdu_header: PduHeader,
|
||||||
metadata_params: MetadataGenericParams,
|
metadata_params: MetadataGenericParams,
|
||||||
src_file_name: Lv<'src_name>,
|
src_file_name: Lv<'src_name>,
|
||||||
dest_file_name: Lv<'dest_name>,
|
dest_file_name: Lv<'dest_name>,
|
||||||
options: Option<&'opts [u8]>,
|
options: Option<&'opts [u8]>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let is_large_file = pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large;
|
||||||
|
let has_crc = pdu_header.common_pdu_conf().crc_flag == CrcFlag::WithCrc;
|
||||||
|
pdu_header.pdu_datafield_len =
|
||||||
|
2 + 4 + src_file_name.len_full() as u16 + dest_file_name.len_full() as u16;
|
||||||
|
if is_large_file {
|
||||||
|
pdu_header.pdu_datafield_len += 4;
|
||||||
|
}
|
||||||
|
if has_crc {
|
||||||
|
pdu_header.pdu_datafield_len += 2;
|
||||||
|
}
|
||||||
|
if let Some(opts) = options {
|
||||||
|
pdu_header.pdu_datafield_len += opts.len() as u16;
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
pdu_header,
|
pdu_header,
|
||||||
metadata_params,
|
metadata_params,
|
||||||
@ -114,10 +127,17 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
|
|||||||
if let Some(opts) = self.options {
|
if let Some(opts) = self.options {
|
||||||
len += opts.len();
|
len += opts.len();
|
||||||
}
|
}
|
||||||
|
if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc {
|
||||||
|
len += 2;
|
||||||
|
}
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
|
pub fn pdu_header(&self) -> &PduHeader {
|
||||||
|
&self.pdu_header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
|
||||||
let expected_len = self.written_len();
|
let expected_len = self.written_len();
|
||||||
if buf.len() < expected_len {
|
if buf.len() < expected_len {
|
||||||
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
@ -126,7 +146,8 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
|
|||||||
})
|
})
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
let mut current_idx = self.pdu_header.write_to_be_bytes(buf)?;
|
|
||||||
|
let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
|
||||||
buf[current_idx] = FileDirectiveType::MetadataPdu as u8;
|
buf[current_idx] = FileDirectiveType::MetadataPdu as u8;
|
||||||
current_idx += 1;
|
current_idx += 1;
|
||||||
buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 7)
|
buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 7)
|
||||||
@ -154,13 +175,19 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
|
|||||||
buf[current_idx..current_idx + opts.len()].copy_from_slice(opts);
|
buf[current_idx..current_idx + opts.len()].copy_from_slice(opts);
|
||||||
current_idx += opts.len();
|
current_idx += opts.len();
|
||||||
}
|
}
|
||||||
|
if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc {
|
||||||
|
let mut digest = CRC_CCITT_FALSE.digest();
|
||||||
|
digest.update(&buf[..current_idx]);
|
||||||
|
buf[current_idx..current_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
|
||||||
|
current_idx += 2;
|
||||||
|
}
|
||||||
Ok(current_idx)
|
Ok(current_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_be_bytes<'longest: 'src_name + 'dest_name + 'opts>(
|
pub fn from_bytes<'longest: 'src_name + 'dest_name + 'opts>(
|
||||||
buf: &'longest [u8],
|
buf: &'longest [u8],
|
||||||
) -> Result<MetadataPdu<'src_name, 'dest_name, 'opts>, PduError> {
|
) -> Result<MetadataPdu<'src_name, 'dest_name, 'opts>, PduError> {
|
||||||
let (pdu_header, mut current_idx) = PduHeader::from_be_bytes(buf)?;
|
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
|
||||||
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
|
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
|
||||||
let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
|
let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
|
||||||
// Minimal length: 1 byte + FSS (4 byte) + 2 empty LV (1 byte)
|
// Minimal length: 1 byte + FSS (4 byte) + 2 empty LV (1 byte)
|
||||||
@ -272,14 +299,14 @@ pub mod tests {
|
|||||||
let metadata_pdu =
|
let metadata_pdu =
|
||||||
MetadataPdu::new(pdu_header, metadata_params, src_filename, dest_filename);
|
MetadataPdu::new(pdu_header, metadata_params, src_filename, dest_filename);
|
||||||
let mut buf: [u8; 64] = [0; 64];
|
let mut buf: [u8; 64] = [0; 64];
|
||||||
let res = metadata_pdu.write_to_be_bytes(&mut buf);
|
let res = metadata_pdu.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let written = res.unwrap();
|
let written = res.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
written,
|
written,
|
||||||
pdu_header.header_len() + 1 + 1 + 4 + src_len + dest_len
|
pdu_header.header_len() + 1 + 1 + 4 + src_len + dest_len
|
||||||
);
|
);
|
||||||
verify_raw_header(&pdu_header, &buf);
|
verify_raw_header(metadata_pdu.pdu_header(), &buf);
|
||||||
assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8);
|
assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8);
|
||||||
assert_eq!(buf[8] >> 6, false as u8);
|
assert_eq!(buf[8] >> 6, false as u8);
|
||||||
assert_eq!(buf[8] & 0b1111, ChecksumType::Crc32 as u8);
|
assert_eq!(buf[8] & 0b1111, ChecksumType::Crc32 as u8);
|
||||||
|
@ -257,7 +257,7 @@ impl PduHeader {
|
|||||||
self.header_len() + self.pdu_datafield_len as usize
|
self.header_len() + self.pdu_datafield_len as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
|
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
|
||||||
// Internal note: There is currently no way to pass a PDU configuration like this, but
|
// Internal note: There is currently no way to pass a PDU configuration like this, but
|
||||||
// this check is still kept for defensive programming.
|
// this check is still kept for defensive programming.
|
||||||
if self.pdu_conf.source_entity_id.len() != self.pdu_conf.dest_entity_id.len() {
|
if self.pdu_conf.source_entity_id.len() != self.pdu_conf.dest_entity_id.len() {
|
||||||
@ -340,7 +340,7 @@ impl PduHeader {
|
|||||||
/// to hold the full PDU.
|
/// to hold the full PDU.
|
||||||
///
|
///
|
||||||
/// Both functions can however be performed with the [verify_length_and_checksum] function.
|
/// Both functions can however be performed with the [verify_length_and_checksum] function.
|
||||||
pub fn from_be_bytes(buf: &[u8]) -> Result<(Self, usize), PduError> {
|
pub fn from_bytes(buf: &[u8]) -> Result<(Self, usize), PduError> {
|
||||||
if buf.len() < FIXED_HEADER_LEN {
|
if buf.len() < FIXED_HEADER_LEN {
|
||||||
return Err(PduError::ByteConversionError(
|
return Err(PduError::ByteConversionError(
|
||||||
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
@ -546,7 +546,7 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
// 4 byte fixed header plus three bytes src, dest ID and transaction ID
|
// 4 byte fixed header plus three bytes src, dest ID and transaction ID
|
||||||
assert_eq!(res.unwrap(), 7);
|
assert_eq!(res.unwrap(), 7);
|
||||||
@ -562,9 +562,9 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let deser_res = PduHeader::from_be_bytes(&buf);
|
let deser_res = PduHeader::from_bytes(&buf);
|
||||||
assert!(deser_res.is_ok());
|
assert!(deser_res.is_ok());
|
||||||
let (header_read_back, read_size) = deser_res.unwrap();
|
let (header_read_back, read_size) = deser_res.unwrap();
|
||||||
assert_eq!(read_size, 7);
|
assert_eq!(read_size, 7);
|
||||||
@ -591,7 +591,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(pdu_header.header_len(), 10);
|
assert_eq!(pdu_header.header_len(), 10);
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok(), "{}", format!("Result {res:?} not okay"));
|
assert!(res.is_ok(), "{}", format!("Result {res:?} not okay"));
|
||||||
// 4 byte fixed header, 6 bytes additional fields
|
// 4 byte fixed header, 6 bytes additional fields
|
||||||
assert_eq!(res.unwrap(), 10);
|
assert_eq!(res.unwrap(), 10);
|
||||||
@ -617,9 +617,9 @@ mod tests {
|
|||||||
SegmentationControl::WithRecordBoundaryPreservation,
|
SegmentationControl::WithRecordBoundaryPreservation,
|
||||||
);
|
);
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let deser_res = PduHeader::from_be_bytes(&buf);
|
let deser_res = PduHeader::from_bytes(&buf);
|
||||||
assert!(deser_res.is_ok());
|
assert!(deser_res.is_ok());
|
||||||
let (header_read_back, read_size) = deser_res.unwrap();
|
let (header_read_back, read_size) = deser_res.unwrap();
|
||||||
assert_eq!(read_size, 10);
|
assert_eq!(read_size, 10);
|
||||||
@ -635,11 +635,11 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
buf[0] &= !0b1110_0000;
|
buf[0] &= !0b1110_0000;
|
||||||
buf[0] |= (CFDP_VERSION_2 + 1) << 5;
|
buf[0] |= (CFDP_VERSION_2 + 1) << 5;
|
||||||
let res = PduHeader::from_be_bytes(&buf);
|
let res = PduHeader::from_bytes(&buf);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let error = res.unwrap_err();
|
let error = res.unwrap_err();
|
||||||
if let PduError::CfdpVersionMissmatch(raw_version) = error {
|
if let PduError::CfdpVersionMissmatch(raw_version) = error {
|
||||||
@ -652,7 +652,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_buf_too_small_1() {
|
fn test_buf_too_small_1() {
|
||||||
let buf: [u8; 3] = [0; 3];
|
let buf: [u8; 3] = [0; 3];
|
||||||
let res = PduHeader::from_be_bytes(&buf);
|
let res = PduHeader::from_bytes(&buf);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let error = res.unwrap_err();
|
let error = res.unwrap_err();
|
||||||
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
|
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
|
||||||
@ -674,9 +674,9 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let header = PduHeader::from_be_bytes(&buf[0..6]);
|
let header = PduHeader::from_bytes(&buf[0..6]);
|
||||||
assert!(header.is_err());
|
assert!(header.is_err());
|
||||||
let error = header.unwrap_err();
|
let error = header.unwrap_err();
|
||||||
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
|
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
|
||||||
@ -737,12 +737,12 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
buf[3] &= !0b0111_0000;
|
buf[3] &= !0b0111_0000;
|
||||||
// Equivalent to the length of three
|
// Equivalent to the length of three
|
||||||
buf[3] |= 0b10 << 4;
|
buf[3] |= 0b10 << 4;
|
||||||
let header_res = PduHeader::from_be_bytes(&buf);
|
let header_res = PduHeader::from_bytes(&buf);
|
||||||
assert!(header_res.is_err());
|
assert!(header_res.is_err());
|
||||||
let error = header_res.unwrap_err();
|
let error = header_res.unwrap_err();
|
||||||
if let PduError::InvalidEntityLen(len) = error {
|
if let PduError::InvalidEntityLen(len) = error {
|
||||||
@ -761,12 +761,12 @@ mod tests {
|
|||||||
.expect("common config creation failed");
|
.expect("common config creation failed");
|
||||||
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
let res = pdu_header.write_to_be_bytes(&mut buf);
|
let res = pdu_header.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
buf[3] &= !0b0000_0111;
|
buf[3] &= !0b0000_0111;
|
||||||
// Equivalent to the length of three
|
// Equivalent to the length of three
|
||||||
buf[3] |= 0b10;
|
buf[3] |= 0b10;
|
||||||
let header_res = PduHeader::from_be_bytes(&buf);
|
let header_res = PduHeader::from_bytes(&buf);
|
||||||
assert!(header_res.is_err());
|
assert!(header_res.is_err());
|
||||||
let error = header_res.unwrap_err();
|
let error = header_res.unwrap_err();
|
||||||
if let PduError::InvalidTransactionSeqNumLen(len) = error {
|
if let PduError::InvalidTransactionSeqNumLen(len) = error {
|
||||||
|
Loading…
Reference in New Issue
Block a user