blob: 4f9545f92494aa9f7fc0938aa8f1a61f234ae4d6 [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 UDP packets.
#[cfg(test)]
use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroU16;
use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use error::ParseError;
use ip::{Ip, IpAddr, IpProto};
use wire::util::{fits_in_u16, fits_in_u32, BufferAndRange, Checksum};
// Header has the same memory layout (thanks to repr(C, packed)) as a UDP
// header. Thus, we can simply reinterpret the bytes of the UDP 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.
#[repr(C, packed)]
struct Header {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
/// The length of a UDP header in bytes.
///
/// When calling `UdpPacket::serialize`, provide at least `HEADER_LEN` bytes for
/// the header in order to guarantee that `serialize` will not panic.
pub const HEADER_LEN: usize = 8;
unsafe impl FromBytes for Header {}
unsafe impl AsBytes for Header {}
unsafe impl Unaligned for Header {}
impl Header {
fn src_port(&self) -> u16 {
NetworkEndian::read_u16(&self.src_port)
}
fn set_src_port(&mut self, src_port: u16) {
NetworkEndian::write_u16(&mut self.src_port, src_port);
}
fn dst_port(&self) -> u16 {
NetworkEndian::read_u16(&self.dst_port)
}
fn set_dst_port(&mut self, dst_port: u16) {
NetworkEndian::write_u16(&mut self.dst_port, dst_port);
}
fn length(&self) -> u16 {
NetworkEndian::read_u16(&self.length)
}
fn checksum(&self) -> u16 {
NetworkEndian::read_u16(&self.checksum)
}
}
/// A UDP packet.
///
/// A `UdpPacket` shares its underlying memory with the byte slice it was parsed
/// from or serialized to, meaning that no copying or extra allocation is
/// necessary.
///
/// A `UdpPacket` - whether parsed using `parse` or created using `serialize` -
/// maintains the invariant that the checksum is always valid.
pub struct UdpPacket<B> {
header: LayoutVerified<B, Header>,
body: B,
}
impl<B: ByteSlice> UdpPacket<B> {
/// Parse a UDP packet.
///
/// `parse` parses `bytes` as a UDP packet and validates the checksum. It
/// returns the byte range corresponding to the body within `bytes`. This
/// can be useful when extracting the encapsulated payload to send to
/// another layer of the stack.
///
/// `src_ip` is the source address in the IP header. In IPv4, `dst_ip` is
/// the destination address in the IPv4 header. In IPv6, it's more
/// complicated:
/// - If there's no routing header, the destination is the one in the IPv6
/// header.
/// - If there is a routing header, then the sender will compute the
/// checksum using the last address in the routing header, while the
/// receiver will compute the checksum using the destination address in
/// the IPv6 header.
pub fn parse<A: IpAddr>(
bytes: B, src_ip: A, dst_ip: A,
) -> Result<(UdpPacket<B>, Range<usize>), ParseError> {
// See for details: https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure
let bytes_len = bytes.len();
let (header, body) = LayoutVerified::<B, Header>::new_unaligned_from_prefix(bytes)
.ok_or(ParseError::Format)?;
let packet = UdpPacket { header, body };
let len = if packet.header.length() == 0 && A::Version::VERSION.is_v6() {
// IPv6 supports jumbograms, so a UDP packet may be greater than
// 2^16 bytes in size. In this case, the size doesn't fit in the
// 16-bit length field in the header, and so the length field is set
// to zero to indicate this.
bytes_len
} else {
packet.header.length() as usize
};
if len != bytes_len {
return Err(ParseError::Format);
}
if packet.header.dst_port() == 0 {
return Err(ParseError::Format);
}
// A 0 checksum indicates that the checksum wasn't computed. In IPv4,
// this means that it shouldn't be validated. In IPv6, the checksum is
// mandatory, so this is an error.
if packet.header.checksum != [0, 0] {
// When computing the checksum, a checksum of 0 is sent as 0xFFFF.
let target = if packet.header.checksum == [0xFF, 0xFF] {
0
} else {
NetworkEndian::read_u16(&packet.header.checksum)
};
if packet
.compute_checksum(src_ip, dst_ip)
.ok_or(ParseError::Format)? != target
{
return Err(ParseError::Checksum);
}
} else if A::Version::VERSION.is_v6() {
return Err(ParseError::Checksum);
}
let hdr_len = packet.header.bytes().len();
let total_len = hdr_len + packet.body.len();
Ok((packet, hdr_len..total_len))
}
}
impl<B: ByteSlice> UdpPacket<B> {
// Compute the UDP checksum, skipping the checksum field itself. Returns
// None if the packet size is too large.
fn compute_checksum<A: IpAddr>(&self, src_ip: A, dst_ip: A) -> Option<u16> {
// See for details: https://en.wikipedia.org/wiki/User_Datagram_Protocol#Checksum_computation
let mut c = Checksum::new();
c.add_bytes(src_ip.bytes());
c.add_bytes(dst_ip.bytes());
if A::Version::VERSION.is_v4() {
c.add_bytes(&[0, IpProto::Udp as u8]);
c.add_bytes(&self.header.length);
} else {
let len = self.total_packet_len();
// For IPv6, the "UDP length" field in the pseudo-header is 32 bits.
if !fits_in_u32(len) {
return None;
}
let mut len_bytes = [0; 4];
NetworkEndian::write_u32(&mut len_bytes, len as u32);
c.add_bytes(&len_bytes);
c.add_bytes(&[0, 0, 0, IpProto::Udp as u8]);
}
c.add_bytes(&self.header.src_port);
c.add_bytes(&self.header.dst_port);
c.add_bytes(&self.header.length);
c.add_bytes(&self.body);
Some(c.checksum())
}
/// The packet body.
pub fn body(&self) -> &[u8] {
self.body.deref()
}
/// The source UDP port, if any.
///
/// The source port is optional, and may have been omitted by the sender.
pub fn src_port(&self) -> Option<NonZeroU16> {
NonZeroU16::new(self.header.src_port())
}
/// The destination UDP port.
pub fn dst_port(&self) -> NonZeroU16 {
NonZeroU16::new(self.header.dst_port()).unwrap()
}
/// Did this packet have a checksum?
///
/// On IPv4, the sender may optionally omit the checksum. If this function
/// returns false, the sender ommitted the checksum, and `parse` will not
/// have validated it.
///
/// On IPv6, it is guaranteed that `checksummed` will return true because
/// IPv6 requires a checksum, and so any UDP packet missing one will fail
/// validation in `parse`.
pub fn checksummed(&self) -> bool {
self.header.checksum() != 0
}
// The length of the header.
fn header_len(&self) -> usize {
self.header.bytes().len()
}
// The length of the packet as calculated from the header and body. This is
// not the same as the length field in the header.
fn total_packet_len(&self) -> usize {
self.header_len() + self.body.len()
}
}
// NOTE(joshlf): In order to ensure that the checksum is always valid, we don't
// expose any setters for the fields of the UDP packet; the only way to set them
// is via UdpPacket::serialize. This, combined with checksum validation performed
// in UdpPacket::parse, provides the invariant that a UdpPacket always has a
// valid checksum.
impl<B> UdpPacket<B>
where
B: AsMut<[u8]>,
{
/// Serialize a UDP packet in an existing buffer.
///
/// `serialize` creates a `UdpPacket` which uses the provided `buffer` for
/// its storage, initializing all header fields and calculating the
/// checksum. It treats `buffer.range()` as the packet body. It uses the
/// last bytes of `buffer` before the body to store the header, and returns
/// a new `BufferAndRange` with a range equal to the bytes of the UDP packet
/// (including the header). This range can be used to indicate the range for
/// encapsulation in another packet.
///
/// # Examples
///
/// ```rust
/// let mut buffer = [0u8; 1024];
/// (&mut buffer[512..]).copy_from_slice(body);
/// let buffer = UdpPacket::serialize(
/// BufferAndRange::new(&mut buffer[..], 512..),
/// src_ip,
/// dst_ip,
/// src_port,
/// dst_port,
/// );
/// send_ip_packet(dst_ip, buffer);
/// ```
///
/// # Panics
///
/// `serialize` panics if there is insufficient room preceding the body to
/// store the UDP header. The caller can guarantee that there will be enough
/// room by providing at least `MAX_HEADER_LEN` pre-body bytes.
pub fn serialize<A: IpAddr>(
mut buffer: BufferAndRange<B>, src_ip: A, dst_ip: A, src_port: Option<NonZeroU16>,
dst_port: NonZeroU16,
) -> BufferAndRange<B> {
// See for details: https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure
let extend_backwards = {
let (header, body, _) = buffer.parts_mut();
// SECURITY: Use _zeroed constructor to ensure we zero memory to prevent
// leaking information from packets previously stored in this buffer.
let (_, header) = LayoutVerified::<_, Header>::new_unaligned_from_suffix_zeroed(header)
.expect("too few bytes for UDP header");
let mut packet = UdpPacket { header, body };
packet
.header
.set_src_port(src_port.map(|port| port.get()).unwrap_or(0));
packet.header.set_dst_port(dst_port.get());
let total_len = packet.total_packet_len();
let len_field = if fits_in_u16(total_len) {
total_len as u16
} else if A::Version::VERSION.is_v6() {
// IPv6 supports jumbograms, so a UDP packet may be greater than
// 2^16 bytes in size. In this case, the size doesn't fit in the
// 16-bit length field in the header, and so the length field is set
// to zero to indicate this.
0u16
} else {
panic!(
"total UDP packet length of {} bytes overflows 16-bit length field of UDP \
header",
total_len
);
};
NetworkEndian::write_u16(&mut packet.header.length, len_field);
// This ignores the checksum field in the header, so it's fine that we
// haven't set it yet, and so it could be filled with arbitrary bytes.
let c = packet.compute_checksum(src_ip, dst_ip).expect(&format!(
"total UDP packet length of {} bytes overflow 32-bit length field of pseudo-header",
total_len
));
NetworkEndian::write_u16(
&mut packet.header.checksum,
if c == 0 {
// When computing the checksum, a checksum of 0 is sent as 0xFFFF.
0xFFFF
} else {
c
},
);
packet.header_len()
};
buffer.extend_backwards(extend_backwards);
buffer
}
}
// needed by Result::unwrap_err in the tests below
#[cfg(test)]
impl<B> Debug for UdpPacket<B> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "UdpPacket")
}
}
#[cfg(test)]
mod tests {
use device::ethernet::EtherType;
use ip::{Ipv4Addr, Ipv6Addr};
use wire::ethernet::EthernetFrame;
use wire::ipv4::Ipv4Packet;
use super::*;
const TEST_SRC_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const TEST_DST_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
const TEST_SRC_IPV6: Ipv6Addr =
Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
const TEST_DST_IPV6: Ipv6Addr = Ipv6Addr::new([
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
]);
#[test]
fn test_parse_full() {
use wire::testdata::dns_request::*;
let (frame, body_range) = EthernetFrame::parse(ETHERNET_FRAME_BYTES).unwrap();
assert_eq!(body_range, ETHERNET_BODY_RANGE);
assert_eq!(frame.src_mac(), ETHERNET_SRC_MAC);
assert_eq!(frame.dst_mac(), ETHERNET_DST_MAC);
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Ipv4)));
let (packet, body_range) = Ipv4Packet::parse(frame.body()).unwrap();
assert_eq!(body_range, IP_BODY_RANGE);
assert_eq!(packet.proto(), Ok(IpProto::Udp));
assert_eq!(packet.dscp(), IP_DSCP);
assert_eq!(packet.ecn(), IP_ECN);
assert_eq!(packet.df_flag(), IP_DONT_FRAGMENT);
assert_eq!(packet.mf_flag(), IP_MORE_FRAGMENTS);
assert_eq!(packet.fragment_offset(), IP_FRAGMENT_OFFSET);
assert_eq!(packet.id(), IP_ID);
assert_eq!(packet.ttl(), IP_TTL);
assert_eq!(packet.src_ip(), IP_SRC_IP);
assert_eq!(packet.dst_ip(), IP_DST_IP);
let (packet, body_range) =
UdpPacket::parse(packet.body(), packet.src_ip(), packet.dst_ip()).unwrap();
assert_eq!(body_range, UDP_BODY_RANGE);
assert_eq!(
packet.src_port().map(|p| p.get()).unwrap_or(0),
UDP_SRC_PORT
);
assert_eq!(packet.dst_port().get(), UDP_DST_PORT);
assert_eq!(packet.body(), UDP_BODY);
}
#[test]
fn test_parse() {
// source port of 0 (meaning none) is allowed, as is a missing checksum
let buf = [0, 0, 1, 2, 0, 8, 0, 0];
let (packet, body_range) =
UdpPacket::parse(&buf[..], TEST_SRC_IPV4, TEST_DST_IPV4).unwrap();
assert_eq!(body_range, 8..8);
assert!(packet.src_port().is_none());
assert_eq!(packet.dst_port().get(), NetworkEndian::read_u16(&[1, 2]));
assert!(!packet.checksummed());
assert!(packet.body().is_empty());
// length of 0 is allowed in IPv6
let buf = [0, 0, 1, 2, 0, 0, 0xFD, 0xD3];
let (packet, body_range) =
UdpPacket::parse(&buf[..], TEST_SRC_IPV6, TEST_DST_IPV6).unwrap();
assert_eq!(body_range, 8..8);
assert!(packet.src_port().is_none());
assert_eq!(packet.dst_port().get(), NetworkEndian::read_u16(&[1, 2]));
assert!(packet.checksummed());
assert!(packet.body().is_empty());
}
#[test]
fn test_serialize() {
let mut buf = [0; 8];
{
let buffer = UdpPacket::serialize(
BufferAndRange::new(&mut buf, 8..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
NonZeroU16::new(1),
NonZeroU16::new(2).unwrap(),
);
assert_eq!(buffer.range(), 0..8);
}
// assert that we get the literal bytes we expected
assert_eq!(buf, [0, 1, 0, 2, 0, 8, 239, 199]);
let (packet, _) = UdpPacket::parse(&buf[..], TEST_SRC_IPV4, TEST_DST_IPV4).unwrap();
// assert that when we parse those bytes, we get the values we set in
// the builder
assert_eq!(packet.src_port().unwrap().get(), 1);
assert_eq!(packet.dst_port().get(), 2);
assert!(packet.checksummed());
}
#[test]
fn test_serialize_zeroes() {
// Test that UdpPacket::serialize properly zeroes memory before serializing
// the header.
let mut buf_0 = [0; 8];
UdpPacket::serialize(
BufferAndRange::new(&mut buf_0[..], 8..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
NonZeroU16::new(1),
NonZeroU16::new(2).unwrap(),
);
let mut buf_1 = [0xFF; 8];
UdpPacket::serialize(
BufferAndRange::new(&mut buf_1[..], 8..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
NonZeroU16::new(1),
NonZeroU16::new(2).unwrap(),
);
assert_eq!(buf_0, buf_1);
}
#[test]
fn test_parse_fail() {
// Test that while a given byte pattern optionally succeeds, zeroing out
// certain bytes causes failure. `zero` is a list of byte indices to
// zero out that should cause failure.
fn test_zero<I: IpAddr>(src: I, dst: I, succeeds: bool, zero: &[usize], err: ParseError) {
// Set checksum to zero so that, in IPV4, it will be ignored. In
// IPv6, this /is/ the test.
let mut buf = [1, 2, 3, 4, 0, 8, 0, 0];
if succeeds {
assert!(UdpPacket::parse(&buf[..], src, dst).is_ok());
}
for idx in zero {
buf[*idx] = 0;
}
assert_eq!(UdpPacket::parse(&buf[..], src, dst).unwrap_err(), err);
}
// destination port of 0 is disallowed
test_zero(
TEST_SRC_IPV4,
TEST_DST_IPV4,
true,
&[2, 3],
ParseError::Format,
);
// length of 0 is disallowed in IPv4
test_zero(
TEST_SRC_IPV4,
TEST_DST_IPV4,
true,
&[4, 5],
ParseError::Format,
);
// missing checksum is disallowed in IPv6; this won't succeed ahead of
// time because the checksum bytes are already zero
test_zero(
TEST_SRC_IPV6,
TEST_DST_IPV6,
false,
&[],
ParseError::Checksum,
);
// 2^32 overflows on 32-bit platforms
#[cfg(target_pointer_width = "64")]
{
// total length of 2^32 or greater is disallowed in IPv6
let mut buf = vec![0u8; 1 << 32];
(&mut buf[..8]).copy_from_slice(&[0, 0, 1, 2, 0, 0, 0xFF, 0xE4]);
assert_eq!(
UdpPacket::parse(&buf[..], TEST_SRC_IPV6, TEST_DST_IPV6).unwrap_err(),
ParseError::Format
);
}
}
#[test]
#[should_panic]
fn test_serialize_fail_header_too_short() {
let mut buf = [0; 7];
UdpPacket::serialize(
BufferAndRange::new(&mut buf[..], 7..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
None,
NonZeroU16::new(1).unwrap(),
);
}
#[test]
#[should_panic]
fn test_serialize_fail_packet_too_long_ipv4() {
let mut buf = [0; 1 << 16];
UdpPacket::serialize(
BufferAndRange::new(&mut buf[..], 8..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
None,
NonZeroU16::new(1).unwrap(),
);
}
#[test]
#[should_panic]
#[cfg(target_pointer_width = "64")] // 2^32 overflows on 32-bit platforms
fn test_serialize_fail_packet_too_long_ipv6() {
// total length of 2^32 or greater is disallowed in IPv6
let mut buf = vec![0u8; 1 << 32];
UdpPacket::serialize(
BufferAndRange::new(&mut buf[..], 8..),
TEST_SRC_IPV4,
TEST_DST_IPV4,
None,
NonZeroU16::new(1).unwrap(),
);
}
}