arp_parse/
lib.rs

1//! ARP package parsing and building.
2#![no_std]
3
4use core::fmt::{Debug, Display, Formatter};
5
6use byteorder::{ByteOrder, NetworkEndian};
7
8/// Hardware type ethernet
9pub const HARDWARE_ETHERNET: u16 = 0x0001;
10
11/// Hardware ethernet address size
12pub const HARDWARE_SIZE_ETHERNET: u8 = 6;
13
14/// Protocol type ipv4
15pub const PROTOCOL_IPV4: u16 = 0x0800;
16
17/// Protocol ipv4 address size
18pub const PROTOCOL_SIZE_IPV4: u8 = 4;
19
20/// Opcode request
21pub const OPCODE_REQUEST: u16 = 1;
22
23/// Opcode reply
24pub const OPCODE_REPLY: u16 = 2;
25
26/// ARP size
27pub const ARP_SIZE: usize = 28;
28
29#[derive(Debug)]
30pub enum Error {
31    /// Invalid size, too small (at least 28 bytes)
32    InvalidSize,
33
34    /// Invalid hardware type, only ethernet supported
35    InvalidHardwareType,
36
37    /// Invalid hardware size, only ethernet address size (6 bytes) supported
38    InvalidHardwareSize,
39
40    /// Invalid protocol type, only ipv4 supported
41    InvalidProtocolType,
42
43    /// Invalid protocol size, only ipv4 address size (4 bytes) supported
44    InvalidProtocolSize,
45
46    /// Invalid operation code, only REQUEST (1) or REPLY (2) allowed
47    InvalidOpCode,
48}
49
50impl Display for Error {
51    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
52        write!(f, "{:?}", self)
53    }
54}
55
56/// ARP packet slice
57pub struct ARPSlice<'a> {
58    data: &'a [u8],
59}
60
61impl<'a> ARPSlice<'a> {
62    /// Hardware type (only ethernet supported)
63    #[inline]
64    pub fn hardware_type(&self) -> u16 {
65        NetworkEndian::read_u16(&self.data[0..2])
66    }
67
68    /// Protocol (only ipv4 supported)
69    #[inline]
70    pub fn protocol_type(&self) -> u16 {
71        NetworkEndian::read_u16(&self.data[2..4])
72    }
73
74    /// Hardware address size (ethernet address size, always 6)
75    #[inline]
76    pub fn hardware_size(&self) -> u8 {
77        self.data[4]
78    }
79
80    /// Protocol address size (ipv4 address size, always 4)
81    #[inline]
82    pub fn protocol_size(&self) -> u8 {
83        self.data[5]
84    }
85
86    /// Operation code, REQUEST or REPLY
87    #[inline]
88    pub fn op_code(&self) -> u16 {
89        NetworkEndian::read_u16(&self.data[6..8])
90    }
91
92    /// Sender hardware address (ethernet address)
93    #[inline]
94    pub fn sender_hardware_addr(&self) -> &[u8] {
95        &self.data[8..14]
96    }
97
98    /// Sender protocol address (ipv4 address)
99    #[inline]
100    pub fn sender_protocol_addr(&self) -> &[u8] {
101        &self.data[14..18]
102    }
103
104    /// Target hardware address (ethernet address)
105    #[inline]
106    pub fn target_hardware_addr(&self) -> &[u8] {
107        &self.data[18..24]
108    }
109
110    /// Target protocol address (ipv4 address)
111    #[inline]
112    pub fn target_protocol_addr(&self) -> &[u8] {
113        &self.data[24..28]
114    }
115}
116
117impl<'a> AsRef<[u8]> for ARPSlice<'a> {
118    fn as_ref(&self) -> &[u8] {
119        self.data
120    }
121}
122
123/// Parse a byte buffer (at least 28 bytes) to a ARPSlice
124pub fn parse(data: &[u8]) -> Result<ARPSlice, Error> {
125    if data.len() < ARP_SIZE {
126        return Err(Error::InvalidSize);
127    }
128    let slice = ARPSlice { data };
129    if slice.hardware_type() != HARDWARE_ETHERNET {
130        return Err(Error::InvalidHardwareType);
131    }
132    if slice.protocol_type() != PROTOCOL_IPV4 {
133        return Err(Error::InvalidProtocolType);
134    }
135    if slice.op_code() != OPCODE_REQUEST && slice.op_code() != OPCODE_REPLY {
136        return Err(Error::InvalidOpCode);
137    }
138    if slice.hardware_size() != HARDWARE_SIZE_ETHERNET {
139        return Err(Error::InvalidHardwareSize);
140    }
141    if slice.protocol_size() != PROTOCOL_SIZE_IPV4 {
142        return Err(Error::InvalidProtocolSize);
143    }
144    Ok(slice)
145}
146
147/// ARP builder
148pub struct ARPSliceBuilder<'a> {
149    buf: &'a mut [u8],
150}
151
152impl<'a> ARPSliceBuilder<'a> {
153    /// Create a new builder with a mutable buffer, initialize it with proper values
154    pub fn new(buf: &'a mut [u8]) -> Result<Self, Error> {
155        if buf.len() < ARP_SIZE {
156            return Err(Error::InvalidSize);
157        }
158        NetworkEndian::write_u16(&mut buf[0..2], HARDWARE_ETHERNET);
159        NetworkEndian::write_u16(&mut buf[2..4], PROTOCOL_IPV4);
160        buf[4] = HARDWARE_SIZE_ETHERNET;
161        buf[5] = PROTOCOL_SIZE_IPV4;
162        NetworkEndian::write_u16(&mut buf[6..8], OPCODE_REQUEST);
163        Ok(Self { buf })
164    }
165
166    /// Update the operation code
167    pub fn op_code(self, op_code: u16) -> Result<Self, Error> {
168        if op_code != OPCODE_REQUEST && op_code != OPCODE_REPLY {
169            return Err(Error::InvalidOpCode);
170        }
171        NetworkEndian::write_u16(&mut self.buf[6..8], op_code);
172        Ok(Self { buf: self.buf })
173    }
174
175    /// Update the sender hardware address
176    pub fn sender_hardware_addr(self, ether_addr: &[u8]) -> Result<Self, Error> {
177        if ether_addr.len() < HARDWARE_SIZE_ETHERNET as usize {
178            return Err(Error::InvalidHardwareSize);
179        }
180        self.buf[8..14].copy_from_slice(&ether_addr[0..HARDWARE_SIZE_ETHERNET as usize]);
181        Ok(Self { buf: self.buf })
182    }
183
184    /// Update the sender protocol address
185    pub fn sender_protocol_addr(self, ipv4_addr: &[u8]) -> Result<Self, Error> {
186        if ipv4_addr.len() < PROTOCOL_SIZE_IPV4 as usize {
187            return Err(Error::InvalidProtocolSize);
188        }
189        self.buf[14..18].copy_from_slice(&ipv4_addr[..4]);
190        Ok(Self { buf: self.buf })
191    }
192
193    /// Update the target ethernet address
194    pub fn target_hardware_addr(self, ether_addr: &[u8]) -> Result<Self, Error> {
195        if ether_addr.len() < HARDWARE_SIZE_ETHERNET as usize {
196            return Err(Error::InvalidHardwareSize);
197        }
198        self.buf[18..24].copy_from_slice(&ether_addr[..6]);
199        Ok(Self { buf: self.buf })
200    }
201
202    /// Update the target protocol address
203    pub fn target_protocol_addr(self, ipv4_addr: &[u8]) -> Result<Self, Error> {
204        if ipv4_addr.len() < PROTOCOL_SIZE_IPV4 as usize {
205            return Err(Error::InvalidProtocolSize);
206        }
207        self.buf[24..28].copy_from_slice(&ipv4_addr[..4]);
208        Ok(Self { buf: self.buf })
209    }
210
211    /// Finish
212    pub fn build(self) -> &'a mut [u8] {
213        self.buf
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use crate::{ARPSliceBuilder, OPCODE_REPLY, OPCODE_REQUEST, parse};
220
221    #[test]
222    fn parse_request() {
223        let data = [0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x38, 0xfc, 0x98, 0x8b, 0x46, 0x10, 0xc0, 0xa8, 0x13, 0x97, 0xf0, 0x18, 0x98, 0x74, 0xe5, 0xd9, 0xc0, 0xa8, 0x12, 0x70];
224        let arp_slice = parse(&data);
225        assert!(arp_slice.is_ok());
226
227        let arp_slice = arp_slice.unwrap();
228        assert_eq!(arp_slice.op_code(), OPCODE_REQUEST);
229
230        assert_eq!(arp_slice.sender_hardware_addr(), &[0x38, 0xfc, 0x98, 0x8b, 0x46, 0x10]);
231        assert_eq!(arp_slice.sender_protocol_addr(), &[0xc0, 0xa8, 0x13, 0x97]);
232        assert_eq!(arp_slice.target_hardware_addr(), &[0xf0, 0x18, 0x98, 0x74, 0xe5, 0xd9]);
233        assert_eq!(arp_slice.target_protocol_addr(), &[0xc0, 0xa8, 0x12, 0x70]);
234    }
235
236    #[test]
237    fn parse_reply() {
238        let data = [0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02, 0xf0, 0x18, 0x98, 0x74, 0xe5, 0xd9, 0xc0, 0xa8, 0x12, 0x70, 0x38, 0xfc, 0x98, 0x8b, 0x46, 0x10, 0xc0, 0xa8, 0x13, 0x97];
239        let arp_slice = parse(&data);
240        assert!(arp_slice.is_ok());
241
242        let arp_slice = arp_slice.unwrap();
243        assert_eq!(arp_slice.op_code(), OPCODE_REPLY);
244
245        assert_eq!(arp_slice.sender_hardware_addr(), &[0xf0, 0x18, 0x98, 0x74, 0xe5, 0xd9]);
246        assert_eq!(arp_slice.sender_protocol_addr(), &[0xc0, 0xa8, 0x12, 0x70]);
247        assert_eq!(arp_slice.target_hardware_addr(), &[0x38, 0xfc, 0x98, 0x8b, 0x46, 0x10]);
248        assert_eq!(arp_slice.target_protocol_addr(), &[0xc0, 0xa8, 0x13, 0x97]);
249    }
250
251    #[test]
252    fn build_request() {
253        let mut buff = [0; 40];
254        let builder = ARPSliceBuilder::new(&mut buff);
255        assert!(builder.is_ok());
256
257        let builder = builder.unwrap();
258        builder.op_code(OPCODE_REQUEST).unwrap()
259            .sender_hardware_addr(&[1, 2, 3, 4, 5, 6]).unwrap()
260            .sender_protocol_addr(&[192, 168, 0, 10]).unwrap()
261            .target_hardware_addr(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).unwrap()
262            .target_protocol_addr(&[192, 168, 0, 1]).unwrap();
263
264        let arp_slice = parse(&buff);
265        assert!(arp_slice.is_ok());
266
267        let arp_slice = arp_slice.unwrap();
268        assert_eq!(arp_slice.op_code(), OPCODE_REQUEST);
269
270        assert_eq!(arp_slice.sender_hardware_addr(), &[1, 2, 3, 4, 5, 6]);
271        assert_eq!(arp_slice.sender_protocol_addr(), &[192, 168, 0, 10]);
272        assert_eq!(arp_slice.target_hardware_addr(), &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
273        assert_eq!(arp_slice.target_protocol_addr(), &[192, 168, 0, 1]);
274    }
275}