Skip to main content

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}