pnet_packet/
ipv4.rs

1// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! An IPv4 packet abstraction.
10
11use crate::PrimitiveValues;
12use crate::ip::IpNextHeaderProtocol;
13
14use alloc::vec::Vec;
15
16use pnet_macros::packet;
17use pnet_macros_support::types::*;
18
19use pnet_base::core_net::Ipv4Addr;
20
21/// The IPv4 header flags.
22#[allow(non_snake_case)]
23#[allow(non_upper_case_globals)]
24pub mod Ipv4Flags {
25    use pnet_macros_support::types::*;
26
27    /// Don't Fragment flag.
28    pub const DontFragment: u3 = 0b010;
29    /// More Fragments flag.
30    pub const MoreFragments: u3 = 0b001;
31}
32
33/// IPv4 header options numbers as defined in
34/// <http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml>
35#[allow(non_snake_case)]
36#[allow(non_upper_case_globals)]
37pub mod Ipv4OptionNumbers {
38    use super::Ipv4OptionNumber;
39
40    /// End of Options List.
41    pub const EOL: Ipv4OptionNumber = Ipv4OptionNumber(0);
42
43    /// No Operation.
44    pub const NOP: Ipv4OptionNumber = Ipv4OptionNumber(1);
45
46    /// Security.
47    pub const SEC: Ipv4OptionNumber = Ipv4OptionNumber(2);
48
49    /// Loose Source Route.
50    pub const LSR: Ipv4OptionNumber = Ipv4OptionNumber(3);
51
52    /// Time Stamp.
53    pub const TS: Ipv4OptionNumber = Ipv4OptionNumber(4);
54
55    /// Extended Security.
56    pub const ESEC: Ipv4OptionNumber = Ipv4OptionNumber(5);
57
58    /// Commercial Security.
59    pub const CIPSO: Ipv4OptionNumber = Ipv4OptionNumber(6);
60
61    /// Record Route.
62    pub const RR: Ipv4OptionNumber = Ipv4OptionNumber(7);
63
64    /// Stream ID.
65    pub const SID: Ipv4OptionNumber = Ipv4OptionNumber(8);
66
67    /// Strict Source Route.
68    pub const SSR: Ipv4OptionNumber = Ipv4OptionNumber(9);
69
70    /// Experimental Measurement.
71    pub const ZSU: Ipv4OptionNumber = Ipv4OptionNumber(10);
72
73    /// MTU Probe.
74    pub const MTUP: Ipv4OptionNumber = Ipv4OptionNumber(11);
75
76    /// MTU Reply.
77    pub const MTUR: Ipv4OptionNumber = Ipv4OptionNumber(12);
78
79    /// Experimental Flow Control.
80    pub const FINN: Ipv4OptionNumber = Ipv4OptionNumber(13);
81
82    /// Experimental Access Control.
83    pub const VISA: Ipv4OptionNumber = Ipv4OptionNumber(14);
84
85    /// ENCODE.
86    pub const ENCODE: Ipv4OptionNumber = Ipv4OptionNumber(15);
87
88    /// IMI Traffic Descriptor.
89    pub const IMITD: Ipv4OptionNumber = Ipv4OptionNumber(16);
90
91    /// Extended Internet Protocol.
92    pub const EIP: Ipv4OptionNumber = Ipv4OptionNumber(17);
93
94    /// Traceroute.
95    pub const TR: Ipv4OptionNumber = Ipv4OptionNumber(18);
96
97    /// Address Extension.
98    pub const ADDEXT: Ipv4OptionNumber = Ipv4OptionNumber(19);
99
100    /// Router Alert.
101    pub const RTRALT: Ipv4OptionNumber = Ipv4OptionNumber(20);
102
103    /// Selective Directed Broadcast.
104    pub const SDB: Ipv4OptionNumber = Ipv4OptionNumber(21);
105
106    /// Dynamic Packet State.
107    pub const DPS: Ipv4OptionNumber = Ipv4OptionNumber(23);
108
109    /// Upstream Multicast Pkt.
110    pub const UMP: Ipv4OptionNumber = Ipv4OptionNumber(24);
111
112    /// Quick-Start.
113    pub const QS: Ipv4OptionNumber = Ipv4OptionNumber(25);
114
115    /// RFC3692-style Experiment.
116    pub const EXP: Ipv4OptionNumber = Ipv4OptionNumber(30);
117}
118
119/// Represents an IPv4 option.
120#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
121pub struct Ipv4OptionNumber(pub u8);
122
123impl Ipv4OptionNumber {
124    /// Create a new `Ipv4OptionNumber` instance.
125    pub fn new(value: u8) -> Ipv4OptionNumber {
126        Ipv4OptionNumber(value)
127    }
128}
129
130impl PrimitiveValues for Ipv4OptionNumber {
131    type T = (u8,);
132    fn to_primitive_values(&self) -> (u8,) {
133        (self.0,)
134    }
135}
136
137/// Represents an IPv4 Packet.
138#[packet]
139pub struct Ipv4 {
140    pub version: u4,
141    pub header_length: u4,
142    pub dscp: u6,
143    pub ecn: u2,
144    pub total_length: u16be,
145    pub identification: u16be,
146    pub flags: u3,
147    pub fragment_offset: u13be,
148    pub ttl: u8,
149    #[construct_with(u8)]
150    pub next_level_protocol: IpNextHeaderProtocol,
151    pub checksum: u16be,
152    #[construct_with(u8, u8, u8, u8)]
153    pub source: Ipv4Addr,
154    #[construct_with(u8, u8, u8, u8)]
155    pub destination: Ipv4Addr,
156    #[length_fn = "ipv4_options_length"]
157    pub options: Vec<Ipv4Option>,
158    #[length_fn = "ipv4_payload_length"]
159    #[payload]
160    pub payload: Vec<u8>,
161}
162
163/// Calculates a checksum of an IPv4 packet header.
164/// The checksum field of the packet is regarded as zeros during the calculation.
165pub fn checksum(packet: &Ipv4Packet) -> u16be {
166    use crate::Packet;
167    use crate::util;
168
169    let min = Ipv4Packet::minimum_packet_size();
170    let max = packet.packet().len();
171    let header_length = match packet.get_header_length() as usize * 4 {
172        length if length < min => min,
173        length if length > max => max,
174        length => length,
175    };
176    let data = &packet.packet()[..header_length];
177    util::checksum(data, 5)
178}
179
180#[cfg(test)]
181mod checksum_tests {
182    use super::*;
183    use alloc::vec;
184
185    #[test]
186    fn checksum_zeros() {
187        let mut data = vec![0; 20];
188        let expected = 64255;
189        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
190        pkg.set_header_length(5);
191        assert_eq!(checksum(&pkg.to_immutable()), expected);
192        pkg.set_checksum(123);
193        assert_eq!(checksum(&pkg.to_immutable()), expected);
194    }
195
196    #[test]
197    fn checksum_nonzero() {
198        let mut data = vec![255; 20];
199        let expected = 2560;
200        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
201        pkg.set_header_length(5);
202        assert_eq!(checksum(&pkg.to_immutable()), expected);
203        pkg.set_checksum(123);
204        assert_eq!(checksum(&pkg.to_immutable()), expected);
205    }
206
207    #[test]
208    fn checksum_too_small_header_length() {
209        let mut data = vec![148; 20];
210        let expected = 51910;
211        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
212        pkg.set_header_length(0);
213        assert_eq!(checksum(&pkg.to_immutable()), expected);
214    }
215
216    #[test]
217    fn checksum_too_large_header_length() {
218        let mut data = vec![148; 20];
219        let expected = 51142;
220        let mut pkg = MutableIpv4Packet::new(&mut data[..]).unwrap();
221        pkg.set_header_length(99);
222        assert_eq!(checksum(&pkg.to_immutable()), expected);
223    }
224}
225
226fn ipv4_options_length(ipv4: &Ipv4Packet) -> usize {
227    // the header_length unit is the "word"
228    // - and a word is made of 4 bytes,
229    // - and the header length (without the options) is 5 words long
230    (ipv4.get_header_length() as usize * 4).saturating_sub(20)
231}
232
233#[test]
234fn ipv4_options_length_test() {
235    let mut packet = [0u8; 20];
236    let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
237    ip_header.set_header_length(5);
238    assert_eq!(ipv4_options_length(&ip_header.to_immutable()), 0);
239}
240
241fn ipv4_payload_length(ipv4: &Ipv4Packet) -> usize {
242    (ipv4.get_total_length() as usize).saturating_sub(ipv4.get_header_length() as usize * 4)
243}
244
245#[test]
246fn ipv4_payload_length_test() {
247    let mut packet = [0u8; 30];
248    let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
249    ip_header.set_header_length(5);
250    ip_header.set_total_length(20);
251    assert_eq!(ipv4_payload_length(&ip_header.to_immutable()), 0);
252    // just comparing with 0 is prone to false positives in this case.
253    // for instance if one forgets to set total_length, one always gets 0
254    ip_header.set_total_length(30);
255    assert_eq!(ipv4_payload_length(&ip_header.to_immutable()), 10);
256}
257
258/// Represents the IPv4 Option field.
259#[packet]
260pub struct Ipv4Option {
261    copied: u1,
262    class: u2,
263    #[construct_with(u5)]
264    number: Ipv4OptionNumber,
265    #[length_fn = "ipv4_option_length"]
266    // The length field is an optional field, using a Vec is a way to implement
267    // it
268    length: Vec<u8>,
269    #[length_fn = "ipv4_option_payload_length"]
270    #[payload]
271    data: Vec<u8>,
272}
273
274/// This function gets the 'length' of the length field of the IPv4Option packet
275/// Few options (EOL, NOP) are 1 bytes long, and then have a length field equal
276/// to 0.
277fn ipv4_option_length(option: &Ipv4OptionPacket) -> usize {
278    match option.get_number() {
279        Ipv4OptionNumbers::EOL => 0,
280        Ipv4OptionNumbers::NOP => 0,
281        _ => 1,
282    }
283}
284
285fn ipv4_option_payload_length(ipv4_option: &Ipv4OptionPacket) -> usize {
286    match ipv4_option.get_length().first() {
287        Some(len) => (*len as usize).saturating_sub(2),
288        None => 0,
289    }
290}
291
292#[test]
293fn ipv4_packet_test() {
294    use crate::ip::IpNextHeaderProtocols;
295    use crate::Packet;
296    use crate::PacketSize;
297
298    let mut packet = [0u8; 200];
299    {
300        let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
301        ip_header.set_version(4);
302        assert_eq!(ip_header.get_version(), 4);
303
304        ip_header.set_header_length(5);
305        assert_eq!(ip_header.get_header_length(), 5);
306
307        ip_header.set_dscp(4);
308        assert_eq!(ip_header.get_dscp(), 4);
309
310        ip_header.set_ecn(1);
311        assert_eq!(ip_header.get_ecn(), 1);
312
313        ip_header.set_total_length(115);
314        assert_eq!(ip_header.get_total_length(), 115);
315        assert_eq!(95, ip_header.payload().len());
316        assert_eq!(ip_header.get_total_length(), ip_header.packet_size() as u16);
317
318        ip_header.set_identification(257);
319        assert_eq!(ip_header.get_identification(), 257);
320
321        ip_header.set_flags(Ipv4Flags::DontFragment);
322        assert_eq!(ip_header.get_flags(), 2);
323
324        ip_header.set_fragment_offset(257);
325        assert_eq!(ip_header.get_fragment_offset(), 257);
326
327        ip_header.set_ttl(64);
328        assert_eq!(ip_header.get_ttl(), 64);
329
330        ip_header.set_next_level_protocol(IpNextHeaderProtocols::Udp);
331        assert_eq!(ip_header.get_next_level_protocol(),
332                   IpNextHeaderProtocols::Udp);
333
334        ip_header.set_source(Ipv4Addr::new(192, 168, 0, 1));
335        assert_eq!(ip_header.get_source(), Ipv4Addr::new(192, 168, 0, 1));
336
337        ip_header.set_destination(Ipv4Addr::new(192, 168, 0, 199));
338        assert_eq!(ip_header.get_destination(), Ipv4Addr::new(192, 168, 0, 199));
339
340        let imm_header = checksum(&ip_header.to_immutable());
341        ip_header.set_checksum(imm_header);
342        assert_eq!(ip_header.get_checksum(), 0xb64e);
343    }
344
345    let ref_packet = [0x45,           /* ver/ihl */
346                      0x11,           /* dscp/ecn */
347                      0x00, 0x73,     /* total len */
348                      0x01, 0x01,     /* identification */
349                      0x41, 0x01,     /* flags/frag offset */
350                      0x40,           /* ttl */
351                      0x11,           /* proto */
352                      0xb6, 0x4e,     /* checksum */
353                      0xc0, 0xa8, 0x00, 0x01, /* source ip */
354                      0xc0, 0xa8, 0x00, 0xc7  /* dest ip */];
355
356    assert_eq!(&ref_packet[..], &packet[..ref_packet.len()]);
357}
358
359#[test]
360fn ipv4_packet_option_test() {
361    use alloc::vec;
362
363    let mut packet = [0u8; 3];
364    {
365        let mut ipv4_options = MutableIpv4OptionPacket::new(&mut packet[..]).unwrap();
366
367        ipv4_options.set_copied(1);
368        assert_eq!(ipv4_options.get_copied(), 1);
369
370        ipv4_options.set_class(0);
371        assert_eq!(ipv4_options.get_class(), 0);
372
373        ipv4_options.set_number(Ipv4OptionNumber(3));
374        assert_eq!(ipv4_options.get_number(), Ipv4OptionNumbers::LSR);
375
376        ipv4_options.set_length(&vec![3]);
377        assert_eq!(ipv4_options.get_length(), vec![3]);
378
379        ipv4_options.set_data(&vec![16]);
380    }
381
382    let ref_packet = [0x83,           /* copy / class / number */
383                      0x03,           /* length */
384                      0x10,           /* data */];
385
386    assert_eq!(&ref_packet[..], &packet[..]);
387}
388
389#[test]
390fn ipv4_packet_set_payload_test() {
391    use crate::Packet;
392
393    let mut packet = [0u8; 25]; // allow 20 byte header and 5 byte payload
394    let mut ip_packet = MutableIpv4Packet::new(&mut packet[..]).unwrap();
395    ip_packet.set_total_length(25);
396    ip_packet.set_header_length(5);
397    let payload = b"stuff"; // 5 bytes
398    ip_packet.set_payload(&payload[..]);
399    assert_eq!(ip_packet.payload(), payload);
400}
401
402#[test]
403#[should_panic(expected = "index 25 out of range for slice of length 24")]
404fn ipv4_packet_set_payload_test_panic() {
405    let mut packet = [0u8; 24]; // allow 20 byte header and 4 byte payload
406    let mut ip_packet = MutableIpv4Packet::new(&mut packet[..]).unwrap();
407    ip_packet.set_total_length(25);
408    ip_packet.set_header_length(5);
409    let payload = b"stuff"; // 5 bytes
410    ip_packet.set_payload(&payload[..]); // panic
411}