blob: aeee6b2fb259cbd8122d7c970331c65cfc4ea7dd [file] [log] [blame]
// Copyright 2019 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.
//! Utilities for parsing and serializing PPP packets.
//!
//! Currently supports parsing and serialization of LCP, IPCP, and IPV6CP packets and their
//! configuration options.
#![deny(missing_docs)]
pub mod ipv4;
pub mod ipv6;
pub mod link;
pub mod records;
use {
byteorder::NetworkEndian,
packet::{
BufferView, BufferViewMut, PacketBuilder, PacketConstraints, ParsablePacket, ParseMetadata,
SerializeBuffer,
},
std::convert::TryInto,
thiserror::Error,
zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned, U16, U32},
};
/// The type of error that occurred while attempting to parse a packet.
#[derive(Error, Debug, PartialEq)]
pub enum ParseError {
/// Too few bytes for header.
#[error("Too few bytes for header.")]
InsufficientHeaderBytes,
/// Too few bytes for body (per header).
#[error("Too few bytes for body (per header).")]
InsufficientBodyBytes,
/// Too many bytes for body (per header).
#[error("Too many bytes for body (per header).")]
ExcessBodyBytes,
}
#[derive(FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct PppHeader {
protocol: U16<NetworkEndian>,
}
impl PppHeader {
pub fn protocol(&self) -> u16 {
self.protocol.get()
}
}
/// Wrapper around a parsed on-the-wire PPP header and the rest of the packet.
pub struct PppPacket<B> {
header: LayoutVerified<B, PppHeader>,
body: B,
}
impl<B: ByteSlice> PppPacket<B> {
/// Extract the protocol from the wire format.
pub fn protocol(&self) -> u16 {
self.header.protocol()
}
}
impl<B: ByteSlice> ParsablePacket<B, ()> for PppPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(self.header.bytes().len(), self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, Self::Error> {
let header = buffer
.take_obj_front::<PppHeader>()
.ok_or_else(|| ParseError::InsufficientHeaderBytes)?;
Ok(Self { header, body: buffer.into_rest() })
}
}
/// Builder for a PPP packet.
pub struct PppPacketBuilder {
protocol: u16,
}
impl PppPacketBuilder {
/// Construct with the given protocol.
pub fn new(protocol: u16) -> Self {
Self { protocol }
}
}
impl PacketBuilder for PppPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(std::mem::size_of::<PppHeader>(), 0, 0, usize::max_value())
}
fn serialize(&self, buffer: &mut SerializeBuffer<'_>) {
let (mut header, _, _) = buffer.parts();
let mut header = &mut header;
let mut header = header.take_obj_front_zero::<PppHeader>().unwrap();
header.protocol = U16::new(self.protocol);
}
}
#[derive(FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct ControlProtocolHeader {
code: u8,
identifier: u8,
length: U16<NetworkEndian>,
}
impl ControlProtocolHeader {
pub fn code(&self) -> u8 {
self.code
}
pub fn identifier(&self) -> u8 {
self.identifier
}
pub fn length(&self) -> u16 {
self.length.get()
}
}
/// Wrapper around a parsed on-the-wire control protocol header and the rest of the packet.
pub struct ControlProtocolPacket<B> {
header: LayoutVerified<B, ControlProtocolHeader>,
body: B,
}
impl<B: ByteSlice> ControlProtocolPacket<B> {
/// Extract the code from the wire format.
pub fn code(&self) -> u8 {
self.header.code()
}
/// Extract the identifier from the wire format.
pub fn identifier(&self) -> u8 {
self.header.identifier()
}
}
impl<B: ByteSlice> ParsablePacket<B, ()> for ControlProtocolPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(self.header.bytes().len(), self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, Self::Error> {
let header = buffer
.take_obj_front::<ControlProtocolHeader>()
.ok_or(ParseError::InsufficientHeaderBytes)?;
let body_length = (header.length() as usize)
.checked_sub(header.bytes().len())
.ok_or(ParseError::InsufficientBodyBytes)?;
let padding = buffer.len().checked_sub(body_length).ok_or(ParseError::ExcessBodyBytes)?;
// We did the necessary bounds check above for this to be safe. `parse`
// is required to consume this padding from the suffix to maintain the
// body invariant for encapsulated packets.
buffer.take_back(padding).unwrap();
Ok(Self { header, body: buffer.into_rest() })
}
}
/// Builder for a control protocol packet.
pub struct ControlProtocolPacketBuilder {
code: u8,
identifier: u8,
}
impl ControlProtocolPacketBuilder {
/// Construct with the given code and identifier.
pub fn new(code: u8, identifier: u8) -> Self {
Self { code, identifier }
}
}
impl PacketBuilder for ControlProtocolPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(
std::mem::size_of::<ControlProtocolHeader>(),
0,
0,
usize::max_value(),
)
}
fn serialize(&self, buffer: &mut SerializeBuffer<'_>) {
let (mut header, body, _) = buffer.parts();
let mut header = &mut header;
let mut header = header.take_obj_front_zero::<ControlProtocolHeader>().unwrap();
let length = body
.len()
.try_into()
.ok()
.and_then(|c: u16| c.checked_add(self.constraints().header_len() as u16))
.unwrap();
header.code = self.code;
header.identifier = self.identifier;
header.length = U16::new(length);
}
}
/// Wrapper around a parsed on-the-wire configuration packet header and the rest of the packet.
pub struct ConfigurationPacket<B> {
body: B,
}
impl<B: ByteSlice> ParsablePacket<B, ()> for ConfigurationPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(0, self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(buffer: BV, _args: ()) -> Result<Self, Self::Error> {
Ok(Self { body: buffer.into_rest() })
}
}
/// Builder for a configuration packet.
#[derive(Default)]
pub struct ConfigurationPacketBuilder;
impl ConfigurationPacketBuilder {
/// Construct.
pub fn new() -> Self {
Self {}
}
}
impl PacketBuilder for ConfigurationPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(0, 0, 0, usize::max_value())
}
fn serialize(&self, _buffer: &mut SerializeBuffer<'_>) {}
}
/// Wrapper around a parsed on-the-wire termination packet header and the rest of the packet.
pub struct TerminationPacket<B> {
body: B,
}
impl<B: ByteSlice> ParsablePacket<B, ()> for TerminationPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(0, self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(buffer: BV, _args: ()) -> Result<Self, Self::Error> {
Ok(Self { body: buffer.into_rest() })
}
}
/// Builder for a termination packet.
#[derive(Default)]
pub struct TerminationPacketBuilder;
impl TerminationPacketBuilder {
/// Construct.
pub fn new() -> Self {
Self {}
}
}
impl PacketBuilder for TerminationPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(0, 0, 0, usize::max_value())
}
fn serialize(&self, _buffer: &mut SerializeBuffer<'_>) {}
}
/// Wrapper around a parsed on-the-wire code reject packet header and the rest of the packet.
pub struct CodeRejectPacket<B> {
body: B,
}
impl<B: ByteSlice> ParsablePacket<B, ()> for CodeRejectPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(0, self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(buffer: BV, _args: ()) -> Result<Self, Self::Error> {
Ok(Self { body: buffer.into_rest() })
}
}
/// Builder for a code reject packet.
#[derive(Default)]
pub struct CodeRejectPacketBuilder;
impl CodeRejectPacketBuilder {
/// Construct.
pub fn new() -> Self {
Self {}
}
}
impl PacketBuilder for CodeRejectPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(0, 0, 0, usize::max_value())
}
fn serialize(&self, _buffer: &mut SerializeBuffer<'_>) {}
}
#[derive(FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct ProtocolRejectHeader {
rejected_protocol: U16<NetworkEndian>,
}
impl ProtocolRejectHeader {
pub fn rejected_protocol(&self) -> u16 {
self.rejected_protocol.get()
}
}
/// Wrapper around a parsed on-the-wire protocol reject packet header and the rest of the packet.
pub struct ProtocolRejectPacket<B> {
header: LayoutVerified<B, ProtocolRejectHeader>,
body: B,
}
impl<B: ByteSlice> ProtocolRejectPacket<B> {
/// Extract the rejected protocol from the wire format.
pub fn rejected_protocol(&self) -> u16 {
self.header.rejected_protocol()
}
}
impl<B: ByteSlice> ParsablePacket<B, ()> for ProtocolRejectPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(self.header.bytes().len(), self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, Self::Error> {
let header = buffer
.take_obj_front::<ProtocolRejectHeader>()
.ok_or(ParseError::InsufficientHeaderBytes)?;
Ok(Self { header, body: buffer.into_rest() })
}
}
/// Builder for a protocol reject packet.
pub struct ProtocolRejectPacketBuilder {
rejected_protocol: u16,
}
impl ProtocolRejectPacketBuilder {
/// Construct with a rejected protocol.
pub fn new(rejected_protocol: u16) -> Self {
Self { rejected_protocol }
}
}
impl PacketBuilder for ProtocolRejectPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(
std::mem::size_of::<ProtocolRejectHeader>(),
0,
0,
usize::max_value(),
)
}
fn serialize(&self, buffer: &mut SerializeBuffer<'_>) {
let (mut header, _, _) = buffer.parts();
let mut header = &mut header;
let mut header = header.take_obj_front_zero::<ProtocolRejectHeader>().unwrap();
header.rejected_protocol = U16::new(self.rejected_protocol);
}
}
#[derive(FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct EchoDiscardHeader {
magic_number: U32<NetworkEndian>,
}
impl EchoDiscardHeader {
pub fn magic_number(&self) -> u32 {
self.magic_number.get()
}
}
/// Wrapper around a parsed on-the-wire echo-discard packet header and the rest of the packet.
pub struct EchoDiscardPacket<B> {
header: LayoutVerified<B, EchoDiscardHeader>,
body: B,
}
impl<B: ByteSlice> EchoDiscardPacket<B> {
/// Extract the magic number from the wire format.
pub fn magic_number(&self) -> u32 {
self.header.magic_number()
}
}
impl<B: ByteSlice> ParsablePacket<B, ()> for EchoDiscardPacket<B> {
type Error = ParseError;
fn parse_metadata(&self) -> ParseMetadata {
ParseMetadata::from_packet(self.header.bytes().len(), self.body.len(), 0)
}
fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, Self::Error> {
let header = buffer
.take_obj_front::<EchoDiscardHeader>()
.ok_or(ParseError::InsufficientHeaderBytes)?;
Ok(Self { header, body: buffer.into_rest() })
}
}
/// Builder for an echo-discard packet.
pub struct EchoDiscardPacketBuilder {
magic_number: u32,
}
impl EchoDiscardPacketBuilder {
/// Construct with a magic number.
pub fn new(magic_number: u32) -> Self {
Self { magic_number }
}
}
impl PacketBuilder for EchoDiscardPacketBuilder {
fn constraints(&self) -> PacketConstraints {
PacketConstraints::new(std::mem::size_of::<EchoDiscardHeader>(), 0, 0, usize::max_value())
}
fn serialize(&self, buffer: &mut SerializeBuffer<'_>) {
let (mut header, _, _) = buffer.parts();
let mut header = &mut header;
let mut header = header.take_obj_front_zero::<EchoDiscardHeader>().unwrap();
header.magic_number = U32::new(self.magic_number);
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
ipv4, ipv6, link,
records::options::{Options, OptionsSerializer},
},
packet::{Buf, InnerPacketBuilder, ParseBuffer, Serializer},
std::sync::Once,
};
static START: Once = Once::new();
#[test]
fn test_link_parse_serialize() {
START.call_once(|| {
fuchsia_syslog::init().unwrap();
});
let expected_link_options = [
link::ControlOption::MaximumReceiveUnit(0x8888),
link::ControlOption::AddressControlFieldCompression,
link::ControlOption::QualityProtocol(0xc025, vec![0xab, 0xcd, 0xef]),
link::ControlOption::MagicNumber(0x01234567),
];
const EXPECT_BUFFER: [u8; 25] = [
0xc0, 0x21, 3, 0xe6, 0x00, 0x17, 1, 4, 0x05, 0xdc, 8, 2, 4, 7, 0xc0, 0x25, 0xab, 0xcd,
0xef, 5, 6, 0x01, 0x23, 0x45, 0x67,
];
let buf: [u8; 25] = [
0xc0, 0x21, 1, 0xe6, 0x00, 0x17, 1, 4, 0x88, 0x88, 8, 2, 4, 7, 0xc0, 0x25, 0xab, 0xcd,
0xef, 5, 6, 0x01, 0x23, 0x45, 0x67,
];
let mut buf = Buf::new(buf, ..);
let ppp = buf.parse::<PppPacket<_>>().unwrap();
let protocol = ppp.protocol();
assert_eq!(protocol, 0xc021);
let lcp = buf.parse::<ControlProtocolPacket<_>>().unwrap();
let code = lcp.code();
let identifier = lcp.identifier();
assert_eq!(code, 1);
assert_eq!(identifier, 0xe6);
let _configuration_packet = buf.parse::<ConfigurationPacket<_>>().unwrap();
let mut options: Vec<_> = Options::<_, link::ControlOptionsImpl>::parse(buf.as_ref())
.map(|options| options.iter().collect())
.unwrap();
assert_eq!(options, expected_link_options);
if let link::ControlOption::MaximumReceiveUnit(ref mut mru) = options[0] {
*mru = 0x05dc;
};
let buffer = OptionsSerializer::<link::ControlOptionsImpl, link::ControlOption, _>::new(
options.iter(),
)
.into_serializer()
.encapsulate(ControlProtocolPacketBuilder::new(3, identifier))
.encapsulate(PppPacketBuilder::new(protocol))
.serialize_vec_outer()
.ok()
.unwrap();
assert_eq!(buffer.as_ref(), EXPECT_BUFFER);
}
#[test]
fn test_ipv4_parse_serialize() {
START.call_once(|| {
fuchsia_syslog::init().unwrap();
});
let expected_ipv4_options = [
ipv4::ControlOption::Unrecognized(0xa, vec![0x01, 0x02, 0x03]),
ipv4::ControlOption::IpAddress(0xffee_ddcc),
ipv4::ControlOption::IpCompressionProtocol(0x002d, vec![0x89]),
];
const EXPECT_BUFFER: [u8; 22] = [
0x80, 0x21, 4, 0x03, 0x00, 0x14, 0xa, 5, 0x01, 0x02, 0x03, 0x3, 6, 0xaa, 0xbb, 0xcc,
0xdd, 0x2, 5, 0x00, 0x2d, 0x89,
];
let buf: [u8; 22] = [
0x80, 0x21, 1, 0x03, 0x00, 0x14, 0xa, 5, 0x01, 0x02, 0x03, 0x3, 6, 0xff, 0xee, 0xdd,
0xcc, 0x2, 5, 0x00, 0x2d, 0x89,
];
let mut buf = Buf::new(buf, ..);
let ppp = buf.parse::<PppPacket<_>>().unwrap();
let protocol = ppp.protocol();
assert_eq!(protocol, 0x8021);
let lcp = buf.parse::<ControlProtocolPacket<_>>().unwrap();
let code = lcp.code();
let identifier = lcp.identifier();
assert_eq!(code, 1);
assert_eq!(identifier, 0x03);
let _configuration_packet = buf.parse::<ConfigurationPacket<_>>().unwrap();
let mut options: Vec<_> = Options::<_, ipv4::ControlOptionsImpl>::parse(buf.as_ref())
.map(|options| options.iter().collect())
.unwrap();
assert_eq!(options, expected_ipv4_options);
if let ipv4::ControlOption::IpAddress(ref mut address) = options[1] {
*address = 0xaabbccdd;
};
let buffer = OptionsSerializer::<ipv4::ControlOptionsImpl, ipv4::ControlOption, _>::new(
options.iter(),
)
.into_serializer()
.encapsulate(ControlProtocolPacketBuilder::new(4, identifier))
.encapsulate(PppPacketBuilder::new(protocol))
.serialize_vec_outer()
.ok()
.unwrap();
assert_eq!(buffer.as_ref(), EXPECT_BUFFER);
}
#[test]
fn test_ipv6_parse_serialize() {
START.call_once(|| {
fuchsia_syslog::init().unwrap();
});
let expected_ipv6_options = [
ipv6::ControlOption::Unrecognized(0x5, vec![0x99]),
ipv6::ControlOption::InterfaceIdentifier(0x0123_4567_890a_bcde),
];
const EXPECT_BUFFER: [u8; 21] = [
0x80, 0x57, 2, 0x09, 0x00, 0x13, 0x5, 5, 0x99, 0x88, 0x77, 0x01, 10, 0x01, 0x23, 0x45,
0x67, 0x89, 0x0a, 0xbc, 0xde,
];
let buf: [u8; 19] = [
0x80, 0x57, 1, 0x09, 0x00, 0x11, 0x5, 3, 0x99, 0x01, 10, 0x01, 0x23, 0x45, 0x67, 0x89,
0x0a, 0xbc, 0xde,
];
let mut buf = Buf::new(buf, ..);
let ppp = buf.parse::<PppPacket<_>>().unwrap();
let protocol = ppp.protocol();
assert_eq!(protocol, 0x8057);
let lcp = buf.parse::<ControlProtocolPacket<_>>().unwrap();
let code = lcp.code();
let identifier = lcp.identifier();
assert_eq!(code, 1);
assert_eq!(identifier, 0x09);
let _configuration_packet = buf.parse::<ConfigurationPacket<_>>().unwrap();
let mut options: Vec<_> = Options::<_, ipv6::ControlOptionsImpl>::parse(buf.as_ref())
.map(|options| options.iter().collect())
.unwrap();
assert_eq!(options, expected_ipv6_options);
if let ipv6::ControlOption::Unrecognized(ref mut _type, ref mut data) = options[0] {
data.push(0x88);
data.push(0x77);
};
let buffer = OptionsSerializer::<ipv6::ControlOptionsImpl, ipv6::ControlOption, _>::new(
options.iter(),
)
.into_serializer()
.encapsulate(ControlProtocolPacketBuilder::new(2, identifier))
.encapsulate(PppPacketBuilder::new(protocol))
.serialize_vec_outer()
.ok()
.unwrap();
assert_eq!(buffer.as_ref(), EXPECT_BUFFER);
}
}