stackforge_core/layer/icmp/
extensions.rs1use crate::layer::icmp::checksum::icmp_checksum;
14use std::net::{Ipv4Addr, Ipv6Addr};
15
16pub mod class_nums {
18 pub const MPLS: u8 = 1;
19 pub const INTERFACE_INFORMATION: u8 = 2;
20 pub const INTERFACE_IDENTIFICATION: u8 = 3;
21}
22
23pub fn class_name(classnum: u8) -> &'static str {
25 match classnum {
26 class_nums::MPLS => "MPLS",
27 class_nums::INTERFACE_INFORMATION => "Interface Information",
28 class_nums::INTERFACE_IDENTIFICATION => "Interface Identification",
29 _ => "unknown",
30 }
31}
32
33pub mod header_offsets {
35 pub const VERSION_RESERVED: usize = 0; pub const CHECKSUM: usize = 2;
37}
38
39pub const EXTENSION_HEADER_LEN: usize = 4;
40
41pub mod object_offsets {
43 pub const LEN: usize = 0;
44 pub const CLASSNUM: usize = 2;
45 pub const CLASSTYPE: usize = 3;
46 pub const DATA: usize = 4;
47}
48
49pub const EXTENSION_OBJECT_MIN_LEN: usize = 4;
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct InterfaceInfoFlags {
54 pub has_ifindex: bool,
55 pub has_ipaddr: bool,
56 pub has_ifname: bool,
57 pub has_mtu: bool,
58}
59
60impl InterfaceInfoFlags {
61 pub fn from_byte(byte: u8) -> Self {
63 Self {
64 has_ifindex: (byte & 0b0001_0000) != 0,
65 has_ipaddr: (byte & 0b0000_1000) != 0,
66 has_ifname: (byte & 0b0000_0100) != 0,
67 has_mtu: (byte & 0b0000_0001) != 0,
68 }
69 }
70
71 pub fn to_byte(&self) -> u8 {
73 let mut byte = 0u8;
74 if self.has_ifindex {
75 byte |= 0b0001_0000;
76 }
77 if self.has_ipaddr {
78 byte |= 0b0000_1000;
79 }
80 if self.has_ifname {
81 byte |= 0b0000_0100;
82 }
83 if self.has_mtu {
84 byte |= 0b0000_0001;
85 }
86 byte
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum Afi {
93 Ipv4 = 1,
94 Ipv6 = 2,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct InterfaceInformation {
100 pub flags: InterfaceInfoFlags,
101 pub ifindex: Option<u32>,
102 pub ip_addr: Option<IpAddr>,
103 pub ifname: Option<String>,
104 pub mtu: Option<u32>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum IpAddr {
110 V4(Ipv4Addr),
111 V6(Ipv6Addr),
112}
113
114pub fn has_extensions(icmp_type: u8, payload_len: usize) -> bool {
120 use crate::layer::icmp::error;
121
122 if !error::is_error_type(icmp_type) {
124 return false;
125 }
126
127 if payload_len < error::ERROR_MIN_PAYLOAD + EXTENSION_HEADER_LEN {
130 return false;
131 }
132
133 payload_len >= 128 + EXTENSION_HEADER_LEN + EXTENSION_OBJECT_MIN_LEN
136}
137
138pub fn extension_offset(icmp_header_start: usize, icmp_payload: &[u8]) -> Option<usize> {
142 if icmp_payload.len() >= 128 + EXTENSION_HEADER_LEN {
145 Some(icmp_header_start + 8 + 128) } else {
147 None
148 }
149}
150
151pub fn parse_extension_header(buf: &[u8], offset: usize) -> Option<(u8, u16)> {
153 if offset + EXTENSION_HEADER_LEN > buf.len() {
154 return None;
155 }
156
157 let version_reserved = u16::from_be_bytes([buf[offset], buf[offset + 1]]);
158 let version = ((version_reserved >> 12) & 0x0F) as u8;
159 let checksum = u16::from_be_bytes([buf[offset + 2], buf[offset + 3]]);
160
161 if version != 2 {
163 return None;
164 }
165
166 Some((version, checksum))
167}
168
169pub fn verify_extension_checksum(buf: &[u8], ext_start: usize, ext_len: usize) -> bool {
171 if ext_start + ext_len > buf.len() || ext_len < EXTENSION_HEADER_LEN {
172 return false;
173 }
174
175 let mut data = vec![0u8; ext_len];
177 data.copy_from_slice(&buf[ext_start..ext_start + ext_len]);
178 data[2] = 0;
179 data[3] = 0;
180
181 let computed = icmp_checksum(&data);
182 let stored = u16::from_be_bytes([buf[ext_start + 2], buf[ext_start + 3]]);
183
184 computed == stored
185}
186
187pub fn parse_extension_object(buf: &[u8], offset: usize) -> Option<(u16, u8, u8, usize)> {
189 if offset + EXTENSION_OBJECT_MIN_LEN > buf.len() {
190 return None;
191 }
192
193 let len = u16::from_be_bytes([buf[offset], buf[offset + 1]]);
194 let classnum = buf[offset + 2];
195 let classtype = buf[offset + 3];
196
197 if len < 4 || len % 4 != 0 {
199 return None;
200 }
201
202 let data_offset = offset + EXTENSION_OBJECT_MIN_LEN;
203
204 Some((len, classnum, classtype, data_offset))
205}
206
207pub fn parse_interface_information(
209 buf: &[u8],
210 data_offset: usize,
211 data_len: usize,
212) -> Option<InterfaceInformation> {
213 if data_len == 0 {
214 return None;
215 }
216
217 let flags_byte = buf[data_offset];
219 let flags = InterfaceInfoFlags::from_byte(flags_byte);
220
221 let mut offset = data_offset + 1; let mut result = InterfaceInformation {
223 flags,
224 ifindex: None,
225 ip_addr: None,
226 ifname: None,
227 mtu: None,
228 };
229
230 if flags.has_ifindex {
232 if offset + 4 > buf.len() {
233 return None;
234 }
235 result.ifindex = Some(u32::from_be_bytes([
236 buf[offset],
237 buf[offset + 1],
238 buf[offset + 2],
239 buf[offset + 3],
240 ]));
241 offset += 4;
242 }
243
244 if flags.has_ipaddr {
245 if offset + 4 > buf.len() {
246 return None;
247 }
248 let afi = u16::from_be_bytes([buf[offset], buf[offset + 1]]);
249 offset += 4; match afi {
252 1 => {
253 if offset + 4 > buf.len() {
255 return None;
256 }
257 let ip = Ipv4Addr::new(
258 buf[offset],
259 buf[offset + 1],
260 buf[offset + 2],
261 buf[offset + 3],
262 );
263 result.ip_addr = Some(IpAddr::V4(ip));
264 offset += 4;
265 },
266 2 => {
267 if offset + 16 > buf.len() {
269 return None;
270 }
271 let mut octets = [0u8; 16];
272 octets.copy_from_slice(&buf[offset..offset + 16]);
273 result.ip_addr = Some(IpAddr::V6(Ipv6Addr::from(octets)));
274 offset += 16;
275 },
276 _ => return None, }
278 }
279
280 if flags.has_ifname {
281 if offset + 1 > buf.len() {
282 return None;
283 }
284 let name_len = buf[offset] as usize;
285 offset += 1;
286
287 if offset + name_len > buf.len() {
288 return None;
289 }
290 let name_bytes = &buf[offset..offset + name_len];
291 if let Ok(name) = String::from_utf8(name_bytes.to_vec()) {
292 result.ifname = Some(name);
293 }
294 offset += name_len;
295 }
296
297 if flags.has_mtu {
298 if offset + 4 > buf.len() {
299 return None;
300 }
301 result.mtu = Some(u32::from_be_bytes([
302 buf[offset],
303 buf[offset + 1],
304 buf[offset + 2],
305 buf[offset + 3],
306 ]));
307 }
308
309 Some(result)
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn test_interface_info_flags() {
318 let flags = InterfaceInfoFlags {
319 has_ifindex: true,
320 has_ipaddr: true,
321 has_ifname: false,
322 has_mtu: true,
323 };
324
325 let byte = flags.to_byte();
326 assert_eq!(byte, 0b0001_1001);
327
328 let parsed = InterfaceInfoFlags::from_byte(byte);
329 assert_eq!(parsed, flags);
330 }
331
332 #[test]
333 fn test_parse_extension_header() {
334 let buf = [0x20, 0x00, 0x12, 0x34];
336 let result = parse_extension_header(&buf, 0);
337 assert_eq!(result, Some((2, 0x1234)));
338 }
339
340 #[test]
341 fn test_parse_extension_object() {
342 let buf = [0x00, 0x08, 0x02, 0x19, 0x00, 0x00, 0x00, 0x00];
344 let result = parse_extension_object(&buf, 0);
345 assert_eq!(result, Some((8, 2, 0x19, 4)));
346 }
347
348 #[test]
349 fn test_class_name() {
350 assert_eq!(class_name(class_nums::MPLS), "MPLS");
351 assert_eq!(
352 class_name(class_nums::INTERFACE_INFORMATION),
353 "Interface Information"
354 );
355 assert_eq!(class_name(99), "unknown");
356 }
357
358 #[test]
359 fn test_has_extensions() {
360 use crate::layer::icmp::types::types;
361
362 assert!(has_extensions(types::TIME_EXCEEDED, 200));
364
365 assert!(!has_extensions(types::ECHO_REQUEST, 200));
367
368 assert!(!has_extensions(types::TIME_EXCEEDED, 30));
370 }
371}