1use std::convert::From;
11use std::fmt::Debug;
12use std::net::Ipv4Addr;
13
14use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut};
15use super::ethernet::{self, ETHERTYPE_IPV4};
16use crate::utils::net::mac::{MAC_ADDR_LEN, MacAddr};
17
18pub const OPER_REQUEST: u16 = 0x0001;
20
21pub const OPER_REPLY: u16 = 0x0002;
23
24pub const HTYPE_ETHERNET: u16 = 0x0001;
26
27pub const ETH_IPV4_FRAME_LEN: usize = 28;
29
30const HTYPE_OFFSET: usize = 0;
31const PTYPE_OFFSET: usize = 2;
32const HLEN_OFFSET: usize = 4;
33const PLEN_OFFSET: usize = 5;
34const OPER_OFFSET: usize = 6;
35const SHA_OFFSET: usize = 8;
36
37const ETH_IPV4_SPA_OFFSET: usize = 14;
40const ETH_IPV4_THA_OFFSET: usize = 18;
41const ETH_IPV4_TPA_OFFSET: usize = 24;
42
43const IPV4_ADDR_LEN: u8 = 4;
44
45#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
47pub enum ArpError {
48 HLen,
50 HType,
52 Operation,
54 PLen,
56 PType,
58 SliceExactLen,
60}
61
62#[derive(Debug)]
68pub struct EthIPv4ArpFrame<'a, T: 'a> {
69 bytes: InnerBytes<'a, T>,
70}
71
72#[allow(clippy::len_without_is_empty)]
73impl<T: NetworkBytes + Debug> EthIPv4ArpFrame<'_, T> {
74 #[inline]
81 pub fn from_bytes_unchecked(bytes: T) -> Self {
82 EthIPv4ArpFrame {
83 bytes: InnerBytes::new(bytes),
84 }
85 }
86
87 pub fn request_from_bytes(bytes: T) -> Result<Self, ArpError> {
92 if bytes.len() != ETH_IPV4_FRAME_LEN {
94 return Err(ArpError::SliceExactLen);
95 }
96
97 let maybe = EthIPv4ArpFrame::from_bytes_unchecked(bytes);
98
99 if maybe.htype() != HTYPE_ETHERNET {
100 return Err(ArpError::HType);
101 }
102
103 if maybe.ptype() != ETHERTYPE_IPV4 {
104 return Err(ArpError::PType);
105 }
106
107 if maybe.hlen() != MAC_ADDR_LEN {
109 return Err(ArpError::HLen);
110 }
111
112 if maybe.plen() != IPV4_ADDR_LEN {
113 return Err(ArpError::PLen);
114 }
115
116 if maybe.operation() != OPER_REQUEST {
117 return Err(ArpError::Operation);
118 }
119
120 Ok(maybe)
121 }
122
123 #[inline]
125 pub fn htype(&self) -> u16 {
126 self.bytes.ntohs_unchecked(HTYPE_OFFSET)
127 }
128
129 #[inline]
131 pub fn ptype(&self) -> u16 {
132 self.bytes.ntohs_unchecked(PTYPE_OFFSET)
133 }
134
135 #[inline]
137 pub fn hlen(&self) -> u8 {
138 self.bytes[HLEN_OFFSET]
139 }
140
141 #[inline]
143 pub fn plen(&self) -> u8 {
144 self.bytes[PLEN_OFFSET]
145 }
146
147 #[inline]
149 pub fn operation(&self) -> u16 {
150 self.bytes.ntohs_unchecked(OPER_OFFSET)
151 }
152
153 #[inline]
155 pub fn sha(&self) -> MacAddr {
156 MacAddr::from_bytes_unchecked(&self.bytes[SHA_OFFSET..ETH_IPV4_SPA_OFFSET])
157 }
158
159 #[inline]
161 pub fn spa(&self) -> Ipv4Addr {
162 Ipv4Addr::from(self.bytes.ntohl_unchecked(ETH_IPV4_SPA_OFFSET))
163 }
164
165 #[inline]
167 pub fn tha(&self) -> MacAddr {
168 MacAddr::from_bytes_unchecked(&self.bytes[ETH_IPV4_THA_OFFSET..ETH_IPV4_TPA_OFFSET])
169 }
170
171 #[inline]
173 pub fn tpa(&self) -> Ipv4Addr {
174 Ipv4Addr::from(self.bytes.ntohl_unchecked(ETH_IPV4_TPA_OFFSET))
175 }
176
177 #[inline]
179 pub fn len(&self) -> usize {
180 self.bytes.len()
183 }
184}
185
186impl<T: NetworkBytesMut + Debug> EthIPv4ArpFrame<'_, T> {
187 #[allow(clippy::too_many_arguments)]
188 fn write_raw(
189 buf: T,
190 htype: u16,
191 ptype: u16,
192 hlen: u8,
193 plen: u8,
194 operation: u16,
195 sha: MacAddr,
196 spa: Ipv4Addr,
197 tha: MacAddr,
198 tpa: Ipv4Addr,
199 ) -> Result<Self, ArpError> {
200 if buf.len() != ETH_IPV4_FRAME_LEN {
201 return Err(ArpError::SliceExactLen);
202 }
203
204 let mut frame = EthIPv4ArpFrame::from_bytes_unchecked(buf);
206
207 frame.set_htype(htype);
208 frame.set_ptype(ptype);
209 frame.set_hlen(hlen);
210 frame.set_plen(plen);
211 frame.set_operation(operation);
212 frame.set_sha(sha);
213 frame.set_spa(spa);
214 frame.set_tha(tha);
215 frame.set_tpa(tpa);
216
217 Ok(frame)
218 }
219
220 #[inline]
223 pub fn write_request(
224 buf: T,
225 sha: MacAddr,
226 spa: Ipv4Addr,
227 tha: MacAddr,
228 tpa: Ipv4Addr,
229 ) -> Result<Self, ArpError> {
230 Self::write_raw(
231 buf,
232 HTYPE_ETHERNET,
233 ETHERTYPE_IPV4,
234 MAC_ADDR_LEN,
235 IPV4_ADDR_LEN,
236 OPER_REQUEST,
237 sha,
238 spa,
239 tha,
240 tpa,
241 )
242 }
243
244 #[inline]
247 pub fn write_reply(
248 buf: T,
249 sha: MacAddr,
250 spa: Ipv4Addr,
251 tha: MacAddr,
252 tpa: Ipv4Addr,
253 ) -> Result<Self, ArpError> {
254 Self::write_raw(
255 buf,
256 HTYPE_ETHERNET,
257 ETHERTYPE_IPV4,
258 MAC_ADDR_LEN,
259 IPV4_ADDR_LEN,
260 OPER_REPLY,
261 sha,
262 spa,
263 tha,
264 tpa,
265 )
266 }
267
268 #[inline]
270 pub fn set_htype(&mut self, value: u16) {
271 self.bytes.htons_unchecked(HTYPE_OFFSET, value);
272 }
273
274 #[inline]
276 pub fn set_ptype(&mut self, value: u16) {
277 self.bytes.htons_unchecked(PTYPE_OFFSET, value);
278 }
279
280 #[inline]
282 pub fn set_hlen(&mut self, value: u8) {
283 self.bytes[HLEN_OFFSET] = value;
284 }
285
286 #[inline]
288 pub fn set_plen(&mut self, value: u8) {
289 self.bytes[PLEN_OFFSET] = value;
290 }
291
292 #[inline]
294 pub fn set_operation(&mut self, value: u16) {
295 self.bytes.htons_unchecked(OPER_OFFSET, value);
296 }
297
298 #[inline]
300 pub fn set_sha(&mut self, addr: MacAddr) {
301 self.bytes[SHA_OFFSET..ETH_IPV4_SPA_OFFSET].copy_from_slice(addr.get_bytes());
302 }
303
304 #[inline]
306 pub fn set_spa(&mut self, addr: Ipv4Addr) {
307 self.bytes
308 .htonl_unchecked(ETH_IPV4_SPA_OFFSET, u32::from(addr));
309 }
310
311 #[inline]
313 pub fn set_tha(&mut self, addr: MacAddr) {
314 self.bytes[ETH_IPV4_THA_OFFSET..ETH_IPV4_TPA_OFFSET].copy_from_slice(addr.get_bytes());
315 }
316
317 #[inline]
319 pub fn set_tpa(&mut self, addr: Ipv4Addr) {
320 self.bytes
321 .htonl_unchecked(ETH_IPV4_TPA_OFFSET, u32::from(addr));
322 }
323}
324
325#[inline]
328pub fn test_speculative_tpa(buf: &[u8], addr: Ipv4Addr) -> bool {
329 if buf.len() >= ethernet::PAYLOAD_OFFSET + ETH_IPV4_FRAME_LEN {
331 let bytes = &buf[ethernet::PAYLOAD_OFFSET..];
332 if EthIPv4ArpFrame::from_bytes_unchecked(bytes).tpa() == addr {
333 return true;
334 }
335 }
336 false
337}
338
339#[cfg(test)]
340mod tests {
341 use std::str::FromStr;
342
343 use super::*;
344
345 #[test]
346 fn test_eth_ipv4_arp_frame() {
347 let mut a = [0u8; 1000];
348 let mut bad_array = [0u8; 1];
349
350 let sha = MacAddr::from_str("01:23:45:67:89:ab").unwrap();
351 let tha = MacAddr::from_str("cd:ef:01:23:45:67").unwrap();
352 let spa = Ipv4Addr::new(10, 1, 2, 3);
353 let tpa = Ipv4Addr::new(10, 4, 5, 6);
354
355 assert_eq!(
357 EthIPv4ArpFrame::request_from_bytes(bad_array.as_ref()).unwrap_err(),
358 ArpError::SliceExactLen
359 );
360
361 assert_eq!(
363 EthIPv4ArpFrame::write_reply(bad_array.as_mut(), sha, spa, tha, tpa).unwrap_err(),
364 ArpError::SliceExactLen
365 );
366
367 assert_eq!(
369 EthIPv4ArpFrame::write_reply(a.as_mut(), sha, spa, tha, tpa).unwrap_err(),
370 ArpError::SliceExactLen
371 );
372
373 {
375 let f = EthIPv4ArpFrame::write_reply(&mut a[..ETH_IPV4_FRAME_LEN], sha, spa, tha, tpa)
376 .unwrap();
377
378 assert_eq!(f.htype(), HTYPE_ETHERNET);
380 assert_eq!(f.ptype(), ETHERTYPE_IPV4);
381 assert_eq!(f.hlen(), MAC_ADDR_LEN);
382 assert_eq!(f.plen(), IPV4_ADDR_LEN);
383 assert_eq!(f.operation(), OPER_REPLY);
384 assert_eq!(f.sha(), sha);
385 assert_eq!(f.spa(), spa);
386 assert_eq!(f.tha(), tha);
387 assert_eq!(f.tpa(), tpa);
388 }
389
390 assert_eq!(
394 EthIPv4ArpFrame::request_from_bytes(a.as_ref()).unwrap_err(),
395 ArpError::SliceExactLen
396 );
397
398 assert_eq!(
400 EthIPv4ArpFrame::request_from_bytes(&a[..ETH_IPV4_FRAME_LEN]).unwrap_err(),
401 ArpError::Operation
402 );
403
404 let requests = [
406 (
407 HTYPE_ETHERNET,
408 ETHERTYPE_IPV4,
409 MAC_ADDR_LEN,
410 IPV4_ADDR_LEN,
411 None,
412 ), (
414 HTYPE_ETHERNET + 1,
415 ETHERTYPE_IPV4,
416 MAC_ADDR_LEN,
417 IPV4_ADDR_LEN,
418 Some(ArpError::HType),
419 ), (
421 HTYPE_ETHERNET,
422 ETHERTYPE_IPV4 + 1,
423 MAC_ADDR_LEN,
424 IPV4_ADDR_LEN,
425 Some(ArpError::PType),
426 ), (
428 HTYPE_ETHERNET,
429 ETHERTYPE_IPV4,
430 MAC_ADDR_LEN + 1,
431 IPV4_ADDR_LEN,
432 Some(ArpError::HLen),
433 ), (
435 HTYPE_ETHERNET,
436 ETHERTYPE_IPV4,
437 MAC_ADDR_LEN,
438 IPV4_ADDR_LEN + 1,
439 Some(ArpError::PLen),
440 ), ];
442
443 for (htype, ptype, hlen, plen, err) in requests.iter() {
444 EthIPv4ArpFrame::write_raw(
445 &mut a[..ETH_IPV4_FRAME_LEN],
446 *htype,
447 *ptype,
448 *hlen,
449 *plen,
450 OPER_REQUEST,
451 sha,
452 spa,
453 tha,
454 tpa,
455 )
456 .unwrap();
457 match err {
458 None => {
459 EthIPv4ArpFrame::request_from_bytes(&a[..ETH_IPV4_FRAME_LEN]).unwrap();
460 }
461 Some(arp_error) => assert_eq!(
462 EthIPv4ArpFrame::request_from_bytes(&a[..ETH_IPV4_FRAME_LEN]).unwrap_err(),
463 *arp_error
464 ),
465 }
466 }
467 }
468
469 #[test]
470 fn test_speculative() {
471 let mut a = [0u8; 1000];
472 let addr = Ipv4Addr::new(1, 2, 3, 4);
473
474 assert!(!test_speculative_tpa(a.as_ref(), addr));
475
476 {
477 let mac = MacAddr::from_bytes_unchecked(&[0; 6]);
478 let mut eth = crate::dumbo::pdu::ethernet::EthernetFrame::write_incomplete(
479 a.as_mut(),
480 mac,
481 mac,
482 0,
483 )
484 .unwrap();
485 let mut arp = EthIPv4ArpFrame::from_bytes_unchecked(eth.inner_mut().payload_mut());
486 arp.set_tpa(addr);
487 }
488
489 assert!(test_speculative_tpa(a.as_ref(), addr));
490
491 let small = [0u8; 1];
493 assert!(!test_speculative_tpa(small.as_ref(), addr));
494 }
495}