pnet 0.7.2

Cross-platform, low level networking using the Rust programming language.
diff --git a/src/packet/ipv4.rs.in b/src/packet/ipv4.rs.in
index 1128905..37201e4 100644
--- a/src/packet/ipv4.rs
+++ b/src/packet/ipv4.rs
@@ -10,10 +10,117 @@
 
 use packet::ip::IpNextHeaderProtocol;
 
+use packet::PrimitiveValues;
+
 use pnet_macros_support::types::*;
 
 use std::net::Ipv4Addr;
 
+/// IPv4 header options numbers as defined in
+/// http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
+#[allow(non_snake_case)]
+#[allow(non_upper_case_globals)]
+pub mod Ipv4OptionNumbers {
+    use super::Ipv4OptionNumber;
+
+    /// End of Options List
+    pub const EOL: Ipv4OptionNumber = Ipv4OptionNumber(0);
+
+    /// No Operation
+    pub const NOP: Ipv4OptionNumber = Ipv4OptionNumber(1);
+
+    /// Security
+    pub const SEC: Ipv4OptionNumber = Ipv4OptionNumber(2);
+
+    /// Loose Source Route
+    pub const LSR: Ipv4OptionNumber = Ipv4OptionNumber(3);
+
+    /// Time Stamp
+    pub const TS: Ipv4OptionNumber = Ipv4OptionNumber(4);
+
+    /// Extended Security
+    pub const ESEC: Ipv4OptionNumber = Ipv4OptionNumber(5);
+
+    /// Commercial Security
+    pub const CIPSO: Ipv4OptionNumber = Ipv4OptionNumber(6);
+
+    /// Record Route
+    pub const RR: Ipv4OptionNumber = Ipv4OptionNumber(7);
+
+    /// Stream ID
+    pub const SID: Ipv4OptionNumber = Ipv4OptionNumber(8);
+
+    /// Strict Source Route
+    pub const SSR: Ipv4OptionNumber = Ipv4OptionNumber(9);
+
+    /// Experimental Measurement
+    pub const ZSU: Ipv4OptionNumber = Ipv4OptionNumber(10);
+
+    /// MTU Probe
+    pub const MTUP: Ipv4OptionNumber = Ipv4OptionNumber(11);
+
+    /// MTU Reply
+    pub const MTUR: Ipv4OptionNumber = Ipv4OptionNumber(12);
+
+    /// Experimental Flow Control
+    pub const FINN: Ipv4OptionNumber = Ipv4OptionNumber(13);
+
+    /// Experimental Access Control
+    pub const VISA: Ipv4OptionNumber = Ipv4OptionNumber(14);
+
+    /// ENCODE
+    pub const ENCODE: Ipv4OptionNumber = Ipv4OptionNumber(15);
+
+    /// IMI Traffic Descriptor
+    pub const IMITD: Ipv4OptionNumber = Ipv4OptionNumber(16);
+
+    /// Extended Internet Protocol
+    pub const EIP: Ipv4OptionNumber = Ipv4OptionNumber(17);
+
+    /// Traceroute
+    pub const TR: Ipv4OptionNumber = Ipv4OptionNumber(18);
+
+    /// Address Extension
+    pub const ADDEXT: Ipv4OptionNumber = Ipv4OptionNumber(19);
+
+    /// Router Alert
+    pub const RTRALT: Ipv4OptionNumber = Ipv4OptionNumber(20);
+
+    /// Selective Directed Broadcast
+    pub const SDB: Ipv4OptionNumber = Ipv4OptionNumber(21);
+
+    /// Dynamic Packet State
+    pub const DPS: Ipv4OptionNumber = Ipv4OptionNumber(23);
+
+    /// Upstream Multicast Pkt.
+    pub const UMP: Ipv4OptionNumber = Ipv4OptionNumber(24);
+
+    /// Quick-Start
+    pub const QS: Ipv4OptionNumber = Ipv4OptionNumber(25);
+
+    /// RFC3692-style Experiment
+    pub const EXP: Ipv4OptionNumber = Ipv4OptionNumber(30);
+}
+
+/// Represents an IPv4 option 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Ipv4OptionNumber(pub u8);
+
+impl Ipv4OptionNumber {
+    /// Create a new Ipv4OptionNumber
+    pub fn new(value: u8) -> Ipv4OptionNumber {
+        println!("{:?}", Ipv4OptionNumbers::EOL);
+        Ipv4OptionNumber(value)
+    }
+}
+
+impl PrimitiveValues for Ipv4OptionNumber {
+    type T = (u8,);
+    fn to_primitive_values(&self) -> (u8,) {
+        (self.0,)
+    }
+}
+
 /// Represents an IPv4 Packet
 #[packet]
 pub struct Ipv4 {
@@ -59,7 +166,10 @@ pub fn checksum(packet: &Ipv4Packet) -> u16be {
 }
 
 fn ipv4_options_length(ipv4: &Ipv4Packet) -> usize {
-    ipv4.get_header_length() as usize - 5
+    // the header_length unit is the "word"
+    // - and a word is made of 4 bytes,
+    // - and the header length (without the options) is 5 words long
+    ipv4.get_header_length() as usize * 4 - 20
 }
 
 #[test]
@@ -75,12 +185,35 @@ fn ipv4_options_length_test() {
 pub struct Ipv4Option {
     copied: u1,
     class: u2,
-    number: u5,
-    length: u8,
+    #[construct_with(u5)]
+    number: Ipv4OptionNumber,
+    #[length_fn = "ipv4_option_length"]
+    // The length field is an optional field, using a Vec is a way to implement
+    // it
+    length: Vec<u8>,
+    #[length_fn = "ipv4_option_payload_length"]
     #[payload]
     data: Vec<u8>,
 }
 
+/// This function gets the 'length' of the length field of the IPv4Option packet
+/// Few options (EOL, NOP) are 1 bytes long, and then have a length field equal
+/// to 0
+fn ipv4_option_length(option: &Ipv4OptionPacket) -> usize {
+    match option.get_number() {
+        Ipv4OptionNumbers::EOL => 0,
+        Ipv4OptionNumbers::NOP => 0,
+        _ => 1,
+    }
+}
+
+fn ipv4_option_payload_length(ipv4_option: &Ipv4OptionPacket) -> usize {
+    match ipv4_option.get_length().first() {
+        Some(len) => *len as usize - 2,
+        None => 0,
+    }
+}
+
 #[test]
 fn ipv4_packet_test() {
     use packet::ip::IpNextHeaderProtocols;
@@ -128,6 +261,7 @@ fn ipv4_packet_test() {
         let imm_header = checksum(&ip_header.to_immutable());
         ip_header.set_checksum(imm_header);
         assert_eq!(ip_header.get_checksum(), 0xb64e);
+
     }
 
     let ref_packet = [0x45,           /* ver/ihl */
@@ -143,3 +277,32 @@ fn ipv4_packet_test() {
 
     assert_eq!(&ref_packet[..], &packet[..]);
 }
+
+#[test]
+fn ipv4_packet_option_test() {
+
+    let mut packet = [0u8; 3];
+    {
+        let mut ipv4_options = MutableIpv4OptionPacket::new(&mut packet[..]).unwrap();
+
+        ipv4_options.set_copied(1);
+        assert_eq!(ipv4_options.get_copied(), 1);
+
+        ipv4_options.set_class(0);
+        assert_eq!(ipv4_options.get_class(), 0);
+
+        ipv4_options.set_number(Ipv4OptionNumber(3));
+        assert_eq!(ipv4_options.get_number(), Ipv4OptionNumbers::LSR);
+
+        ipv4_options.set_length(vec![3]);
+        assert_eq!(ipv4_options.get_length(), vec![3]);
+
+        ipv4_options.set_data(vec![16]);
+    }
+
+    let ref_packet = [0x83,           /* copy / class / number */
+                      0x03,           /* length */
+                      0x10,           /* data */];
+
+    assert_eq!(&ref_packet[..], &packet[..]);
+}