CFDP initial packet support #14

Merged
muellerr merged 49 commits from cfdp_first_support into main 2023-07-02 17:31:17 +02:00
4 changed files with 194 additions and 61 deletions
Showing only changes of commit 81eb8e7887 - Show all commits

View File

@ -0,0 +1,19 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Check" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="check --all-features" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<option name="emulateTerminal" value="false" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="true" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<envs />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,4 +1,4 @@
use crate::cfdp::pdu::{write_file_size, PduError, PduHeader};
use crate::cfdp::pdu::{read_fss_field, write_fss_field, PduError, PduHeader};
use crate::cfdp::{CrcFlag, LargeFileFlag, PduType, SegmentMetadataFlag};
use crate::{ByteConversionError, SizeMissmatch};
use num_enum::{IntoPrimitive, TryFromPrimitive};
@ -23,12 +23,12 @@ pub struct SegmentMetadata<'seg_meta> {
metadata: Option<&'seg_meta [u8]>,
}
impl SegmentMetadata<'_> {
impl<'seg_meta> SegmentMetadata<'seg_meta> {
pub fn written_len(&self) -> usize {
1 + self.seg_metadata_len as usize
}
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
pub(crate) fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
if buf.len() < self.written_len() {
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(),
@ -41,6 +41,27 @@ impl SegmentMetadata<'_> {
}
Ok(self.written_len())
}
pub(crate) fn from_bytes(buf: &'seg_meta [u8]) -> Result<Self, ByteConversionError> {
if buf.is_empty() {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: 2,
}));
}
let mut metadata = None;
let seg_metadata_len = (buf[0] & 0b111111) as usize;
if seg_metadata_len > 0 {
metadata = Some(&buf[1..1 + seg_metadata_len]);
}
Ok(Self {
// Can't fail, only 2 bits
record_continuation_state: RecordContinuationState::try_from((buf[0] >> 6) & 0b11)
.unwrap(),
seg_metadata_len: seg_metadata_len as u8,
metadata,
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -81,30 +102,33 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
if segment_metadata.is_some() {
pdu_header.seg_metadata_flag = SegmentMetadataFlag::Present;
}
Self {
let mut pdu = Self {
pdu_header,
segment_metadata,
offset,
file_data,
}
};
pdu.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16;
pdu
}
pub fn written_len(&self) -> usize {
let mut len = self.pdu_header.header_len();
if self.segment_metadata.is_some() {
len += self.segment_metadata.as_ref().unwrap().written_len()
}
// Regular file size
len += core::mem::size_of::<u32>();
fn calc_pdu_datafield_len(&self) -> usize {
let mut len = core::mem::size_of::<u32>();
if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
len += core::mem::size_of::<u32>();
}
if self.segment_metadata.is_some() {
len += self.segment_metadata.as_ref().unwrap().written_len()
}
len += self.file_data.len();
if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc {
len += 2;
}
len
}
pub fn written_len(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
pub fn offset(&self) -> u64 {
self.offset
@ -114,7 +138,7 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
self.file_data
}
pub fn seg_metadata(&self) -> Option<&SegmentMetadata> {
pub fn segment_metadata(&self) -> Option<&SegmentMetadata> {
self.segment_metadata.as_ref()
}
@ -134,15 +158,51 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
.unwrap()
.write_to_bytes(&mut buf[current_idx..])?;
}
write_file_size(
&mut current_idx,
current_idx += write_fss_field(
self.pdu_header.common_pdu_conf().file_flag,
self.offset,
buf,
&mut buf[current_idx..],
)?;
buf[current_idx..current_idx + self.file_data.len()].copy_from_slice(self.file_data);
current_idx += self.file_data.len();
Ok(current_idx)
}
pub fn from_bytes<'longest: 'seg_meta + 'file_data>(
buf: &'longest [u8],
) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
let mut min_expected_len = current_idx + core::mem::size_of::<u32>();
min_expected_len = core::cmp::max(min_expected_len, pdu_header.pdu_len());
if buf.len() < min_expected_len {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: min_expected_len,
})
.into());
}
let mut segment_metadata = None;
if pdu_header.seg_metadata_flag == SegmentMetadataFlag::Present {
segment_metadata = Some(SegmentMetadata::from_bytes(&buf[current_idx..])?);
current_idx += segment_metadata.as_ref().unwrap().written_len();
}
let (fss, offset) = read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]);
current_idx += fss;
if current_idx > full_len_without_crc {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: current_idx,
expected: full_len_without_crc,
})
.into());
}
Ok(Self {
pdu_header,
segment_metadata,
offset,
file_data: &buf[current_idx..full_len_without_crc],
})
}
}
#[cfg(test)]
@ -163,5 +223,63 @@ mod tests {
let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
assert_eq!(fd_pdu.file_data(), file_data);
assert_eq!(fd_pdu.offset(), 10);
assert!(fd_pdu.segment_metadata().is_none());
assert_eq!(
fd_pdu.written_len(),
fd_pdu.pdu_header.header_len() + core::mem::size_of::<u32>() + 4
);
}
#[test]
fn test_serialization() {
let src_id = UbfU8::new(1);
let dest_id = UbfU8::new(2);
let transaction_seq_num = UbfU8::new(3);
let common_conf =
CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_seq_num).unwrap();
let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
let file_data: [u8; 4] = [1, 2, 3, 4];
let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
let mut buf: [u8; 32] = [0; 32];
let res = fd_pdu.write_to_bytes(&mut buf);
assert!(res.is_ok());
let written = res.unwrap();
assert_eq!(
written,
fd_pdu.pdu_header.header_len() + core::mem::size_of::<u32>() + 4
);
let mut current_idx = fd_pdu.pdu_header.header_len();
let file_size = u32::from_be_bytes(
buf[fd_pdu.pdu_header.header_len()..fd_pdu.pdu_header.header_len() + 4]
.try_into()
.unwrap(),
);
current_idx += 4;
assert_eq!(file_size, 10);
assert_eq!(buf[current_idx], 1);
current_idx += 1;
assert_eq!(buf[current_idx], 2);
current_idx += 1;
assert_eq!(buf[current_idx], 3);
current_idx += 1;
assert_eq!(buf[current_idx], 4);
}
#[test]
fn test_deserialization() {
let src_id = UbfU8::new(1);
let dest_id = UbfU8::new(2);
let transaction_seq_num = UbfU8::new(3);
let common_conf =
CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_seq_num).unwrap();
let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
let file_data: [u8; 4] = [1, 2, 3, 4];
let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
let mut buf: [u8; 32] = [0; 32];
fd_pdu.write_to_bytes(&mut buf).unwrap();
let fd_pdu_read_back = FileDataPdu::from_bytes(&buf);
assert!(fd_pdu_read_back.is_ok());
let fd_pdu_read_back = fd_pdu_read_back.unwrap();
assert_eq!(fd_pdu_read_back, fd_pdu);
}
}

