network_types/
llc.rs

1use core::mem;
2
3/// Represents Logical Link Control according to ISO/IEC 8802-2 Definition
4#[repr(C, packed)]
5#[derive(Debug, Copy, Clone)]
6#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
7pub struct LlcHdr {
8    /// Destination SAP address
9    pub dsap: u8,
10    /// Source SAP address
11    pub ssap: u8,
12    /// Byte array for Control field.
13    /// ctrl[0] is always used.
14    /// ctrl[1] is used for I-format and S-format (16-bit control fields).
15    /// For U-format (8-bit control field), ctrl[1] is not part of the logical control field.
16    pub ctrl: [u8; 2],
17}
18
19/// Represents the type of LLC PDU based on its control field.
20#[derive(Debug, PartialEq, Eq, Copy, Clone)]
21pub enum LlcFrameType {
22    I,       // Information
23    S,       // Supervisory
24    U,       // Unnumbered
25    Invalid, // Should not happen with valid LLC frames
26}
27
28impl LlcHdr {
29    pub const LEN: usize = mem::size_of::<LlcHdr>();
30
31    /// Gets the 7-bit DSAP address part.
32    #[inline]
33    pub fn dsap_addr(&self) -> u8 {
34        self.dsap >> 1
35    }
36
37    /// Checks if the DSAP I/G (Individual/Group) bit is set (Individual address).
38    /// true if Individual address, false if Group address.
39    #[inline]
40    pub fn dsap_is_individual_addr(&self) -> bool {
41        self.dsap & 0x01 == 0
42    }
43
44    /// Checks if the DSAP I/G (Individual/Group) bit is set (Group address).
45    /// true if Group address, false if Individual address.
46    #[inline]
47    pub fn dsap_is_group_addr(&self) -> bool {
48        self.dsap & 0x01 == 1
49    }
50
51    /// Sets the DSAP field.
52    /// `addr` should be a 7-bit value.
53    /// `is_group` sets the I/G bit.
54    #[inline]
55    pub fn set_dsap(&mut self, addr: u8, is_group: bool) {
56        self.dsap = ((addr & 0x7F) << 1) | (is_group as u8);
57    }
58
59    /// Gets the 7-bit SSAP address part.
60    #[inline]
61    pub fn ssap_address(&self) -> u8 {
62        self.ssap >> 1
63    }
64
65    /// Checks if the SSAP C/R (Command/Response) bit is set (Command PDU).
66    /// Returns `true` if it's a Command PDU, `false` if it's a Response PDU.
67    #[inline]
68    pub fn ssap_is_command(&self) -> bool {
69        self.ssap & 0x01 == 0
70    }
71
72    /// Checks if the SSAP C/R (Command/Response) bit is set (Response PDU).
73    /// Returns `true` if it's a Response PDU, `false` if it's a Command PDU.
74    #[inline]
75    pub fn ssap_is_response(&self) -> bool {
76        self.ssap & 0x01 == 1
77    }
78
79    /// Sets the SSAP field.
80    /// `address` should be a 7-bit value.
81    /// `is_response` sets the C/R bit.
82    #[inline]
83    pub fn set_ssap(&mut self, address: u8, is_response: bool) {
84        self.ssap = ((address & 0x7F) << 1) | (is_response as u8);
85    }
86
87    /// Determines the LLC PDU frame type based on the control field's first byte.
88    #[inline]
89    pub fn frame_type(&self) -> LlcFrameType {
90        let su = self.ctrl[0] & 0x03;
91        if (self.ctrl[0] & 0x01) == 0x00 {
92            LlcFrameType::I
93        } else if (su) == 0x01 {
94            LlcFrameType::S
95        } else if (su) == 0x03 {
96            LlcFrameType::U
97        } else {
98            LlcFrameType::Invalid // Should not be reachable if LLC frame is valid
99        }
100    }
101
102    /// Returns true if the control field is I-format (16 bits).
103    #[inline]
104    pub fn is_i_format(&self) -> bool {
105        self.frame_type() == LlcFrameType::I
106    }
107
108    /// Returns true if the control field is S-format (16 bits).
109    #[inline]
110    pub fn is_s_format(&self) -> bool {
111        self.frame_type() == LlcFrameType::S
112    }
113
114    /// Returns true if the control field is U-format (8 bits).
115    #[inline]
116    pub fn is_u_format(&self) -> bool {
117        self.frame_type() == LlcFrameType::U
118    }
119
120    /// Gets the raw value of the first byte of the control field.
121    #[inline]
122    pub fn control_byte0(&self) -> u8 {
123        self.ctrl[0]
124    }
125
126    /// Gets the raw value of the second byte of the control field, if applicable.
127    /// Returns Some(u8) for I-Frames and S-Frames, None for U-Frames or Invalid.
128    #[inline]
129    pub fn control_byte1(&self) -> Option<u8> {
130        match self.frame_type() {
131            LlcFrameType::I | LlcFrameType::S => Some(self.ctrl[1]),
132            _ => None,
133        }
134    }
135}
136
137// --- Test Module ---
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    fn new_llc() -> LlcHdr {
143        LlcHdr {
144            dsap: 0,
145            ssap: 0,
146            ctrl: [0; 2],
147        }
148    }
149
150    #[test]
151    fn test_dsap_methods() {
152        let mut llc = new_llc();
153
154        // Test individual address
155        llc.set_dsap(0x42, false); // Address 0x42 (66), Individual
156        assert_eq!(llc.dsap_addr(), 0x42);
157        assert!(!llc.dsap_is_group_addr());
158        assert_eq!(llc.dsap, (0x42 << 1) | 0x00); // 0x84
159
160        // Test group address
161        llc.set_dsap(0x7F, true); // Max 7-bit address, Group
162        assert_eq!(llc.dsap_addr(), 0x7F);
163        assert!(llc.dsap_is_group_addr());
164        assert_eq!(llc.dsap, (0x7F << 1) | 0x01); // 0xFF
165
166        // Test setting with address larger than 7 bits (should be masked)
167        llc.set_dsap(0xFF, false); // Address 0xFF (should become 0x7F), Individual
168        assert_eq!(llc.dsap_addr(), 0x7F);
169        assert!(!llc.dsap_is_group_addr());
170        assert_eq!(llc.dsap, (0x7F << 1) | 0x00); // 0xFE
171    }
172
173    #[test]
174    fn test_ssap_methods() {
175        let mut llc = new_llc();
176
177        // Test command PDU
178        llc.set_ssap(0x3A, false); // Address 0x3A (58), Command
179        assert_eq!(llc.ssap_address(), 0x3A);
180        assert!(!llc.ssap_is_response());
181        assert_eq!(llc.ssap, (0x3A << 1) | 0x00); // 0x74
182
183        // Test response PDU
184        llc.set_ssap(0x01, true); // Address 0x01, Response
185        assert_eq!(llc.ssap_address(), 0x01);
186        assert!(llc.ssap_is_response());
187        assert_eq!(llc.ssap, (0x01 << 1) | 0x01); // 0x03
188
189        // Test setting with address larger than 7 bits (should be masked)
190        llc.set_ssap(0b10101010, true); // Address 0xAA (should become 0x2A), Response
191        assert_eq!(llc.ssap_address(), 0x2A); // 0b0101010
192        assert!(llc.ssap_is_response());
193        assert_eq!(llc.ssap, (0x2A << 1) | 0x01); // 0x55
194    }
195
196    #[test]
197    fn test_u_format_identification_and_bytes() {
198        let mut llc = new_llc();
199        llc.ctrl[0] = 0x03; // Typical UI frame (LSBs are 11)
200        llc.ctrl[1] = 0xFF; // Should be ignored for U-format
201
202        assert_eq!(llc.frame_type(), LlcFrameType::U);
203        assert!(llc.is_u_format());
204        assert!(!llc.is_i_format());
205        assert!(!llc.is_s_format());
206        assert_eq!(llc.control_byte0(), 0x03);
207        assert_eq!(llc.control_byte1(), None); // ctrl[1] is not logically part of U-frame control
208
209        llc.ctrl[0] = 0x6F; // (LSBs are 11)
210        assert_eq!(llc.frame_type(), LlcFrameType::U);
211        assert_eq!(llc.control_byte0(), 0x6F);
212        assert_eq!(llc.control_byte1(), None);
213    }
214
215    #[test]
216    fn test_i_format_identification_and_bytes() {
217        let mut llc = new_llc();
218        // I-frame: LSB of ctrl[0] is 0
219        llc.ctrl[0] = 0x0A; // Example: (00001010)
220        llc.ctrl[1] = 0x83; // Example: (10000011)
221
222        assert_eq!(llc.frame_type(), LlcFrameType::I);
223        assert!(llc.is_i_format());
224        assert!(!llc.is_u_format());
225        assert!(!llc.is_s_format());
226        assert_eq!(llc.control_byte0(), 0x0A);
227        assert_eq!(llc.control_byte1(), Some(0x83));
228
229        llc.ctrl[0] = 0xFE; // Example: (1111111 -> 11111110)
230        llc.ctrl[1] = 0x42; // Example: (0 1000010)
231        assert_eq!(llc.frame_type(), LlcFrameType::I);
232        assert_eq!(llc.control_byte0(), 0xFE);
233        assert_eq!(llc.control_byte1(), Some(0x42));
234    }
235
236    #[test]
237    fn test_s_format_identification_and_bytes() {
238        let mut llc = new_llc();
239        // S-frame: LSBs of ctrl[0] are 01
240        llc.ctrl[0] = 0x01; // Example: 00000001
241        llc.ctrl[1] = 0x07; // Example: (0 0000111)
242
243        assert_eq!(llc.frame_type(), LlcFrameType::S);
244        assert!(llc.is_s_format());
245        assert!(!llc.is_u_format());
246        assert!(!llc.is_i_format());
247        assert_eq!(llc.control_byte0(), 0x01);
248        assert_eq!(llc.control_byte1(), Some(0x07));
249
250        llc.ctrl[0] = 0x0D; // Example: 00001001 -> is 0x09
251                            //  LSBs 01 -> 00001101 is 0x0D
252        llc.ctrl[0] = 0x09; // (000010_01)
253        llc.ctrl[1] = 0x00;
254        assert_eq!(llc.frame_type(), LlcFrameType::S);
255        assert_eq!(llc.control_byte0(), 0x09);
256        assert_eq!(llc.control_byte1(), Some(0x00));
257    }
258
259    #[test]
260    fn test_frame_type_priority() {
261        // Test that I-frame (LSB=0) takes precedence if bits might also look like S/U
262        let mut llc = new_llc();
263        llc.ctrl[0] = 0b0000_0010; // LSB is 0 (I-Frame pattern)
264        assert_eq!(llc.frame_type(), LlcFrameType::I);
265
266        // LSBs 11 (U-Frame pattern)
267        llc.ctrl[0] = 0b0000_0011;
268        assert_eq!(llc.frame_type(), LlcFrameType::U);
269
270        // LSBs 01 (S-Frame pattern)
271        llc.ctrl[0] = 0b0000_0001;
272        assert_eq!(llc.frame_type(), LlcFrameType::S);
273    }
274
275    #[test]
276    fn test_len_constant() {
277        // For a packed struct with 1 u8, 1 u8, and [u8; 2]
278        assert_eq!(LlcHdr::LEN, 1 + 1 + 2);
279        assert_eq!(LlcHdr::LEN, 4);
280    }
281}