dvb_ule/type_field.rs
1//! ULE Type field interpretation — RFC 4326 §4.4 / §5, RFC 5163 §3.
2//!
3//! The 16-bit Type field of an SNDU base header (or of a chained extension
4//! header) is split at decimal `1536` (`0x0600`):
5//!
6//! - `Type < 0x0600` — a Next-Header (Extension Header): a 5-bit zero prefix,
7//! a 3-bit `H-LEN`, and an 8-bit `H-Type` (§5, Figure 7).
8//! - `Type >= 0x0600` — an EtherType for the carried PDU (§4.4.2).
9
10/// The boundary between Next-Header codes and EtherTypes (RFC 4326 §4.4):
11/// decimal 1536. Values below are Next-Headers; values at or above are
12/// EtherTypes.
13pub const ETHERTYPE_BOUNDARY: u16 = 0x0600;
14
15/// IPv4 EtherType (RFC 4326 §4.7.2).
16pub const ETHERTYPE_IPV4: u16 = 0x0800;
17/// IPv6 EtherType (RFC 4326 §4.7.3).
18pub const ETHERTYPE_IPV6: u16 = 0x86DD;
19
20/// A decoded ULE Type field (RFC 4326 §4.4).
21///
22/// `Type < 0x0600` is a [`TypeField::NextHeader`] (the start of an extension
23/// header); `Type >= 0x0600` is a [`TypeField::EtherType`] naming the PDU that
24/// directly follows the Type field.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27#[non_exhaustive]
28pub enum TypeField {
29 /// A Next-Header: `H-LEN` (3 bits) + `H-Type` (8 bits), with the 5-bit
30 /// zero prefix that keeps the raw value `< 0x0600`.
31 NextHeader {
32 /// 3-bit length/class selector (RFC 4326 §5). `0` = Mandatory; `1..=5`
33 /// = Optional (total extension bytes = `2 * h_len`).
34 h_len: u8,
35 /// 8-bit extension-header type code.
36 h_type: u8,
37 },
38 /// An EtherType (`>= 0x0600`) of the carried PDU (RFC 4326 §4.4.2).
39 EtherType(u16),
40}
41
42impl TypeField {
43 /// Decode a raw 16-bit Type-field value.
44 pub fn from_u16(raw: u16) -> Self {
45 if raw < ETHERTYPE_BOUNDARY {
46 // 5-bit zero | 3-bit H-LEN | 8-bit H-Type.
47 let h_len = ((raw >> 8) & 0x07) as u8;
48 let h_type = (raw & 0x00FF) as u8;
49 TypeField::NextHeader { h_len, h_type }
50 } else {
51 TypeField::EtherType(raw)
52 }
53 }
54
55 /// Encode back to the raw 16-bit wire value.
56 pub fn to_u16(self) -> u16 {
57 match self {
58 TypeField::NextHeader { h_len, h_type } => ((h_len as u16 & 0x07) << 8) | h_type as u16,
59 TypeField::EtherType(et) => et,
60 }
61 }
62
63 /// `true` if this Type field starts an extension header (Next-Header).
64 pub fn is_next_header(self) -> bool {
65 matches!(self, TypeField::NextHeader { .. })
66 }
67
68 /// Spec label for this kind of Type field.
69 pub fn name(&self) -> &'static str {
70 match self {
71 TypeField::NextHeader { .. } => "next-header",
72 TypeField::EtherType(_) => "ethertype",
73 }
74 }
75}
76
77dvb_common::impl_spec_display!(TypeField);
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn boundary_split() {
85 assert!(TypeField::from_u16(0x05FF).is_next_header());
86 assert!(!TypeField::from_u16(0x0600).is_next_header());
87 assert_eq!(TypeField::from_u16(0x0800), TypeField::EtherType(0x0800));
88 assert_eq!(TypeField::from_u16(0x86DD), TypeField::EtherType(0x86DD));
89 }
90
91 #[test]
92 fn next_header_split_round_trips() {
93 // 0x0301 = H-LEN 3, H-Type 0x01 (TimeStamp).
94 let t = TypeField::from_u16(0x0301);
95 assert_eq!(
96 t,
97 TypeField::NextHeader {
98 h_len: 3,
99 h_type: 0x01
100 }
101 );
102 assert_eq!(t.to_u16(), 0x0301);
103
104 // Mandatory: 0x0001 = H-LEN 0, H-Type 0x01 (Bridged-Frame).
105 let m = TypeField::from_u16(0x0001);
106 assert_eq!(
107 m,
108 TypeField::NextHeader {
109 h_len: 0,
110 h_type: 0x01
111 }
112 );
113 assert_eq!(m.to_u16(), 0x0001);
114 }
115
116 #[test]
117 fn all_u16_round_trip() {
118 for raw in 0u32..=0xFFFF {
119 let raw = raw as u16;
120 assert_eq!(TypeField::from_u16(raw).to_u16(), raw, "raw={raw:#06X}");
121 }
122 }
123}