blob: 5608ae493dbc4a0bb9f5ca902c6b89c99b196b4e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Parsing and serialization of ARP packets.
#![allow(private_in_public)]
#[cfg(test)]
use std::fmt::{self, Debug, Formatter};
use std::mem;
use byteorder::{ByteOrder, NetworkEndian};
use packet::{BufferView, BufferViewMut, InnerPacketBuilder, ParsablePacket, ParseMetadata};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use crate::device::arp::{ArpHardwareType, ArpOp};
use crate::device::ethernet::{EtherType, Mac};
use crate::error::ParseError;
use crate::ip::Ipv4Addr;
// Header has the same memory layout (thanks to repr(C, packed)) as an ARP
// header. Thus, we can simply reinterpret the bytes of the ARP header as a
// Header and then safely access its fields. Note the following caveats:
// - We cannot make any guarantees about the alignment of an instance of this
// struct in memory or of any of its fields. This is true both because
// repr(packed) removes the padding that would be used to ensure the alignment
// of individual fields, but also because we are given no guarantees about
// where within a given memory buffer a particular packet (and thus its
// header) will be located.
// - Individual fields are all either u8 or [u8; N] rather than u16, u32, etc.
// This is for two reasons:
// - u16 and larger have larger-than-1 alignments, which are forbidden as
// described above
// - We are not guaranteed that the local platform has the same endianness as
// network byte order (big endian), so simply treating a sequence of bytes
// as a u16 or other multi-byte number would not necessarily be correct.
// Instead, we use the NetworkEndian type and its reader and writer methods
// to correctly access these fields.
#[derive(Default)]
#[repr(C, packed)]
struct Header {
htype: [u8; 2], // Hardware (e.g. Ethernet)
ptype: [u8; 2], // Protocol (e.g. IPv4)
hlen: u8, // Length (in octets) of hardware address
plen: u8, // Length (in octets) of protocol address
oper: [u8; 2], // Operation: 1 for Req, 2 for Reply
}
unsafe impl FromBytes for Header {}
unsafe impl AsBytes for Header {}
unsafe impl Unaligned for Header {}
impl Header {
fn hardware_protocol(&self) -> u16 {
NetworkEndian::read_u16(&self.htype)
}
fn set_hardware_protocol(&mut self, htype: ArpHardwareType, hlen: u8) -> &mut Self {
NetworkEndian::write_u16(&mut self.htype, htype as u16);
self.hlen = hlen;
self
}
fn network_protocol(&self) -> u16 {
NetworkEndian::read_u16(&self.ptype)
}
fn set_network_protocol(&mut self, ptype: EtherType, plen: u8) -> &mut Self {
NetworkEndian::write_u16(&mut self.ptype, ptype as u16);
self.plen = plen;
self
}
fn op_code(&self) -> u16 {
NetworkEndian::read_u16(&self.oper)
}
fn set_op_code(&mut self, op: ArpOp) -> &mut Self {
NetworkEndian::write_u16(&mut self.oper, op as u16);
self
}
fn hardware_address_len(&self) -> u8 {
self.hlen
}
fn protocol_address_len(&self) -> u8 {
self.plen
}
}
/// Peek at an ARP header to see what hardware and protocol address types are
/// used.
///
/// Since `ArpPacket` is statically typed with the hardware and protocol address
/// types expected in the header and body, these types must be known ahead of
/// time before calling `parse`. If multiple different types are valid in a
/// given parsing context, and so the caller cannot know ahead of time which
/// types to use, `peek_arp_types` can be used to peek at the header first to
/// figure out which static types should be used in a subsequent call to
/// `parse`.
///
/// Note that `peek_arp_types` only inspects certain fields in the header, and
/// so `peek_arp_types` succeeding does not guarantee that a subsequent call to
/// `parse` will also succeed.
pub fn peek_arp_types<B: ByteSlice>(bytes: B) -> Result<(ArpHardwareType, EtherType), ParseError> {
let (header, _) = LayoutVerified::<B, Header>::new_unaligned_from_prefix(bytes).ok_or_else(
debug_err_fn!(ParseError::Format, "too few bytes for header"),
)?;
let hw = ArpHardwareType::from_u16(header.hardware_protocol()).ok_or_else(debug_err_fn!(
ParseError::NotSupported,
"unrecognized hardware protocol: {:x}",
header.hardware_protocol()
))?;
let proto = EtherType::from_u16(header.network_protocol()).ok_or_else(debug_err_fn!(
ParseError::NotSupported,
"unrecognized network protocol: {:x}",
header.network_protocol()
))?;
let hlen = match hw {
ArpHardwareType::Ethernet => <Mac as HType>::hlen(),
};
let plen = match proto {
EtherType::Ipv4 => <Ipv4Addr as PType>::plen(),
_ => {
return debug_err!(
Err(ParseError::NotSupported),
"unsupported network protocol: {}",
proto
);
}
};
if header.hardware_address_len() != hlen || header.protocol_address_len() != plen {
return debug_err!(
Err(ParseError::Format),
"unexpected hardware or protocol address length for protocol {}",
proto
);
}
Ok((hw, proto))
}
// See comment on Header for an explanation of the memory safety requirements.
#[repr(C, packed)]
struct Body<HwAddr, ProtoAddr> {
sha: HwAddr,
spa: ProtoAddr,
tha: HwAddr,
tpa: ProtoAddr,
}
unsafe impl<HwAddr: FromBytes, ProtoAddr: FromBytes> FromBytes for Body<HwAddr, ProtoAddr> {}
unsafe impl<HwAddr: AsBytes, ProtoAddr: AsBytes> AsBytes for Body<HwAddr, ProtoAddr> {}
unsafe impl<HwAddr: Unaligned, ProtoAddr: Unaligned> Unaligned for Body<HwAddr, ProtoAddr> {}
impl<HwAddr: Copy, ProtoAddr: Copy> Body<HwAddr, ProtoAddr> {
fn set_sha(&mut self, sha: HwAddr) -> &mut Self {
self.sha = sha;
self
}
fn set_spa(&mut self, spa: ProtoAddr) -> &mut Self {
self.spa = spa;
self
}
fn set_tha(&mut self, tha: HwAddr) -> &mut Self {
self.tha = tha;
self
}
fn set_tpa(&mut self, tpa: ProtoAddr) -> &mut Self {
self.tpa = tpa;
self
}
}
/// A trait to represent a ARP hardware type.
pub trait HType: FromBytes + AsBytes + Unaligned + Copy + Clone {
/// The hardware type.
fn htype() -> ArpHardwareType;
/// The in-memory size of an instance of the type.
fn hlen() -> u8;
}
/// A trait to represent a ARP protocol type.
pub trait PType: FromBytes + AsBytes + Unaligned + Copy + Clone {
/// The protocol type.
fn ptype() -> EtherType;
/// The in-memory size of an instance of the type.
fn plen() -> u8;
/// Returns a concrete instance of the protocol address
///
/// This is a hack, since we only support Ipv4 - if we support more
/// protocols in the future, we'll need to change this to return an enum of
/// all the possible protocol addresses.
fn addr(self) -> Ipv4Addr;
}
impl HType for Mac {
fn htype() -> ArpHardwareType {
ArpHardwareType::Ethernet
}
fn hlen() -> u8 {
use std::convert::TryFrom;
u8::try_from(mem::size_of::<Mac>()).unwrap()
}
}
impl PType for Ipv4Addr {
fn ptype() -> EtherType {
EtherType::Ipv4
}
fn plen() -> u8 {
use std::convert::TryFrom;
u8::try_from(mem::size_of::<Ipv4Addr>()).unwrap()
}
fn addr(self) -> Ipv4Addr {
self
}
}
/// An ARP packet.
///
/// A `ArpPacket` shares its underlying memory with the byte slice it was parsed
/// from or serialized to, meaning that no copying or extra allocation is
/// necessary.
pub struct ArpPacket<B, HwAddr, ProtoAddr> {
header: LayoutVerified<B, Header>,
body: LayoutVerified<B, Body<HwAddr, ProtoAddr>>,
}
impl<B: ByteSlice, HwAddr, ProtoAddr> ParsablePacket<B, ()> for ArpPacket<B, HwAddr, ProtoAddr>
where
HwAddr: Copy + HType + FromBytes + Unaligned,
ProtoAddr: Copy + PType + FromBytes + Unaligned,
{
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_inner_packet(self.header.bytes().len() + self.body.bytes().len())
}
fn parse<BV: BufferView<B>>(mut buffer: BV, args: ()) -> Result<Self, ParseError> {
let header = buffer.take_obj_front::<Header>().ok_or_else(debug_err_fn!(
ParseError::Format,
"too few bytes for header"
))?;
let body = buffer
.take_obj_front::<Body<HwAddr, ProtoAddr>>()
.ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for body"))?;
// Consume any padding bytes added by the previous layer.
buffer.take_rest_front();
if header.hardware_protocol() != <HwAddr as HType>::htype() as u16
|| header.network_protocol() != <ProtoAddr as PType>::ptype() as u16
{
return debug_err!(
Err(ParseError::NotExpected),
"unexpected hardware or network protocols"
);
}
if header.hardware_address_len() != <HwAddr as HType>::hlen()
|| header.protocol_address_len() != <ProtoAddr as PType>::plen()
{
return debug_err!(
Err(ParseError::Format),
"unexpected hardware or protocol address length"
);
}
if ArpOp::from_u16(header.op_code()).is_none() {
return debug_err!(
Err(ParseError::Format),
"unrecognized op code: {:x}",
header.op_code()
);
}
Ok(ArpPacket { header, body })
}
}
impl<B: ByteSlice, HwAddr, ProtoAddr> ArpPacket<B, HwAddr, ProtoAddr>
where
HwAddr: Copy + HType + FromBytes + Unaligned,
ProtoAddr: Copy + PType + FromBytes + Unaligned,
{
/// The type of ARP packet
pub fn operation(&self) -> ArpOp {
// This is verified in `parse`, so should be safe to unwrap
ArpOp::from_u16(self.header.op_code()).unwrap()
}
/// The hardware address of the ARP packet sender.
pub fn sender_hardware_address(&self) -> HwAddr {
self.body.sha
}
/// The protocol address of the ARP packet sender.
pub fn sender_protocol_address(&self) -> ProtoAddr {
self.body.spa
}
/// The hardware address of the ARP packet target.
pub fn target_hardware_address(&self) -> HwAddr {
self.body.tha
}
/// The protocol address of the ARP packet target.
pub fn target_protocol_address(&self) -> ProtoAddr {
self.body.tpa
}
/// Construct a builder with the same contents as this packet.
pub fn builder(&self) -> ArpPacketBuilder<HwAddr, ProtoAddr> {
ArpPacketBuilder {
op: self.operation(),
sha: self.sender_hardware_address(),
spa: self.sender_protocol_address(),
tha: self.target_hardware_address(),
tpa: self.target_protocol_address(),
}
}
}
/// A builder for ARP packets.
pub struct ArpPacketBuilder<HwAddr, ProtoAddr> {
op: ArpOp,
sha: HwAddr,
spa: ProtoAddr,
tha: HwAddr,
tpa: ProtoAddr,
}
impl<HwAddr, ProtoAddr> ArpPacketBuilder<HwAddr, ProtoAddr> {
/// Construct a new `ArpPacketBuilder`.
pub fn new(
operation: ArpOp, sender_hardware_addr: HwAddr, sender_protocol_addr: ProtoAddr,
target_hardware_addr: HwAddr, target_protocol_addr: ProtoAddr,
) -> ArpPacketBuilder<HwAddr, ProtoAddr> {
ArpPacketBuilder {
op: operation,
sha: sender_hardware_addr,
spa: sender_protocol_addr,
tha: target_hardware_addr,
tpa: target_protocol_addr,
}
}
}
impl<HwAddr, ProtoAddr> InnerPacketBuilder for ArpPacketBuilder<HwAddr, ProtoAddr>
where
HwAddr: Copy + HType + FromBytes + AsBytes + Unaligned,
ProtoAddr: Copy + PType + FromBytes + AsBytes + Unaligned,
{
fn bytes(&self) -> usize {
mem::size_of::<Header>() + mem::size_of::<Body<HwAddr, ProtoAddr>>()
}
fn serialize(self, mut buffer: &mut [u8]) {
// implements BufferViewMut, giving us take_obj_xxx_zero methods
let mut buffer = &mut buffer;
// SECURITY: Use _zero constructors to ensure we zero memory to prevent
// leaking information from packets previously stored in this buffer.
let mut header = buffer
.take_obj_front_zero::<Header>()
.expect("not enough bytes for an ARP packet");
let mut body = buffer
.take_obj_front_zero::<Body<HwAddr, ProtoAddr>>()
.expect("not enough bytes for an ARP packet");
header
.set_hardware_protocol(<HwAddr as HType>::htype(), <HwAddr as HType>::hlen())
.set_network_protocol(<ProtoAddr as PType>::ptype(), <ProtoAddr as PType>::plen())
.set_op_code(self.op);
body.set_sha(self.sha)
.set_spa(self.spa)
.set_tha(self.tha)
.set_tpa(self.tpa);
}
}
#[cfg(test)]
impl<B, HwAddr, ProtoAddr> Debug for ArpPacket<B, HwAddr, ProtoAddr> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "ArpPacket")
}
}
#[cfg(test)]
mod tests {
use packet::{FnSerializer, ParseBuffer, Serializer};
use super::*;
use crate::ip::Ipv4Addr;
use crate::wire::ethernet::EthernetFrame;
const TEST_SENDER_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const TEST_TARGET_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
const TEST_SENDER_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
const TEST_TARGET_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
#[test]
fn test_parse_serialize_full() {
crate::testutil::set_logger_for_test();
use crate::wire::testdata::*;
let mut req = &ARP_REQUEST[..];
let frame = req.parse::<EthernetFrame<_>>().unwrap();
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Arp)));
let (hw, proto) = peek_arp_types(frame.body()).unwrap();
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
let mut body = frame.body();
let arp = body.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
assert_eq!(arp.operation(), ArpOp::Request);
assert_eq!(frame.src_mac(), arp.sender_hardware_address());
let frame_bytes = arp.builder().encapsulate(frame.builder()).serialize_outer();
assert_eq!(frame_bytes.as_ref(), ARP_REQUEST);
}
fn header_to_bytes(header: Header) -> [u8; 8] {
let mut bytes = [0; 8];
{
let mut lv = LayoutVerified::new_unaligned(&mut bytes[..]).unwrap();
*lv = header;
}
bytes
}
// Return a new Header for an Ethernet/IPv4 ARP request.
fn new_header() -> Header {
let mut header = Header::default();
header.set_hardware_protocol(<Mac as HType>::htype(), <Mac as HType>::hlen());
header.set_network_protocol(<Ipv4Addr as PType>::ptype(), <Ipv4Addr as PType>::plen());
header.set_op_code(ArpOp::Request);
header
}
#[test]
fn test_peek() {
let header = new_header();
let (hw, proto) = peek_arp_types(&header_to_bytes(header)[..]).unwrap();
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
// Test that an invalid operation is not rejected; peek_arp_types does
// not inspect the operation.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.oper[..], 3);
let (hw, proto) = peek_arp_types(&header_to_bytes(header)[..]).unwrap();
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
}
#[test]
fn test_parse() {
let mut buf = &mut [
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 5, 6, 7, 8,
][..];
(&mut buf[..8]).copy_from_slice(&header_to_bytes(new_header()));
let (hw, proto) = peek_arp_types(&buf[..]).unwrap();
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
let mut buf = &mut buf;
let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
assert_eq!(packet.sender_hardware_address(), TEST_SENDER_MAC);
assert_eq!(packet.sender_protocol_address(), TEST_SENDER_IPV4);
assert_eq!(packet.target_hardware_address(), TEST_TARGET_MAC);
assert_eq!(packet.target_protocol_address(), TEST_TARGET_IPV4);
assert_eq!(packet.operation(), ArpOp::Request);
}
#[test]
fn test_serialize() {
let mut buf = FnSerializer::new_vec(ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
))
.serialize_outer();
assert_eq!(
AsRef::<[u8]>::as_ref(&buf),
&[
0, 1, 8, 0, 6, 4, 0, 1, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 5, 6, 7,
8,
]
);
let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
assert_eq!(packet.sender_hardware_address(), TEST_SENDER_MAC);
assert_eq!(packet.sender_protocol_address(), TEST_SENDER_IPV4);
assert_eq!(packet.target_hardware_address(), TEST_TARGET_MAC);
assert_eq!(packet.target_protocol_address(), TEST_TARGET_IPV4);
assert_eq!(packet.operation(), ArpOp::Request);
}
#[test]
fn test_peek_error() {
// Test that a header which is too short is rejected.
let buf = [0; 7];
assert_eq!(peek_arp_types(&buf[..]).unwrap_err(), ParseError::Format);
// Test that an unexpected hardware protocol type is rejected.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.htype[..], 0);
assert_eq!(
peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
ParseError::NotSupported
);
// Test that an unexpected network protocol type is rejected.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.ptype[..], 0);
assert_eq!(
peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
ParseError::NotSupported
);
// Test that an incorrect hardware address len is rejected.
let mut header = new_header();
header.hlen = 7;
assert_eq!(
peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
ParseError::Format
);
// Test that an incorrect protocol address len is rejected.
let mut header = new_header();
header.plen = 5;
assert_eq!(
peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
ParseError::Format
);
}
#[test]
fn test_parse_error() {
// Assert that parsing a buffer results in an error.
fn assert_err(mut buf: &[u8], err: ParseError) {
assert_eq!(buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap_err(), err);
}
// Assert that parsing a particular header results in an error.
fn assert_header_err(header: Header, err: ParseError) {
let mut buf = [0; 28];
*LayoutVerified::<_, Header>::new_unaligned_from_prefix(&mut buf[..])
.unwrap()
.0 = header;
assert_err(&buf[..], err);
}
// Test that a packet which is too short is rejected.
let buf = [0; 27];
assert_err(&[0; 27][..], ParseError::Format);
let mut buf = [0; 28];
// Test that an unexpected hardware protocol type is rejected.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.htype[..], 0);
assert_header_err(header, ParseError::NotExpected);
// Test that an unexpected network protocol type is rejected.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.ptype[..], 0);
assert_header_err(header, ParseError::NotExpected);
// Test that an incorrect hardware address len is rejected.
let mut header = new_header();
header.hlen = 7;
assert_header_err(header, ParseError::Format);
// Test that an incorrect protocol address len is rejected.
let mut header = new_header();
header.plen = 5;
assert_header_err(header, ParseError::Format);
// Test that an invalid operation is rejected.
let mut header = new_header();
NetworkEndian::write_u16(&mut header.oper[..], 3);
assert_header_err(header, ParseError::Format);
}
#[test]
fn test_serialize_zeroes() {
// Test that ArpPacket::serialize properly zeroes memory before
// serializing the packet.
let mut buf_0 = [0; 28];
InnerPacketBuilder::serialize(
ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
&mut buf_0[..],
);
let mut buf_1 = [0xFF; 28];
InnerPacketBuilder::serialize(
ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
&mut buf_1[..],
);
assert_eq!(buf_0, buf_1);
}
#[test]
#[should_panic]
fn test_serialize_panic_insufficient_packet_space() {
// Test that a buffer which doesn't leave enough room for the packet is
// rejected.
InnerPacketBuilder::serialize(
ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
&mut [0; 27],
);
}
}