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