blob: 768407e0a89af0a7abef916abe3ff60781ca741c [file] [log] [blame]
pub use self::checksum::*;
pub use self::options::*;
pub use self::serialize::*;
mod checksum {
use byteorder::{BigEndian, ByteOrder};
/// A checksum used by IPv4 and TCP.
///
/// This checksum operates by computing the 1s complement sum of successive
/// 16-bit words of the input.
pub struct Checksum(u32);
impl Checksum {
/// Initialize a new checksum.
pub fn new() -> Self {
Checksum(0)
}
/// Add bytes to the checksum.
///
/// If `bytes` does not contain an even number of bytes, a single zero byte
/// will be added to the end before updating the checksum.
pub fn add_bytes(&mut self, mut bytes: &[u8]) {
while bytes.len() > 1 {
self.0 += u32::from(BigEndian::read_u16(bytes));
bytes = &bytes[2..];
}
if bytes.len() == 1 {
self.0 += u32::from(BigEndian::read_u16(&[bytes[0], 0]));
}
}
/// Compute the checksum.
///
/// `sum` returns the checksum of all data added using `add_bytes` so far.
/// Calling `sum` does *not* reset the checksum. More bytes may be added
/// after calling `sum`, and they will be added to the checksum as expected.
pub fn sum(&self) -> u16 {
let mut sum = self.0;
while (sum >> 16) != 0 {
sum = (sum >> 16) + (sum & 0xFF);
}
!sum as u16
}
}
/// Checksum bytes.
///
/// `checksum` is a shorthand for
///
/// ```rust
/// let mut c = Checksum::new();
/// c.add_bytes(bytes);
/// c.sum()
/// ```
pub fn checksum(bytes: &[u8]) -> u16 {
let mut c = Checksum::new();
c.add_bytes(bytes);
c.sum()
}
}
mod options {
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Deref;
use zerocopy::ByteSlice;
/// A parsed set of header options.
///
/// `Options` represents a parsed set of options from a TCP or IPv4 header.
pub struct Options<B, O> {
bytes: B,
_marker: PhantomData<O>,
}
/// An iterator over header options.
///
/// `OptionIter` is an iterator over packet header options stored in the
/// format used by IPv4 and TCP, where each option is either a single kind
/// byte or a kind byte, a length byte, and length - 2 data bytes.
///
/// In both IPv4 and TCP, the only single-byte options are End of Options
/// List (EOL) and No Operation (NOP), both of which can be handled
/// internally by OptionIter. Thus, the caller only needs to be able to
/// parse multi-byte options.
pub struct OptionIter<'a, O> {
bytes: &'a [u8],
idx: usize,
_marker: PhantomData<O>,
}
/// Errors returned from parsing options.
///
/// `OptionParseErr` is either `Internal`, which indicates that this module
/// encountered a malformed sequence of options (likely with a length field
/// larger than the remaining bytes in the options buffer), or `External`,
/// which indicates that the `OptionImpl::parse` callback returned an error.
#[derive(Debug)]
pub enum OptionParseErr<E> {
Internal,
External(E),
}
/// An implementation of an options parser.
///
/// `OptionImpl` provides functions to parse fixed- and variable-length
/// options. It is required in order to construct an `Options` or
/// `OptionIter`.
pub trait OptionImpl {
type Output;
type Error;
/// Parse an option.
///
/// `parse` takes a kind byte and variable-length data associated and
/// returns `Ok(Some(o))` if the option successfully parsed as `o`,
/// `Ok(None)` if the kind byte was unrecognized, and `Err(err)` if the
/// kind byte was recognized but `data` was malformed for that option
/// kind. `parse` is allowed to not recognize certain option kinds, as
/// the length field can still be used to safely skip over them.
///
/// `parse` must be deterministic, or else `Options::parse` cannot
/// guarantee that future iterations will not produce errors (and
/// panic).
fn parse(kind: u8, data: &[u8]) -> Result<Option<Self::Output>, Self::Error>;
}
impl<B, O> Options<B, O>
where
B: ByteSlice,
O: OptionImpl,
{
/// Parse a set of options.
///
/// `parse` parses `bytes` as a sequence of options. `parse` performs a
/// single pass over all of the options to verify that they are
/// well-formed. Once `parse` returns successfully, the resulting
/// `Options` can be used to construct infallible iterators.
pub fn parse(bytes: B) -> Result<Options<B, O>, OptionParseErr<O::Error>> {
// First, do a single pass over the bytes to detect any errors up
// front. Once this is done, since we have a reference to `bytes`,
// these bytes can't change out from under us, and so we can treat
// any iterator over these bytes as infallible. This makes a few
// assumptions, but none of them are that big of a deal. In all
// cases, breaking these assumptions would just result in a runtime
// panic.
// - B could return different bytes each time
// - O::parse could be non-deterministic
while next::<B, O>(&bytes, &mut 0)?.is_some() {}
Ok(Options {
bytes,
_marker: PhantomData,
})
}
}
impl<B: Deref<Target = [u8]>, O> Options<B, O> {
/// Get the underlying bytes.
///
/// `bytes` returns a reference to the byte slice backing this
/// `Options`.
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
}
impl<'a, B, O> Options<B, O>
where
B: 'a + ByteSlice,
O: OptionImpl,
{
/// Create an iterator over options.
///
/// `iter` constructs an iterator over the options. Since the options
/// were validated in `parse`, then so long as `from_kind` and
/// `from_data` are deterministic, the iterator is infallible.
pub fn iter(&'a self) -> OptionIter<'a, O> {
OptionIter {
bytes: &self.bytes,
idx: 0,
_marker: PhantomData,
}
}
}
impl<'a, O> Iterator for OptionIter<'a, O>
where
O: OptionImpl,
O::Error: Debug,
{
type Item = O::Output;
fn next(&mut self) -> Option<O::Output> {
next::<&'a [u8], O>(&self.bytes, &mut self.idx)
.expect("already-validated options should not fail to parse")
}
}
// End of Options List in both IPv4 and TCP
const END_OF_OPTIONS: u8 = 0;
// NOP in both IPv4 and TCP
const NOP: u8 = 1;
fn next<B, O>(bytes: &B, idx: &mut usize) -> Result<Option<O::Output>, OptionParseErr<O::Error>>
where
B: ByteSlice,
O: OptionImpl,
{
// For an explanation of this format, see the "Options" section of
// https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure
loop {
let bytes = &bytes[*idx..];
if bytes.is_empty() {
return Ok(None);
}
if bytes[0] == END_OF_OPTIONS {
return Ok(None);
}
if bytes[0] == NOP {
*idx += 1;
continue;
}
let len = bytes[1] as usize;
if len < 2 || len > bytes.len() {
return Err(OptionParseErr::Internal);
}
*idx += len;
match O::parse(bytes[0], &bytes[2..]) {
Ok(Some(o)) => return Ok(Some(o)),
Ok(None) => {}
Err(err) => return Err(OptionParseErr::External(err)),
}
}
}
}
mod serialize {
pub trait PacketFormat {
/// The maximum length of a packet header in bytes.
///
/// If `MAX_HEADER_BYTES` bytes are allocated in a buffer preceding a
/// payload, it is guaranteed that any header generated by this packet
/// format will be able to fit in the space preceding the payload.
const MAX_HEADER_BYTES: usize;
/// The maximum length of a packet footer in bytes.
///
/// If `MAX_FOOTER_BYTES` bytes are allocated in a buffer following a
/// payload, it is guaranteed that any footer generated by this packet
/// format will be able to fit in the space following the payload.
const MAX_FOOTER_BYTES: usize;
}
}