use super::BitSlice;
use bitvec::prelude::*;
use num_enum::TryFromPrimitive;
use std::fmt::{Display, Formatter};
use thiserror::Error;
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct GSEHeader {
start: bool,
end: bool,
label_type: LabelType,
gse_length: u16,
frag_id: Option<u8>,
total_length: Option<u16>,
protocol_type: Option<u16>,
label: Option<Label>,
}
impl GSEHeader {
pub fn from_slice(slice: &[u8], re_used_label: Option<&Label>) -> Option<GSEHeader> {
let fixed_len = 2;
if slice.len() < fixed_len {
return None;
}
let fixed = BitSlice::from_slice(&slice[..fixed_len]);
let start = fixed[0];
let end = fixed[1];
let label_type = LabelType::try_from(fixed[2..4].load_be::<u8>()).unwrap();
if !start && !end && matches!(label_type, LabelType::Label6Byte) {
return None;
}
let gse_length = fixed[4..].load_be::<u16>();
let mut remain = &slice[fixed_len..];
let frag_id = if !start || !end {
let (&value, r) = remain.split_first()?;
remain = r;
Some(value)
} else {
None
};
let total_length = if start && !end {
if remain.len() < 2 {
return None;
}
let (field, r) = remain.split_at(2);
remain = r;
Some(u16::from_be_bytes(field.try_into().unwrap()))
} else {
None
};
let protocol_type = if start {
if remain.len() < 2 {
return None;
}
let (field, r) = remain.split_at(2);
remain = r;
Some(u16::from_be_bytes(field.try_into().unwrap()))
} else {
None
};
let label = if start {
if matches!(label_type, LabelType::ReUse) {
if let Some(label) = re_used_label {
Some(label.clone())
} else {
log::error!("LT = re-use, but not label to re-use");
return None;
}
} else {
let label_size = match label_type {
LabelType::Label6Byte => LabelSize::Size6Bytes,
LabelType::Label3Byte => LabelSize::Size3Bytes,
LabelType::Broadcast => LabelSize::Zero,
LabelType::ReUse => unreachable!(),
};
if remain.len() < label_size.len() {
log::error!("not enough bytes for label remain in slice");
return None;
}
let mut data = [0; 6];
data[..label_size.len()].copy_from_slice(&remain[..label_size.len()]);
Some(Label {
data,
size: label_size,
})
}
} else {
None
};
Some(GSEHeader {
start,
end,
label_type,
gse_length,
frag_id,
total_length,
protocol_type,
label,
})
}
pub fn start(&self) -> bool {
self.start
}
pub fn end(&self) -> bool {
self.end
}
pub fn is_single_fragment(&self) -> bool {
self.start() && self.end()
}
pub fn label_type(&self) -> LabelType {
self.label_type
}
pub fn gse_length(&self) -> u16 {
self.gse_length
}
pub fn fragment_id(&self) -> Option<u8> {
self.frag_id
}
pub fn total_length(&self) -> Option<u16> {
self.total_length
}
pub fn protocol_type(&self) -> Option<u16> {
self.protocol_type
}
pub fn label(&self) -> Option<&Label> {
self.label.as_ref()
}
pub fn len(&self) -> usize {
let mut len = 2; if self.frag_id.is_some() {
len += 1;
}
if self.total_length.is_some() {
len += 2;
}
if self.protocol_type.is_some() {
len += 2;
}
if !matches!(self.label_type, LabelType::ReUse)
&& let Some(label) = &self.label
{
len += label.len();
}
len
}
pub fn is_empty(&self) -> bool {
false
}
}
impl Display for GSEHeader {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(
f,
"GSE Header (S = {}, E = {}, LT = {}, GSE Length = {} bytes",
self.start, self.end, self.label_type, self.gse_length
)?;
if let Some(frag_id) = self.frag_id {
write!(f, ", Fragment ID = {}", frag_id)?;
}
if let Some(total_length) = self.total_length {
write!(f, ", Total Length = {}", total_length)?;
}
if let Some(protocol_type) = self.protocol_type {
write!(f, ", Protocol Type = {:#06x}", protocol_type)?;
}
if let Some(label) = &self.label {
write!(f, ", Label = {}", label)?;
}
write!(f, ")")
}
}
#[derive(Debug, Clone, Eq)]
pub struct Label {
data: [u8; 6],
size: LabelSize,
}
impl Label {
pub fn broadcast() -> Label {
Label {
data: [0; 6],
size: LabelSize::Zero,
}
}
pub fn as_slice(&self) -> &[u8] {
&self.data[..self.len()]
}
pub fn len(&self) -> usize {
self.size.len()
}
pub fn is_broadcast(&self) -> bool {
self.is_empty()
}
pub fn is_empty(&self) -> bool {
matches!(self.size, LabelSize::Zero)
}
pub fn from_hex(hex_label: &str) -> Result<Label, LabelParseErr> {
let mut data = [0; 6];
let mut num_data = 0;
for (n, part) in hex_label.split(":").enumerate() {
if n >= 6 {
return Err(LabelParseErr::WrongHexFormat);
}
let Ok(x) = u8::from_str_radix(part, 16) else {
return Err(LabelParseErr::WrongHexFormat);
};
data[n] = x;
num_data = n;
}
let size = match num_data + 1 {
3 => LabelSize::Size3Bytes,
6 => LabelSize::Size6Bytes,
_ => return Err(LabelParseErr::WrongHexFormat),
};
Ok(Label { data, size })
}
}
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum LabelParseErr {
#[error("The hex format for the label is wrong")]
WrongHexFormat,
}
impl PartialEq for Label {
fn eq(&self, other: &Label) -> bool {
self.as_slice() == other.as_slice()
}
}
impl std::hash::Hash for Label {
fn hash<H>(&self, state: &mut H)
where
H: std::hash::Hasher,
{
self.as_slice().hash(state)
}
}
impl Display for Label {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
if let Some((first, rest)) = self.as_slice().split_first() {
write!(f, "{:02x}", first)?;
for b in rest {
write!(f, ":{:02x}", b)?;
}
Ok(())
} else {
write!(f, "broadcast")
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TryFromPrimitive)]
#[repr(u8)]
pub enum LabelType {
Label6Byte = 0b00,
Label3Byte = 0b01,
Broadcast = 0b10,
ReUse = 0b11,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
enum LabelSize {
Size6Bytes,
Size3Bytes,
Zero,
}
impl Display for LabelType {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(
f,
"{}",
match self {
LabelType::Label6Byte => "6 byte label",
LabelType::Label3Byte => "3 byte label",
LabelType::Broadcast => "broadcast label",
LabelType::ReUse => "label re-use",
}
)
}
}
impl LabelSize {
fn len(&self) -> usize {
match self {
LabelSize::Size6Bytes => 6,
LabelSize::Size3Bytes => 3,
LabelSize::Zero => 0,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use hex_literal::hex;
use test_log::test;
const GSE_HEADER_SINGLE_PACKET: [u8; 10] = hex!("c0 5c 08 00 02 00 48 55 4c 4b");
#[test]
fn single_packet() {
let header = GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = true, E = true, LT = 6 byte label, \
GSE Length = 92 bytes, Protocol Type = 0x0800, \
Label = 02:00:48:55:4c:4b)"
);
assert!(header.start());
assert!(header.end());
assert!(header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::Label6Byte);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), None);
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), Some(0x0800));
let label = header.label().unwrap();
assert_eq!(label.as_slice(), &GSE_HEADER_SINGLE_PACKET[4..]);
assert_eq!(label.len(), 6);
assert!(!label.is_empty());
assert_eq!(header.len(), GSE_HEADER_SINGLE_PACKET.len());
assert!(!header.is_empty());
}
#[test]
fn too_short() {
assert!(GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET[..9], None).is_none());
}
const GSE_HEADER_FIRST_FRAGMENT: [u8; 13] = hex!("80 5c 17 01 23 08 00 02 00 48 55 4c 4b");
#[test]
fn first_fragment() {
let header = GSEHeader::from_slice(&GSE_HEADER_FIRST_FRAGMENT, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = true, E = false, LT = 6 byte label, \
GSE Length = 92 bytes, Fragment ID = 23, Total Length = 291, \
Protocol Type = 0x0800, Label = 02:00:48:55:4c:4b)"
);
assert!(header.start());
assert!(!header.end());
assert!(!header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::Label6Byte);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), Some(23));
assert_eq!(header.total_length(), Some(291));
assert_eq!(header.protocol_type(), Some(0x0800));
let label = header.label().unwrap();
assert_eq!(label.as_slice(), &GSE_HEADER_FIRST_FRAGMENT[7..]);
assert_eq!(label.len(), 6);
assert!(!label.is_empty());
assert_eq!(header.len(), GSE_HEADER_FIRST_FRAGMENT.len());
assert!(!header.is_empty());
}
const GSE_HEADER_INTERMEDIATE_FRAGMENT: [u8; 3] = hex!("30 5c 17");
#[test]
fn intermediate_fragment() {
let header = GSEHeader::from_slice(&GSE_HEADER_INTERMEDIATE_FRAGMENT, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = false, E = false, LT = label re-use, \
GSE Length = 92 bytes, Fragment ID = 23)"
);
assert!(!header.start());
assert!(!header.end());
assert!(!header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::ReUse);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), Some(23));
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), None);
assert_eq!(header.label(), None);
assert_eq!(header.len(), GSE_HEADER_INTERMEDIATE_FRAGMENT.len());
assert!(!header.is_empty());
}
const GSE_HEADER_LAST_FRAGMENT: [u8; 3] = hex!("70 5c 17");
#[test]
fn last_fragment() {
let header = GSEHeader::from_slice(&GSE_HEADER_LAST_FRAGMENT, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = false, E = true, LT = label re-use, \
GSE Length = 92 bytes, Fragment ID = 23)"
);
assert!(!header.start());
assert!(header.end());
assert!(!header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::ReUse);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), Some(23));
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), None);
assert_eq!(header.label(), None);
assert_eq!(header.len(), GSE_HEADER_LAST_FRAGMENT.len());
assert!(!header.is_empty());
}
const GSE_HEADER_SINGLE_PACKET_3BYTE_LABEL: [u8; 7] = hex!("d0 5c 08 00 55 4c 4b");
#[test]
fn single_packet_3byte_label() {
let header = GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET_3BYTE_LABEL, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = true, E = true, LT = 3 byte label, \
GSE Length = 92 bytes, Protocol Type = 0x0800, \
Label = 55:4c:4b)"
);
assert!(header.start());
assert!(header.end());
assert!(header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::Label3Byte);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), None);
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), Some(0x0800));
let label = header.label().unwrap();
assert_eq!(label.as_slice(), &GSE_HEADER_SINGLE_PACKET_3BYTE_LABEL[4..]);
assert_eq!(label.len(), 3);
assert!(!label.is_empty());
assert_eq!(header.len(), GSE_HEADER_SINGLE_PACKET_3BYTE_LABEL.len());
assert!(!header.is_empty());
}
const GSE_HEADER_SINGLE_PACKET_BROADCAST_LABEL: [u8; 4] = hex!("e0 5c 08 00");
#[test]
fn single_packet_broadcast_label() {
let header =
GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET_BROADCAST_LABEL, None).unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = true, E = true, LT = broadcast label, \
GSE Length = 92 bytes, Protocol Type = 0x0800, Label = broadcast)"
);
assert!(header.start());
assert!(header.end());
assert!(header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::Broadcast);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), None);
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), Some(0x0800));
let label = header.label().unwrap();
assert_eq!(label.as_slice(), &[]);
assert_eq!(label.len(), 0);
assert!(label.is_empty());
assert_eq!(header.len(), GSE_HEADER_SINGLE_PACKET_BROADCAST_LABEL.len());
assert!(!header.is_empty());
}
const GSE_HEADER_SINGLE_PACKET_LABEL_REUSE: [u8; 4] = hex!("f0 5c 08 00");
#[test]
fn single_packet_label_reuse() {
let g0 = GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET, None).unwrap();
let re_used_label = g0.label().unwrap();
let header =
GSEHeader::from_slice(&GSE_HEADER_SINGLE_PACKET_LABEL_REUSE, Some(re_used_label))
.unwrap();
assert_eq!(
format!("{}", header),
"GSE Header (S = true, E = true, LT = label re-use, \
GSE Length = 92 bytes, Protocol Type = 0x0800, \
Label = 02:00:48:55:4c:4b)"
);
assert!(header.start());
assert!(header.end());
assert!(header.is_single_fragment());
assert_eq!(header.label_type(), LabelType::ReUse);
assert_eq!(header.gse_length(), 92);
assert_eq!(header.fragment_id(), None);
assert_eq!(header.total_length(), None);
assert_eq!(header.protocol_type(), Some(0x0800));
let label = header.label().unwrap();
assert_eq!(label, re_used_label);
assert_eq!(header.len(), GSE_HEADER_SINGLE_PACKET_LABEL_REUSE.len());
assert!(!header.is_empty());
}
#[test]
fn padding_packet() {
assert_eq!(GSEHeader::from_slice(&[0; 2], None), None);
}
#[test]
fn parse_3byte_label() {
assert_eq!(
Label::from_hex("01:27:3a").unwrap(),
Label {
data: [0x01, 0x27, 0x3a, 0x00, 0x00, 0x00],
size: LabelSize::Size3Bytes,
}
);
}
#[test]
fn parse_6byte_label() {
assert_eq!(
Label::from_hex("af:3c:14:59:00:15").unwrap(),
Label {
data: [0xaf, 0x3c, 0x14, 0x59, 0x00, 0x15],
size: LabelSize::Size6Bytes,
}
);
}
#[test]
fn parse_wrong_labels() {
assert!(Label::from_hex("foo").is_err());
assert!(Label::from_hex("01:00").is_err());
assert!(Label::from_hex("01:23:45:67:89:ab:cd").is_err());
assert!(Label::from_hex("01:00:02:00").is_err());
assert!(Label::from_hex("01273a").is_err());
assert!(Label::from_hex("af3c14590015").is_err());
}
#[test]
fn broadcast_label() {
let label = Label::broadcast();
assert!(label.is_broadcast());
assert!(label.is_empty());
}
#[test]
fn non_broadcast_label() {
let label = Label {
data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
size: LabelSize::Size6Bytes,
};
assert!(!label.is_broadcast());
assert!(!label.is_empty());
}
#[test]
fn hash_3bytes_does_not_read_extra_bytes() {
use std::hash::{DefaultHasher, Hash, Hasher};
let a = Label {
data: [0x01, 0x02, 0x03, 0x00, 0x00, 0x00],
size: LabelSize::Size3Bytes,
};
let b = Label {
data: [0x01, 0x02, 0x03, 0xff, 0xff, 0xff],
size: LabelSize::Size3Bytes,
};
let mut s = DefaultHasher::new();
let mut t = DefaultHasher::new();
a.hash(&mut s);
b.hash(&mut t);
assert_eq!(s.finish(), t.finish());
}
}
#[cfg(test)]
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn random_header(header in proptest::collection::vec(any::<u8>(), 0..=32)) {
if let Some(header) = GSEHeader::from_slice(&header, None) {
let _ = format!("{}", header);
header.start();
header.end();
header.is_single_fragment();
header.label_type();
header.gse_length();
header.fragment_id();
header.total_length();
header.protocol_type();
if let Some(label) = header.label() {
label.as_slice();
let len = label.len();
assert_eq!(label.is_empty(), len == 0);
}
assert!(header.len() >= 3);
assert!(!header.is_empty());
}
}
}
}