1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Handles parsing of Arp pakets

use nom::{IResult, be_u8, be_u16, le_u16};
use nom::Endianness::{self, Big};

use std::net::Ipv4Addr;

use ethernet::{MacAddress, to_mac_address};
use ipv4::to_ipv4_address;

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
pub enum HardwareAddressType {
    Ethernet,
    Other(u16),
}

fn to_hw_addr_type(i: u16) -> Option<HardwareAddressType> {
    match i {
        0x0001 => Some(HardwareAddressType::Ethernet),
        other => Some(HardwareAddressType::Other(other)),
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
pub enum ProtocolAddressType {
    IPv4,
    Other(u16),
}

fn to_proto_addr_type(i: u16) -> Option<ProtocolAddressType> {
    match i {
        0x0800 => Some(ProtocolAddressType::IPv4),
        other => Some(ProtocolAddressType::Other(other)),
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
pub enum Operation {
    Request,
    Reply,
    Other(u16),
}

fn to_operation(i: u16) -> Option<Operation> {
    match i {
        0x0001 => Some(Operation::Request),
        0x0002 => Some(Operation::Reply),
        other => Some(Operation::Other(other)),
    }
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
pub struct ArpPacket {
    pub hw_addr_type: HardwareAddressType,
    pub proto_addr_type: ProtocolAddressType,

    pub hw_addr_size: u8,
    pub proto_addr_size: u8,

    pub operation : Operation,

    pub src_mac: MacAddress,
    pub src_addr: Ipv4Addr,

    pub dest_mac: MacAddress,
    pub dest_addr: Ipv4Addr,
}

named!(hw_addr_type<&[u8], HardwareAddressType>, map_opt!(u16!(Big), to_hw_addr_type));
named!(proto_addr_type<&[u8], ProtocolAddressType>, map_opt!(u16!(Big), to_proto_addr_type));
named!(operation<&[u8], Operation>, map_opt!(u16!(Big), to_operation));
named!(mac_address<&[u8], MacAddress>, map!(take!(6), to_mac_address));
named!(address<&[u8], Ipv4Addr>, map!(take!(4), to_ipv4_address));
named!(arp_packet<&[u8], ArpPacket>, do_parse!(
    hw_addr_type: hw_addr_type >>
    proto_addr_type: proto_addr_type >>

    hw_addr_size:  be_u8 >>
    proto_addr_size: be_u8 >>

    operation: operation >>

    src_mac: mac_address >>
    src_addr: address >>

    dest_mac: mac_address >>
    dest_addr: address >>

    ({ ArpPacket{
        hw_addr_type: hw_addr_type,
        proto_addr_type: proto_addr_type,

        hw_addr_size: hw_addr_size,
        proto_addr_size: proto_addr_size,

        operation: operation,

        src_mac: src_mac,
        src_addr: src_addr,

        dest_mac: dest_mac,
        dest_addr: dest_addr,
    }})
));

pub fn parse_arp_pkt(i: &[u8]) -> IResult<&[u8], ArpPacket> {
    arp_packet(i)
}

#[cfg(test)]
mod tests {
    use super::{arp_packet, ArpPacket, MacAddress, HardwareAddressType, ProtocolAddressType, Operation};
    use std::net::Ipv4Addr;

    const EMPTY_SLICE: &'static [u8] = &[];

    #[test]
    fn arp_packet_works() {
        let bytes = [
            0, 1, // hardware type
            8, 0, // proto type
            6, 4, // sizes
            0, 1, // arp operation
            0x00, 0x1b, 0x21, 0x0f, 0x91, 0x9b, // src mac
            10, 10, 1, 135, // src ip
            0xde, 0xad, 0xc0, 0x00, 0xff, 0xee, // dest mac
            192, 168, 1, 253, // dest ip
        ];

        let expectation = ArpPacket {
            hw_addr_type: HardwareAddressType::Ethernet,
            proto_addr_type: ProtocolAddressType::IPv4,

            hw_addr_size: 6,
            proto_addr_size: 4,

            operation: Operation::Request,

            src_mac: MacAddress([0x00, 0x1b, 0x21, 0x0f, 0x91, 0x9b]),
            src_addr: Ipv4Addr::new(10, 10, 1, 135),

            dest_mac: MacAddress([0xde, 0xad, 0xc0, 0x00, 0xff, 0xee]),
            dest_addr: Ipv4Addr::new(192, 168, 1, 253),
        };
        assert_eq!(arp_packet(&bytes), Ok((EMPTY_SLICE, expectation)));
    }
}