scalpel/layers/
ipv4.rs

1//! IPv4 Layer
2
3use core::convert::TryInto as _;
4
5use std::collections::HashMap;
6use std::sync::{OnceLock, RwLock};
7
8use serde::Serialize;
9
10use crate::errors::Error;
11use crate::types::{IPv4Address, LayerCreatorFn};
12use crate::Layer;
13
14/// Basic Length of the IPv4 Header when no options are present
15pub const IPV4_BASE_HEADER_LENGTH: usize = 20_usize;
16
17pub const IPV4_OPTION_EOOL: u8 = 0;
18pub const IPV4_OPTION_NOP: u8 = 1;
19pub const IPV4_OPTION_RR: u8 = 7;
20pub const IPV4_OPTION_MTUP: u8 = 11;
21pub const IPV4_OPTION_MTUR: u8 = 12;
22
23pub const IPPROTO_RAW: u8 = 0xFF;
24
25fn get_protocol_map() -> &'static RwLock<HashMap<u8, LayerCreatorFn>> {
26    static PROTOCOLS_MAP: OnceLock<RwLock<HashMap<u8, LayerCreatorFn>>> = OnceLock::new();
27    PROTOCOLS_MAP.get_or_init(|| RwLock::new(HashMap::new()))
28}
29
30// Register ourselves to well-known Layer 2
31//
32// Right now only Ethernet is Supported
33pub(crate) fn register_defaults() -> Result<(), Error> {
34    use crate::layers::ethernet::register_ethertype;
35
36    get_protocol_map();
37
38    register_ethertype(crate::types::ETHERTYPE_IP, IPv4::creator)?;
39
40    Ok(())
41}
42
43/// Register a Transport Protocol for dissection.
44///
45/// Higher level protocols should call this function to register themselves for decoding with the
46/// IPv4 Layer. For example, [TCP Protocol][`crate::layers::tcp`] would call this function with a
47/// protocol number 6 and similarly [UDP Protocol][`crate::layers::udp`] would call this function
48/// with a protocol number of 17.
49pub fn register_protocol(proto: u8, creator: LayerCreatorFn) -> Result<(), Error> {
50    let mut map = get_protocol_map().write().unwrap();
51    if map.contains_key(&proto) {
52        return Err(Error::RegisterError(format!("proto: {}", proto)));
53    }
54    map.insert(proto, creator);
55
56    Ok(())
57}
58
59#[derive(Debug, Serialize, PartialEq)]
60#[serde(tag = "type")]
61pub enum IPOption {
62    EOOL,
63    NOP,
64    RR {
65        len: u8,
66        ptr: u8,
67        route: Vec<IPv4Address>,
68    },
69    MTUP {
70        len: u8,
71        value: u16,
72    },
73    MTUR {
74        len: u8,
75        value: u16,
76    },
77    Other {
78        value: u8,
79        len: u8,
80        data: Vec<u8>,
81    },
82}
83
84#[derive(Debug, Default, Serialize)]
85pub struct IPv4 {
86    version: u8,
87    hdr_len: u8,
88    tos: u8,
89    len: u16,
90    #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")]
91    id: u16,
92    #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u8")]
93    flags: u8,
94    frag_offset: u16,
95    ttl: u8,
96    proto: u8,
97    #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")]
98    checksum: u16,
99    src_addr: IPv4Address,
100    dst_addr: IPv4Address,
101    #[serde(skip_serializing_if = "Vec::is_empty")]
102    options: Vec<IPOption>,
103}
104
105impl IPv4 {
106    pub fn new() -> Self {
107        Self {
108            version: 4,
109            hdr_len: (IPV4_BASE_HEADER_LENGTH / 4) as u8,
110            proto: IPPROTO_RAW,
111            len: IPV4_BASE_HEADER_LENGTH as u16,
112            ..Default::default()
113        }
114    }
115
116    pub(crate) fn creator() -> Box<dyn Layer + Send> {
117        Box::<IPv4>::default()
118    }
119}
120
121impl IPv4 {
122    fn options_from_bytes(&mut self, bytes: &[u8], mut remaining: usize) -> Result<usize, Error> {
123        let mut i = 0_usize;
124        let max_option_bytes = self.hdr_len as usize * 4 - IPV4_BASE_HEADER_LENGTH;
125        let mut done = i >= max_option_bytes;
126
127        while !done {
128            let (option, consumed) = Self::option_from_bytes(&bytes[i..], remaining)?;
129            i += consumed;
130            remaining -= consumed;
131            if i >= max_option_bytes || option == IPOption::EOOL {
132                done = true;
133            }
134            self.options.push(option);
135        }
136
137        Ok(i)
138    }
139
140    fn option_from_bytes(bytes: &[u8], remaining: usize) -> Result<(IPOption, usize), Error> {
141        let mut i = 0_usize;
142
143        if remaining < 1 {
144            return Err(Error::TooShort {
145                required: 2,
146                available: remaining,
147                data: hex::encode(bytes),
148            });
149        }
150
151        let value = bytes[0];
152
153        // from: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
154        let option = match value {
155            IPV4_OPTION_EOOL => {
156                i += 1;
157                // remaining -= 1;
158                IPOption::EOOL
159            }
160            IPV4_OPTION_NOP => {
161                i += 1;
162                // remaining -= 1;
163                IPOption::NOP
164            }
165            IPV4_OPTION_RR => {
166                let ((len, data), consumed) = Self::option_data_from_bytes(&bytes[i..], remaining)?;
167                i += consumed;
168                // remaining -= consumed;
169
170                let ptr = data[0];
171                let mut route = Vec::new();
172
173                {
174                    // ptr within data
175                    let ptr = ptr as usize - 3;
176                    let mut i = 1_usize;
177                    // run till either data is exhausted or we hit ptr
178                    while i + 2 < len as usize && i < ptr {
179                        let addr = data[i..i + 4].try_into().unwrap();
180                        route.push(addr);
181                        i += 4;
182                    }
183                }
184
185                IPOption::RR { len, ptr, route }
186            }
187            IPV4_OPTION_MTUP | IPV4_OPTION_MTUR => {
188                let ((len, data), consumed) = Self::option_data_from_bytes(&bytes[i..], remaining)?;
189                i += consumed;
190                // remaining -= consumed;
191
192                if len < 4 {
193                    return Err(Error::TooShort {
194                        required: 4,
195                        available: len.into(),
196                        data: hex::encode(data),
197                    });
198                }
199
200                let mtu = u16::from_be_bytes(data[0..2].try_into().unwrap());
201
202                match value {
203                    IPV4_OPTION_MTUP => IPOption::MTUP { len, value: mtu },
204                    IPV4_OPTION_MTUR => IPOption::MTUR { len, value: mtu },
205                    _ => unreachable!("Value should either be 11 or 12"),
206                }
207            }
208            value => {
209                let ((len, data), consumed) = Self::option_data_from_bytes(&bytes[i..], remaining)?;
210                i += consumed;
211                // remaining -= consumed;
212
213                IPOption::Other {
214                    value,
215                    len,
216                    data: data.into(),
217                }
218            }
219        };
220
221        Ok((option, i))
222    }
223
224    fn option_data_from_bytes(
225        bytes: &[u8],
226        mut remaining: usize,
227    ) -> Result<((u8, &[u8]), usize), Error> {
228        // skip first byte(type)
229        let mut i = 1_usize;
230        remaining -= 1;
231        // check if we have enough bytes for length field
232        if remaining < 1 {
233            return Err(Error::TooShort {
234                required: 1,
235                available: remaining,
236                data: hex::encode(bytes),
237            });
238        }
239        // len also includes the type and len octets
240        let len = bytes[i] as usize;
241        i += 1;
242        remaining -= 1;
243
244        if remaining + 2 < len {
245            return Err(Error::TooShort {
246                required: len,
247                available: remaining + 2,
248                data: hex::encode(bytes),
249            });
250        }
251        let data = &bytes[i..i + len - 2];
252        i += len - 2;
253        // remaining -= len - 2;
254
255        Ok(((len as u8, data), i))
256    }
257
258    #[cfg(feature = "sculpting")]
259    fn calculate_checksum(_bytes: &[u8]) -> u16 {
260        0
261    }
262}
263
264impl Layer for IPv4 {
265    fn decode_bytes(
266        &mut self,
267        bytes: &[u8],
268    ) -> Result<(Option<Box<dyn Layer + Send>>, usize), Error> {
269        let mut decoded = 0_usize;
270        let mut remaining = bytes.len();
271
272        self.version = bytes[0] >> 4;
273        self.hdr_len = bytes[0] & 0x0f;
274        // Length is in 4 octets
275        if bytes.len() < (self.hdr_len * 4).into() {
276            return Err(Error::TooShort {
277                required: self.hdr_len as usize * 4,
278                available: bytes.len(),
279                data: hex::encode(bytes),
280            });
281        }
282        self.tos = bytes[1];
283        self.len = u16::from_be_bytes(bytes[2..4].try_into().unwrap());
284        self.id = u16::from_be_bytes(bytes[4..6].try_into().unwrap());
285        let flags_offset = u16::from_be_bytes(bytes[6..8].try_into().unwrap());
286        self.flags = (flags_offset >> 13) as u8;
287        self.frag_offset = flags_offset & 0x1fff;
288        self.ttl = bytes[8];
289        self.proto = bytes[9];
290        self.checksum = u16::from_be_bytes(bytes[10..12].try_into().unwrap());
291        self.src_addr = bytes[12..16].try_into().unwrap();
292        self.dst_addr = bytes[16..20].try_into().unwrap();
293
294        decoded += IPV4_BASE_HEADER_LENGTH;
295        remaining -= IPV4_BASE_HEADER_LENGTH;
296
297        // check if enough bytes exist for options
298        if bytes.len() < self.hdr_len as usize * 4 {
299            return Err(Error::TooShort {
300                required: self.hdr_len as usize * 4,
301                available: bytes.len(),
302                data: hex::encode(bytes),
303            });
304        }
305
306        let consumed = self.options_from_bytes(&bytes[decoded..], remaining)?;
307        decoded += consumed;
308        // remaining -= consumed;
309        let map = get_protocol_map().read().unwrap();
310        let layer = map.get(&self.proto);
311
312        match layer {
313            None => Ok((None, decoded)),
314            Some(l4_creator) => Ok((Some(l4_creator()), decoded)),
315        }
316    }
317
318    fn name(&self) -> &'static str {
319        "IPv4"
320    }
321
322    fn short_name(&self) -> &'static str {
323        "ip"
324    }
325
326    #[cfg(feature = "sculpting")]
327    fn stack_and_encode(
328        &mut self,
329        next_layer: Option<&[u8]>,
330        info: &str,
331    ) -> Result<Vec<u8>, Error> {
332        self.len = self.hdr_len as u16 * 4 + next_layer.unwrap_or_default().len() as u16;
333        self.proto = match info {
334            "raw" => IPPROTO_RAW,
335            _ => self.proto,
336        };
337
338        let mut result = Vec::with_capacity(self.len as usize);
339
340        let byte = (self.version << 4) | self.hdr_len;
341        result.push(byte);
342        result.push(self.tos);
343        result.extend(self.len.to_be_bytes());
344        result.extend(self.id.to_be_bytes());
345
346        let word = (self.flags as u16) << 13 | self.frag_offset;
347        result.extend(word.to_be_bytes());
348        result.push(self.ttl);
349        result.push(self.proto);
350
351        let checksum_start = result.len();
352        result.extend(self.checksum.to_be_bytes());
353        result.extend(self.src_addr.as_slice());
354        result.extend(self.dst_addr.as_slice());
355
356        if !self.options.is_empty() {
357            todo!();
358        }
359
360        result.extend(next_layer.unwrap_or_default());
361
362        let checksum = IPv4::calculate_checksum(&result);
363        result[checksum_start..checksum_start + 2].copy_from_slice(&checksum.to_be_bytes());
364        Ok(result)
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use crate::{layers, Layer};
371
372    fn test_options(packet: &[u8], options: &[super::IPOption]) {
373        let _ = layers::register_defaults();
374
375        let mut ipv4 = Box::new(super::IPv4::default());
376        let p = ipv4.decode_bytes(packet);
377        assert!(p.is_ok(), "{:#?}", ipv4);
378
379        assert_eq!(ipv4.options.as_slice(), options);
380    }
381
382    wasm_tests! {
383        #[test]
384        fn parse_ipv4_option_packet_1() {
385            let ipv4_packet = hex::decode("08003715e6bc00123f4a33d208004600004caa1d0000801111caac1f1336ac1f1349010101003e3000a10034fa4e302a02010004067075626c6963a01d02012a02010002010030123010060c2b060102012b0e01010601050500").unwrap();
386            let options = [
387                super::IPOption::NOP,
388                super::IPOption::NOP,
389                super::IPOption::NOP,
390                super::IPOption::EOOL,
391            ];
392
393            test_options(&ipv4_packet[14..], &options);
394        }
395
396        #[test]
397        fn parse_ipv4_option_packet_2() {
398            let ipv4_packet = hex::decode("08003715e6bc00123f4a33d208004600004caa1d0000801111caac1f1336ac1f13495f03ff003e3000a10034fa4e302a02010004067075626c6963a01d02012a02010002010030123010060c2b060102012b0e01010601050500").unwrap();
399            let options = [
400                super::IPOption::Other {
401                    value: 0x5f,
402                    len: 3,
403                    data: vec![0xff],
404                },
405                super::IPOption::EOOL,
406            ];
407
408            test_options(&ipv4_packet[14..], &options);
409        }
410
411        #[test]
412        fn parse_ipv4_option_packet_3() {
413            let ipv4_packet = hex::decode("08003715e6bc00123f4a33d2080047000050aa1d0000801111caac1f1336ac1f1349070707deadbeef003e3000a10034fa4e302a02010004067075626c6963a01d02012a02010002010030123010060c2b060102012b0e01010601050500").unwrap();
414            let options = [
415                super::IPOption::RR {
416                    len: 7,
417                    ptr: 7,
418                    route: vec![[0xde, 0xad, 0xbe, 0xef].into()],
419                },
420                super::IPOption::EOOL,
421            ];
422
423            test_options(&ipv4_packet[14..], &options);
424        }
425
426        #[test]
427        fn parse_ipv4_option_packet_4() {
428            let ipv4_packet = hex::decode("08003715e6bc00123f4a33d2080047000050aa1d0000801111caac1f1336ac1f13490b04dead0c04beef3e3000a10034fa4e302a02010004067075626c6963a01d02012a02010002010030123010060c2b060102012b0e01010601050500").unwrap();
429            let options = [
430                super::IPOption::MTUP {
431                    len: 4,
432                    value: 57005,
433                },
434                super::IPOption::MTUR {
435                    len: 4,
436                    value: 48879,
437                },
438            ];
439
440            test_options(&ipv4_packet[14..], &options);
441        }
442    }
443}