use crate::misc::{bytes_to_mac, IpFormatter};
use core::{fmt, str::from_utf8};
pub const ARP_HEADER_LENGTH: usize = 28;
pub struct ArpWriter<'a> {
pub bytes: &'a mut [u8],
}
impl<'a> ArpWriter<'a> {
#[inline]
pub fn new(bytes: &'a mut [u8]) -> Result<Self, &'static str> {
if bytes.len() < ARP_HEADER_LENGTH {
return Err("Slice is too short to contain an ARP header.");
}
Ok(Self { bytes })
}
#[inline]
pub fn header_len(&self) -> usize {
ARP_HEADER_LENGTH
}
#[inline]
pub fn set_htype(&mut self, hardware_type: u16) {
self.bytes[0] = (hardware_type >> 8) as u8;
self.bytes[1] = hardware_type as u8;
}
#[inline]
pub fn set_ptype(&mut self, protocol_type: u16) {
self.bytes[2] = (protocol_type >> 8) as u8;
self.bytes[3] = protocol_type as u8;
}
#[inline]
pub fn set_hlen(&mut self, hardware_address_length: u8) {
self.bytes[4] = hardware_address_length;
}
#[inline]
pub fn set_plen(&mut self, protocol_address_length: u8) {
self.bytes[5] = protocol_address_length;
}
#[inline]
pub fn set_oper(&mut self, operation: u16) {
self.bytes[6] = (operation >> 8) as u8;
self.bytes[7] = operation as u8;
}
#[inline]
pub fn set_sha(&mut self, sender_hardware_address: &[u8; 6]) {
self.bytes[8] = sender_hardware_address[0];
self.bytes[9] = sender_hardware_address[1];
self.bytes[10] = sender_hardware_address[2];
self.bytes[11] = sender_hardware_address[3];
self.bytes[12] = sender_hardware_address[4];
self.bytes[13] = sender_hardware_address[5];
}
#[inline]
pub fn set_spa(&mut self, sender_protocol_address: &[u8; 4]) {
self.bytes[14] = sender_protocol_address[0];
self.bytes[15] = sender_protocol_address[1];
self.bytes[16] = sender_protocol_address[2];
self.bytes[17] = sender_protocol_address[3];
}
#[inline]
pub fn set_tha(&mut self, target_hardware_address: &[u8; 6]) {
self.bytes[18] = target_hardware_address[0];
self.bytes[19] = target_hardware_address[1];
self.bytes[20] = target_hardware_address[2];
self.bytes[21] = target_hardware_address[3];
self.bytes[22] = target_hardware_address[4];
self.bytes[23] = target_hardware_address[5];
}
#[inline]
pub fn set_tpa(&mut self, target_protocol_address: &[u8; 4]) {
self.bytes[24] = target_protocol_address[0];
self.bytes[25] = target_protocol_address[1];
self.bytes[26] = target_protocol_address[2];
self.bytes[27] = target_protocol_address[3];
}
}
#[derive(PartialEq)]
pub struct ArpReader<'a> {
pub bytes: &'a [u8],
}
impl<'a> ArpReader<'a> {
#[inline]
pub fn new(bytes: &'a [u8]) -> Result<Self, &'static str> {
if bytes.len() < ARP_HEADER_LENGTH {
return Err("Slice is too short to contain an ARP header.");
}
Ok(Self { bytes })
}
#[inline]
pub fn htype(&self) -> u16 {
((self.bytes[0] as u16) << 8) | (self.bytes[1] as u16)
}
#[inline]
pub fn ptype(&self) -> u16 {
((self.bytes[2] as u16) << 8) | (self.bytes[3] as u16)
}
#[inline]
pub fn hlen(&self) -> u8 {
self.bytes[4]
}
#[inline]
pub fn plen(&self) -> u8 {
self.bytes[5]
}
#[inline]
pub fn oper(&self) -> u16 {
((self.bytes[6] as u16) << 8) | (self.bytes[7] as u16)
}
#[inline]
pub fn sha(&self) -> &[u8] {
&self.bytes[8..14]
}
#[inline]
pub fn spa(&self) -> &[u8] {
&self.bytes[14..18]
}
#[inline]
pub fn tha(&self) -> &[u8] {
&self.bytes[18..24]
}
#[inline]
pub fn tpa(&self) -> &[u8] {
&self.bytes[24..28]
}
#[inline]
pub fn header_len(&self) -> usize {
ARP_HEADER_LENGTH
}
#[inline]
pub fn header(&self) -> &'a [u8] {
&self.bytes[..ARP_HEADER_LENGTH]
}
#[inline]
pub fn payload(&self) -> &[u8] {
&self.bytes[ARP_HEADER_LENGTH..]
}
}
impl fmt::Debug for ArpReader<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sender_ip = self.spa();
let target_ip = self.tpa();
let mut sha_buf = [0u8; 18];
let sha_len = bytes_to_mac(self.sha(), &mut sha_buf);
let sha_hex = from_utf8(&sha_buf[..sha_len]).unwrap();
let mut tha_buf = [0u8; 18];
let tha_len = bytes_to_mac(self.tha(), &mut tha_buf);
let tha_hex = from_utf8(&tha_buf[..tha_len]).unwrap();
f.debug_struct("Arp")
.field("hardware_type", &self.htype())
.field("protocol_type", &self.ptype())
.field("hardware_address_length", &self.hlen())
.field("protocol_address_length", &self.plen())
.field("operation", &self.oper())
.field("sender_hardware_address", &sha_hex)
.field("sender_protocol_address", &IpFormatter(sender_ip))
.field("target_hardware_address", &tha_hex)
.field("target_protocol_address", &IpFormatter(target_ip))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn getters_and_setters() {
let mut bytes = [0; 28];
let mut writer = ArpWriter::new(&mut bytes).unwrap();
writer.set_htype(1);
writer.set_ptype(2);
writer.set_hlen(3);
writer.set_plen(4);
writer.set_oper(5);
writer.set_sha(&[6, 7, 8, 9, 10, 11]);
writer.set_spa(&[12, 13, 14, 15]);
writer.set_tha(&[16, 17, 18, 19, 20, 21]);
writer.set_tpa(&[22, 23, 24, 25]);
let reader = ArpReader::new(&bytes).unwrap();
assert_eq!(reader.ptype(), 2);
assert_eq!(reader.htype(), 1);
assert_eq!(reader.hlen(), 3);
assert_eq!(reader.plen(), 4);
assert_eq!(reader.oper(), 5);
assert_eq!(reader.sha(), &[6, 7, 8, 9, 10, 11]);
assert_eq!(reader.spa(), &[12, 13, 14, 15]);
assert_eq!(reader.tha(), &[16, 17, 18, 19, 20, 21]);
assert_eq!(reader.tpa(), &[22, 23, 24, 25]);
}
}