View File

@ -1,5 +1,5 @@
use crate::cfdp::lv::Lv;
use crate::cfdp::pdu::{write_file_size, FileDirectiveType, PduError, PduHeader};
use crate::cfdp::pdu::{read_fss_field, write_fss_field, FileDirectiveType, PduError, PduHeader};
use crate::cfdp::tlv::Tlv;
use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag, PduType};
use crate::{ByteConversionError, SizeMissmatch, CRC_CCITT_FALSE};
@ -131,26 +131,15 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
options: Option<&'opts [u8]>,
) -> Self {
pdu_header.pdu_type = PduType::FileDirective;
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 {
let mut pdu = Self {
pdu_header,
metadata_params,
src_file_name,
dest_file_name,
options,
}
};
pdu.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16;
pdu
}
pub fn src_file_name(&self) -> Lv<'src_name> {
@ -177,7 +166,12 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
pub fn written_len(&self) -> usize {
// One directive type octet, and one byte of the parameter field.
let mut len = self.pdu_header.header_len() + 2;
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
fn calc_pdu_datafield_len(&self) -> usize {
// One directve type octet and one byte of the directive parameter field.
let mut len = 2;
if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
len += 8;
} else {
@ -214,11 +208,10 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 7)
| (self.metadata_params.checksum_type as u8);
current_idx += 1;
write_file_size(
&mut current_idx,
current_idx += write_fss_field(
self.pdu_header.common_pdu_conf().file_flag,
self.metadata_params.file_size,
buf,
&mut buf[current_idx..],
)?;
current_idx += self
.src_file_name
@ -241,7 +234,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
pub fn from_bytes<'longest: 'src_name + 'dest_name + 'opts>(
buf: &'longest [u8],
) -> Result<MetadataPdu<'src_name, 'dest_name, 'opts>, PduError> {
) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(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;
@ -268,22 +261,15 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> {
)));
}
current_idx += 1;
let file_size = if pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
u64::from_be_bytes(buf[current_idx + 1..current_idx + 9].try_into().unwrap())
} else {
u32::from_be_bytes(buf[current_idx + 1..current_idx + 5].try_into().unwrap()) as u64
};
let (fss_len, file_size) =
read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx + 1..]);
let metadata_params = MetadataGenericParams {
closure_requested: ((buf[current_idx] >> 6) & 0b1) != 0,
checksum_type: ChecksumType::try_from(buf[current_idx] & 0b1111)
.map_err(|_| PduError::InvalidChecksumType(buf[current_idx] & 0b1111))?,
file_size,
};
current_idx += 5;
if is_large_file {
current_idx += 4;
}
current_idx += 1 + fss_len;
let src_file_name = Lv::from_bytes(&buf[current_idx..])?;
current_idx += src_file_name.len_full();
let dest_file_name = Lv::from_bytes(&buf[current_idx..])?;
@ -343,7 +329,7 @@ pub mod tests {
MetadataPdu<'static, 'static, 'opts>,
) {
let pdu_header = PduHeader::new_no_file_data(common_pdu_conf(crc_flag, fss), 0);
let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 10);
let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 0x1010);
let src_filename = Lv::new_from_str(SRC_FILENAME).expect("Generating string LV failed");
let dest_filename =
Lv::new_from_str(DEST_FILENAME).expect("Generating destination LV failed");
@ -399,7 +385,7 @@ pub mod tests {
assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8);
assert_eq!(buf[8] >> 6, false as u8);
assert_eq!(buf[8] & 0b1111, ChecksumType::Crc32 as u8);
assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 10);
assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010);
let mut current_idx = 13;
let src_name_from_raw =
Lv::from_bytes(&buf[current_idx..]).expect("Creating source name LV failed");

