xenet_packet/
ipv4.rs

1//! An IPv4 packet abstraction.
2
3use crate::ip::IpNextLevelProtocol;
4use crate::PrimitiveValues;
5
6use alloc::vec::Vec;
7
8use xenet_macro::packet;
9use xenet_macro_helper::types::*;
10
11use std::net::Ipv4Addr;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16/// IPv4 Header Length
17pub const IPV4_HEADER_LEN: usize = MutableIpv4Packet::minimum_packet_size();
18/// IPv4 Header Byte Unit (32 bits)
19pub const IPV4_HEADER_LENGTH_BYTE_UNITS: usize = 4;
20
21/// Represents the IPv4 option header.
22#[derive(Clone, Debug, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub struct Ipv4OptionHeader {
25    pub copied: u1,
26    pub class: u2,
27    pub number: Ipv4OptionType,
28    pub length: Option<u8>,
29}
30
31/// Represents the IPv4 header.
32#[derive(Clone, Debug, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct Ipv4Header {
35    pub version: u4,
36    pub header_length: u4,
37    pub dscp: u6,
38    pub ecn: u2,
39    pub total_length: u16be,
40    pub identification: u16be,
41    pub flags: u3,
42    pub fragment_offset: u13be,
43    pub ttl: u8,
44    pub next_level_protocol: IpNextLevelProtocol,
45    pub checksum: u16be,
46    pub source: Ipv4Addr,
47    pub destination: Ipv4Addr,
48    pub options: Vec<Ipv4OptionHeader>,
49}
50
51impl Ipv4Header {
52    /// Construct an IPv4 header from a byte slice.
53    pub fn from_bytes(packet: &[u8]) -> Result<Ipv4Header, String> {
54        if packet.len() < IPV4_HEADER_LEN {
55            return Err("Packet is too small for IPv4 header".to_string());
56        }
57        match Ipv4Packet::new(packet) {
58            Some(ipv4_packet) => Ok(Ipv4Header {
59                version: ipv4_packet.get_version(),
60                header_length: ipv4_packet.get_header_length(),
61                dscp: ipv4_packet.get_dscp(),
62                ecn: ipv4_packet.get_ecn(),
63                total_length: ipv4_packet.get_total_length(),
64                identification: ipv4_packet.get_identification(),
65                flags: ipv4_packet.get_flags(),
66                fragment_offset: ipv4_packet.get_fragment_offset(),
67                ttl: ipv4_packet.get_ttl(),
68                next_level_protocol: ipv4_packet.get_next_level_protocol(),
69                checksum: ipv4_packet.get_checksum(),
70                source: ipv4_packet.get_source(),
71                destination: ipv4_packet.get_destination(),
72                options: ipv4_packet
73                    .get_options_iter()
74                    .map(|o| Ipv4OptionHeader {
75                        copied: o.get_copied(),
76                        class: o.get_class(),
77                        number: o.get_number(),
78                        length: o.get_length().first().cloned(),
79                    })
80                    .collect(),
81            }),
82            None => Err("Failed to parse IPv4 packet".to_string()),
83        }
84    }
85    /// Construct an IPv4 header from a Ipv4Packet.
86    pub(crate) fn from_packet(ipv4_packet: &Ipv4Packet) -> Ipv4Header {
87        Ipv4Header {
88            version: ipv4_packet.get_version(),
89            header_length: ipv4_packet.get_header_length(),
90            dscp: ipv4_packet.get_dscp(),
91            ecn: ipv4_packet.get_ecn(),
92            total_length: ipv4_packet.get_total_length(),
93            identification: ipv4_packet.get_identification(),
94            flags: ipv4_packet.get_flags(),
95            fragment_offset: ipv4_packet.get_fragment_offset(),
96            ttl: ipv4_packet.get_ttl(),
97            next_level_protocol: ipv4_packet.get_next_level_protocol(),
98            checksum: ipv4_packet.get_checksum(),
99            source: ipv4_packet.get_source(),
100            destination: ipv4_packet.get_destination(),
101            options: ipv4_packet
102                .get_options_iter()
103                .map(|o| Ipv4OptionHeader {
104                    copied: o.get_copied(),
105                    class: o.get_class(),
106                    number: o.get_number(),
107                    length: o.get_length().first().cloned(),
108                })
109                .collect(),
110        }
111    }
112}
113
114/// Represents the IPv4 header flags.
115#[allow(non_snake_case)]
116#[allow(non_upper_case_globals)]
117pub mod Ipv4Flags {
118    use xenet_macro_helper::types::*;
119
120    /// Don't Fragment flag.
121    pub const DontFragment: u3 = 0b010;
122    /// More Fragments flag.
123    pub const MoreFragments: u3 = 0b001;
124}
125
126/// Represents the IPv4 options.
127/// <http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml>
128#[repr(u8)]
129#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
130#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131pub enum Ipv4OptionType {
132    /// End of Options List
133    EOL = 0,
134    /// No Operation
135    NOP = 1,
136    /// Security
137    SEC = 2,
138    /// Loose Source Route
139    LSR = 3,
140    /// Time Stamp
141    TS = 4,
142    /// Extended Security
143    ESEC = 5,
144    /// Commercial Security
145    CIPSO = 6,
146    /// Record Route
147    RR = 7,
148    /// Stream ID
149    SID = 8,
150    /// Strict Source Route
151    SSR = 9,
152    /// Experimental Measurement
153    ZSU = 10,
154    /// MTU Probe
155    MTUP = 11,
156    /// MTU Reply
157    MTUR = 12,
158    /// Experimental Flow Control
159    FINN = 13,
160    /// Experimental Access Control
161    VISA = 14,
162    /// Encode
163    ENCODE = 15,
164    /// IMI Traffic Descriptor
165    IMITD = 16,
166    /// Extended Internet Protocol
167    EIP = 17,
168    /// Traceroute
169    TR = 18,
170    /// Address Extension
171    ADDEXT = 19,
172    /// Router Alert
173    RTRALT = 20,
174    /// Selective Directed Broadcast
175    SDB = 21,
176    /// Unassigned
177    Unassigned = 22,
178    /// Dynamic Packet State
179    DPS = 23,
180    /// Upstream Multicast Packet
181    UMP = 24,
182    /// Quick-Start
183    QS = 25,
184    /// RFC3692-style Experiment
185    EXP = 30,
186    /// Unknown
187    Unknown(u8),
188}
189
190impl Ipv4OptionType {
191    /// Constructs a new Ipv4OptionType from u8
192    pub fn new(n: u8) -> Ipv4OptionType {
193        match n {
194            0 => Ipv4OptionType::EOL,
195            1 => Ipv4OptionType::NOP,
196            2 => Ipv4OptionType::SEC,
197            3 => Ipv4OptionType::LSR,
198            4 => Ipv4OptionType::TS,
199            5 => Ipv4OptionType::ESEC,
200            6 => Ipv4OptionType::CIPSO,
201            7 => Ipv4OptionType::RR,
202            8 => Ipv4OptionType::SID,
203            9 => Ipv4OptionType::SSR,
204            10 => Ipv4OptionType::ZSU,
205            11 => Ipv4OptionType::MTUP,
206            12 => Ipv4OptionType::MTUR,
207            13 => Ipv4OptionType::FINN,
208            14 => Ipv4OptionType::VISA,
209            15 => Ipv4OptionType::ENCODE,
210            16 => Ipv4OptionType::IMITD,
211            17 => Ipv4OptionType::EIP,
212            18 => Ipv4OptionType::TR,
213            19 => Ipv4OptionType::ADDEXT,
214            20 => Ipv4OptionType::RTRALT,
215            21 => Ipv4OptionType::SDB,
216            22 => Ipv4OptionType::Unassigned,
217            23 => Ipv4OptionType::DPS,
218            24 => Ipv4OptionType::UMP,
219            25 => Ipv4OptionType::QS,
220            30 => Ipv4OptionType::EXP,
221            _ => Ipv4OptionType::Unknown(n),
222        }
223    }
224}
225
226impl PrimitiveValues for Ipv4OptionType {
227    type T = (u8,);
228    fn to_primitive_values(&self) -> (u8,) {
229        match *self {
230            Ipv4OptionType::EOL => (0,),
231            Ipv4OptionType::NOP => (1,),
232            Ipv4OptionType::SEC => (2,),
233            Ipv4OptionType::LSR => (3,),
234            Ipv4OptionType::TS => (4,),
235            Ipv4OptionType::ESEC => (5,),
236            Ipv4OptionType::CIPSO => (6,),
237            Ipv4OptionType::RR => (7,),
238            Ipv4OptionType::SID => (8,),
239            Ipv4OptionType::SSR => (9,),
240            Ipv4OptionType::ZSU => (10,),
241            Ipv4OptionType::MTUP => (11,),
242            Ipv4OptionType::MTUR => (12,),
243            Ipv4OptionType::FINN => (13,),
244            Ipv4OptionType::VISA => (14,),
245            Ipv4OptionType::ENCODE => (15,),
246            Ipv4OptionType::IMITD => (16,),
247            Ipv4OptionType::EIP => (17,),
248            Ipv4OptionType::TR => (18,),
249            Ipv4OptionType::ADDEXT => (19,),
250            Ipv4OptionType::RTRALT => (20,),
251            Ipv4OptionType::SDB => (21,),
252            Ipv4OptionType::Unassigned => (22,),
253            Ipv4OptionType::DPS => (23,),
254            Ipv4OptionType::UMP => (24,),
255            Ipv4OptionType::QS => (25,),
256            Ipv4OptionType::EXP => (30,),
257            Ipv4OptionType::Unknown(n) => (n,),
258        }
259    }
260}
261
262/// Represents an IPv4 Packet.
263#[packet]
264pub struct Ipv4 {
265    pub version: u4,
266    pub header_length: u4,
267    pub dscp: u6,
268    pub ecn: u2,
269    pub total_length: u16be,
270    pub identification: u16be,
271    pub flags: u3,
272    pub fragment_offset: u13be,
273    pub ttl: u8,
274    #[construct_with(u8)]
275    pub next_level_protocol: IpNextLevelProtocol,
276    pub checksum: u16be,
277    #[construct_with(u8, u8, u8, u8)]
278    pub source: Ipv4Addr,
279    #[construct_with(u8, u8, u8, u8)]
280    pub destination: Ipv4Addr,
281    #[length_fn = "ipv4_options_length"]
282    pub options: Vec<Ipv4Option>,
283    #[length_fn = "ipv4_payload_length"]
284    #[payload]
285    pub payload: Vec<u8>,
286}
287
288/// Calculates a checksum of an IPv4 packet header.
289/// The checksum field of the packet is regarded as zeros during the calculation.
290pub fn checksum(packet: &Ipv4Packet) -> u16be {
291    use crate::util;
292    use crate::Packet;
293
294    let min = Ipv4Packet::minimum_packet_size();
295    let max = packet.packet().len();
296    let header_length = match packet.get_header_length() as usize * 4 {
297        length if length < min => min,
298        length if length > max => max,
299        length => length,
300    };
301    let data = &packet.packet()[..header_length];
302    util::checksum(data, 5)
303}
304
305#[cfg(test)]
306mod checksum_tests {
307    use super::*;
308    use alloc::vec;
309
310    #[test]
311    fn checksum_zeros() {
312        let mut data = vec![0; 20];
313        let expected = 64255;
314        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
315        pkg.set_header_length(5);
316        assert_eq!(checksum(&pkg.to_immutable()), expected);
317        pkg.set_checksum(123);
318        assert_eq!(checksum(&pkg.to_immutable()), expected);
319    }
320
321    #[test]
322    fn checksum_nonzero() {
323        let mut data = vec![255; 20];
324        let expected = 2560;
325        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
326        pkg.set_header_length(5);
327        assert_eq!(checksum(&pkg.to_immutable()), expected);
328        pkg.set_checksum(123);
329        assert_eq!(checksum(&pkg.to_immutable()), expected);
330    }
331
332    #[test]
333    fn checksum_too_small_header_length() {
334        let mut data = vec![148; 20];
335        let expected = 51910;
336        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
337        pkg.set_header_length(0);
338        assert_eq!(checksum(&pkg.to_immutable()), expected);
339    }
340
341    #[test]
342    fn checksum_too_large_header_length() {
343        let mut data = vec![148; 20];
344        let expected = 51142;
345        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
346        pkg.set_header_length(99);
347        assert_eq!(checksum(&pkg.to_immutable()), expected);
348    }
349}
350
351fn ipv4_options_length(ipv4: &Ipv4Packet) -> usize {
352    // the header_length unit is the "word"
353    // - and a word is made of 4 bytes,
354    // - and the header length (without the options) is 5 words long
355    (ipv4.get_header_length() as usize * 4).saturating_sub(20)
356}
357
358#[test]
359fn ipv4_options_length_test() {
360    let mut packet = [0u8; 20];
361    let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
362    ip_header.set_header_length(5);
363    assert_eq!(ipv4_options_length(&ip_header.to_immutable()), 0);
364}
365
366fn ipv4_payload_length(ipv4: &Ipv4Packet) -> usize {
367    (ipv4.get_total_length() as usize).saturating_sub(ipv4.get_header_length() as usize * 4)
368}
369
370#[test]
371fn ipv4_payload_length_test() {
372    let mut packet = [0u8; 30];
373    let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
374    ip_header.set_header_length(5);
375    ip_header.set_total_length(20);
376    assert_eq!(ipv4_payload_length(&ip_header.to_immutable()), 0);
377    // just comparing with 0 is prone to false positives in this case.
378    // for instance if one forgets to set total_length, one always gets 0
379    ip_header.set_total_length(30);
380    assert_eq!(ipv4_payload_length(&ip_header.to_immutable()), 10);
381}
382
383/// Represents the IPv4 Option field.
384#[packet]
385pub struct Ipv4Option {
386    copied: u1,
387    class: u2,
388    #[construct_with(u5)]
389    number: Ipv4OptionType,
390    #[length_fn = "ipv4_option_length"]
391    // The length field is an optional field, using a Vec is a way to implement
392    // it
393    length: Vec<u8>,
394    #[length_fn = "ipv4_option_payload_length"]
395    #[payload]
396    data: Vec<u8>,
397}
398
399/// This function gets the 'length' of the length field of the IPv4Option packet
400/// Few options (EOOL, NOP) are 1 bytes long, and then have a length field equal
401/// to 0.
402fn ipv4_option_length(option: &Ipv4OptionPacket) -> usize {
403    match option.get_number() {
404        Ipv4OptionType::EOL => 0,
405        Ipv4OptionType::NOP => 0,
406        _ => 1,
407    }
408}
409
410fn ipv4_option_payload_length(ipv4_option: &Ipv4OptionPacket) -> usize {
411    match ipv4_option.get_length().first() {
412        Some(len) => (*len as usize).saturating_sub(2),
413        None => 0,
414    }
415}
416
417#[test]
418fn ipv4_packet_test() {
419    use crate::ip::IpNextLevelProtocol;
420    use crate::Packet;
421    use crate::PacketSize;
422
423    let mut packet = [0u8; 200];
424    {
425        let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
426        ip_header.set_version(4);
427        assert_eq!(ip_header.get_version(), 4);
428
429        ip_header.set_header_length(5);
430        assert_eq!(ip_header.get_header_length(), 5);
431
432        ip_header.set_dscp(4);
433        assert_eq!(ip_header.get_dscp(), 4);
434
435        ip_header.set_ecn(1);
436        assert_eq!(ip_header.get_ecn(), 1);
437
438        ip_header.set_total_length(115);
439        assert_eq!(ip_header.get_total_length(), 115);
440        assert_eq!(95, ip_header.payload().len());
441        assert_eq!(ip_header.get_total_length(), ip_header.packet_size() as u16);
442
443        ip_header.set_identification(257);
444        assert_eq!(ip_header.get_identification(), 257);
445
446        ip_header.set_flags(Ipv4Flags::DontFragment as u3);
447        assert_eq!(ip_header.get_flags(), 2);
448
449        ip_header.set_fragment_offset(257);
450        assert_eq!(ip_header.get_fragment_offset(), 257);
451
452        ip_header.set_ttl(64);
453        assert_eq!(ip_header.get_ttl(), 64);
454
455        ip_header.set_next_level_protocol(IpNextLevelProtocol::Udp);
456        assert_eq!(
457            ip_header.get_next_level_protocol(),
458            IpNextLevelProtocol::Udp
459        );
460
461        ip_header.set_source(Ipv4Addr::new(192, 168, 0, 1));
462        assert_eq!(ip_header.get_source(), Ipv4Addr::new(192, 168, 0, 1));
463
464        ip_header.set_destination(Ipv4Addr::new(192, 168, 0, 199));
465        assert_eq!(ip_header.get_destination(), Ipv4Addr::new(192, 168, 0, 199));
466
467        let imm_header = checksum(&ip_header.to_immutable());
468        ip_header.set_checksum(imm_header);
469        assert_eq!(ip_header.get_checksum(), 0xb64e);
470    }
471
472    let ref_packet = [
473        0x45, /* ver/ihl */
474        0x11, /* dscp/ecn */
475        0x00, 0x73, /* total len */
476        0x01, 0x01, /* identification */
477        0x41, 0x01, /* flags/frag offset */
478        0x40, /* ttl */
479        0x11, /* proto */
480        0xb6, 0x4e, /* checksum */
481        0xc0, 0xa8, 0x00, 0x01, /* source ip */
482        0xc0, 0xa8, 0x00, 0xc7, /* dest ip */
483    ];
484
485    assert_eq!(&ref_packet[..], &packet[..ref_packet.len()]);
486}
487
488#[test]
489fn ipv4_packet_option_test() {
490    use alloc::vec;
491
492    let mut packet = [0u8; 3];
493    {
494        let mut ipv4_options = MutableIpv4OptionPacket::new(&mut packet[..]).unwrap();
495
496        ipv4_options.set_copied(1);
497        assert_eq!(ipv4_options.get_copied(), 1);
498
499        ipv4_options.set_class(0);
500        assert_eq!(ipv4_options.get_class(), 0);
501
502        ipv4_options.set_number(Ipv4OptionType::new(3));
503        assert_eq!(ipv4_options.get_number(), Ipv4OptionType::LSR);
504
505        ipv4_options.set_length(&vec![3]);
506        assert_eq!(ipv4_options.get_length(), vec![3]);
507
508        ipv4_options.set_data(&vec![16]);
509    }
510
511    let ref_packet = [
512        0x83, /* copy / class / number */
513        0x03, /* length */
514        0x10, /* data */
515    ];
516
517    assert_eq!(&ref_packet[..], &packet[..]);
518}
519
520#[test]
521fn ipv4_packet_set_payload_test() {
522    use crate::Packet;
523
524    let mut packet = [0u8; 25]; // allow 20 byte header and 5 byte payload
525    let mut ip_packet = MutableIpv4Packet::new(&mut packet[..]).unwrap();
526    ip_packet.set_total_length(25);
527    ip_packet.set_header_length(5);
528    let payload = b"stuff"; // 5 bytes
529    ip_packet.set_payload(&payload[..]);
530    assert_eq!(ip_packet.payload(), payload);
531}
532
533#[test]
534#[should_panic(expected = "index 25 out of range for slice of length 24")]
535fn ipv4_packet_set_payload_test_panic() {
536    let mut packet = [0u8; 24]; // allow 20 byte header and 4 byte payload
537    let mut ip_packet = MutableIpv4Packet::new(&mut packet[..]).unwrap();
538    ip_packet.set_total_length(25);
539    ip_packet.set_header_length(5);
540    let payload = b"stuff"; // 5 bytes
541    ip_packet.set_payload(&payload[..]); // panic
542}