/// # Support of the CCSDS Unified Space Data Link Protocol (USLP) use crate::{crc::CRC_CCITT_FALSE, ByteConversionError}; /// Only this version is supported by the library pub const USLP_VERSION_NUMBER: u8 = 0b1100; /// Identifies the association of the data contained in the transfer frame. #[derive( Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum SourceOrDestField { /// SCID refers to the source of the transfer frame. Source = 0, /// SCID refers to the destination of the transfer frame. Dest = 1, } #[derive( Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum BypassSequenceControlFlag { /// Acceptance of this frame on the receiving end is subject to normal frame acceptance /// checks of FARM. SequenceControlledQoS = 0, /// Frame Acceptance Checks of FARM by the receiving end shall be bypassed. ExpeditedQoS = 1, } #[derive( Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum ProtocolControlCommandFlag { TfdfContainsUserData = 0, TfdfContainsProtocolInfo = 1, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum UslpError { ByteConversion(ByteConversionError), HeaderIsTruncated, InvalidProtocolId(u8), InvalidConstructionRule(u8), InvalidVersionNumber(u8), InvalidVcid(u8), InvalidMapId(u8), ChecksumFailure(u16), } impl From for UslpError { fn from(value: ByteConversionError) -> Self { Self::ByteConversion(value) } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct InvalidValueForLen { value: u64, len: u8, } #[derive(Debug, Copy, Clone, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PrimaryHeader { pub spacecraft_id: u16, pub source_or_dest_field: SourceOrDestField, pub vc_id: u8, pub map_id: u8, frame_len_field: u16, pub sequence_control_flag: BypassSequenceControlFlag, pub protocol_control_command_flag: ProtocolControlCommandFlag, pub ocf_flag: bool, vc_frame_count_len: u8, vc_frame_count: u64, } impl PrimaryHeader { pub fn new( spacecraft_id: u16, source_or_dest_field: SourceOrDestField, vc_id: u8, map_id: u8, frame_len: u16, ) -> Result { if vc_id > 0b111111 { return Err(UslpError::InvalidVcid(vc_id)); } if map_id > 0b1111 { return Err(UslpError::InvalidMapId(map_id)); } Ok(Self { spacecraft_id, source_or_dest_field, vc_id, map_id, frame_len_field: frame_len.saturating_sub(1), sequence_control_flag: BypassSequenceControlFlag::SequenceControlledQoS, protocol_control_command_flag: ProtocolControlCommandFlag::TfdfContainsUserData, ocf_flag: false, vc_frame_count_len: 0, vc_frame_count: 0, }) } pub fn set_vc_frame_count( &mut self, count_len: u8, count: u64, ) -> Result<(), InvalidValueForLen> { if count > 2_u64.pow(count_len as u32 * 8) - 1 { return Err(InvalidValueForLen { value: count, len: count_len, }); } self.vc_frame_count_len = count_len; self.vc_frame_count = count; Ok(()) } #[inline] pub fn vc_frame_count(&self) -> u64 { self.vc_frame_count } #[inline] pub fn vc_frame_count_len(&self) -> u8 { self.vc_frame_count_len } pub fn from_bytes(buf: &[u8]) -> Result { if buf.len() < 4 { return Err(ByteConversionError::FromSliceTooSmall { found: buf.len(), expected: 4, } .into()); } // Can only deal with regular frames for now. if (buf[3] & 0b1) == 1 { return Err(UslpError::HeaderIsTruncated); } // We could check this above, but this is a better error for the case where the user // tries to read a truncated frame. if buf.len() < 7 { return Err(ByteConversionError::FromSliceTooSmall { found: buf.len(), expected: 7, } .into()); } let version_number = (buf[0] >> 4) & 0b1111; if version_number != USLP_VERSION_NUMBER { return Err(UslpError::InvalidVersionNumber(version_number)); } let source_or_dest_field = match (buf[2] >> 3) & 1 { 0 => SourceOrDestField::Source, 1 => SourceOrDestField::Dest, _ => unreachable!(), }; let vc_frame_count_len = buf[6] & 0b111; if buf.len() < 7 + vc_frame_count_len as usize { return Err(ByteConversionError::FromSliceTooSmall { found: buf.len(), expected: 7 + vc_frame_count_len as usize, } .into()); } let vc_frame_count = match vc_frame_count_len { 1 => buf[7] as u64, 2 => u16::from_be_bytes(buf[7..9].try_into().unwrap()) as u64, 4 => u32::from_be_bytes(buf[7..11].try_into().unwrap()) as u64, len => { let mut vcf_count = 0u64; let mut end = len; for byte in buf[7..7 + len as usize].iter() { vcf_count |= (*byte as u64) << ((end - 1) * 8); end -= 1; } vcf_count } }; Ok(Self { spacecraft_id: (((buf[0] as u16) & 0b1111) << 12) | ((buf[1] as u16) << 4) | ((buf[2] as u16) >> 4) & 0b1111, source_or_dest_field, vc_id: ((buf[2] & 0b111) << 3) | (buf[3] >> 5) & 0b111, map_id: (buf[3] >> 1) & 0b1111, frame_len_field: ((buf[4] as u16) << 8) | buf[5] as u16, sequence_control_flag: ((buf[6] >> 7) & 0b1).try_into().unwrap(), protocol_control_command_flag: ((buf[6] >> 6) & 0b1).try_into().unwrap(), ocf_flag: ((buf[6] >> 3) & 0b1) != 0, vc_frame_count_len, vc_frame_count, }) } pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { if buf.len() < self.len_header() { return Err(ByteConversionError::ToSliceTooSmall { found: buf.len(), expected: self.len_header(), }); } buf[0] = (USLP_VERSION_NUMBER << 4) | ((self.spacecraft_id >> 12) as u8) & 0b1111; buf[1] = (self.spacecraft_id >> 4) as u8; buf[2] = (((self.spacecraft_id & 0b1111) as u8) << 4) | ((self.source_or_dest_field as u8) << 3) | (self.vc_id >> 3) & 0b111; buf[3] = ((self.vc_id & 0b111) << 5) | (self.map_id << 1); buf[4..6].copy_from_slice(&self.frame_len_field.to_be_bytes()); buf[6] = ((self.sequence_control_flag as u8) << 7) | ((self.protocol_control_command_flag as u8) << 6) | ((self.ocf_flag as u8) << 3) | self.vc_frame_count_len; let mut packet_idx = 7; for idx in (0..self.vc_frame_count_len).rev() { buf[packet_idx] = ((self.vc_frame_count >> (idx * 8)) & 0xff) as u8; packet_idx += 1; } Ok(self.len_header()) } #[inline(always)] pub fn set_frame_len(&mut self, frame_len: usize) { // 4.1.2.7.2 // The field contains a length count C that equals one fewer than the total octets // in the transfer frame. self.frame_len_field = frame_len.saturating_sub(1) as u16; } #[inline(always)] pub fn len_header(&self) -> usize { 7 + self.vc_frame_count_len as usize } #[inline(always)] pub fn len_frame(&self) -> usize { // 4.1.2.7.2 // The field contains a length count C that equals one fewer than the total octets // in the transfer frame. self.frame_len_field as usize + 1 } } /// Custom implementation which skips the check whether the VC frame count length field is equal. /// Only the actual VC count value is compared. impl PartialEq for PrimaryHeader { fn eq(&self, other: &Self) -> bool { self.spacecraft_id == other.spacecraft_id && self.source_or_dest_field == other.source_or_dest_field && self.vc_id == other.vc_id && self.map_id == other.map_id && self.frame_len_field == other.frame_len_field && self.sequence_control_flag == other.sequence_control_flag && self.protocol_control_command_flag == other.protocol_control_command_flag && self.ocf_flag == other.ocf_flag && self.vc_frame_count == other.vc_frame_count } } #[derive( Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] #[non_exhaustive] pub enum UslpProtocolId { SpacePacketsOrEncapsulation = 0b00000, /// COP-1 control commands within the TFDZ. Cop1ControlCommands = 0b00001, /// COP-P control commands within the TFDZ. CopPControlCommands = 0b00010, /// SDLS control commands within the TFDZ. Sdls = 0b00011, UserDefinedOctetStream = 0b00100, /// Proximity-1 Supervisory Protocol Data Units (SPDUs) within the TFDZ. Spdu = 0b00111, /// Entire fixed-length TFDZ contains idle data. Idle = 0b11111, } #[derive( Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum ConstructionRule { /// Indicated fixed-length TFDZ whose contents are CCSDS packets concatenated together, which /// span transfer frame boundaries. The First Header Pointer (FHP) is required for packet /// extraction. PacketSpanningMultipleFrames = 0b000, StartOfMapaSduOrVcaSdu = 0b001, ContinuingPortionOfMapaSdu = 0b010, OctetStream = 0b011, StartingSegment = 0b100, ContinuingSegment = 0b101, LastSegment = 0b110, NoSegmentation = 0b111, } impl ConstructionRule { pub const fn applicable_to_fixed_len_tfdz(&self) -> bool { match self { ConstructionRule::PacketSpanningMultipleFrames => true, ConstructionRule::StartOfMapaSduOrVcaSdu => true, ConstructionRule::ContinuingPortionOfMapaSdu => true, ConstructionRule::OctetStream => false, ConstructionRule::StartingSegment => false, ConstructionRule::ContinuingSegment => false, ConstructionRule::LastSegment => false, ConstructionRule::NoSegmentation => false, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TransferFrameDataFieldHeader { /// Construction rule for the TFDZ. construction_rule: ConstructionRule, uslp_protocol_id: UslpProtocolId, /// First header or last valid octet pointer. Only present if the constuction rule indicated /// a fixed-length TFDZ. fhp_or_lvo: Option, } impl TransferFrameDataFieldHeader { pub fn len_header(&self) -> usize { if self.construction_rule.applicable_to_fixed_len_tfdz() { 3 } else { 1 } } pub fn construction_rule(&self) -> ConstructionRule { self.construction_rule } pub fn uslp_protocol_id(&self) -> UslpProtocolId { self.uslp_protocol_id } pub fn fhp_or_lvo(&self) -> Option { self.fhp_or_lvo } pub fn from_bytes(buf: &[u8]) -> Result { if buf.is_empty() { return Err(ByteConversionError::FromSliceTooSmall { found: 0, expected: 1, } .into()); } let construction_rule = ConstructionRule::try_from((buf[0] >> 5) & 0b111) .map_err(|e| UslpError::InvalidConstructionRule(e.number))?; let mut fhp_or_lvo = None; if construction_rule.applicable_to_fixed_len_tfdz() { if buf.len() < 3 { return Err(ByteConversionError::FromSliceTooSmall { found: buf.len(), expected: 3, } .into()); } fhp_or_lvo = Some(u16::from_be_bytes(buf[1..3].try_into().unwrap())); } Ok(Self { construction_rule, uslp_protocol_id: UslpProtocolId::try_from(buf[0] & 0b11111) .map_err(|e| UslpError::InvalidProtocolId(e.number))?, fhp_or_lvo, }) } } /// Simple USLP transfer frame reader. /// /// Currently, only insert zone lengths of 0 are supported. #[derive(Debug)] pub struct TransferFrameReader<'buf> { primary_header: PrimaryHeader, data_field_header: TransferFrameDataFieldHeader, data: &'buf [u8], operational_control_field: Option, } impl<'buf> TransferFrameReader<'buf> { /// This function assumes an insert zone length of 0. pub fn from_bytes( buf: &'buf [u8], has_fecf: bool, ) -> Result, UslpError> { let primary_header = PrimaryHeader::from_bytes(buf)?; if primary_header.len_frame() > buf.len() { return Err(ByteConversionError::FromSliceTooSmall { expected: primary_header.len_frame(), found: buf.len(), } .into()); } let data_field_header = TransferFrameDataFieldHeader::from_bytes(&buf[primary_header.len_header()..])?; let data_idx = primary_header.len_header() + data_field_header.len_header(); let frame_len = primary_header.len_frame(); let mut operational_control_field = None; let mut data_len = frame_len - data_idx; if has_fecf { data_len -= 2; } if primary_header.ocf_flag { data_len -= 4; operational_control_field = Some(u32::from_be_bytes( buf[data_idx + data_len..data_idx + data_len + 4] .try_into() .unwrap(), )); } let data_end = data_idx + data_len; if has_fecf { let mut digest = CRC_CCITT_FALSE.digest(); digest.update(&buf[0..frame_len]); if digest.finalize() != 0 { return Err(UslpError::ChecksumFailure(u16::from_be_bytes( buf[frame_len - 2..frame_len].try_into().unwrap(), ))); } } Ok(Self { primary_header, data_field_header, data: buf[data_idx..data_end].try_into().unwrap(), operational_control_field, }) } pub fn len_frame(&self) -> usize { self.primary_header.len_frame() } pub fn primary_header(&self) -> &PrimaryHeader { &self.primary_header } pub fn data_field_header(&self) -> &TransferFrameDataFieldHeader { &self.data_field_header } pub fn data(&self) -> &'buf [u8] { self.data } pub fn operational_control_field(&self) -> &Option { &self.operational_control_field } } #[cfg(test)] mod tests { use super::*; fn common_basic_check(buf: &[u8]) { assert_eq!(buf[0] >> 4, USLP_VERSION_NUMBER); // First four bits SCID. assert_eq!(buf[0] & 0b1111, 0b1010); // Next eight bits SCID. assert_eq!(buf[1], 0b01011100); // Last four bits SCID. assert_eq!(buf[2] >> 4, 0b0011); assert_eq!((buf[2] >> 3) & 0b1, SourceOrDestField::Dest as u8); // First three bits VCID. assert_eq!(buf[2] & 0b111, 0b110); // Last three bits VCID. assert_eq!(buf[3] >> 5, 0b101); // MAP ID assert_eq!((buf[3] >> 1) & 0b1111, 0b1010); // End of primary header flag assert_eq!(buf[3] & 0b1, 0); assert_eq!(u16::from_be_bytes(buf[4..6].try_into().unwrap()), 0x2344); } #[test] fn test_basic_0() { let mut buf: [u8; 8] = [0; 8]; // Should be all zeros after writing. buf[6] = 0xff; let primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); // Virtual channel count 0. assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 7); common_basic_check(&buf); assert_eq!(primary_header.vc_frame_count_len(), 0); assert_eq!(primary_header.vc_frame_count(), 0); // Bypass / Sequence Control Flag. assert_eq!( (buf[6] >> 7) & 0b1, BypassSequenceControlFlag::SequenceControlledQoS as u8 ); // Protocol Control Command Flag. assert_eq!( (buf[6] >> 6) & 0b1, ProtocolControlCommandFlag::TfdfContainsUserData as u8 ); // OCF flag. assert_eq!((buf[6] >> 3) & 0b1, false as u8); // VCF count length. assert_eq!(buf[6] & 0b111, 0); } #[test] fn test_basic_1() { let mut buf: [u8; 16] = [0; 16]; // Should be all zeros after writing. buf[6] = 0xff; let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); primary_header.sequence_control_flag = BypassSequenceControlFlag::ExpeditedQoS; primary_header.protocol_control_command_flag = ProtocolControlCommandFlag::TfdfContainsProtocolInfo; primary_header.ocf_flag = true; primary_header.set_vc_frame_count(4, 0x12345678).unwrap(); // Virtual channel count 4. assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 11); assert_eq!(primary_header.vc_frame_count_len(), 4); assert_eq!(primary_header.vc_frame_count(), 0x12345678); common_basic_check(&buf); // Bypass / Sequence Control Flag. assert_eq!( (buf[6] >> 7) & 0b1, BypassSequenceControlFlag::ExpeditedQoS as u8 ); // Protocol Control Command Flag. assert_eq!( (buf[6] >> 6) & 0b1, ProtocolControlCommandFlag::TfdfContainsProtocolInfo as u8 ); // OCF flag. assert_eq!((buf[6] >> 3) & 0b1, true as u8); // VCF count length. assert_eq!(buf[6] & 0b111, 4); assert_eq!( u32::from_be_bytes(buf[7..11].try_into().unwrap()), 0x12345678 ); } #[test] fn test_vcf_count_len_two() { let mut buf: [u8; 16] = [0; 16]; // Should be all zeros after writing. buf[6] = 0xff; let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); primary_header.set_vc_frame_count(2, 5).unwrap(); assert_eq!(primary_header.vc_frame_count_len(), 2); assert_eq!(primary_header.vc_frame_count(), 5); assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 9); assert_eq!(buf[6] & 0b111, 2); assert_eq!(u16::from_be_bytes(buf[7..9].try_into().unwrap()), 5); let primary_header = PrimaryHeader::from_bytes(&buf).unwrap(); assert_eq!(primary_header.vc_frame_count_len(), 2); assert_eq!(primary_header.vc_frame_count(), 5); } #[test] fn test_vcf_count_len_one() { let mut buf: [u8; 16] = [0; 16]; // Should be all zeros after writing. buf[6] = 0xff; let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); primary_header.set_vc_frame_count(1, 255).unwrap(); assert_eq!(primary_header.vc_frame_count_len(), 1); assert_eq!(primary_header.vc_frame_count(), 255); assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 8); assert_eq!(buf[6] & 0b111, 1); assert_eq!(buf[7], 255); let primary_header = PrimaryHeader::from_bytes(&buf).unwrap(); assert_eq!(primary_header.vc_frame_count_len(), 1); assert_eq!(primary_header.vc_frame_count(), 255); } #[test] fn test_reading_0() { let mut buf: [u8; 8] = [0; 8]; let primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 7); let parsed_header = PrimaryHeader::from_bytes(&buf).unwrap(); assert_eq!(parsed_header, primary_header); } #[test] fn test_reading_1() { let mut buf: [u8; 16] = [0; 16]; let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); primary_header.sequence_control_flag = BypassSequenceControlFlag::ExpeditedQoS; primary_header.protocol_control_command_flag = ProtocolControlCommandFlag::TfdfContainsProtocolInfo; primary_header.ocf_flag = true; primary_header.set_vc_frame_count(4, 0x12345678).unwrap(); assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 11); let parsed_header = PrimaryHeader::from_bytes(&buf).unwrap(); assert_eq!(parsed_header, primary_header); } #[test] fn test_invalid_vcid() { let error = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b1101011, 0b1010, 0x2345, ); assert!(error.is_err()); let error = error.unwrap_err(); matches!(error, UslpError::InvalidVcid(0b1101011)); } #[test] fn test_invalid_mapid() { let error = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b10101, 0x2345, ); assert!(error.is_err()); let error = error.unwrap_err(); matches!(error, UslpError::InvalidMapId(0b10101)); } #[test] fn test_invalid_vc_count() { let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); matches!( primary_header.set_vc_frame_count(0, 1).unwrap_err(), InvalidValueForLen { value: 1, len: 0 } ); matches!( primary_header.set_vc_frame_count(1, 256).unwrap_err(), InvalidValueForLen { value: 256, len: 1 } ); } #[test] fn test_frame_parser() { let mut buf: [u8; 32] = [0; 32]; // Build a variable frame manually. let mut primary_header = PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); let header_len = primary_header.len_header(); buf[header_len] = ((ConstructionRule::NoSegmentation as u8) << 5) | (UslpProtocolId::UserDefinedOctetStream as u8) & 0b11111; buf[header_len + 1] = 0x42; // 1 byte TFDH, 1 byte data, 2 bytes CRC. primary_header.set_frame_len(header_len + 4); primary_header.write_to_be_bytes(&mut buf).unwrap(); // Calculate and write CRC16. let mut digest = CRC_CCITT_FALSE.digest(); digest.update(&buf[0..header_len + 2]); buf[header_len + 2..header_len + 4].copy_from_slice(&digest.finalize().to_be_bytes()); // Now parse the frame. let frame = TransferFrameReader::from_bytes(&buf, true).unwrap(); assert_eq!(*frame.primary_header(), primary_header); assert_eq!(frame.data().len(), 1); assert_eq!(frame.data()[0], 0x42); assert_eq!( frame.data_field_header().uslp_protocol_id(), UslpProtocolId::UserDefinedOctetStream ); assert_eq!( frame.data_field_header().construction_rule(), ConstructionRule::NoSegmentation ); assert!(frame.data_field_header().fhp_or_lvo().is_none()); assert_eq!(frame.len_frame(), 11); assert!(frame.operational_control_field().is_none()); } #[test] fn test_frame_parser_invalid_checksum() { let mut buf: [u8; 32] = [0; 32]; // Build a variable frame manually. let mut primary_header = PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); let header_len = primary_header.len_header(); buf[header_len] = ((ConstructionRule::NoSegmentation as u8) << 5) | (UslpProtocolId::UserDefinedOctetStream as u8) & 0b11111; buf[header_len + 1] = 0x42; // 1 byte TFDH, 1 byte data, 2 bytes CRC. primary_header.set_frame_len(header_len + 4); primary_header.write_to_be_bytes(&mut buf).unwrap(); // Now parse the frame without having calculated the checksum. match TransferFrameReader::from_bytes(&buf, true) { Ok(_) => panic!("transfer frame read call did not fail"), Err(e) => { assert_eq!(e, UslpError::ChecksumFailure(0)); } } } #[test] fn test_frame_parser_buf_too_small() { let mut buf: [u8; 32] = [0; 32]; // Build a variable frame manually. let mut primary_header = PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); let header_len = primary_header.len_header(); buf[header_len] = ((ConstructionRule::NoSegmentation as u8) << 5) | (UslpProtocolId::UserDefinedOctetStream as u8) & 0b11111; buf[header_len + 1] = 0x42; // 1 byte TFDH, 1 byte data, 2 bytes CRC. primary_header.set_frame_len(header_len + 4); primary_header.write_to_be_bytes(&mut buf).unwrap(); // Now parse the frame. let error = TransferFrameReader::from_bytes(&buf[0..7], true).unwrap_err(); assert_eq!( error, ByteConversionError::FromSliceTooSmall { expected: primary_header.len_frame(), found: 7 } .into() ); } #[test] fn test_from_bytes_too_small_0() { let buf: [u8; 3] = [0; 3]; assert_eq!( PrimaryHeader::from_bytes(&buf).unwrap_err(), ByteConversionError::FromSliceTooSmall { found: 3, expected: 4 } .into() ); } #[test] fn test_from_bytes_too_small_1() { let buf: [u8; 6] = [0; 6]; assert_eq!( PrimaryHeader::from_bytes(&buf).unwrap_err(), ByteConversionError::FromSliceTooSmall { found: 6, expected: 7 } .into() ); } #[test] fn test_from_bytes_truncated_not_supported() { let mut buf: [u8; 7] = [0; 7]; let primary_header = PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); primary_header.write_to_be_bytes(&mut buf).unwrap(); // Set truncated header flag manually. buf[3] |= 0b1; assert_eq!( PrimaryHeader::from_bytes(&buf).unwrap_err(), UslpError::HeaderIsTruncated ); } #[test] fn test_from_bytes_too_small_2() { let mut buf: [u8; 16] = [0; 16]; // Should be all zeros after writing. buf[6] = 0xff; let mut primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); primary_header.set_vc_frame_count(4, 0x12345678).unwrap(); primary_header.write_to_be_bytes(&mut buf).unwrap(); assert_eq!( PrimaryHeader::from_bytes(&buf[0..8]).unwrap_err(), UslpError::ByteConversion(ByteConversionError::FromSliceTooSmall { found: 8, expected: 11 }) ); } #[test] fn test_invalid_version_number() { let mut buf: [u8; 7] = [0; 7]; let primary_header = PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); primary_header.write_to_be_bytes(&mut buf).unwrap(); buf[0] &= 0b00001111; assert_eq!( PrimaryHeader::from_bytes(&buf).unwrap_err(), UslpError::InvalidVersionNumber(0) ); } #[test] fn test_primary_header_buf_too_small() { let primary_header = PrimaryHeader::new( 0b10100101_11000011, SourceOrDestField::Dest, 0b110101, 0b1010, 0x2345, ) .unwrap(); if let Err(ByteConversionError::ToSliceTooSmall { found, expected }) = primary_header.write_to_be_bytes(&mut [0; 4]) { assert_eq!(found, 4); assert_eq!(expected, 7); } else { panic!("writing primary header did not fail or failed with wrong error"); } } #[test] fn test_applicability_contr_rules() { assert!(ConstructionRule::PacketSpanningMultipleFrames.applicable_to_fixed_len_tfdz()); assert!(ConstructionRule::StartOfMapaSduOrVcaSdu.applicable_to_fixed_len_tfdz()); assert!(ConstructionRule::ContinuingPortionOfMapaSdu.applicable_to_fixed_len_tfdz()); assert!(!ConstructionRule::OctetStream.applicable_to_fixed_len_tfdz()); assert!(!ConstructionRule::StartingSegment.applicable_to_fixed_len_tfdz()); assert!(!ConstructionRule::ContinuingSegment.applicable_to_fixed_len_tfdz()); assert!(!ConstructionRule::LastSegment.applicable_to_fixed_len_tfdz()); assert!(!ConstructionRule::NoSegmentation.applicable_to_fixed_len_tfdz()); } #[test] fn test_header_len_correctness() { let mut tfdh = TransferFrameDataFieldHeader { construction_rule: ConstructionRule::PacketSpanningMultipleFrames, uslp_protocol_id: UslpProtocolId::UserDefinedOctetStream, fhp_or_lvo: Some(0), }; assert_eq!(tfdh.len_header(), 3); tfdh = TransferFrameDataFieldHeader { construction_rule: ConstructionRule::StartOfMapaSduOrVcaSdu, uslp_protocol_id: UslpProtocolId::UserDefinedOctetStream, fhp_or_lvo: Some(0), }; assert_eq!(tfdh.len_header(), 3); tfdh = TransferFrameDataFieldHeader { construction_rule: ConstructionRule::ContinuingPortionOfMapaSdu, uslp_protocol_id: UslpProtocolId::UserDefinedOctetStream, fhp_or_lvo: Some(0), }; assert_eq!(tfdh.len_header(), 3); tfdh = TransferFrameDataFieldHeader { construction_rule: ConstructionRule::OctetStream, uslp_protocol_id: UslpProtocolId::UserDefinedOctetStream, fhp_or_lvo: None, }; assert_eq!(tfdh.len_header(), 1); } #[test] fn test_frame_data_field_header_from_bytes_too_small() { let buf: [u8; 0] = []; assert_eq!( TransferFrameDataFieldHeader::from_bytes(&buf).unwrap_err(), ByteConversionError::FromSliceTooSmall { found: 0, expected: 1 } .into() ); } }