Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ARP module #15

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ Read the documentations of the different methods for a more details:
* IEEE 802.3

## References
* An Ethernet Address Resolution Protocol or Converting Network Protocol Addresses [RFC 826](https://tools.ietf.org/html/rfc826)
* Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791)
* Internet Protocol, Version 6 (IPv6) Specification [RFC 8200](https://tools.ietf.org/html/rfc8200)
* [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)
Expand Down
201 changes: 201 additions & 0 deletions src/link/arp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use super::*;
use std::slice::from_raw_parts;

#[derive(Clone, Debug, Eq, PartialEq)]
/// There are many other possible hardware types, but
/// this library focuses on Ethernet.
pub enum ArpHardwareType {
Ethernet = 0x0001,
}

pub mod arp_hardware_type {
use super::ArpHardwareType;
pub const ETHERNET: u16 = ArpHardwareType::Ethernet as u16;
}

impl ArpHardwareType {
pub fn from_u16(value: u16) -> Option<ArpHardwareType> {
use self::ArpHardwareType::*;
match value {
0x0001 => Some(Ethernet),
_ => None,
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ArpOpcode {
Request = 0x0001,
Reply = 0x0002,
}

#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct ArpHeader {
pub hardware_type: u16,
pub protocol_type: u16,
pub hardware_len: u8,
pub protocol_len: u8,
pub opcode: u16,
}

impl SerializedSize for ArpHeader {
///Size of the header itself in bytes.
const SERIALIZED_SIZE: usize = 8;
}

impl ArpHeader {
/// Creates an arp header for ethernet & IPv4 with the given opcode.
pub fn new_ipv4(
opcode: ArpOpcode,
) -> Self {
ArpHeader {
hardware_type: arp_hardware_type::ETHERNET,
protocol_type: ether_type::IPV4,
hardware_len: 3,
protocol_len: 4,
opcode: opcode as u16,
}
}

pub fn header_len(&self) -> usize {
ArpHeader::SERIALIZED_SIZE
}

pub fn total_len(&self) -> usize {
let payload_len = 2 * (self.hardware_len + self.protocol_len);
ArpHeader::SERIALIZED_SIZE + (payload_len as usize)
}

pub fn from_slice(slice: &[u8]) -> Result<(ArpHeader, &[u8]), ReadError> {
let header = ArpHeaderSlice::from_slice(slice)?.to_header();
let rest = &slice[header.header_len()..];
Ok((header, rest))
}

pub fn read<T: io::Read + io::Seek + Sized>(reader: &mut T) -> Result<ArpHeader, ReadError> {
let mut buffer : [u8;ArpHeader::SERIALIZED_SIZE] = [0;ArpHeader::SERIALIZED_SIZE];
reader.read_exact(&mut buffer)?;
Ok(
// SAFETY: Safe as the buffer has the required size `ArpHeader::SERIALIZED_SIZE`.
unsafe {
ArpHeaderSlice::from_slice_unchecked(&buffer).to_header()
}
)
}
}

///A slice containing an arp header of a network packet.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ArpHeaderSlice<'a> {
slice: &'a [u8],
}

impl<'a> ArpHeaderSlice<'a> {
/// Creates a slice containing an arp header.
pub fn from_slice(slice: &'a [u8]) -> Result<ArpHeaderSlice<'a>, ReadError> {
// check len
use crate::ReadError::*;
if slice.len() < ArpHeader::SERIALIZED_SIZE {
return Err(UnexpectedEndOfSlice(ArpHeader::SERIALIZED_SIZE));
}

Ok(ArpHeaderSlice {
slice: &slice[..ArpHeader::SERIALIZED_SIZE],
})
}

/// Creates a arp header slice from a slice (assumes slice size & content was validated before).
///
/// # Safety
///
/// This method assumes that the slice was previously validated to contain
/// a valid arp header. This means the slice length must at least be at least 8.
/// The data that the slice points must also be valid (meaning no nullptr or alike allowed).
///
/// If these precondtions are not fullfilled the behavior of this function
/// and the methods of the return ArpHeaderSlice will be undefined.
pub unsafe fn from_slice_unchecked(slice: &'a[u8]) -> ArpHeaderSlice<'a> {
ArpHeaderSlice{
slice: from_raw_parts(
slice.as_ptr(),
ArpHeader::SERIALIZED_SIZE
)
}
}

/// Returns the slice containing the arp header
#[inline]
pub fn slice(&self) -> &'a [u8] {
self.slice
}

/// Read the "hardware type" field of the ARP header (should usually be 1 for Ethernet)
#[inline]
pub fn hardware_type(&self) -> u16 {
// SAFETY:
// Safe as the slice length is checked to be at least
// SERIALIZED_SIZE (8) in the constructor.
unsafe {
get_unchecked_be_u16(self.slice.as_ptr())
}
}

/// Read the "protocol type" field of the ARP header (should be 0x0800 for IPv4, or 0x86DD for IPv6).
#[inline]
pub fn protocol_type(&self) -> u16 {
// SAFETY:
// Safe as the slice length is checked to be at least
// SERIALIZED_SIZE (8) in the constructor.
unsafe {
get_unchecked_be_u16(self.slice.as_ptr().add(2))
}
}

/// Read the "hardware length" field of the ARP header (should be 3 for Ethernet).
#[inline]
pub fn hardware_len(&self) -> u8 {
// SAFETY:
// Safe as the slice length is checked to be at least
// SERIALIZED_SIZE (8) in the constructor.
unsafe {
*self.slice.get_unchecked(4)
}
}

/// Read the "protocol length" field of the ARP header (should be 4 for IPv4, or 16 for IPv6).
#[inline]
pub fn protocol_len(&self) -> u8 {
// SAFETY:
// Safe as the slice length is checked to be at least
// SERIALIZED_SIZE (8) in the constructor.
unsafe {
*self.slice.get_unchecked(5)
}
}

/// Read the opcode field of the ARP header
#[inline]
pub fn opcode(&self) -> u16 {
// SAFETY:
// Safe as the slice length is checked to be at least
// SERIALIZED_SIZE (8) in the constructor.
unsafe {
get_unchecked_be_u16(self.slice.as_ptr().add(6))
}
}

pub fn total_len(&self) -> usize {
let payload_len = 2 * (usize::from(self.hardware_len()) + usize::from(self.protocol_len()));
ArpHeader::SERIALIZED_SIZE + (payload_len as usize)
}

pub fn to_header(&self) -> ArpHeader {
ArpHeader {
hardware_type: self.hardware_type(),
protocol_type: self.protocol_type(),
hardware_len: self.hardware_len(),
protocol_len: self.protocol_len(),
opcode: self.opcode(),
}
}
}
1 change: 1 addition & 0 deletions src/link/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod arp;
pub mod ethernet;
pub mod vlan_tagging;

Expand Down