Skip to main content

network_types/
llc.rs

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