View File

@ -468,25 +468,35 @@ impl PduHeader {
}
}
pub(crate) fn write_file_size(
current_idx: &mut usize,
fss: LargeFileFlag,
pub(crate) fn write_fss_field(
file_flag: LargeFileFlag,
file_size: u64,
buf: &mut [u8],
) -> Result<(), PduError> {
if fss == LargeFileFlag::Large {
buf[*current_idx..*current_idx + core::mem::size_of::<u64>()]
.copy_from_slice(&file_size.to_be_bytes());
*current_idx += core::mem::size_of::<u64>()
) -> Result<usize, PduError> {
Ok(if file_flag == LargeFileFlag::Large {
buf[..core::mem::size_of::<u64>()].copy_from_slice(&file_size.to_be_bytes());
core::mem::size_of::<u64>()
} else {
if file_size > u32::MAX as u64 {
return Err(PduError::FileSizeTooLarge(file_size));
}
buf[*current_idx..*current_idx + core::mem::size_of::<u32>()]
.copy_from_slice(&(file_size as u32).to_be_bytes());
*current_idx += core::mem::size_of::<u32>()
buf[..core::mem::size_of::<u32>()].copy_from_slice(&(file_size as u32).to_be_bytes());
core::mem::size_of::<u32>()
})
}
pub(crate) fn read_fss_field(file_flag: LargeFileFlag, buf: &[u8]) -> (usize, u64) {
if file_flag == LargeFileFlag::Large {
(
core::mem::size_of::<u64>(),
u64::from_be_bytes(buf[..core::mem::size_of::<u64>()].try_into().unwrap()),
)
} else {
(
core::mem::size_of::<u32>(),
u32::from_be_bytes(buf[..core::mem::size_of::<u32>()].try_into().unwrap()).into(),
)
}
Ok(())
}
#[cfg(test)]