Module wire

Source
Expand description

Low-level packet access and construction.

§An overview over packet representations

The wire module deals with the packet representation. It provides three levels of functionality.

  • First, it provides functions to extract fields from sequences of octets, and to insert fields into sequences of octets. This happens in the lowercase structures e.g. ethernet_frame or udp_packet 1.
  • Second, it provides a compact, high-level representation of header data that can be created from parsing and emitted into a sequence of octets. This happens through the Repr family of structs and enums, e.g. ArpRepr or Ipv4Repr.
  • Third, it provides an type wrapper around sequences of octets valid as a particular packet format which potentially owns its data. This can memoize parts of the layout, avoiding re-calculating it on every access. While this restricts mutability of header data, fixed-length checksum fields and the payload can still be accessed normally. This happens in the uppercase Frame or Packet family of structs, e.g. ArpPacket or UdpPacket.

An important part is also the underlying trait for byte containers, Payload and PayloadMut. None of the standard reference traits accurately captures the relationship of a framing outer packet with its payload. It should be the case that the payload content changes only when accessed directly and changing its length should be possible. These two traits model such a relationship, providing a few methods to efficiently request layout changes of the payload from the container. This makes it possible to recursively parse packets while being able to resize the innermost packet or to insert additional data into an intermediate layer without mutating the payload.

The packet family of data structures guarantees that, if the packet::check_len() method returned Ok(()), then no field accessor or setter method will panic; however, the guarantee only hold while specific fields are mutated, which are listed in the documentation for the specific packet.

The owning Packet family makes a stronger guarantee. It only exposes fields for which mutation will not cause panics (some panics are still possible, read on), as long as Packet::new_checked constructor was used or the new_unchecked constructor with previously parsed data. Where such a mutation causes the layout to change (i.e. payload length) the underlying container is first asked to perform the necessary reframing and then dependent fields and offsets are updated. Note that this ties panicking to the container: A misbehaving implementation that implements the PayloadMut trait incorrectly or a fallible allocation could still cause panics.

The packet::new_unchecked method is a shorthand for combining new_unchecked and check_len while the Packet::new_checked method is a shorthand for a combination of Packet::new_unchecked and Repr::parse. When parsing untrusted input, it is necessary to use either of the checked methods; so long as the buffer is not modified, no accessor will fail. When emitting output, though, it is incorrect to use Packet::new_checked(); the length check is likely to succeed on a zeroed buffer, but fail on a buffer filled with data from a previous packet, such as when reusing buffers, resulting in nondeterministic panics with some network devices but not others. The buffer length for emission is often calculated by the Repr struct but not in general provided by the Packet layer.

In the Repr family of data structures, the Repr::parse() method never panics and the Repr::emit() method never panics as long as the underlying buffer is exactly Repr::buffer_len() octets long if provided.

§Examples

To emit an IP packet header into an octet buffer, and then parse it back:

use ethox::wire::{ip::v4, Checksum, ip::Protocol};
let repr = v4::Repr {
    src_addr:    v4::Address::new(10, 0, 0, 1),
    dst_addr:    v4::Address::new(10, 0, 0, 2),
    protocol:    Protocol::Tcp,
    payload_len: 10,
    hop_limit:   64
};
let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
{ // emission
    let packet = v4::packet::new_unchecked_mut(&mut buffer);
    repr.emit(packet, Checksum::Manual);
}
{ // parsing
    let packet = v4::packet::new_checked(&buffer)
        .expect("truncated packet");
    let parsed = v4::Repr::parse(packet, Checksum::Manual)
        .expect("malformed packet");
    assert_eq!(repr, parsed);
}

  1. The TCP structures differ since I haven’t gotten around to reworking them. It does not have a dynamically sized byte wrapper so its Packet implements this functionality as well but come with all downsides. In particular, its accessors may panic. 

Re-exports§

pub use self::pretty_print::PrettyPrinter;

Modules§

arp
ethernet
icmpv4
ip
pretty_print
Pretty-printing of packet representation.
tcp
udp

Structs§

Reframe
Groups parameters and utilities for payload reframing.
ReframePayload
Describes the resizing that keeps a payload intact.
payload
A dynamically sized type representing a packet payload.

Enums§

Checksum
Describes how to handle checksums.
Error
The error type for parsing of the network stack.
PayloadError
Error variants for resizing.

Traits§

Payload
A specialized, internal variant of Borrow<payload>.
PayloadMut
A specialized, internal variant of BorrowMut<payload>.
PayloadMutExt
Extends the mutable payload structures with new reorganization methods.

Type Aliases§

PayloadResult
The result type of a reframing operation on PayloadMut.
Result
The result type for the networking stack.